diff --git a/.cruft.json b/.cruft.json index 28789e3..85ede31 100644 --- a/.cruft.json +++ b/.cruft.json @@ -6,7 +6,7 @@ "cookiecutter": { "project_type": "package", "project_name": "RAGLite", - "project_description": "A Python package for Retrieval-Augmented Generation (RAG) with SQLite or PostgreSQL.", + "project_description": "A Python toolkit for Retrieval-Augmented Generation (RAG) with SQLite or PostgreSQL.", "project_url": "https://github.com/superlinear-ai/raglite", "author_name": "Laurent Sorber", "author_email": "laurent@superlinear.eu", @@ -26,4 +26,4 @@ } }, "directory": null -} +} \ No newline at end of file diff --git a/.gitignore b/.gitignore index e2d2da3..b23bdc9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,8 @@ +# Chainlit +.chainlit/ +.files/ +chainlit.md + # Coverage.py htmlcov/ reports/ @@ -19,7 +24,7 @@ data/ # dotenv .env -# Rerankers +# rerankers .*_cache/ # Hypothesis @@ -57,6 +62,10 @@ dist/ __pycache__/ *.py[cdo] +# RAGLite +*.db +*.sqlite + # Ruff .ruff_cache/ diff --git a/README.md b/README.md index 853a5f4..2cf9e4c 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ # 🥤 RAGLite -RAGLite is a Python package for Retrieval-Augmented Generation (RAG) with PostgreSQL or SQLite. +RAGLite is a Python toolkit for Retrieval-Augmented Generation (RAG) with PostgreSQL or SQLite. ## Features @@ -27,6 +27,7 @@ RAGLite is a Python package for Retrieval-Augmented Generation (RAG) with Postgr ##### Extensible +- 💬 Optional customizable ChatGPT-like frontend for [web](https://docs.chainlit.io/deploy/copilot), [Slack](https://docs.chainlit.io/deploy/slack), and [Teams](https://docs.chainlit.io/deploy/teams) with [Chainlit](https://github.com/Chainlit/chainlit) - ✍️ Optional conversion of any input document to Markdown with [Pandoc](https://github.com/jgm/pandoc) - ✅ Optional evaluation of retrieval and generation performance with [Ragas](https://github.com/explodinggradients/ragas) @@ -60,6 +61,12 @@ Finally, install RAGLite with: pip install raglite ``` +To add support for a customizable ChatGPT-like frontend, use the `chainlit` extra: + +```sh +pip install raglite[chainlit] +``` + To add support for filetypes other than PDF, use the `pandoc` extra: ```sh @@ -81,6 +88,7 @@ pip install raglite[ragas] 3. [Searching and Retrieval-Augmented Generation (RAG)](#3-searching-and-retrieval-augmented-generation-rag) 4. [Computing and using an optimal query adapter](#4-computing-and-using-an-optimal-query-adapter) 5. [Evaluation of retrieval and generation](#5-evaluation-of-retrieval-and-generation) +6. [Serving a customizable ChatGPT-like frontend](#6-serving-a-customizable-chatgpt-like-frontend) ### 1. Configuring RAGLite @@ -166,9 +174,9 @@ from raglite import retrieve_chunks chunks_hybrid = retrieve_chunks(chunk_ids_hybrid, config=my_config) # Rerank chunks: -from raglite import rerank +from raglite import rerank_chunks -chunks_reranked = rerank(prompt, chunks_hybrid, config=my_config) +chunks_reranked = rerank_chunks(prompt, chunks_hybrid, config=my_config) # Answer questions with RAG: from raglite import rag @@ -208,6 +216,33 @@ answered_evals_df = answer_evals(num_evals=10, config=my_config) evaluation_df = evaluate(answered_evals_df, config=my_config) ``` +### 6. Serving a customizable ChatGPT-like frontend + +If you installed the `chainlit` extra, you can serve a customizable ChatGPT-like frontend with: + +```sh +raglite chainlit +``` + +The application is also deployable to [web](https://docs.chainlit.io/deploy/copilot), [Slack](https://docs.chainlit.io/deploy/slack), and [Teams](https://docs.chainlit.io/deploy/teams). + +You can specify the database URL, LLM, and embedder directly in the Chainlit frontend, or with the CLI as follows: + +```sh +raglite chainlit \ + --db_url sqlite:///raglite.sqlite \ + --llm llama-cpp-python/bartowski/Llama-3.2-3B-Instruct-GGUF/*Q4_K_M.gguf@4096 \ + --embedder llama-cpp-python/lm-kit/bge-m3-gguf/*F16.gguf +``` + +To use an API-based LLM, make sure to include your credentials in a `.env` file or supply them inline: + +```sh +OPENAI_API_KEY=sk-... raglite chainlit --llm gpt-4o-mini --embedder text-embedding-3-large +``` + +
+ ## Contributing
diff --git a/poetry.lock b/poetry.lock index 8f52e6d..31776eb 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,5 +1,16 @@ # This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. +[[package]] +name = "aiofiles" +version = "23.2.1" +description = "File support for asyncio." +optional = true +python-versions = ">=3.7" +files = [ + {file = "aiofiles-23.2.1-py3-none-any.whl", hash = "sha256:19297512c647d4b27a2cf7c34caa7e405c0d60b5560618a29a9fe027b18b0107"}, + {file = "aiofiles-23.2.1.tar.gz", hash = "sha256:84ec2218d8419404abcb9f0c02df3f34c6e0a68ed41072acfb1cef5cbc29051a"}, +] + [[package]] name = "aiohappyeyeballs" version = "2.3.7" @@ -250,6 +261,20 @@ files = [ {file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"}, ] +[[package]] +name = "asyncer" +version = "0.0.7" +description = "Asyncer, async and await, focused on developer experience." +optional = true +python-versions = ">=3.8" +files = [ + {file = "asyncer-0.0.7-py3-none-any.whl", hash = "sha256:f0d579d4f67c4ead52ede3a45c854f462cae569058a8a6a68a4ebccac1c335d8"}, + {file = "asyncer-0.0.7.tar.gz", hash = "sha256:d5e563fb0f56eb87b97257984703658a4f5bbdb52ff851b3e8ed864cc200b1d2"}, +] + +[package.dependencies] +anyio = ">=3.4.0,<5.0" + [[package]] name = "attrs" version = "24.2.0" @@ -283,6 +308,17 @@ files = [ [package.dependencies] cryptography = "*" +[[package]] +name = "bidict" +version = "0.23.1" +description = "The bidirectional mapping library for Python." +optional = true +python-versions = ">=3.8" +files = [ + {file = "bidict-0.23.1-py3-none-any.whl", hash = "sha256:5dae8d4d79b552a71cbabc7deb25dfe8ce710b17ff41711e13010ead2abfc3e5"}, + {file = "bidict-0.23.1.tar.gz", hash = "sha256:03069d763bc387bbd20e7d49914e75fc4132a41937fa3405417e1a5a2d006d71"}, +] + [[package]] name = "binaryornot" version = "0.4.4" @@ -455,6 +491,42 @@ files = [ {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, ] +[[package]] +name = "chainlit" +version = "1.2.0" +description = "Build Conversational AI." +optional = true +python-versions = "<4.0.0,>=3.9" +files = [ + {file = "chainlit-1.2.0-py3-none-any.whl", hash = "sha256:8eb7751e5c0d29559b32ba706d8abc31274ece37c5218bdf99ba6b5992ef2efb"}, + {file = "chainlit-1.2.0.tar.gz", hash = "sha256:3467b6e1740cb7a437e020496b07a7f433c777d7c8a241c5f45d981e29458f17"}, +] + +[package.dependencies] +aiofiles = ">=23.1.0,<24.0.0" +asyncer = ">=0.0.7,<0.0.8" +click = ">=8.1.3,<9.0.0" +dataclasses_json = ">=0.6.7,<0.7.0" +fastapi = ">=0.110.1,<0.113" +filetype = ">=1.2.0,<2.0.0" +httpx = ">=0.23.0" +lazify = ">=0.4.0,<0.5.0" +literalai = "0.0.607" +nest-asyncio = ">=1.6.0,<2.0.0" +numpy = ">=1.26,<2.0" +packaging = ">=23.1,<24.0" +pydantic = ">=1,<3" +pyjwt = ">=2.8.0,<3.0.0" +python-dotenv = ">=1.0.0,<2.0.0" +python-multipart = ">=0.0.9,<0.0.10" +python-socketio = ">=5.11.0,<6.0.0" +starlette = ">=0.37.2,<0.38.0" +syncer = ">=2.0.3,<3.0.0" +tomli = ">=2.0.1,<3.0.0" +uptrace = ">=1.22.0,<2.0.0" +uvicorn = ">=0.25.0,<0.26.0" +watchfiles = ">=0.20.0,<0.21.0" + [[package]] name = "chardet" version = "5.2.0" @@ -565,6 +637,17 @@ files = [ {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, ] +[[package]] +name = "chevron" +version = "0.14.0" +description = "Mustache templating language renderer" +optional = true +python-versions = "*" +files = [ + {file = "chevron-0.14.0-py3-none-any.whl", hash = "sha256:fbf996a709f8da2e745ef763f482ce2d311aa817d287593a5b990d6d6e4f0443"}, + {file = "chevron-0.14.0.tar.gz", hash = "sha256:87613aafdf6d77b6a90ff073165a61ae5086e21ad49057aa0e53681601800ebf"}, +] + [[package]] name = "click" version = "8.1.7" @@ -1091,6 +1174,23 @@ files = [ {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"}, ] +[[package]] +name = "deprecated" +version = "1.2.14" +description = "Python @deprecated decorator to deprecate old python classes, functions or methods." +optional = true +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "Deprecated-1.2.14-py2.py3-none-any.whl", hash = "sha256:6fac8b097794a90302bdbb17b9b815e732d3c4720583ff1b198499d78470466c"}, + {file = "Deprecated-1.2.14.tar.gz", hash = "sha256:e5323eb936458dccc2582dc6f9c322c852a775a27065ff2b0c4970b9d53d01b3"}, +] + +[package.dependencies] +wrapt = ">=1.10,<2" + +[package.extras] +dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "sphinx (<2)", "tox"] + [[package]] name = "dill" version = "0.3.8" @@ -1188,6 +1288,26 @@ files = [ [package.extras] tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich"] +[[package]] +name = "fastapi" +version = "0.112.4" +description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" +optional = true +python-versions = ">=3.8" +files = [ + {file = "fastapi-0.112.4-py3-none-any.whl", hash = "sha256:6d4f9c3301825d4620665cace8e2bc34e303f61c05a5382d1d61a048ea7f2f37"}, + {file = "fastapi-0.112.4.tar.gz", hash = "sha256:b1f72e1f72afe7902ccd639ba320abb5d57a309804f45c10ab0ce3693cadeb33"}, +] + +[package.dependencies] +pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0" +starlette = ">=0.37.2,<0.39.0" +typing-extensions = ">=4.8.0" + +[package.extras] +all = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.7)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] +standard = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "jinja2 (>=2.11.2)", "python-multipart (>=0.0.7)", "uvicorn[standard] (>=0.12.0)"] + [[package]] name = "filelock" version = "3.15.4" @@ -1204,6 +1324,17 @@ docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1 testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-asyncio (>=0.21)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)", "virtualenv (>=20.26.2)"] typing = ["typing-extensions (>=4.8)"] +[[package]] +name = "filetype" +version = "1.2.0" +description = "Infer file type and MIME type of any file/buffer. No external dependencies." +optional = true +python-versions = "*" +files = [ + {file = "filetype-1.2.0-py2.py3-none-any.whl", hash = "sha256:7ce71b6880181241cf7ac8697a2f1eb6a8bd9b429f7ad6d27b8db9ba5f1c2d25"}, + {file = "filetype-1.2.0.tar.gz", hash = "sha256:66b56cd6474bf41d8c54660347d37afcc3f7d1970648de365c102ef77548aadb"}, +] + [[package]] name = "flashrank" version = "0.2.9" @@ -1461,6 +1592,23 @@ gitdb = ">=4.0.1,<5" doc = ["sphinx (==4.3.2)", "sphinx-autodoc-typehints", "sphinx-rtd-theme", "sphinxcontrib-applehelp (>=1.0.2,<=1.0.4)", "sphinxcontrib-devhelp (==1.0.2)", "sphinxcontrib-htmlhelp (>=2.0.0,<=2.0.1)", "sphinxcontrib-qthelp (==1.0.3)", "sphinxcontrib-serializinghtml (==1.1.5)"] test = ["coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mock", "mypy", "pre-commit", "pytest (>=7.3.1)", "pytest-cov", "pytest-instafail", "pytest-mock", "pytest-sugar", "typing-extensions"] +[[package]] +name = "googleapis-common-protos" +version = "1.65.0" +description = "Common protobufs used in Google APIs" +optional = true +python-versions = ">=3.7" +files = [ + {file = "googleapis_common_protos-1.65.0-py2.py3-none-any.whl", hash = "sha256:2972e6c496f435b92590fd54045060867f3fe9be2c82ab148fc8885035479a63"}, + {file = "googleapis_common_protos-1.65.0.tar.gz", hash = "sha256:334a29d07cddc3aa01dee4988f9afd9b2916ee2ff49d6b757155dc0d197852c0"}, +] + +[package.dependencies] +protobuf = ">=3.20.2,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<6.0.0.dev0" + +[package.extras] +grpc = ["grpcio (>=1.44.0,<2.0.0.dev0)"] + [[package]] name = "greenlet" version = "3.0.3" @@ -1532,6 +1680,73 @@ files = [ docs = ["Sphinx", "furo"] test = ["objgraph", "psutil"] +[[package]] +name = "grpcio" +version = "1.67.0" +description = "HTTP/2-based RPC framework" +optional = true +python-versions = ">=3.8" +files = [ + {file = "grpcio-1.67.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:bd79929b3bb96b54df1296cd3bf4d2b770bd1df6c2bdf549b49bab286b925cdc"}, + {file = "grpcio-1.67.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:16724ffc956ea42967f5758c2f043faef43cb7e48a51948ab593570570d1e68b"}, + {file = "grpcio-1.67.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:2b7183c80b602b0ad816315d66f2fb7887614ead950416d60913a9a71c12560d"}, + {file = "grpcio-1.67.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:efe32b45dd6d118f5ea2e5deaed417d8a14976325c93812dd831908522b402c9"}, + {file = "grpcio-1.67.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe89295219b9c9e47780a0f1c75ca44211e706d1c598242249fe717af3385ec8"}, + {file = "grpcio-1.67.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:aa8d025fae1595a207b4e47c2e087cb88d47008494db258ac561c00877d4c8f8"}, + {file = "grpcio-1.67.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f95e15db43e75a534420e04822df91f645664bf4ad21dfaad7d51773c80e6bb4"}, + {file = "grpcio-1.67.0-cp310-cp310-win32.whl", hash = "sha256:a6b9a5c18863fd4b6624a42e2712103fb0f57799a3b29651c0e5b8119a519d65"}, + {file = "grpcio-1.67.0-cp310-cp310-win_amd64.whl", hash = "sha256:b6eb68493a05d38b426604e1dc93bfc0137c4157f7ab4fac5771fd9a104bbaa6"}, + {file = "grpcio-1.67.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:e91d154689639932305b6ea6f45c6e46bb51ecc8ea77c10ef25aa77f75443ad4"}, + {file = "grpcio-1.67.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:cb204a742997277da678611a809a8409657b1398aaeebf73b3d9563b7d154c13"}, + {file = "grpcio-1.67.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:ae6de510f670137e755eb2a74b04d1041e7210af2444103c8c95f193340d17ee"}, + {file = "grpcio-1.67.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:74b900566bdf68241118f2918d312d3bf554b2ce0b12b90178091ea7d0a17b3d"}, + {file = "grpcio-1.67.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4e95e43447a02aa603abcc6b5e727d093d161a869c83b073f50b9390ecf0fa8"}, + {file = "grpcio-1.67.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:0bb94e66cd8f0baf29bd3184b6aa09aeb1a660f9ec3d85da615c5003154bc2bf"}, + {file = "grpcio-1.67.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:82e5bd4b67b17c8c597273663794a6a46a45e44165b960517fe6d8a2f7f16d23"}, + {file = "grpcio-1.67.0-cp311-cp311-win32.whl", hash = "sha256:7fc1d2b9fd549264ae585026b266ac2db53735510a207381be509c315b4af4e8"}, + {file = "grpcio-1.67.0-cp311-cp311-win_amd64.whl", hash = "sha256:ac11ecb34a86b831239cc38245403a8de25037b448464f95c3315819e7519772"}, + {file = "grpcio-1.67.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:227316b5631260e0bef8a3ce04fa7db4cc81756fea1258b007950b6efc90c05d"}, + {file = "grpcio-1.67.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d90cfdafcf4b45a7a076e3e2a58e7bc3d59c698c4f6470b0bb13a4d869cf2273"}, + {file = "grpcio-1.67.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:77196216d5dd6f99af1c51e235af2dd339159f657280e65ce7e12c1a8feffd1d"}, + {file = "grpcio-1.67.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:15c05a26a0f7047f720da41dc49406b395c1470eef44ff7e2c506a47ac2c0591"}, + {file = "grpcio-1.67.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3840994689cc8cbb73d60485c594424ad8adb56c71a30d8948d6453083624b52"}, + {file = "grpcio-1.67.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:5a1e03c3102b6451028d5dc9f8591131d6ab3c8a0e023d94c28cb930ed4b5f81"}, + {file = "grpcio-1.67.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:682968427a63d898759474e3b3178d42546e878fdce034fd7474ef75143b64e3"}, + {file = "grpcio-1.67.0-cp312-cp312-win32.whl", hash = "sha256:d01793653248f49cf47e5695e0a79805b1d9d4eacef85b310118ba1dfcd1b955"}, + {file = "grpcio-1.67.0-cp312-cp312-win_amd64.whl", hash = "sha256:985b2686f786f3e20326c4367eebdaed3e7aa65848260ff0c6644f817042cb15"}, + {file = "grpcio-1.67.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:8c9a35b8bc50db35ab8e3e02a4f2a35cfba46c8705c3911c34ce343bd777813a"}, + {file = "grpcio-1.67.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:42199e704095b62688998c2d84c89e59a26a7d5d32eed86d43dc90e7a3bd04aa"}, + {file = "grpcio-1.67.0-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:c4c425f440fb81f8d0237c07b9322fc0fb6ee2b29fbef5f62a322ff8fcce240d"}, + {file = "grpcio-1.67.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:323741b6699cd2b04a71cb38f502db98f90532e8a40cb675393d248126a268af"}, + {file = "grpcio-1.67.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:662c8e105c5e5cee0317d500eb186ed7a93229586e431c1bf0c9236c2407352c"}, + {file = "grpcio-1.67.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:f6bd2ab135c64a4d1e9e44679a616c9bc944547357c830fafea5c3caa3de5153"}, + {file = "grpcio-1.67.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:2f55c1e0e2ae9bdd23b3c63459ee4c06d223b68aeb1961d83c48fb63dc29bc03"}, + {file = "grpcio-1.67.0-cp313-cp313-win32.whl", hash = "sha256:fd6bc27861e460fe28e94226e3673d46e294ca4673d46b224428d197c5935e69"}, + {file = "grpcio-1.67.0-cp313-cp313-win_amd64.whl", hash = "sha256:cf51d28063338608cd8d3cd64677e922134837902b70ce00dad7f116e3998210"}, + {file = "grpcio-1.67.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:7f200aca719c1c5dc72ab68be3479b9dafccdf03df530d137632c534bb6f1ee3"}, + {file = "grpcio-1.67.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0892dd200ece4822d72dd0952f7112c542a487fc48fe77568deaaa399c1e717d"}, + {file = "grpcio-1.67.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:f4d613fbf868b2e2444f490d18af472ccb47660ea3df52f068c9c8801e1f3e85"}, + {file = "grpcio-1.67.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c69bf11894cad9da00047f46584d5758d6ebc9b5950c0dc96fec7e0bce5cde9"}, + {file = "grpcio-1.67.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9bca3ca0c5e74dea44bf57d27e15a3a3996ce7e5780d61b7c72386356d231db"}, + {file = "grpcio-1.67.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:014dfc020e28a0d9be7e93a91f85ff9f4a87158b7df9952fe23cc42d29d31e1e"}, + {file = "grpcio-1.67.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d4ea4509d42c6797539e9ec7496c15473177ce9abc89bc5c71e7abe50fc25737"}, + {file = "grpcio-1.67.0-cp38-cp38-win32.whl", hash = "sha256:9d75641a2fca9ae1ae86454fd25d4c298ea8cc195dbc962852234d54a07060ad"}, + {file = "grpcio-1.67.0-cp38-cp38-win_amd64.whl", hash = "sha256:cff8e54d6a463883cda2fab94d2062aad2f5edd7f06ae3ed030f2a74756db365"}, + {file = "grpcio-1.67.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:62492bd534979e6d7127b8a6b29093161a742dee3875873e01964049d5250a74"}, + {file = "grpcio-1.67.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eef1dce9d1a46119fd09f9a992cf6ab9d9178b696382439446ca5f399d7b96fe"}, + {file = "grpcio-1.67.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:f623c57a5321461c84498a99dddf9d13dac0e40ee056d884d6ec4ebcab647a78"}, + {file = "grpcio-1.67.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54d16383044e681f8beb50f905249e4e7261dd169d4aaf6e52eab67b01cbbbe2"}, + {file = "grpcio-1.67.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2a44e572fb762c668e4812156b81835f7aba8a721b027e2d4bb29fb50ff4d33"}, + {file = "grpcio-1.67.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:391df8b0faac84d42f5b8dfc65f5152c48ed914e13c522fd05f2aca211f8bfad"}, + {file = "grpcio-1.67.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:cfd9306511fdfc623a1ba1dc3bc07fbd24e6cfbe3c28b4d1e05177baa2f99617"}, + {file = "grpcio-1.67.0-cp39-cp39-win32.whl", hash = "sha256:30d47dbacfd20cbd0c8be9bfa52fdb833b395d4ec32fe5cff7220afc05d08571"}, + {file = "grpcio-1.67.0-cp39-cp39-win_amd64.whl", hash = "sha256:f55f077685f61f0fbd06ea355142b71e47e4a26d2d678b3ba27248abfe67163a"}, + {file = "grpcio-1.67.0.tar.gz", hash = "sha256:e090b2553e0da1c875449c8e75073dd4415dd71c9bde6a406240fdf4c0ee467c"}, +] + +[package.extras] +protobuf = ["grpcio-tools (>=1.67.0)"] + [[package]] name = "h11" version = "0.14.0" @@ -1663,26 +1878,22 @@ files = [ [[package]] name = "importlib-metadata" -version = "8.5.0" +version = "8.4.0" description = "Read metadata from Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "importlib_metadata-8.5.0-py3-none-any.whl", hash = "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b"}, - {file = "importlib_metadata-8.5.0.tar.gz", hash = "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7"}, + {file = "importlib_metadata-8.4.0-py3-none-any.whl", hash = "sha256:66f342cc6ac9818fc6ff340576acd24d65ba0b3efabb2b4ac08b598965a4a2f1"}, + {file = "importlib_metadata-8.4.0.tar.gz", hash = "sha256:9a547d3bc3608b025f93d403fdd1aae741c24fbb8314df4b155675742ce303c5"}, ] [package.dependencies] -zipp = ">=3.20" +zipp = ">=0.5" [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] -cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -enabler = ["pytest-enabler (>=2.2)"] perf = ["ipython"] -test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] -type = ["pytest-mypy"] +test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"] [[package]] name = "iniconfig" @@ -2303,6 +2514,17 @@ marisa-trie = ">=0.7.7" build = ["build", "twine"] test = ["pytest", "pytest-cov"] +[[package]] +name = "lazify" +version = "0.4.0" +description = "Lazify all the things!" +optional = true +python-versions = "*" +files = [ + {file = "Lazify-0.4.0-py2.py3-none-any.whl", hash = "sha256:c2c17a7a33e9406897e3f66fde4cd3f84716218d580330e5af10cfe5a0cd195a"}, + {file = "Lazify-0.4.0.tar.gz", hash = "sha256:7102bfe63e56de2ab62b3bc661a7190c4056771a8624f04a8b785275c3dd1f9b"}, +] + [[package]] name = "linkify-it-py" version = "2.0.3" @@ -2351,6 +2573,22 @@ tokenizers = "*" extra-proxy = ["azure-identity (>=1.15.0,<2.0.0)", "azure-keyvault-secrets (>=4.8.0,<5.0.0)", "google-cloud-kms (>=2.21.3,<3.0.0)", "prisma (==0.11.0)", "resend (>=0.8.0,<0.9.0)"] proxy = ["PyJWT (>=2.8.0,<3.0.0)", "apscheduler (>=3.10.4,<4.0.0)", "backoff", "cryptography (>=42.0.5,<43.0.0)", "fastapi (>=0.111.0,<0.112.0)", "fastapi-sso (>=0.10.0,<0.11.0)", "gunicorn (>=22.0.0,<23.0.0)", "orjson (>=3.9.7,<4.0.0)", "pynacl (>=1.5.0,<2.0.0)", "python-multipart (>=0.0.9,<0.0.10)", "pyyaml (>=6.0.1,<7.0.0)", "rq", "uvicorn (>=0.22.0,<0.23.0)"] +[[package]] +name = "literalai" +version = "0.0.607" +description = "An SDK for observability in Python applications" +optional = true +python-versions = "*" +files = [ + {file = "literalai-0.0.607.tar.gz", hash = "sha256:783c495d9fb2ae9f84e5877ecb34587f6a6ac0224d9f0936336dbc8b6765b30d"}, +] + +[package.dependencies] +chevron = ">=0.14.0" +httpx = ">=0.23.0" +packaging = ">=23.0" +pydantic = ">=1,<3" + [[package]] name = "llama-cpp-python" version = "0.2.88" @@ -3196,6 +3434,151 @@ typing-extensions = ">=4.11,<5" [package.extras] datalib = ["numpy (>=1)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)"] +[[package]] +name = "opentelemetry-api" +version = "1.27.0" +description = "OpenTelemetry Python API" +optional = true +python-versions = ">=3.8" +files = [ + {file = "opentelemetry_api-1.27.0-py3-none-any.whl", hash = "sha256:953d5871815e7c30c81b56d910c707588000fff7a3ca1c73e6531911d53065e7"}, + {file = "opentelemetry_api-1.27.0.tar.gz", hash = "sha256:ed673583eaa5f81b5ce5e86ef7cdaf622f88ef65f0b9aab40b843dcae5bef342"}, +] + +[package.dependencies] +deprecated = ">=1.2.6" +importlib-metadata = ">=6.0,<=8.4.0" + +[[package]] +name = "opentelemetry-exporter-otlp" +version = "1.27.0" +description = "OpenTelemetry Collector Exporters" +optional = true +python-versions = ">=3.8" +files = [ + {file = "opentelemetry_exporter_otlp-1.27.0-py3-none-any.whl", hash = "sha256:7688791cbdd951d71eb6445951d1cfbb7b6b2d7ee5948fac805d404802931145"}, + {file = "opentelemetry_exporter_otlp-1.27.0.tar.gz", hash = "sha256:4a599459e623868cc95d933c301199c2367e530f089750e115599fccd67cb2a1"}, +] + +[package.dependencies] +opentelemetry-exporter-otlp-proto-grpc = "1.27.0" +opentelemetry-exporter-otlp-proto-http = "1.27.0" + +[[package]] +name = "opentelemetry-exporter-otlp-proto-common" +version = "1.27.0" +description = "OpenTelemetry Protobuf encoding" +optional = true +python-versions = ">=3.8" +files = [ + {file = "opentelemetry_exporter_otlp_proto_common-1.27.0-py3-none-any.whl", hash = "sha256:675db7fffcb60946f3a5c43e17d1168a3307a94a930ecf8d2ea1f286f3d4f79a"}, + {file = "opentelemetry_exporter_otlp_proto_common-1.27.0.tar.gz", hash = "sha256:159d27cf49f359e3798c4c3eb8da6ef4020e292571bd8c5604a2a573231dd5c8"}, +] + +[package.dependencies] +opentelemetry-proto = "1.27.0" + +[[package]] +name = "opentelemetry-exporter-otlp-proto-grpc" +version = "1.27.0" +description = "OpenTelemetry Collector Protobuf over gRPC Exporter" +optional = true +python-versions = ">=3.8" +files = [ + {file = "opentelemetry_exporter_otlp_proto_grpc-1.27.0-py3-none-any.whl", hash = "sha256:56b5bbd5d61aab05e300d9d62a6b3c134827bbd28d0b12f2649c2da368006c9e"}, + {file = "opentelemetry_exporter_otlp_proto_grpc-1.27.0.tar.gz", hash = "sha256:af6f72f76bcf425dfb5ad11c1a6d6eca2863b91e63575f89bb7b4b55099d968f"}, +] + +[package.dependencies] +deprecated = ">=1.2.6" +googleapis-common-protos = ">=1.52,<2.0" +grpcio = ">=1.0.0,<2.0.0" +opentelemetry-api = ">=1.15,<2.0" +opentelemetry-exporter-otlp-proto-common = "1.27.0" +opentelemetry-proto = "1.27.0" +opentelemetry-sdk = ">=1.27.0,<1.28.0" + +[[package]] +name = "opentelemetry-exporter-otlp-proto-http" +version = "1.27.0" +description = "OpenTelemetry Collector Protobuf over HTTP Exporter" +optional = true +python-versions = ">=3.8" +files = [ + {file = "opentelemetry_exporter_otlp_proto_http-1.27.0-py3-none-any.whl", hash = "sha256:688027575c9da42e179a69fe17e2d1eba9b14d81de8d13553a21d3114f3b4d75"}, + {file = "opentelemetry_exporter_otlp_proto_http-1.27.0.tar.gz", hash = "sha256:2103479092d8eb18f61f3fbff084f67cc7f2d4a7d37e75304b8b56c1d09ebef5"}, +] + +[package.dependencies] +deprecated = ">=1.2.6" +googleapis-common-protos = ">=1.52,<2.0" +opentelemetry-api = ">=1.15,<2.0" +opentelemetry-exporter-otlp-proto-common = "1.27.0" +opentelemetry-proto = "1.27.0" +opentelemetry-sdk = ">=1.27.0,<1.28.0" +requests = ">=2.7,<3.0" + +[[package]] +name = "opentelemetry-instrumentation" +version = "0.48b0" +description = "Instrumentation Tools & Auto Instrumentation for OpenTelemetry Python" +optional = true +python-versions = ">=3.8" +files = [ + {file = "opentelemetry_instrumentation-0.48b0-py3-none-any.whl", hash = "sha256:a69750dc4ba6a5c3eb67986a337185a25b739966d80479befe37b546fc870b44"}, + {file = "opentelemetry_instrumentation-0.48b0.tar.gz", hash = "sha256:94929685d906380743a71c3970f76b5f07476eea1834abd5dd9d17abfe23cc35"}, +] + +[package.dependencies] +opentelemetry-api = ">=1.4,<2.0" +setuptools = ">=16.0" +wrapt = ">=1.0.0,<2.0.0" + +[[package]] +name = "opentelemetry-proto" +version = "1.27.0" +description = "OpenTelemetry Python Proto" +optional = true +python-versions = ">=3.8" +files = [ + {file = "opentelemetry_proto-1.27.0-py3-none-any.whl", hash = "sha256:b133873de5581a50063e1e4b29cdcf0c5e253a8c2d8dc1229add20a4c3830ace"}, + {file = "opentelemetry_proto-1.27.0.tar.gz", hash = "sha256:33c9345d91dafd8a74fc3d7576c5a38f18b7fdf8d02983ac67485386132aedd6"}, +] + +[package.dependencies] +protobuf = ">=3.19,<5.0" + +[[package]] +name = "opentelemetry-sdk" +version = "1.27.0" +description = "OpenTelemetry Python SDK" +optional = true +python-versions = ">=3.8" +files = [ + {file = "opentelemetry_sdk-1.27.0-py3-none-any.whl", hash = "sha256:365f5e32f920faf0fd9e14fdfd92c086e317eaa5f860edba9cdc17a380d9197d"}, + {file = "opentelemetry_sdk-1.27.0.tar.gz", hash = "sha256:d525017dea0ccce9ba4e0245100ec46ecdc043f2d7b8315d56b19aff0904fa6f"}, +] + +[package.dependencies] +opentelemetry-api = "1.27.0" +opentelemetry-semantic-conventions = "0.48b0" +typing-extensions = ">=3.7.4" + +[[package]] +name = "opentelemetry-semantic-conventions" +version = "0.48b0" +description = "OpenTelemetry Semantic Conventions" +optional = true +python-versions = ">=3.8" +files = [ + {file = "opentelemetry_semantic_conventions-0.48b0-py3-none-any.whl", hash = "sha256:a0de9f45c413a8669788a38569c7e0a11ce6ce97861a628cca785deecdc32a1f"}, + {file = "opentelemetry_semantic_conventions-0.48b0.tar.gz", hash = "sha256:12d74983783b6878162208be57c9effcb89dc88691c64992d70bb89dc00daa1a"}, +] + +[package.dependencies] +deprecated = ">=1.2.6" +opentelemetry-api = "1.27.0" + [[package]] name = "orjson" version = "3.10.7" @@ -3264,13 +3647,13 @@ files = [ [[package]] name = "packaging" -version = "24.1" +version = "23.2" description = "Core utilities for Python packages" optional = false -python-versions = ">=3.8" +python-versions = ">=3.7" files = [ - {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, - {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, + {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, + {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, ] [[package]] @@ -3663,22 +4046,22 @@ wcwidth = "*" [[package]] name = "protobuf" -version = "5.28.2" +version = "4.25.5" description = "" optional = false python-versions = ">=3.8" files = [ - {file = "protobuf-5.28.2-cp310-abi3-win32.whl", hash = "sha256:eeea10f3dc0ac7e6b4933d32db20662902b4ab81bf28df12218aa389e9c2102d"}, - {file = "protobuf-5.28.2-cp310-abi3-win_amd64.whl", hash = "sha256:2c69461a7fcc8e24be697624c09a839976d82ae75062b11a0972e41fd2cd9132"}, - {file = "protobuf-5.28.2-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a8b9403fc70764b08d2f593ce44f1d2920c5077bf7d311fefec999f8c40f78b7"}, - {file = "protobuf-5.28.2-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:35cfcb15f213449af7ff6198d6eb5f739c37d7e4f1c09b5d0641babf2cc0c68f"}, - {file = "protobuf-5.28.2-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:5e8a95246d581eef20471b5d5ba010d55f66740942b95ba9b872d918c459452f"}, - {file = "protobuf-5.28.2-cp38-cp38-win32.whl", hash = "sha256:87317e9bcda04a32f2ee82089a204d3a2f0d3c8aeed16568c7daf4756e4f1fe0"}, - {file = "protobuf-5.28.2-cp38-cp38-win_amd64.whl", hash = "sha256:c0ea0123dac3399a2eeb1a1443d82b7afc9ff40241433296769f7da42d142ec3"}, - {file = "protobuf-5.28.2-cp39-cp39-win32.whl", hash = "sha256:ca53faf29896c526863366a52a8f4d88e69cd04ec9571ed6082fa117fac3ab36"}, - {file = "protobuf-5.28.2-cp39-cp39-win_amd64.whl", hash = "sha256:8ddc60bf374785fb7cb12510b267f59067fa10087325b8e1855b898a0d81d276"}, - {file = "protobuf-5.28.2-py3-none-any.whl", hash = "sha256:52235802093bd8a2811abbe8bf0ab9c5f54cca0a751fdd3f6ac2a21438bffece"}, - {file = "protobuf-5.28.2.tar.gz", hash = "sha256:59379674ff119717404f7454647913787034f03fe7049cbef1d74a97bb4593f0"}, + {file = "protobuf-4.25.5-cp310-abi3-win32.whl", hash = "sha256:5e61fd921603f58d2f5acb2806a929b4675f8874ff5f330b7d6f7e2e784bbcd8"}, + {file = "protobuf-4.25.5-cp310-abi3-win_amd64.whl", hash = "sha256:4be0571adcbe712b282a330c6e89eae24281344429ae95c6d85e79e84780f5ea"}, + {file = "protobuf-4.25.5-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:b2fde3d805354df675ea4c7c6338c1aecd254dfc9925e88c6d31a2bcb97eb173"}, + {file = "protobuf-4.25.5-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:919ad92d9b0310070f8356c24b855c98df2b8bd207ebc1c0c6fcc9ab1e007f3d"}, + {file = "protobuf-4.25.5-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:fe14e16c22be926d3abfcb500e60cab068baf10b542b8c858fa27e098123e331"}, + {file = "protobuf-4.25.5-cp38-cp38-win32.whl", hash = "sha256:98d8d8aa50de6a2747efd9cceba361c9034050ecce3e09136f90de37ddba66e1"}, + {file = "protobuf-4.25.5-cp38-cp38-win_amd64.whl", hash = "sha256:b0234dd5a03049e4ddd94b93400b67803c823cfc405689688f59b34e0742381a"}, + {file = "protobuf-4.25.5-cp39-cp39-win32.whl", hash = "sha256:abe32aad8561aa7cc94fc7ba4fdef646e576983edb94a73381b03c53728a626f"}, + {file = "protobuf-4.25.5-cp39-cp39-win_amd64.whl", hash = "sha256:7a183f592dc80aa7c8da7ad9e55091c4ffc9497b3054452d629bb85fa27c2a45"}, + {file = "protobuf-4.25.5-py3-none-any.whl", hash = "sha256:0aebecb809cae990f8129ada5ca273d9d670b76d9bfc9b1809f0a9c02b7dbf41"}, + {file = "protobuf-4.25.5.tar.gz", hash = "sha256:7f8249476b4a9473645db7f8ab42b02fe1488cbe5fb72fddd445e0665afd8584"}, ] [[package]] @@ -3954,6 +4337,23 @@ files = [ [package.extras] windows-terminal = ["colorama (>=0.4.6)"] +[[package]] +name = "pyjwt" +version = "2.9.0" +description = "JSON Web Token implementation in Python" +optional = true +python-versions = ">=3.8" +files = [ + {file = "PyJWT-2.9.0-py3-none-any.whl", hash = "sha256:3b02fb0f44517787776cf48f2ae25d8e14f300e6d7545a4315cee571a415e850"}, + {file = "pyjwt-2.9.0.tar.gz", hash = "sha256:7e1e5b56cc735432a7369cbfa0efe50fa113ebecdc04ae6922deba8b84582d0c"}, +] + +[package.extras] +crypto = ["cryptography (>=3.4.0)"] +dev = ["coverage[toml] (==5.0.4)", "cryptography (>=3.4.0)", "pre-commit", "pytest (>=6.0.0,<7.0.0)", "sphinx", "sphinx-rtd-theme", "zope.interface"] +docs = ["sphinx", "sphinx-rtd-theme", "zope.interface"] +tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"] + [[package]] name = "pynndescent" version = "0.5.13" @@ -4115,6 +4515,39 @@ files = [ [package.extras] cli = ["click (>=5.0)"] +[[package]] +name = "python-engineio" +version = "4.10.1" +description = "Engine.IO server and client for Python" +optional = true +python-versions = ">=3.6" +files = [ + {file = "python_engineio-4.10.1-py3-none-any.whl", hash = "sha256:445a94004ec8034960ab99e7ce4209ec619c6e6b6a12aedcb05abeab924025c0"}, + {file = "python_engineio-4.10.1.tar.gz", hash = "sha256:166cea8dd7429638c5c4e3a4895beae95196e860bc6f29ed0b9fe753d1ef2072"}, +] + +[package.dependencies] +simple-websocket = ">=0.10.0" + +[package.extras] +asyncio-client = ["aiohttp (>=3.4)"] +client = ["requests (>=2.21.0)", "websocket-client (>=0.54.0)"] +docs = ["sphinx"] + +[[package]] +name = "python-multipart" +version = "0.0.9" +description = "A streaming multipart parser for Python" +optional = true +python-versions = ">=3.8" +files = [ + {file = "python_multipart-0.0.9-py3-none-any.whl", hash = "sha256:97ca7b8ea7b05f977dc3849c3ba99d51689822fab725c3703af7c866a0c2b215"}, + {file = "python_multipart-0.0.9.tar.gz", hash = "sha256:03f54688c663f1b7977105f021043b0793151e4cb1c1a9d4a11fc13d622c4026"}, +] + +[package.extras] +dev = ["atomicwrites (==1.4.1)", "attrs (==23.2.0)", "coverage (==7.4.1)", "hatch", "invoke (==2.2.0)", "more-itertools (==10.2.0)", "pbr (==6.0.0)", "pluggy (==1.4.0)", "py (==1.11.0)", "pytest (==8.0.0)", "pytest-cov (==4.1.0)", "pytest-timeout (==2.2.0)", "pyyaml (==6.0.1)", "ruff (==0.2.1)"] + [[package]] name = "python-slugify" version = "8.0.4" @@ -4132,6 +4565,26 @@ text-unidecode = ">=1.3" [package.extras] unidecode = ["Unidecode (>=1.1.1)"] +[[package]] +name = "python-socketio" +version = "5.11.4" +description = "Socket.IO server and client for Python" +optional = true +python-versions = ">=3.8" +files = [ + {file = "python_socketio-5.11.4-py3-none-any.whl", hash = "sha256:42efaa3e3e0b166fc72a527488a13caaac2cefc76174252486503bd496284945"}, + {file = "python_socketio-5.11.4.tar.gz", hash = "sha256:8b0b8ff2964b2957c865835e936310190639c00310a47d77321a594d1665355e"}, +] + +[package.dependencies] +bidict = ">=0.21.0" +python-engineio = ">=4.8.0" + +[package.extras] +asyncio-client = ["aiohttp (>=3.4)"] +client = ["requests (>=2.21.0)", "websocket-client (>=0.54.0)"] +docs = ["sphinx"] + [[package]] name = "pytz" version = "2024.1" @@ -4956,6 +5409,35 @@ files = [ {file = "shellcheck_py-0.10.0.1.tar.gz", hash = "sha256:390826b340b8c19173922b0da5ef7b66ef34d4d087dc48aad3e01f7e77e164d9"}, ] +[[package]] +name = "shellingham" +version = "1.5.4" +description = "Tool to Detect Surrounding Shell" +optional = false +python-versions = ">=3.7" +files = [ + {file = "shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686"}, + {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"}, +] + +[[package]] +name = "simple-websocket" +version = "1.1.0" +description = "Simple WebSocket server and client for Python" +optional = true +python-versions = ">=3.6" +files = [ + {file = "simple_websocket-1.1.0-py3-none-any.whl", hash = "sha256:4af6069630a38ed6c561010f0e11a5bc0d4ca569b36306eb257cd9a192497c8c"}, + {file = "simple_websocket-1.1.0.tar.gz", hash = "sha256:7939234e7aa067c534abdab3a9ed933ec9ce4691b0713c78acb195560aa52ae4"}, +] + +[package.dependencies] +wsproto = "*" + +[package.extras] +dev = ["flake8", "pytest", "pytest-cov", "tox"] +docs = ["sphinx"] + [[package]] name = "six" version = "1.16.0" @@ -5286,6 +5768,23 @@ pure-eval = "*" [package.extras] tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"] +[[package]] +name = "starlette" +version = "0.37.2" +description = "The little ASGI library that shines." +optional = true +python-versions = ">=3.8" +files = [ + {file = "starlette-0.37.2-py3-none-any.whl", hash = "sha256:6fe59f29268538e5d0d182f2791a479a0c64638e6935d1c6989e63fb2699c6ee"}, + {file = "starlette-0.37.2.tar.gz", hash = "sha256:9af890290133b79fc3db55474ade20f6220a364a0402e0b556e7cd5e1e093823"}, +] + +[package.dependencies] +anyio = ">=3.4.0,<5" + +[package.extras] +full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.7)", "pyyaml"] + [[package]] name = "sympy" version = "1.13.3" @@ -5303,6 +5802,16 @@ mpmath = ">=1.1.0,<1.4" [package.extras] dev = ["hypothesis (>=6.70.0)", "pytest (>=7.1.0)"] +[[package]] +name = "syncer" +version = "2.0.3" +description = "Async to sync converter" +optional = true +python-versions = "*" +files = [ + {file = "syncer-2.0.3.tar.gz", hash = "sha256:4340eb54b54368724a78c5c0763824470201804fe9180129daf3635cb500550f"}, +] + [[package]] name = "tenacity" version = "8.5.0" @@ -5689,25 +6198,21 @@ test = ["coverage[toml] (>=7)", "mypy (>=1.2.0)", "pytest (>=7)"] [[package]] name = "typer" -version = "0.9.4" +version = "0.12.5" description = "Typer, build great CLIs. Easy to code. Based on Python type hints." optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" files = [ - {file = "typer-0.9.4-py3-none-any.whl", hash = "sha256:aa6c4a4e2329d868b80ecbaf16f807f2b54e192209d7ac9dd42691d63f7a54eb"}, - {file = "typer-0.9.4.tar.gz", hash = "sha256:f714c2d90afae3a7929fcd72a3abb08df305e1ff61719381384211c4070af57f"}, + {file = "typer-0.12.5-py3-none-any.whl", hash = "sha256:62fe4e471711b147e3365034133904df3e235698399bc4de2b36c8579298d52b"}, + {file = "typer-0.12.5.tar.gz", hash = "sha256:f592f089bedcc8ec1b974125d64851029c3b1af145f04aca64d69410f0c9b722"}, ] [package.dependencies] -click = ">=7.1.1,<9.0.0" +click = ">=8.0.0" +rich = ">=10.11.0" +shellingham = ">=1.3.0" typing-extensions = ">=3.7.4.3" -[package.extras] -all = ["colorama (>=0.4.3,<0.5.0)", "rich (>=10.11.0,<14.0.0)", "shellingham (>=1.3.0,<2.0.0)"] -dev = ["autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "pre-commit (>=2.17.0,<3.0.0)"] -doc = ["cairosvg (>=2.5.2,<3.0.0)", "mdx-include (>=1.4.1,<2.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "pillow (>=9.3.0,<10.0.0)"] -test = ["black (>=22.3.0,<23.0.0)", "coverage (>=6.2,<7.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.971)", "pytest (>=4.4.0,<8.0.0)", "pytest-cov (>=2.10.0,<5.0.0)", "pytest-sugar (>=0.9.4,<0.10.0)", "pytest-xdist (>=1.32.0,<4.0.0)", "rich (>=10.11.0,<14.0.0)", "shellingham (>=1.3.0,<2.0.0)"] - [[package]] name = "types-python-dateutil" version = "2.9.0.20240316" @@ -5770,6 +6275,23 @@ files = [ [package.extras] test = ["coverage", "pytest", "pytest-cov"] +[[package]] +name = "uptrace" +version = "1.27.0" +description = "OpenTelemetry Python distribution for Uptrace" +optional = true +python-versions = ">=3.7" +files = [ + {file = "uptrace-1.27.0-py3-none-any.whl", hash = "sha256:d5473efa33c34e3d5738d32d19301dbf004d4e19598c658f2fa9f3f09458f630"}, + {file = "uptrace-1.27.0.tar.gz", hash = "sha256:983f783b2f4303d1d2bdfaf6ace1b7a5f072af47f78a7815f82c51fcf5099cac"}, +] + +[package.dependencies] +opentelemetry-api = ">=1.27,<2.0" +opentelemetry-exporter-otlp = ">=1.27,<2.0" +opentelemetry-instrumentation = ">=0.48b0,<1.0" +opentelemetry-sdk = ">=1.27,<2.0" + [[package]] name = "urllib3" version = "2.2.2" @@ -5787,6 +6309,25 @@ h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] +[[package]] +name = "uvicorn" +version = "0.25.0" +description = "The lightning-fast ASGI server." +optional = true +python-versions = ">=3.8" +files = [ + {file = "uvicorn-0.25.0-py3-none-any.whl", hash = "sha256:ce107f5d9bd02b4636001a77a4e74aab5e1e2b146868ebbad565237145af444c"}, + {file = "uvicorn-0.25.0.tar.gz", hash = "sha256:6dddbad1d7ee0f5140aba5ec138ddc9612c5109399903828b4874c9937f009c2"}, +] + +[package.dependencies] +click = ">=7.0" +h11 = ">=0.8" +typing-extensions = {version = ">=4.0", markers = "python_version < \"3.11\""} + +[package.extras] +standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"] + [[package]] name = "virtualenv" version = "20.26.3" @@ -5821,6 +6362,40 @@ files = [ [package.dependencies] colorama = {version = ">=0.4.6", markers = "sys_platform == \"win32\" and python_version >= \"3.7\""} +[[package]] +name = "watchfiles" +version = "0.20.0" +description = "Simple, modern and high performance file watching and code reload in python." +optional = true +python-versions = ">=3.7" +files = [ + {file = "watchfiles-0.20.0-cp37-abi3-macosx_10_7_x86_64.whl", hash = "sha256:3796312bd3587e14926013612b23066912cf45a14af71cf2b20db1c12dadf4e9"}, + {file = "watchfiles-0.20.0-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:d0002d81c89a662b595645fb684a371b98ff90a9c7d8f8630c82f0fde8310458"}, + {file = "watchfiles-0.20.0-cp37-abi3-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:570848706440373b4cd8017f3e850ae17f76dbdf1e9045fc79023b11e1afe490"}, + {file = "watchfiles-0.20.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9a0351d20d03c6f7ad6b2e8a226a5efafb924c7755ee1e34f04c77c3682417fa"}, + {file = "watchfiles-0.20.0-cp37-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:007dcc4a401093010b389c044e81172c8a2520dba257c88f8828b3d460c6bb38"}, + {file = "watchfiles-0.20.0-cp37-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0d82dbc1832da83e441d112069833eedd4cf583d983fb8dd666fbefbea9d99c0"}, + {file = "watchfiles-0.20.0-cp37-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:99f4c65fd2fce61a571b2a6fcf747d6868db0bef8a934e8ca235cc8533944d95"}, + {file = "watchfiles-0.20.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5392dd327a05f538c56edb1c6ebba6af91afc81b40822452342f6da54907bbdf"}, + {file = "watchfiles-0.20.0-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:08dc702529bb06a2b23859110c214db245455532da5eaea602921687cfcd23db"}, + {file = "watchfiles-0.20.0-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:7d4e66a857621584869cfbad87039e65dadd7119f0d9bb9dbc957e089e32c164"}, + {file = "watchfiles-0.20.0-cp37-abi3-win32.whl", hash = "sha256:a03d1e6feb7966b417f43c3e3783188167fd69c2063e86bad31e62c4ea794cc5"}, + {file = "watchfiles-0.20.0-cp37-abi3-win_amd64.whl", hash = "sha256:eccc8942bcdc7d638a01435d915b913255bbd66f018f1af051cd8afddb339ea3"}, + {file = "watchfiles-0.20.0-cp37-abi3-win_arm64.whl", hash = "sha256:b17d4176c49d207865630da5b59a91779468dd3e08692fe943064da260de2c7c"}, + {file = "watchfiles-0.20.0-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:d97db179f7566dcf145c5179ddb2ae2a4450e3a634eb864b09ea04e68c252e8e"}, + {file = "watchfiles-0.20.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:835df2da7a5df5464c4a23b2d963e1a9d35afa422c83bf4ff4380b3114603644"}, + {file = "watchfiles-0.20.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:608cd94a8767f49521901aff9ae0c92cc8f5a24d528db7d6b0295290f9d41193"}, + {file = "watchfiles-0.20.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89d1de8218874925bce7bb2ae9657efc504411528930d7a83f98b1749864f2ef"}, + {file = "watchfiles-0.20.0-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:13f995d5152a8ba4ed7c2bbbaeee4e11a5944defc7cacd0ccb4dcbdcfd78029a"}, + {file = "watchfiles-0.20.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:9b5c8d3be7b502f8c43a33c63166ada8828dbb0c6d49c8f9ce990a96de2f5a49"}, + {file = "watchfiles-0.20.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e43af4464daa08723c04b43cf978ab86cc55c684c16172622bdac64b34e36af0"}, + {file = "watchfiles-0.20.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87d9e1f75c4f86c93d73b5bd1ebe667558357548f11b4f8af4e0e272f79413ce"}, + {file = "watchfiles-0.20.0.tar.gz", hash = "sha256:728575b6b94c90dd531514677201e8851708e6e4b5fe7028ac506a200b622019"}, +] + +[package.dependencies] +anyio = ">=3.0.0" + [[package]] name = "wcwidth" version = "0.2.13" @@ -5834,24 +6409,24 @@ files = [ [[package]] name = "weasel" -version = "0.3.4" +version = "0.4.1" description = "Weasel: A small and easy workflow system" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" files = [ - {file = "weasel-0.3.4-py3-none-any.whl", hash = "sha256:ee48a944f051d007201c2ea1661d0c41035028c5d5a8bcb29a0b10f1100206ae"}, - {file = "weasel-0.3.4.tar.gz", hash = "sha256:eb16f92dc9f1a3ffa89c165e3a9acd28018ebb656e0da4da02c0d7d8ae3f6178"}, + {file = "weasel-0.4.1-py3-none-any.whl", hash = "sha256:24140a090ea1ac512a2b2f479cc64192fd1d527a7f3627671268d08ed5ac418c"}, + {file = "weasel-0.4.1.tar.gz", hash = "sha256:aabc210f072e13f6744e5c3a28037f93702433405cd35673f7c6279147085aa9"}, ] [package.dependencies] -cloudpathlib = ">=0.7.0,<0.17.0" +cloudpathlib = ">=0.7.0,<1.0.0" confection = ">=0.0.4,<0.2.0" packaging = ">=20.0" pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<3.0.0" requests = ">=2.13.0,<3.0.0" -smart-open = ">=5.2.1,<7.0.0" +smart-open = ">=5.2.1,<8.0.0" srsly = ">=2.4.3,<3.0.0" -typer = ">=0.3.0,<0.10.0" +typer = ">=0.3.0,<1.0.0" wasabi = ">=0.9.1,<1.2.0" [[package]] @@ -5865,6 +6440,99 @@ files = [ {file = "widgetsnbextension-4.0.11.tar.gz", hash = "sha256:8b22a8f1910bfd188e596fe7fc05dcbd87e810c8a4ba010bdb3da86637398474"}, ] +[[package]] +name = "wrapt" +version = "1.16.0" +description = "Module for decorators, wrappers and monkey patching." +optional = true +python-versions = ">=3.6" +files = [ + {file = "wrapt-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ffa565331890b90056c01db69c0fe634a776f8019c143a5ae265f9c6bc4bd6d4"}, + {file = "wrapt-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e4fdb9275308292e880dcbeb12546df7f3e0f96c6b41197e0cf37d2826359020"}, + {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb2dee3874a500de01c93d5c71415fcaef1d858370d405824783e7a8ef5db440"}, + {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a88e6010048489cda82b1326889ec075a8c856c2e6a256072b28eaee3ccf487"}, + {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac83a914ebaf589b69f7d0a1277602ff494e21f4c2f743313414378f8f50a4cf"}, + {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:73aa7d98215d39b8455f103de64391cb79dfcad601701a3aa0dddacf74911d72"}, + {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:807cc8543a477ab7422f1120a217054f958a66ef7314f76dd9e77d3f02cdccd0"}, + {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bf5703fdeb350e36885f2875d853ce13172ae281c56e509f4e6eca049bdfb136"}, + {file = "wrapt-1.16.0-cp310-cp310-win32.whl", hash = "sha256:f6b2d0c6703c988d334f297aa5df18c45e97b0af3679bb75059e0e0bd8b1069d"}, + {file = "wrapt-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:decbfa2f618fa8ed81c95ee18a387ff973143c656ef800c9f24fb7e9c16054e2"}, + {file = "wrapt-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09"}, + {file = "wrapt-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d"}, + {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389"}, + {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060"}, + {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1"}, + {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3"}, + {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956"}, + {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d"}, + {file = "wrapt-1.16.0-cp311-cp311-win32.whl", hash = "sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362"}, + {file = "wrapt-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89"}, + {file = "wrapt-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b"}, + {file = "wrapt-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36"}, + {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73"}, + {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809"}, + {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b"}, + {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81"}, + {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9"}, + {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c"}, + {file = "wrapt-1.16.0-cp312-cp312-win32.whl", hash = "sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc"}, + {file = "wrapt-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8"}, + {file = "wrapt-1.16.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d462f28826f4657968ae51d2181a074dfe03c200d6131690b7d65d55b0f360f8"}, + {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a33a747400b94b6d6b8a165e4480264a64a78c8a4c734b62136062e9a248dd39"}, + {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3646eefa23daeba62643a58aac816945cadc0afaf21800a1421eeba5f6cfb9c"}, + {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ebf019be5c09d400cf7b024aa52b1f3aeebeff51550d007e92c3c1c4afc2a40"}, + {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:0d2691979e93d06a95a26257adb7bfd0c93818e89b1406f5a28f36e0d8c1e1fc"}, + {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:1acd723ee2a8826f3d53910255643e33673e1d11db84ce5880675954183ec47e"}, + {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:bc57efac2da352a51cc4658878a68d2b1b67dbe9d33c36cb826ca449d80a8465"}, + {file = "wrapt-1.16.0-cp36-cp36m-win32.whl", hash = "sha256:da4813f751142436b075ed7aa012a8778aa43a99f7b36afe9b742d3ed8bdc95e"}, + {file = "wrapt-1.16.0-cp36-cp36m-win_amd64.whl", hash = "sha256:6f6eac2360f2d543cc875a0e5efd413b6cbd483cb3ad7ebf888884a6e0d2e966"}, + {file = "wrapt-1.16.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a0ea261ce52b5952bf669684a251a66df239ec6d441ccb59ec7afa882265d593"}, + {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bd2d7ff69a2cac767fbf7a2b206add2e9a210e57947dd7ce03e25d03d2de292"}, + {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9159485323798c8dc530a224bd3ffcf76659319ccc7bbd52e01e73bd0241a0c5"}, + {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a86373cf37cd7764f2201b76496aba58a52e76dedfaa698ef9e9688bfd9e41cf"}, + {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:73870c364c11f03ed072dda68ff7aea6d2a3a5c3fe250d917a429c7432e15228"}, + {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b935ae30c6e7400022b50f8d359c03ed233d45b725cfdd299462f41ee5ffba6f"}, + {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:db98ad84a55eb09b3c32a96c576476777e87c520a34e2519d3e59c44710c002c"}, + {file = "wrapt-1.16.0-cp37-cp37m-win32.whl", hash = "sha256:9153ed35fc5e4fa3b2fe97bddaa7cbec0ed22412b85bcdaf54aeba92ea37428c"}, + {file = "wrapt-1.16.0-cp37-cp37m-win_amd64.whl", hash = "sha256:66dfbaa7cfa3eb707bbfcd46dab2bc6207b005cbc9caa2199bcbc81d95071a00"}, + {file = "wrapt-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1dd50a2696ff89f57bd8847647a1c363b687d3d796dc30d4dd4a9d1689a706f0"}, + {file = "wrapt-1.16.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:44a2754372e32ab315734c6c73b24351d06e77ffff6ae27d2ecf14cf3d229202"}, + {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e9723528b9f787dc59168369e42ae1c3b0d3fadb2f1a71de14531d321ee05b0"}, + {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbed418ba5c3dce92619656802cc5355cb679e58d0d89b50f116e4a9d5a9603e"}, + {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:941988b89b4fd6b41c3f0bfb20e92bd23746579736b7343283297c4c8cbae68f"}, + {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6a42cd0cfa8ffc1915aef79cb4284f6383d8a3e9dcca70c445dcfdd639d51267"}, + {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ca9b6085e4f866bd584fb135a041bfc32cab916e69f714a7d1d397f8c4891ca"}, + {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5e49454f19ef621089e204f862388d29e6e8d8b162efce05208913dde5b9ad6"}, + {file = "wrapt-1.16.0-cp38-cp38-win32.whl", hash = "sha256:c31f72b1b6624c9d863fc095da460802f43a7c6868c5dda140f51da24fd47d7b"}, + {file = "wrapt-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:490b0ee15c1a55be9c1bd8609b8cecd60e325f0575fc98f50058eae366e01f41"}, + {file = "wrapt-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9b201ae332c3637a42f02d1045e1d0cccfdc41f1f2f801dafbaa7e9b4797bfc2"}, + {file = "wrapt-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2076fad65c6736184e77d7d4729b63a6d1ae0b70da4868adeec40989858eb3fb"}, + {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5cd603b575ebceca7da5a3a251e69561bec509e0b46e4993e1cac402b7247b8"}, + {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b47cfad9e9bbbed2339081f4e346c93ecd7ab504299403320bf85f7f85c7d46c"}, + {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8212564d49c50eb4565e502814f694e240c55551a5f1bc841d4fcaabb0a9b8a"}, + {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5f15814a33e42b04e3de432e573aa557f9f0f56458745c2074952f564c50e664"}, + {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db2e408d983b0e61e238cf579c09ef7020560441906ca990fe8412153e3b291f"}, + {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:edfad1d29c73f9b863ebe7082ae9321374ccb10879eeabc84ba3b69f2579d537"}, + {file = "wrapt-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed867c42c268f876097248e05b6117a65bcd1e63b779e916fe2e33cd6fd0d3c3"}, + {file = "wrapt-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:eb1b046be06b0fce7249f1d025cd359b4b80fc1c3e24ad9eca33e0dcdb2e4a35"}, + {file = "wrapt-1.16.0-py3-none-any.whl", hash = "sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1"}, + {file = "wrapt-1.16.0.tar.gz", hash = "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d"}, +] + +[[package]] +name = "wsproto" +version = "1.2.0" +description = "WebSockets state-machine based protocol implementation" +optional = true +python-versions = ">=3.7.0" +files = [ + {file = "wsproto-1.2.0-py3-none-any.whl", hash = "sha256:b9acddd652b585d75b20477888c56642fdade28bdfd3579aa24a4d2c037dd736"}, + {file = "wsproto-1.2.0.tar.gz", hash = "sha256:ad565f26ecb92588a3e43bc3d96164de84cd9902482b130d0ddbaa9664a85065"}, +] + +[package.dependencies] +h11 = ">=0.9.0,<1" + [[package]] name = "xx-sent-ud-sm" version = "3.7.0" @@ -6137,10 +6805,11 @@ test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", type = ["pytest-mypy"] [extras] +chainlit = ["chainlit"] pandoc = ["pypandoc-binary"] ragas = ["ragas"] [metadata] lock-version = "2.0" python-versions = ">=3.10,<4.0" -content-hash = "43e46c87ead1e376f088fb9560919ffdea07af12cc7cb93303ea5e93b30b22fd" +content-hash = "0b68b835b8d0a256c99deb2e0497f912799be1ce022bab73414bcd9d74bfb7ec" diff --git a/pyproject.toml b/pyproject.toml index 169277e..50fbd5c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ build-backend = "poetry.core.masonry.api" [tool.poetry] # https://python-poetry.org/docs/pyproject/ name = "raglite" version = "0.1.4" -description = "A Python package for Retrieval-Augmented Generation (RAG) with SQLite or PostgreSQL." +description = "A Python toolkit for Retrieval-Augmented Generation (RAG) with SQLite or PostgreSQL." authors = ["Laurent Sorber "] readme = "README.md" repository = "https://github.com/superlinear-ai/raglite" @@ -37,7 +37,7 @@ llama-cpp-python = ">=0.2.88" pydantic = ">=2.7.0" # Approximate Nearest Neighbors: pynndescent = ">=0.5.12" -# Reranking +# Reranking: langdetect = ">=1.0.9" rerankers = { extras = ["flashrank"], version = ">=0.5.3" } # Storage: @@ -48,8 +48,13 @@ tqdm = ">=4.66.0" # Evaluation: pandas = ">=2.1.0" ragas = { version = ">=0.1.12", optional = true } +# CLI: +typer = ">=0.12.5" +# Frontend: +chainlit = { version = ">=1.2.0", optional = true } [tool.poetry.extras] # https://python-poetry.org/docs/pyproject/#extras +chainlit = ["chainlit"] pandoc = ["pypandoc-binary"] ragas = ["ragas"] @@ -76,6 +81,9 @@ matplotlib = ">=3.9.0" memory-profiler = ">=0.61.0" pdoc = ">=14.4.0" +[tool.poetry.scripts] # https://python-poetry.org/docs/pyproject/#scripts +raglite = "raglite:cli" + [tool.coverage.report] # https://coverage.readthedocs.io/en/latest/config.html#report fail_under = 50 precision = 1 @@ -104,7 +112,7 @@ show_error_context = true warn_unreachable = true [tool.pytest.ini_options] # https://docs.pytest.org/en/latest/reference/reference.html#ini-options-ref -addopts = "--color=yes --doctest-modules --exitfirst --failed-first --strict-config --strict-markers --verbosity=2 --junitxml=reports/pytest.xml" +addopts = "--color=yes --exitfirst --failed-first --strict-config --strict-markers --verbosity=2 --junitxml=reports/pytest.xml" filterwarnings = ["error", "ignore::DeprecationWarning", "ignore::pytest.PytestUnraisableExceptionWarning"] testpaths = ["src", "tests"] xfail_strict = true diff --git a/src/raglite/__init__.py b/src/raglite/__init__.py index b6d6231..c1c9dc9 100644 --- a/src/raglite/__init__.py +++ b/src/raglite/__init__.py @@ -1,14 +1,15 @@ """RAGLite.""" +from raglite._cli import cli from raglite._config import RAGLiteConfig from raglite._eval import answer_evals, evaluate, insert_evals from raglite._insert import insert_document from raglite._query_adapter import update_query_adapter -from raglite._rag import rag +from raglite._rag import async_rag, rag from raglite._search import ( hybrid_search, keyword_search, - rerank, + rerank_chunks, retrieve_chunks, retrieve_segments, vector_search, @@ -25,8 +26,9 @@ "vector_search", "retrieve_chunks", "retrieve_segments", - "rerank", + "rerank_chunks", # RAG + "async_rag", "rag", # Query adapter "update_query_adapter", @@ -34,4 +36,6 @@ "insert_evals", "answer_evals", "evaluate", + # CLI + "cli", ] diff --git a/src/raglite/_chainlit.py b/src/raglite/_chainlit.py new file mode 100644 index 0000000..9499baf --- /dev/null +++ b/src/raglite/_chainlit.py @@ -0,0 +1,117 @@ +"""Chainlit frontend for RAGLite.""" + +import os +from pathlib import Path + +import chainlit as cl +from chainlit.input_widget import Switch, TextInput + +from raglite import ( + RAGLiteConfig, + async_rag, + hybrid_search, + insert_document, + rerank_chunks, + retrieve_chunks, +) +from raglite._markdown import document_to_markdown + +async_insert_document = cl.make_async(insert_document) +async_hybrid_search = cl.make_async(hybrid_search) +async_retrieve_chunks = cl.make_async(retrieve_chunks) +async_rerank_chunks = cl.make_async(rerank_chunks) + + +@cl.on_chat_start +async def start_chat() -> None: + """Initialize the chat.""" + # Disable tokenizes parallelism to avoid the deadlock warning. + os.environ["TOKENIZERS_PARALLELISM"] = "false" + # Add Chainlit settings with which the user can configure the RAGLite config. + default_config = RAGLiteConfig() + config = RAGLiteConfig( + db_url=os.environ.get("RAGLITE_DB_URL", default_config.db_url), + llm=os.environ.get("RAGLITE_LLM", default_config.llm), + embedder=os.environ.get("RAGLITE_EMBEDDER", default_config.embedder), + ) + settings = await cl.ChatSettings( # type: ignore[no-untyped-call] + [ + TextInput(id="db_url", label="Database URL", initial=str(config.db_url)), + TextInput(id="llm", label="LLM", initial=config.llm), + TextInput(id="embedder", label="Embedder", initial=config.embedder), + Switch(id="vector_search_query_adapter", label="Query adapter", initial=True), + ] + ).send() + await update_config(settings) + + +@cl.on_settings_update # type: ignore[arg-type] +async def update_config(settings: cl.ChatSettings) -> None: + """Update the RAGLite config.""" + # Update the RAGLite config given the Chainlit settings. + config = RAGLiteConfig( + db_url=settings["db_url"], # type: ignore[index] + llm=settings["llm"], # type: ignore[index] + embedder=settings["embedder"], # type: ignore[index] + vector_search_query_adapter=settings["vector_search_query_adapter"], # type: ignore[index] + ) + cl.user_session.set("config", config) # type: ignore[no-untyped-call] + # Run a search to prime the pipeline if it's a local pipeline. + # TODO: Don't do this for SQLite once we switch from PyNNDescent to sqlite-vec. + if str(config.db_url).startswith("sqlite") or config.embedder.startswith("llama-cpp-python"): + # async with cl.Step(name="initialize", type="retrieval"): + query = "Hello world" + chunk_ids, _ = await async_hybrid_search(query=query, config=config) + _ = await async_rerank_chunks(query=query, chunk_ids=chunk_ids, config=config) + + +@cl.on_message +async def handle_message(user_message: cl.Message) -> None: + """Respond to a user message.""" + # Get the config and message history from the user session. + config: RAGLiteConfig = cl.user_session.get("config") # type: ignore[no-untyped-call] + # Determine what to do with the attachments. + inline_attachments = [] + for file in user_message.elements: + if file.path: + doc_md = document_to_markdown(Path(file.path)) + if len(doc_md) // 3 <= 5 * (config.chunk_max_size // 3): + # Document is small enough to attach to the context. + inline_attachments.append(f"{Path(file.path).name}:\n\n{doc_md}") + else: + # Document is too large and must be inserted into the database. + async with cl.Step(name="insert", type="run") as step: + step.input = Path(file.path).name + await async_insert_document(Path(file.path), config=config) + # Append any inline attachments to the user prompt. + user_prompt = f"{user_message.content}\n\n" + "\n\n".join( + f'\n{attachment.strip()}\n' + for i, attachment in enumerate(inline_attachments) + ) + # Search for relevant contexts for RAG. + async with cl.Step(name="search", type="retrieval") as step: + step.input = user_message.content + chunk_ids, _ = await async_hybrid_search(query=user_prompt, num_results=10, config=config) + chunks = await async_retrieve_chunks(chunk_ids=chunk_ids, config=config) + step.output = chunks + step.elements = [ # Show the top 3 chunks inline. + cl.Text(content=str(chunk), display="inline") for chunk in chunks[:3] + ] + # Rerank the chunks. + async with cl.Step(name="rerank", type="rerank") as step: + step.input = chunks + chunks = await async_rerank_chunks(query=user_prompt, chunk_ids=chunks, config=config) + step.output = chunks + step.elements = [ # Show the top 3 chunks inline. + cl.Text(content=str(chunk), display="inline") for chunk in chunks[:3] + ] + # Stream the LLM response. + assistant_message = cl.Message(content="") + async for token in async_rag( + prompt=user_prompt, + search=chunks, + messages=cl.chat_context.to_openai()[-5:], # type: ignore[no-untyped-call] + config=config, + ): + await assistant_message.stream_token(token) + await assistant_message.update() # type: ignore[no-untyped-call] diff --git a/src/raglite/_cli.py b/src/raglite/_cli.py new file mode 100644 index 0000000..9e8b605 --- /dev/null +++ b/src/raglite/_cli.py @@ -0,0 +1,39 @@ +"""RAGLite CLI.""" + +import os + +import typer + +from raglite._config import RAGLiteConfig + +cli = typer.Typer() + + +@cli.callback() +def main() -> None: + """RAGLite CLI.""" + + +@cli.command() +def chainlit( + db_url: str = typer.Option(RAGLiteConfig().db_url, help="Database URL"), + llm: str = typer.Option(RAGLiteConfig().llm, help="LiteLLM LLM"), + embedder: str = typer.Option(RAGLiteConfig().embedder, help="LiteLLM embedder"), +) -> None: + """Serve a Chainlit frontend.""" + # Set the environment variables for the Chainlit frontend. + os.environ["RAGLITE_DB_URL"] = os.environ.get("RAGLITE_DB_URL", db_url) + os.environ["RAGLITE_LLM"] = os.environ.get("RAGLITE_LLM", llm) + os.environ["RAGLITE_EMBEDDER"] = os.environ.get("RAGLITE_EMBEDDER", embedder) + # Import Chainlit here as it's an optional dependency. + try: + from chainlit.cli import run_chainlit + except ImportError as error: + error_message = "To serve a Chainlit frontend, please install the `chainlit` extra." + raise ImportError(error_message) from error + # Serve the frontend. + run_chainlit(__file__.replace("_cli.py", "_chainlit.py")) + + +if __name__ == "__main__": + cli() diff --git a/src/raglite/_config.py b/src/raglite/_config.py index 87e20b1..c3e8fd6 100644 --- a/src/raglite/_config.py +++ b/src/raglite/_config.py @@ -51,7 +51,8 @@ class RAGLiteConfig: default_factory=lambda: ( ("en", FlashRankRanker("ms-marco-MiniLM-L-12-v2", verbose=0)), ("other", FlashRankRanker("ms-marco-MultiBERT-L-12", verbose=0)), - ) + ), + compare=False, # Exclude the reranker from comparison to avoid lru_cache misses. ) def __post_init__(self) -> None: diff --git a/src/raglite/_database.py b/src/raglite/_database.py index 398e058..490772e 100644 --- a/src/raglite/_database.py +++ b/src/raglite/_database.py @@ -1,6 +1,7 @@ """PostgreSQL or SQLite database tables for RAGLite.""" import datetime +import json from functools import lru_cache from hashlib import sha256 from pathlib import Path @@ -19,7 +20,6 @@ Session, SQLModel, create_engine, - select, text, ) @@ -124,13 +124,26 @@ def embedding_matrix(self) -> FloatMatrix: # Uses the relationship chunk.embeddings to access the chunk_embedding table. return np.vstack([embedding.embedding[np.newaxis, :] for embedding in self.embeddings]) + def __hash__(self) -> int: + return hash(self.id) + + def __repr__(self) -> str: + return json.dumps( + { + "id": self.id, + "document_id": self.document_id, + "index": self.index, + "headings": self.headings, + "body": self.body[:100], + "metadata": self.metadata_, + }, + indent=4, + ) + def __str__(self) -> str: """Context representation of this chunk.""" return f"{self.headings.strip()}\n\n{self.body.strip()}".strip() - def __hash__(self) -> int: - return hash(self.id) - class ChunkEmbedding(SQLModel, table=True): """A (sub-)chunk embedding.""" @@ -171,23 +184,9 @@ class IndexMetadata(SQLModel, table=True): default_factory=dict, sa_column=Column("metadata", PickledObject) ) - @staticmethod - def _get_version(id_: str, *, config: RAGLiteConfig | None = None) -> datetime.datetime | None: - """Get the version of the index metadata with a given id.""" - engine = create_database_engine(config) - with Session(engine) as session: - version = session.exec( - select(IndexMetadata.version).where(IndexMetadata.id == id_) - ).first() - return version - @staticmethod @lru_cache(maxsize=4) - def _get( - id_: str, version: datetime.datetime | None, *, config: RAGLiteConfig | None = None - ) -> dict[str, Any] | None: - if version is None: - return None + def _get(id_: str, *, config: RAGLiteConfig | None = None) -> dict[str, Any] | None: engine = create_database_engine(config) with Session(engine) as session: index_metadata_record = session.get(IndexMetadata, id_) @@ -197,8 +196,7 @@ def _get( @staticmethod def get(id_: str = "default", *, config: RAGLiteConfig | None = None) -> dict[str, Any]: - version = IndexMetadata._get_version(id_, config=config) - metadata = IndexMetadata._get(id_, version, config=config) or {} + metadata = IndexMetadata._get(id_, config=config) or {} return metadata diff --git a/src/raglite/_litellm.py b/src/raglite/_litellm.py index 8a98a4d..5f4e565 100644 --- a/src/raglite/_litellm.py +++ b/src/raglite/_litellm.py @@ -1,8 +1,9 @@ """Add support for llama-cpp-python models to LiteLLM.""" +import asyncio import logging import warnings -from collections.abc import Callable, Iterator +from collections.abc import AsyncIterator, Callable, Iterator from functools import cache from typing import Any, ClassVar, cast @@ -14,7 +15,7 @@ ModelResponse, convert_to_model_response_object, ) -from litellm.llms.custom_httpx.http_handler import HTTPHandler +from litellm.llms.custom_httpx.http_handler import AsyncHTTPHandler, HTTPHandler from llama_cpp import ( # type: ignore[attr-defined] ChatCompletionRequestMessage, CreateChatCompletionResponse, @@ -49,6 +50,9 @@ class LlamaCppPythonLLM(CustomLLM): ``` """ + # Create a lock to prevent concurrent access to llama-cpp-python models. + streaming_lock: ClassVar[asyncio.Lock] = asyncio.Lock() + # The set of supported OpenAI parameters is the intersection of [1] and [2]. Not included: # max_completion_tokens, stream_options, n, user, logprobs, top_logprobs, extra_headers. # [1] https://llama-cpp-python.readthedocs.io/en/latest/api-reference/#llama_cpp.Llama.create_chat_completion @@ -204,6 +208,50 @@ def streaming( # noqa: PLR0913 ) yield litellm_generic_streaming_chunk + async def astreaming( # type: ignore[misc,override] # noqa: PLR0913 + self, + model: str, + messages: list[ChatCompletionRequestMessage], + api_base: str, + custom_prompt_dict: dict[str, Any], + model_response: ModelResponse, + print_verbose: Callable, # type: ignore[type-arg] + encoding: str, + api_key: str, + logging_obj: Any, + optional_params: dict[str, Any], + acompletion: Callable | None = None, # type: ignore[type-arg] + litellm_params: dict[str, Any] | None = None, + logger_fn: Callable | None = None, # type: ignore[type-arg] + headers: dict[str, Any] | None = None, + timeout: float | httpx.Timeout | None = None, # noqa: ASYNC109 + client: AsyncHTTPHandler | None = None, + ) -> AsyncIterator[GenericStreamingChunk]: + # Start a synchronous stream. + stream = self.streaming( + model, + messages, + api_base, + custom_prompt_dict, + model_response, + print_verbose, + encoding, + api_key, + logging_obj, + optional_params, + acompletion, + litellm_params, + logger_fn, + headers, + timeout, + ) + await asyncio.sleep(0) # Yield control to the event loop after initialising the context. + # Wrap the synchronous stream in an asynchronous stream. + async with LlamaCppPythonLLM.streaming_lock: + for litellm_generic_streaming_chunk in stream: + yield litellm_generic_streaming_chunk + await asyncio.sleep(0) # Yield control to the event loop after each token. + # Register the LlamaCppPythonLLM provider. if not any(provider["provider"] == "llama-cpp-python" for provider in litellm.custom_provider_map): diff --git a/src/raglite/_markdown.py b/src/raglite/_markdown.py index 389bcba..f6a38bd 100644 --- a/src/raglite/_markdown.py +++ b/src/raglite/_markdown.py @@ -203,10 +203,19 @@ def document_to_markdown(doc_path: Path) -> str: pages = dictionary_output(doc_path, sort=True, keep_chars=False) doc = "\n\n".join(parsed_pdf_to_markdown(pages)) else: - # Use pandoc for everything else. - import pypandoc - - doc = pypandoc.convert_file(doc_path, to="gfm") + try: + # Use pandoc for everything else. + import pypandoc + + doc = pypandoc.convert_file(doc_path, to="gfm") + except ImportError as error: + error_message = ( + "To convert files to Markdown with pandoc, please install the `pandoc` extra." + ) + raise ImportError(error_message) from error + except RuntimeError: + # File format not supported, fall back to reading the text. + doc = doc_path.read_text() # Improve Markdown quality. doc = mdformat.text(doc) return doc diff --git a/src/raglite/_rag.py b/src/raglite/_rag.py index 0c42524..643a374 100644 --- a/src/raglite/_rag.py +++ b/src/raglite/_rag.py @@ -1,72 +1,166 @@ """Retrieval-augmented generation.""" -from collections.abc import Iterator +from collections.abc import AsyncIterator, Iterator -from litellm import completion, get_model_info # type: ignore[attr-defined] +from litellm import acompletion, completion, get_model_info # type: ignore[attr-defined] from raglite._config import RAGLiteConfig from raglite._database import Chunk from raglite._litellm import LlamaCppPythonLLM -from raglite._search import hybrid_search, rerank, retrieve_segments +from raglite._search import hybrid_search, rerank_chunks, retrieve_segments from raglite._typing import SearchMethod +RAG_SYSTEM_PROMPT = """ +You are a friendly and knowledgeable assistant that provides complete and insightful answers. +Answer the user's question using only the context below. +When responding, you MUST NOT reference the existence of the context, directly or indirectly. +Instead, you MUST treat the context as if its contents are entirely part of your working memory. +""".strip() -def rag( + +def _max_contexts( prompt: str, *, max_contexts: int = 5, context_neighbors: tuple[int, ...] | None = (-1, 1), - search: SearchMethod | list[str] | list[Chunk] = hybrid_search, + messages: list[dict[str, str]] | None = None, config: RAGLiteConfig | None = None, -) -> Iterator[str]: - """Retrieval-augmented generation.""" +) -> int: + """Determine the maximum number of contexts for RAG.""" # If the user has configured a llama-cpp-python model, we ensure that LiteLLM's model info is up # to date by loading that LLM. config = config or RAGLiteConfig() if config.llm.startswith("llama-cpp-python"): _ = LlamaCppPythonLLM.llm(config.llm) - # Reduce the maximum number of contexts to take into account the LLM's context size. + # Get the model's maximum context size. llm_provider = "llama-cpp-python" if config.llm.startswith("llama-cpp") else None model_info = get_model_info(config.llm, custom_llm_provider=llm_provider) - max_tokens = (model_info.get("max_tokens") or 2048) - 256 - max_tokens_per_context = round(1.2 * (config.chunk_max_size // 4)) + max_tokens = model_info.get("max_tokens") or 2048 + # Reduce the maximum number of contexts to take into account the LLM's context size. + max_context_tokens = ( + max_tokens + - sum(len(message["content"]) // 3 for message in messages or []) # Previous messages. + - len(RAG_SYSTEM_PROMPT) // 3 # System prompt. + - len(prompt) // 3 # User prompt. + ) + max_tokens_per_context = config.chunk_max_size // 3 max_tokens_per_context *= 1 + len(context_neighbors or []) - max_contexts = min(max_contexts, max_tokens // max_tokens_per_context) + max_contexts = min(max_contexts, max_context_tokens // max_tokens_per_context) + if max_contexts <= 0: + error_message = "Not enough context tokens available for RAG." + raise ValueError(error_message) + return max_contexts + + +def _contexts( # noqa: PLR0913 + prompt: str, + *, + max_contexts: int = 5, + context_neighbors: tuple[int, ...] | None = (-1, 1), + search: SearchMethod | list[str] | list[Chunk] = hybrid_search, + messages: list[dict[str, str]] | None = None, + config: RAGLiteConfig | None = None, +) -> list[str]: + """Retrieve contexts for RAG.""" + # Determine the maximum number of contexts. + max_contexts = _max_contexts( + prompt, + max_contexts=max_contexts, + context_neighbors=context_neighbors, + messages=messages, + config=config, + ) # Retrieve the top chunks. + config = config or RAGLiteConfig() chunks: list[str] | list[Chunk] if callable(search): # If the user has configured a reranker, we retrieve extra contexts to rerank. - extra_contexts = 4 * max_contexts if config.reranker else 0 + extra_contexts = 3 * max_contexts if config.reranker else 0 # Retrieve relevant contexts. chunk_ids, _ = search(prompt, num_results=max_contexts + extra_contexts, config=config) # Rerank the relevant contexts. - chunks = rerank(query=prompt, chunk_ids=chunk_ids, config=config) + chunks = rerank_chunks(query=prompt, chunk_ids=chunk_ids, config=config) else: # The user has passed a list of chunk_ids or chunks directly. chunks = search # Extend the top contexts with their neighbors and group chunks into contiguous segments. segments = retrieve_segments(chunks[:max_contexts], neighbors=context_neighbors, config=config) - # Respond with an LLM. - contexts = "\n\n".join( + return segments + + +def rag( # noqa: PLR0913 + prompt: str, + *, + max_contexts: int = 5, + context_neighbors: tuple[int, ...] | None = (-1, 1), + search: SearchMethod | list[str] | list[Chunk] = hybrid_search, + messages: list[dict[str, str]] | None = None, + system_prompt: str = RAG_SYSTEM_PROMPT, + config: RAGLiteConfig | None = None, +) -> Iterator[str]: + """Retrieval-augmented generation.""" + # Get the contexts for RAG as contiguous segments of chunks. + config = config or RAGLiteConfig() + segments = _contexts( + prompt, + max_contexts=max_contexts, + context_neighbors=context_neighbors, + search=search, + config=config, + ) + system_prompt = f"{system_prompt}\n\n" + "\n\n".join( f'\n{segment.strip()}\n' for i, segment in enumerate(segments) ) - system_prompt = f""" -You are a friendly and knowledgeable assistant that provides complete and insightful answers. -Answer the user's question using only the context below. -When responding, you MUST NOT reference the existence of the context, directly or indirectly. -Instead, you MUST treat the context as if its contents are entirely part of your working memory. - -{contexts}""".strip() + # Stream the LLM response. stream = completion( model=config.llm, messages=[ + *(messages or []), {"role": "system", "content": system_prompt}, {"role": "user", "content": prompt}, ], stream=True, ) - # Stream the response. for output in stream: token: str = output["choices"][0]["delta"].get("content") or "" yield token + + +async def async_rag( # noqa: PLR0913 + prompt: str, + *, + max_contexts: int = 5, + context_neighbors: tuple[int, ...] | None = (-1, 1), + search: SearchMethod | list[str] | list[Chunk] = hybrid_search, + messages: list[dict[str, str]] | None = None, + system_prompt: str = RAG_SYSTEM_PROMPT, + config: RAGLiteConfig | None = None, +) -> AsyncIterator[str]: + """Retrieval-augmented generation.""" + # Get the contexts for RAG as contiguous segments of chunks. + config = config or RAGLiteConfig() + segments = _contexts( + prompt, + max_contexts=max_contexts, + context_neighbors=context_neighbors, + search=search, + config=config, + ) + system_prompt = f"{system_prompt}\n\n" + "\n\n".join( + f'\n{segment.strip()}\n' + for i, segment in enumerate(segments) + ) + # Stream the LLM response. + async_stream = await acompletion( + model=config.llm, + messages=[ + *(messages or []), + {"role": "system", "content": system_prompt}, + {"role": "user", "content": prompt}, + ], + stream=True, + ) + async for output in async_stream: + token: str = output["choices"][0]["delta"].get("content") or "" + yield token diff --git a/src/raglite/_search.py b/src/raglite/_search.py index d1714a5..c4f006a 100644 --- a/src/raglite/_search.py +++ b/src/raglite/_search.py @@ -232,7 +232,7 @@ def retrieve_segments( return segments # type: ignore[return-value] -def rerank( +def rerank_chunks( query: str, chunk_ids: list[str] | list[Chunk], *, diff --git a/tests/test_rerank.py b/tests/test_rerank.py index edbf300..7a10ea2 100644 --- a/tests/test_rerank.py +++ b/tests/test_rerank.py @@ -3,7 +3,7 @@ import pytest from rerankers.models.ranker import BaseRanker -from raglite import RAGLiteConfig, hybrid_search, rerank, retrieve_chunks +from raglite import RAGLiteConfig, hybrid_search, rerank_chunks, retrieve_chunks from raglite._database import Chunk from raglite._flashrank import PatchedFlashRankRanker as FlashRankRanker @@ -46,10 +46,10 @@ def test_reranker( assert all(isinstance(chunk, Chunk) for chunk in chunks) assert all(chunk_id == chunk.id for chunk_id, chunk in zip(chunk_ids, chunks, strict=True)) # Rerank the chunks given an inverted chunk order. - reranked_chunks = rerank(query, chunks[::-1], config=raglite_test_config) + reranked_chunks = rerank_chunks(query, chunks[::-1], config=raglite_test_config) if reranker is not None and "text-embedding-3-small" not in raglite_test_config.embedder: assert reranked_chunks[0] == chunks[0] # Test that we can also rerank given the chunk_ids only. - reranked_chunks = rerank(query, chunk_ids[::-1], config=raglite_test_config) + reranked_chunks = rerank_chunks(query, chunk_ids[::-1], config=raglite_test_config) if reranker is not None and "text-embedding-3-small" not in raglite_test_config.embedder: assert reranked_chunks[0] == chunks[0]