diff --git a/.github/scripts/check_diff.py b/.github/scripts/check_diff.py index fbbe47aa4a99b..a85f05dc613f8 100644 --- a/.github/scripts/check_diff.py +++ b/.github/scripts/check_diff.py @@ -272,6 +272,9 @@ def _get_configs_for_multi_dirs( # TODO: update to include all packages that rely on standard-tests (all partner packages) # note: won't run on external repo partners dirs_to_run["lint"].add("libs/standard-tests") + dirs_to_run["test"].add("libs/standard-tests") + dirs_to_run["lint"].add("libs/cli") + dirs_to_run["test"].add("libs/cli") dirs_to_run["test"].add("libs/partners/mistralai") dirs_to_run["test"].add("libs/partners/openai") dirs_to_run["test"].add("libs/partners/anthropic") @@ -279,8 +282,9 @@ def _get_configs_for_multi_dirs( dirs_to_run["test"].add("libs/partners/groq") elif file.startswith("libs/cli"): - # todo: add cli makefile - pass + dirs_to_run["lint"].add("libs/cli") + dirs_to_run["test"].add("libs/cli") + elif file.startswith("libs/partners"): partner_dir = file.split("/")[2] if os.path.isdir(f"libs/partners/{partner_dir}") and [ diff --git a/.github/workflows/check_diffs.yml b/.github/workflows/check_diffs.yml index 61c921c03b9e4..8e3bdadff8861 100644 --- a/.github/workflows/check_diffs.yml +++ b/.github/workflows/check_diffs.yml @@ -5,6 +5,7 @@ on: push: branches: [master] pull_request: + merge_group: # If another push to the same PR or branch happens while this workflow is still running, # cancel the earlier run in favor of the next run. diff --git a/.github/workflows/scheduled_test.yml b/.github/workflows/scheduled_test.yml index 4134afd1d3900..bcf489e52b107 100644 --- a/.github/workflows/scheduled_test.yml +++ b/.github/workflows/scheduled_test.yml @@ -14,17 +14,7 @@ on: env: POETRY_VERSION: "1.8.4" - DEFAULT_LIBS: > - [ - "libs/partners/openai", - "libs/partners/anthropic", - "libs/partners/fireworks", - "libs/partners/groq", - "libs/partners/mistralai", - "libs/partners/google-vertexai", - "libs/partners/google-genai", - "libs/partners/aws" - ] + DEFAULT_LIBS: '["libs/partners/openai", "libs/partners/anthropic", "libs/partners/fireworks", "libs/partners/groq", "libs/partners/mistralai", "libs/partners/google-vertexai", "libs/partners/google-genai", "libs/partners/aws"]' jobs: compute-matrix: diff --git a/README.md b/README.md index 04de99fad0f4c..dd8643d4b0a40 100644 --- a/README.md +++ b/README.md @@ -38,18 +38,21 @@ conda install langchain -c conda-forge For these applications, LangChain simplifies the entire application lifecycle: -- **Open-source libraries**: Build your applications using LangChain's open-source [building blocks](https://python.langchain.com/docs/concepts/#langchain-expression-language-lcel), [components](https://python.langchain.com/docs/concepts/), and [third-party integrations](https://python.langchain.com/docs/integrations/providers/). + +- **Open-source libraries**: Build your applications using LangChain's open-source +[components](https://python.langchain.com/docs/concepts/) and +[third-party integrations](https://python.langchain.com/docs/integrations/providers/). Use [LangGraph](https://langchain-ai.github.io/langgraph/) to build stateful agents with first-class streaming and human-in-the-loop support. - **Productionization**: Inspect, monitor, and evaluate your apps with [LangSmith](https://docs.smith.langchain.com/) so that you can constantly optimize and deploy with confidence. -- **Deployment**: Turn your LangGraph applications into production-ready APIs and Assistants with [LangGraph Cloud](https://langchain-ai.github.io/langgraph/cloud/). +- **Deployment**: Turn your LangGraph applications into production-ready APIs and Assistants with [LangGraph Platform](https://langchain-ai.github.io/langgraph/cloud/). ### Open-source libraries -- **`langchain-core`**: Base abstractions and LangChain Expression Language. -- **`langchain-community`**: Third party integrations. - - Some integrations have been further split into **partner packages** that only rely on **`langchain-core`**. Examples include **`langchain_openai`** and **`langchain_anthropic`**. +- **`langchain-core`**: Base abstractions. +- **Integration packages** (e.g. **`langchain-openai`**, **`langchain-anthropic`**, etc.): Important integrations have been split into lightweight packages that are co-maintained by the LangChain team and the integration developers. - **`langchain`**: Chains, agents, and retrieval strategies that make up an application's cognitive architecture. -- **[`LangGraph`](https://langchain-ai.github.io/langgraph/)**: A library for building robust and stateful multi-actor applications with LLMs by modeling steps as edges and nodes in a graph. Integrates smoothly with LangChain, but can be used without it. To learn more about LangGraph, check out our first LangChain Academy course, *Introduction to LangGraph*, available [here](https://academy.langchain.com/courses/intro-to-langgraph). +- **`langchain-community`**: Third-party integrations that are community maintained. +- **[LangGraph](https://langchain-ai.github.io/langgraph)**: Build robust and stateful multi-actor applications with LLMs by modeling steps as edges and nodes in a graph. Integrates smoothly with LangChain, but can be used without it. To learn more about LangGraph, check out our first LangChain Academy course, *Introduction to LangGraph*, available [here](https://academy.langchain.com/courses/intro-to-langgraph). ### Productionization: @@ -57,7 +60,7 @@ For these applications, LangChain simplifies the entire application lifecycle: ### Deployment: -- **[LangGraph Cloud](https://langchain-ai.github.io/langgraph/cloud/)**: Turn your LangGraph applications into production-ready APIs and Assistants. +- **[LangGraph Platform](https://langchain-ai.github.io/langgraph/cloud/)**: Turn your LangGraph applications into production-ready APIs and Assistants. ![Diagram outlining the hierarchical organization of the LangChain framework, displaying the interconnected parts across multiple layers.](docs/static/svg/langchain_stack_112024.svg#gh-light-mode-only "LangChain Architecture Overview") ![Diagram outlining the hierarchical organization of the LangChain framework, displaying the interconnected parts across multiple layers.](docs/static/svg/langchain_stack_112024_dark.svg#gh-dark-mode-only "LangChain Architecture Overview") @@ -85,19 +88,12 @@ And much more! Head to the [Tutorials](https://python.langchain.com/docs/tutoria The main value props of the LangChain libraries are: -1. **Components**: composable building blocks, tools and integrations for working with language models. Components are modular and easy-to-use, whether you are using the rest of the LangChain framework or not -2. **Off-the-shelf chains**: built-in assemblages of components for accomplishing higher-level tasks - -Off-the-shelf chains make it easy to get started. Components make it easy to customize existing chains and build new ones. - -## LangChain Expression Language (LCEL) - -LCEL is a key part of LangChain, allowing you to build and organize chains of processes in a straightforward, declarative manner. It was designed to support taking prototypes directly into production without needing to alter any code. This means you can use LCEL to set up everything from basic "prompt + LLM" setups to intricate, multi-step workflows. - -- **[Overview](https://python.langchain.com/docs/concepts/#langchain-expression-language-lcel)**: LCEL and its benefits -- **[Interface](https://python.langchain.com/docs/concepts/#runnable-interface)**: The standard Runnable interface for LCEL objects -- **[Primitives](https://python.langchain.com/docs/how_to/#langchain-expression-language-lcel)**: More on the primitives LCEL includes -- **[Cheatsheet](https://python.langchain.com/docs/how_to/lcel_cheatsheet/)**: Quick overview of the most common usage patterns +1. **Components**: composable building blocks, tools and integrations for working with language models. Components are modular and easy-to-use, whether you are using the rest of the LangChain framework or not. +2. **Easy orchestration with LangGraph**: [LangGraph](https://langchain-ai.github.io/langgraph/), +built on top of `langchain-core`, has built-in support for [messages](https://python.langchain.com/docs/concepts/messages/), [tools](https://python.langchain.com/docs/concepts/tools/), +and other LangChain abstractions. This makes it easy to combine components into +production-ready applications with persistence, streaming, and other key features. +Check out the LangChain [tutorials page](https://python.langchain.com/docs/tutorials/#orchestration) for examples. ## Components @@ -105,15 +101,19 @@ Components fall into the following **modules**: **📃 Model I/O** -This includes [prompt management](https://python.langchain.com/docs/concepts/#prompt-templates), [prompt optimization](https://python.langchain.com/docs/concepts/#example-selectors), a generic interface for [chat models](https://python.langchain.com/docs/concepts/#chat-models) and [LLMs](https://python.langchain.com/docs/concepts/#llms), and common utilities for working with [model outputs](https://python.langchain.com/docs/concepts/#output-parsers). +This includes [prompt management](https://python.langchain.com/docs/concepts/prompt_templates/) +and a generic interface for [chat models](https://python.langchain.com/docs/concepts/chat_models/), including a consistent interface for [tool-calling](https://python.langchain.com/docs/concepts/tool_calling/) and [structured output](https://python.langchain.com/docs/concepts/structured_outputs/) across model providers. **📚 Retrieval** -Retrieval Augmented Generation involves [loading data](https://python.langchain.com/docs/concepts/#document-loaders) from a variety of sources, [preparing it](https://python.langchain.com/docs/concepts/#text-splitters), then [searching over (a.k.a. retrieving from)](https://python.langchain.com/docs/concepts/#retrievers) it for use in the generation step. +Retrieval Augmented Generation involves [loading data](https://python.langchain.com/docs/concepts/document_loaders/) from a variety of sources, [preparing it](https://python.langchain.com/docs/concepts/text_splitters/), then [searching over (a.k.a. retrieving from)](https://python.langchain.com/docs/concepts/retrievers/) it for use in the generation step. **🤖 Agents** -Agents allow an LLM autonomy over how a task is accomplished. Agents make decisions about which Actions to take, then take that Action, observe the result, and repeat until the task is complete. LangChain provides a [standard interface for agents](https://python.langchain.com/docs/concepts/#agents), along with [LangGraph](https://github.com/langchain-ai/langgraph) for building custom agents. +Agents allow an LLM autonomy over how a task is accomplished. Agents make decisions about which Actions to take, then take that Action, observe the result, and repeat until the task is complete. [LangGraph](https://langchain-ai.github.io/langgraph/) makes it easy to use +LangChain components to build both [custom](https://langchain-ai.github.io/langgraph/tutorials/) +and [built-in](https://langchain-ai.github.io/langgraph/how-tos/create-react-agent/) +LLM agents. ## 📖 Documentation diff --git a/SECURITY.md b/SECURITY.md index 50e0632582c68..15e44be0b4314 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,5 +1,30 @@ # Security Policy +LangChain has a large ecosystem of integrations with various external resources like local and remote file systems, APIs and databases. These integrations allow developers to create versatile applications that combine the power of LLMs with the ability to access, interact with and manipulate external resources. + +## Best practices + +When building such applications developers should remember to follow good security practices: + +* [**Limit Permissions**](https://en.wikipedia.org/wiki/Principle_of_least_privilege): Scope permissions specifically to the application's need. Granting broad or excessive permissions can introduce significant security vulnerabilities. To avoid such vulnerabilities, consider using read-only credentials, disallowing access to sensitive resources, using sandboxing techniques (such as running inside a container), specifying proxy configurations to control external requests, etc. as appropriate for your application. +* **Anticipate Potential Misuse**: Just as humans can err, so can Large Language Models (LLMs). Always assume that any system access or credentials may be used in any way allowed by the permissions they are assigned. For example, if a pair of database credentials allows deleting data, it’s safest to assume that any LLM able to use those credentials may in fact delete data. +* [**Defense in Depth**](https://en.wikipedia.org/wiki/Defense_in_depth_(computing)): No security technique is perfect. Fine-tuning and good chain design can reduce, but not eliminate, the odds that a Large Language Model (LLM) may make a mistake. It’s best to combine multiple layered security approaches rather than relying on any single layer of defense to ensure security. For example: use both read-only permissions and sandboxing to ensure that LLMs are only able to access data that is explicitly meant for them to use. + +Risks of not doing so include, but are not limited to: +* Data corruption or loss. +* Unauthorized access to confidential information. +* Compromised performance or availability of critical resources. + +Example scenarios with mitigation strategies: + +* A user may ask an agent with access to the file system to delete files that should not be deleted or read the content of files that contain sensitive information. To mitigate, limit the agent to only use a specific directory and only allow it to read or write files that are safe to read or write. Consider further sandboxing the agent by running it in a container. +* A user may ask an agent with write access to an external API to write malicious data to the API, or delete data from that API. To mitigate, give the agent read-only API keys, or limit it to only use endpoints that are already resistant to such misuse. +* A user may ask an agent with access to a database to drop a table or mutate the schema. To mitigate, scope the credentials to only the tables that the agent needs to access and consider issuing READ-ONLY credentials. + +If you're building applications that access external resources like file systems, APIs +or databases, consider speaking with your company's security team to determine how to best +design and secure your applications. + ## Reporting OSS Vulnerabilities LangChain is partnered with [huntr by Protect AI](https://huntr.com/) to provide @@ -14,7 +39,7 @@ Before reporting a vulnerability, please review: 1) In-Scope Targets and Out-of-Scope Targets below. 2) The [langchain-ai/langchain](https://python.langchain.com/docs/contributing/repo_structure) monorepo structure. -3) LangChain [security guidelines](https://python.langchain.com/docs/security) to +3) The [Best practicies](#best-practices) above to understand what we consider to be a security vulnerability vs. developer responsibility. @@ -33,13 +58,13 @@ The following packages and repositories are eligible for bug bounties: All out of scope targets defined by huntr as well as: - **langchain-experimental**: This repository is for experimental code and is not - eligible for bug bounties, bug reports to it will be marked as interesting or waste of + eligible for bug bounties (see [package warning](https://pypi.org/project/langchain-experimental/)), bug reports to it will be marked as interesting or waste of time and published with no bounty attached. - **tools**: Tools in either langchain or langchain-community are not eligible for bug bounties. This includes the following directories - - langchain/tools - - langchain-community/tools - - Please review our [security guidelines](https://python.langchain.com/docs/security) + - libs/langchain/langchain/tools + - libs/community/langchain_community/tools + - Please review the [best practices](#best-practices) for more details, but generally tools interact with the real world. Developers are expected to understand the security implications of their code and are responsible for the security of their tools. @@ -47,7 +72,7 @@ All out of scope targets defined by huntr as well as: case basis, but likely will not be eligible for a bounty as the code is already documented with guidelines for developers that should be followed for making their application secure. -- Any LangSmith related repositories or APIs see below. +- Any LangSmith related repositories or APIs (see [Reporting LangSmith Vulnerabilities](#reporting-langsmith-vulnerabilities)). ## Reporting LangSmith Vulnerabilities diff --git a/docs/Makefile b/docs/Makefile index a3c41260e3dd0..5df104397aade 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -13,28 +13,21 @@ OUTPUT_NEW_DOCS_DIR = $(OUTPUT_NEW_DIR)/docs PYTHON = .venv/bin/python -PARTNER_DEPS_LIST := $(shell find ../libs/partners -mindepth 1 -maxdepth 1 -type d -exec sh -c ' \ -for dir; do \ - if find "$$dir" -maxdepth 1 -type f \( -name "pyproject.toml" -o -name "setup.py" \) | grep -q .; then \ - echo "$$dir"; \ - fi \ -done' sh {} + | grep -vE "airbyte|ibm|databricks" | tr '\n' ' ') - PORT ?= 3001 clean: rm -rf build install-vercel-deps: - yum -y update - yum install gcc bzip2-devel libffi-devel zlib-devel wget tar gzip rsync -y + yum -y -q update + yum -y -q install gcc bzip2-devel libffi-devel zlib-devel wget tar gzip rsync -y install-py-deps: python3 -m venv .venv - $(PYTHON) -m pip install --upgrade pip - $(PYTHON) -m pip install --upgrade uv - $(PYTHON) -m uv pip install --pre -r vercel_requirements.txt - $(PYTHON) -m uv pip install --pre --editable $(PARTNER_DEPS_LIST) + $(PYTHON) -m pip install -q --upgrade pip + $(PYTHON) -m pip install -q --upgrade uv + $(PYTHON) -m uv pip install -q --pre -r vercel_requirements.txt + $(PYTHON) -m uv pip install -q --pre $$($(PYTHON) scripts/partner_deps_list.py) generate-files: mkdir -p $(INTERMEDIATE_DIR) @@ -47,6 +40,7 @@ generate-files: $(PYTHON) scripts/partner_pkg_table.py $(INTERMEDIATE_DIR) curl https://raw.githubusercontent.com/langchain-ai/langserve/main/README.md | sed 's/<=/\<=/g' > $(INTERMEDIATE_DIR)/langserve.md + cp ../SECURITY.md $(INTERMEDIATE_DIR)/security.md $(PYTHON) scripts/resolve_local_links.py $(INTERMEDIATE_DIR)/langserve.md https://github.com/langchain-ai/langserve/tree/main/ copy-infra: @@ -59,6 +53,7 @@ copy-infra: cp package.json $(OUTPUT_NEW_DIR) cp sidebars.js $(OUTPUT_NEW_DIR) cp -r static $(OUTPUT_NEW_DIR) + cp -r ../libs/cli/langchain_cli/integration_template $(OUTPUT_NEW_DIR)/src/theme cp yarn.lock $(OUTPUT_NEW_DIR) render: @@ -80,6 +75,7 @@ build: install-py-deps generate-files copy-infra render md-sync append-related vercel-build: install-vercel-deps build generate-references rm -rf docs mv $(OUTPUT_NEW_DOCS_DIR) docs + cp -r ../libs/cli/langchain_cli/integration_template src/theme rm -rf build mkdir static/api_reference git clone --depth=1 https://github.com/langchain-ai/langchain-api-docs-html.git diff --git a/docs/api_reference/_static/css/custom.css b/docs/api_reference/_static/css/custom.css index 87195de8f72ef..f98ef26197313 100644 --- a/docs/api_reference/_static/css/custom.css +++ b/docs/api_reference/_static/css/custom.css @@ -80,6 +80,8 @@ html { --pst-font-family-base: 'Inter'; --pst-font-family-heading: 'Inter Tight', sans-serif; + + --pst-icon-versionmodified-deprecated: var(--pst-icon-exclamation-triangle); } /******************************************************************************* @@ -92,50 +94,7 @@ html { * https://sass-lang.com/documentation/interpolation */ /* Defaults to light mode if data-theme is not set */ -html:not([data-theme]) { - --pst-color-primary: #287977; - --pst-color-primary-bg: #80D6D3; - --pst-color-secondary: #6F3AED; - --pst-color-secondary-bg: #DAD6FE; - --pst-color-accent: #c132af; - --pst-color-accent-bg: #f8dff5; - --pst-color-info: #276be9; - --pst-color-info-bg: #dce7fc; - --pst-color-warning: #f66a0a; - --pst-color-warning-bg: #f8e3d0; - --pst-color-success: #00843f; - --pst-color-success-bg: #d6ece1; - --pst-color-attention: var(--pst-color-warning); - --pst-color-attention-bg: var(--pst-color-warning-bg); - --pst-color-danger: #d72d47; - --pst-color-danger-bg: #f9e1e4; - --pst-color-text-base: #222832; - --pst-color-text-muted: #48566b; - --pst-color-heading-color: #ffffff; - --pst-color-shadow: rgba(0, 0, 0, 0.1); - --pst-color-border: #d1d5da; - --pst-color-border-muted: rgba(23, 23, 26, 0.2); - --pst-color-inline-code: #912583; - --pst-color-inline-code-links: #246161; - --pst-color-target: #f3cf95; - --pst-color-background: #ffffff; - --pst-color-on-background: #F4F9F8; - --pst-color-surface: #F4F9F8; - --pst-color-on-surface: #222832; -} -html:not([data-theme]) { - --pst-color-link: var(--pst-color-primary); - --pst-color-link-hover: var(--pst-color-secondary); -} -html:not([data-theme]) .only-dark, -html:not([data-theme]) .only-dark ~ figcaption { - display: none !important; -} - -/* NOTE: @each {...} is like a for-loop - * https://sass-lang.com/documentation/at-rules/control/each - */ -html[data-theme=light] { +html:not([data-theme]), html[data-theme=light] { --pst-color-primary: #287977; --pst-color-primary-bg: #80D6D3; --pst-color-secondary: #6F3AED; @@ -165,15 +124,8 @@ html[data-theme=light] { --pst-color-on-background: #F4F9F8; --pst-color-surface: #F4F9F8; --pst-color-on-surface: #222832; - color-scheme: light; -} -html[data-theme=light] { - --pst-color-link: var(--pst-color-primary); - --pst-color-link-hover: var(--pst-color-secondary); -} -html[data-theme=light] .only-dark, -html[data-theme=light] .only-dark ~ figcaption { - display: none !important; + --pst-color-deprecated: #f47d2e; + --pst-color-deprecated-bg: #fff3e8; } html[data-theme=dark] { @@ -206,6 +158,8 @@ html[data-theme=dark] { --pst-color-on-background: #222832; --pst-color-surface: #29313d; --pst-color-on-surface: #f3f4f5; + --pst-color-deprecated: #b46f3e; + --pst-color-deprecated-bg: #341906; /* Adjust images in dark mode (unless they have class .only-dark or * .dark-light, in which case assume they're already optimized for dark * mode). @@ -216,6 +170,30 @@ html[data-theme=dark] { */ color-scheme: dark; } + +html:not([data-theme]) { + --pst-color-link: var(--pst-color-primary); + --pst-color-link-hover: var(--pst-color-secondary); +} +html:not([data-theme]) .only-dark, +html:not([data-theme]) .only-dark ~ figcaption { + display: none !important; +} + +/* NOTE: @each {...} is like a for-loop + * https://sass-lang.com/documentation/at-rules/control/each + */ +html[data-theme=light] { + color-scheme: light; +} +html[data-theme=light] { + --pst-color-link: var(--pst-color-primary); + --pst-color-link-hover: var(--pst-color-secondary); +} +html[data-theme=light] .only-dark, +html[data-theme=light] .only-dark ~ figcaption { + display: none !important; +} html[data-theme=dark] { --pst-color-link: var(--pst-color-primary); --pst-color-link-hover: var(--pst-color-secondary); @@ -389,6 +367,13 @@ html[data-theme=dark] .MathJax_SVG * { div.deprecated { margin-top: 0.5em; margin-bottom: 2em; + + background-color: var(--pst-color-deprecated-bg); + border-color: var(--pst-color-deprecated); +} + +span.versionmodified.deprecated:before { + color: var(--pst-color-deprecated); } .admonition-beta.admonition, div.admonition-beta.admonition { @@ -408,4 +393,4 @@ dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(. p { font-size: 0.9rem; margin-bottom: 0.5rem; -} \ No newline at end of file +} diff --git a/docs/api_reference/conf.py b/docs/api_reference/conf.py index 3f742b051571a..b6a0464cb8fdc 100644 --- a/docs/api_reference/conf.py +++ b/docs/api_reference/conf.py @@ -87,6 +87,18 @@ def run(self): def setup(app): app.add_directive("example_links", ExampleLinksDirective) app.add_directive("beta", Beta) + app.connect("autodoc-skip-member", skip_private_members) + + +def skip_private_members(app, what, name, obj, skip, options): + if skip: + return True + if hasattr(obj, "__doc__") and obj.__doc__ and ":private:" in obj.__doc__: + return True + if name == "__init__" and obj.__objclass__ is object: + # dont document default init + return True + return None # -- Project information ----------------------------------------------------- @@ -116,6 +128,7 @@ def setup(app): "_extensions.gallery_directive", "sphinx_design", "sphinx_copybutton", + "sphinxcontrib.googleanalytics", ] source_suffix = [".rst", ".md"] @@ -255,6 +268,8 @@ def setup(app): # Set canonical URL from the Read the Docs Domain html_baseurl = os.environ.get("READTHEDOCS_CANONICAL_URL", "") +googleanalytics_id = "G-9B66JQQH2F" + # Tell Jinja2 templates the build is running on Read the Docs if os.environ.get("READTHEDOCS", "") == "True": html_context["READTHEDOCS"] = True diff --git a/docs/api_reference/create_api_rst.py b/docs/api_reference/create_api_rst.py index b66e1938fe399..1d4a35b9f0dd1 100644 --- a/docs/api_reference/create_api_rst.py +++ b/docs/api_reference/create_api_rst.py @@ -72,14 +72,21 @@ def _load_module_members(module_path: str, namespace: str) -> ModuleMembers: Returns: list: A list of loaded module objects. """ + classes_: List[ClassInfo] = [] functions: List[FunctionInfo] = [] module = importlib.import_module(module_path) + + if ":private:" in (module.__doc__ or ""): + return ModuleMembers(classes_=[], functions=[]) + for name, type_ in inspect.getmembers(module): if not hasattr(type_, "__module__"): continue if type_.__module__ != module_path: continue + if ":private:" in (type_.__doc__ or ""): + continue if inspect.isclass(type_): # The type of the class is used to select a template diff --git a/docs/api_reference/requirements.txt b/docs/api_reference/requirements.txt index ea310541ff418..b59b8d4372280 100644 --- a/docs/api_reference/requirements.txt +++ b/docs/api_reference/requirements.txt @@ -9,3 +9,4 @@ pyyaml sphinx-design sphinx-copybutton beautifulsoup4 +sphinxcontrib-googleanalytics diff --git a/docs/docs/additional_resources/tutorials.mdx b/docs/docs/additional_resources/tutorials.mdx index 1b98d8c31af7a..871b12e40812f 100644 --- a/docs/docs/additional_resources/tutorials.mdx +++ b/docs/docs/additional_resources/tutorials.mdx @@ -12,6 +12,7 @@ ### [by Mayo Oshin](https://www.youtube.com/@chatwithdata/search?query=langchain) ### [by 1 little Coder](https://www.youtube.com/playlist?list=PLpdmBGJ6ELUK-v0MK-t4wZmVEbxM5xk6L) ### [by BobLin (Chinese language)](https://www.youtube.com/playlist?list=PLbd7ntv6PxC3QMFQvtWfk55p-Op_syO1C) +### [by Total Technology Zonne](https://youtube.com/playlist?list=PLI8raxzYtfGyE02fAxiM1CPhLUuqcTLWg&si=fkAye16rQKBJVHc9) ## Courses diff --git a/docs/docs/concepts/architecture.mdx b/docs/docs/concepts/architecture.mdx index 923ee5a705f20..66272190080b9 100644 --- a/docs/docs/concepts/architecture.mdx +++ b/docs/docs/concepts/architecture.mdx @@ -65,7 +65,7 @@ A package to deploy LangChain chains as REST APIs. Makes it easy to get a produc :::important LangServe is designed to primarily deploy simple Runnables and work with well-known primitives in langchain-core. -If you need a deployment option for LangGraph, you should instead be looking at LangGraph Cloud (beta) which will be better suited for deploying LangGraph applications. +If you need a deployment option for LangGraph, you should instead be looking at LangGraph Platform (beta) which will be better suited for deploying LangGraph applications. ::: For more information, see the [LangServe documentation](/docs/langserve). diff --git a/docs/docs/contributing/how_to/integrations/index.mdx b/docs/docs/contributing/how_to/integrations/index.mdx index eeb0f73b3fdbb..3623f621b7f3d 100644 --- a/docs/docs/contributing/how_to/integrations/index.mdx +++ b/docs/docs/contributing/how_to/integrations/index.mdx @@ -1,17 +1,20 @@ --- +pagination_prev: null pagination_next: contributing/how_to/integrations/package --- # Contribute Integrations -LangChain integrations are packages that provide access to language models, vector stores, and other components that can be used in LangChain. +Integrations are a core component of LangChain. +LangChain provides standard interfaces for several different components (language models, vector stores, etc) that are crucial when building LLM applications. -This guide will walk you through how to contribute new integrations to LangChain, by -publishing an integration package to PyPi, and adding documentation for it -to the LangChain Monorepo. -These instructions will evolve over the next few months as we improve our integration -processes. +## Why contribute an integration to LangChain? + +- **Discoverability:** LangChain is the most used framework for building LLM applications, with over 20 million monthly downloads. LangChain integrations are discoverable by a large community of GenAI builders. +- **Interoperability:** LangChain components expose a standard interface, allowing developers to easily swap them for each other. If you implement a LangChain integration, any developer using a different component will easily be able to swap yours in. +- **Best Practices:** Through their standard interface, LangChain components encourage and facilitate best practices (streaming, async, etc) + ## Components to Integrate @@ -22,8 +25,7 @@ supported in LangChain ::: -While any component can be integrated into LangChain, at this time we are only accepting -new integrations in the docs of the following kinds: +While any component can be integrated into LangChain, there are specific types of integrations we encourage more: @@ -36,7 +38,6 @@ new integrations in the docs of the following kinds:
  • Chat Models
  • Tools/Toolkits
  • Retrievers
  • -
  • Document Loaders
  • Vector Stores
  • Embedding Models
  • @@ -44,6 +45,7 @@ new integrations in the docs of the following kinds:
    • LLMs (Text-Completion Models)
    • +
    • Document Loaders
    • Key-Value Stores
    • Document Transformers
    • Model Caches
    • @@ -60,18 +62,30 @@ new integrations in the docs of the following kinds: ## How to contribute an integration -The only step necessary to "be" a LangChain integration is to add documentation -that will render on this site (https://python.langchain.com/). - -As a prerequisite to adding your integration to our documentation, you must: +In order to contribute an integration, you should follow these steps: -1. Confirm that your integration is in the [list of components](#components-to-integrate) we are currently accepting. -2. [Implement your package](./package.mdx) and publish it to a public github repository. +1. Confirm that your integration is in the [list of components](#components-to-integrate) we are currently encouraging. +2. [Implement your package](/docs/contributing/how_to/integrations/package/) and publish it to a public github repository. 3. [Implement the standard tests](./standard_tests) for your integration and successfully run them. 4. [Publish your integration](./publish.mdx) by publishing the package to PyPi and add docs in the `docs/docs/integrations` directory of the LangChain monorepo. +5. [Optional] Open and merge a PR to add documentation for your integration to the official LangChain docs. +6. [Optional] Engage with the LangChain team for joint co-marketing ([see below](#co-marketing)). -Once you have completed these steps, you can submit a PR to the LangChain monorepo to add your integration to the documentation. +## Co-Marketing -## Further Reading +With over 20 million monthly downloads, LangChain has a large audience of developers building LLM applications. +Besides just adding integrations, we also like to show them examples of cool tools or APIs they can use. + +While traditionally called "co-marketing", we like to think of this more as "co-education". +For that reason, while we are happy to highlight your integration through our social media channels, we prefer to highlight examples that also serve some educational purpose. +Our main social media channels are Twitter and LinkedIn. + +Here are some heuristics for types of content we are excited to promote: -To get started, let's learn [how to bootstrap a new integration package](./package.mdx) for LangChain. +- **Integration announcement:** If you announce the integration with a link to the LangChain documentation page, we are happy to re-tweet/re-share on Twitter/LinkedIn. +- **Educational content:** We highlight good educational content on the weekends - if you write a good blog or make a good YouTube video, we are happy to share there! Note that we prefer content that is NOT framed as "here's how to use integration XYZ", but rather "here's how to do ABC", as we find that is more educational and helpful for developers. +- **End-to-end applications:** End-to-end applications are great resources for developers looking to build. We prefer to highlight applications that are more complex/agentic in nature, and that use [LangGraph](https://github.com/langchain-ai/langgraph) as the orchestration framework. We get particularly excited about anything involving long-term memory, human-in-the-loop interaction patterns, or multi-agent architectures. +- **Research:** We love highlighting novel research! Whether it is research built on top of LangChain or that integrates with it. + +## Further Reading +To get started, let's learn [how to implement an integration package](/docs/contributing/how_to/integrations/package/) for LangChain. diff --git a/docs/docs/contributing/how_to/integrations/package.mdx b/docs/docs/contributing/how_to/integrations/package.mdx index 8189ce97f8ba3..990f097fd2a19 100644 --- a/docs/docs/contributing/how_to/integrations/package.mdx +++ b/docs/docs/contributing/how_to/integrations/package.mdx @@ -2,23 +2,109 @@ pagination_next: contributing/how_to/integrations/standard_tests pagination_prev: contributing/how_to/integrations/index --- -# How to bootstrap a new integration package +# How to implement an integration package -This guide walks through the process of publishing a new LangChain integration -package to PyPi. +This guide walks through the process of implementing a LangChain integration +package. Integration packages are just Python packages that can be installed with `pip install `, which contain classes that are compatible with LangChain's core interfaces. +We will cover: + +1. (Optional) How to bootstrap a new integration package +2. How to implement components, such as [chat models](/docs/concepts/chat_models/) and [vector stores](/docs/concepts/vectorstores/), that adhere +to the LangChain interface; + +## (Optional) bootstrapping a new integration package + +In this section, we will outline 2 options for bootstrapping a new integration package, +and you're welcome to use other tools if you prefer! + +1. **langchain-cli**: This is a command-line tool that can be used to bootstrap a new integration package with a template for LangChain components and Poetry for dependency management. +2. **Poetry**: This is a Python dependency management tool that can be used to bootstrap a new Python package with dependencies. You can then add LangChain components to this package. + +
      + Option 1: langchain-cli (recommended) + +In this guide, we will be using the `langchain-cli` to create a new integration package +from a template, which can be edited to implement your LangChain components. + +### **Prerequisites** + +- [GitHub](https://github.com) account +- [PyPi](https://pypi.org/) account + +### Boostrapping a new Python package with langchain-cli + +First, install `langchain-cli` and `poetry`: + +```bash +pip install langchain-cli poetry +``` + +Next, come up with a name for your package. For this guide, we'll use `langchain-parrot-link`. +You can confirm that the name is available on PyPi by searching for it on the [PyPi website](https://pypi.org/). + +Next, create your new Python package with `langchain-cli`, and navigate into the new directory with `cd`: + +```bash +langchain-cli integration new + +> The name of the integration to create (e.g. `my-integration`): parrot-link +> Name of integration in PascalCase [ParrotLink]: + +cd parrot-link +``` + +Next, let's add any dependencies we need + +```bash +poetry add my-integration-sdk +``` + +We can also add some `typing` or `test` dependencies in a separate poetry dependency group. + +``` +poetry add --group typing my-typing-dep +poetry add --group test my-test-dep +``` + +And finally, have poetry set up a virtual environment with your dependencies, as well +as your integration package: + +```bash +poetry install --with lint,typing,test,test_integration +``` + +You now have a new Python package with a template for LangChain components! This +template comes with files for each integration type, and you're welcome to duplicate or +delete any of these files as needed (including the associated test files). + +To create any individual files from the [template], you can run e.g.: + +```bash +langchain-cli integration new \ + --name parrot-link \ + --name-class ParrotLink \ + --src integration_template/chat_models.py \ + --dst langchain_parrot_link/chat_models_2.py +``` + +
      + +
      + Option 2: Poetry (manual) + In this guide, we will be using [Poetry](https://python-poetry.org/) for dependency management and packaging, and you're welcome to use any other tools you prefer. -## **Prerequisites** +### **Prerequisites** - [GitHub](https://github.com) account - [PyPi](https://pypi.org/) account -## Boostrapping a new Python package with Poetry +### Boostrapping a new Python package with Poetry First, install Poetry: @@ -64,7 +150,7 @@ poetry install --with test You're now ready to start writing your integration package! -## Writing your integration +### Writing your integration Let's say you're building a simple integration package that provides a `ChatParrotLink` chat model integration for LangChain. Here's a simple example of what your project @@ -86,183 +172,12 @@ All of these files should already exist from step 1, except for `chat_models.py` and `test_chat_models.py`! We will implement `test_chat_models.py` later, following the [standard tests](../standard_tests) guide. -To implement `chat_models.py`, let's copy the implementation from our -[Custom Chat Model Guide](../../../../how_to/custom_chat_model). +For `chat_models.py`, simply paste the contents of the chat model implementation +[above](#implementing-langchain-components). -
      - chat_models.py -```python title="langchain_parrot_link/chat_models.py" -from typing import Any, Dict, Iterator, List, Optional - -from langchain_core.callbacks import ( - CallbackManagerForLLMRun, -) -from langchain_core.language_models import BaseChatModel -from langchain_core.messages import ( - AIMessage, - AIMessageChunk, - BaseMessage, -) -from langchain_core.messages.ai import UsageMetadata -from langchain_core.outputs import ChatGeneration, ChatGenerationChunk, ChatResult -from pydantic import Field - - -class ChatParrotLink(BaseChatModel): - """A custom chat model that echoes the first `parrot_buffer_length` characters - of the input. - - When contributing an implementation to LangChain, carefully document - the model including the initialization parameters, include - an example of how to initialize the model and include any relevant - links to the underlying models documentation or API. - - Example: - - .. code-block:: python - - model = ChatParrotLink(parrot_buffer_length=2, model="bird-brain-001") - result = model.invoke([HumanMessage(content="hello")]) - result = model.batch([[HumanMessage(content="hello")], - [HumanMessage(content="world")]]) - """ - - model_name: str = Field(alias="model") - """The name of the model""" - parrot_buffer_length: int - """The number of characters from the last message of the prompt to be echoed.""" - temperature: Optional[float] = None - max_tokens: Optional[int] = None - timeout: Optional[int] = None - stop: Optional[List[str]] = None - max_retries: int = 2 - - def _generate( - self, - messages: List[BaseMessage], - stop: Optional[List[str]] = None, - run_manager: Optional[CallbackManagerForLLMRun] = None, - **kwargs: Any, - ) -> ChatResult: - """Override the _generate method to implement the chat model logic. - - This can be a call to an API, a call to a local model, or any other - implementation that generates a response to the input prompt. - - Args: - messages: the prompt composed of a list of messages. - stop: a list of strings on which the model should stop generating. - If generation stops due to a stop token, the stop token itself - SHOULD BE INCLUDED as part of the output. This is not enforced - across models right now, but it's a good practice to follow since - it makes it much easier to parse the output of the model - downstream and understand why generation stopped. - run_manager: A run manager with callbacks for the LLM. - """ - # Replace this with actual logic to generate a response from a list - # of messages. - last_message = messages[-1] - tokens = last_message.content[: self.parrot_buffer_length] - ct_input_tokens = sum(len(message.content) for message in messages) - ct_output_tokens = len(tokens) - message = AIMessage( - content=tokens, - additional_kwargs={}, # Used to add additional payload to the message - response_metadata={ # Use for response metadata - "time_in_seconds": 3, - }, - usage_metadata={ - "input_tokens": ct_input_tokens, - "output_tokens": ct_output_tokens, - "total_tokens": ct_input_tokens + ct_output_tokens, - }, - ) - ## - - generation = ChatGeneration(message=message) - return ChatResult(generations=[generation]) - - def _stream( - self, - messages: List[BaseMessage], - stop: Optional[List[str]] = None, - run_manager: Optional[CallbackManagerForLLMRun] = None, - **kwargs: Any, - ) -> Iterator[ChatGenerationChunk]: - """Stream the output of the model. - - This method should be implemented if the model can generate output - in a streaming fashion. If the model does not support streaming, - do not implement it. In that case streaming requests will be automatically - handled by the _generate method. - - Args: - messages: the prompt composed of a list of messages. - stop: a list of strings on which the model should stop generating. - If generation stops due to a stop token, the stop token itself - SHOULD BE INCLUDED as part of the output. This is not enforced - across models right now, but it's a good practice to follow since - it makes it much easier to parse the output of the model - downstream and understand why generation stopped. - run_manager: A run manager with callbacks for the LLM. - """ - last_message = messages[-1] - tokens = str(last_message.content[: self.parrot_buffer_length]) - ct_input_tokens = sum(len(message.content) for message in messages) - - for token in tokens: - usage_metadata = UsageMetadata( - { - "input_tokens": ct_input_tokens, - "output_tokens": 1, - "total_tokens": ct_input_tokens + 1, - } - ) - ct_input_tokens = 0 - chunk = ChatGenerationChunk( - message=AIMessageChunk(content=token, usage_metadata=usage_metadata) - ) - - if run_manager: - # This is optional in newer versions of LangChain - # The on_llm_new_token will be called automatically - run_manager.on_llm_new_token(token, chunk=chunk) - - yield chunk - - # Let's add some other information (e.g., response metadata) - chunk = ChatGenerationChunk( - message=AIMessageChunk(content="", response_metadata={"time_in_sec": 3}) - ) - if run_manager: - # This is optional in newer versions of LangChain - # The on_llm_new_token will be called automatically - run_manager.on_llm_new_token(token, chunk=chunk) - yield chunk - - @property - def _llm_type(self) -> str: - """Get the type of language model used by this chat model.""" - return "echoing-chat-model-advanced" - - @property - def _identifying_params(self) -> Dict[str, Any]: - """Return a dictionary of identifying parameters. - - This information is used by the LangChain callback system, which - is used for tracing purposes make it possible to monitor LLMs. - """ - return { - # The model name allows users to specify custom token counting - # rules in LLM monitoring applications (e.g., in LangSmith users - # can provide per token pricing for their model and monitor - # costs for the given LLM.) - "model_name": self.model_name, - } -```
      -## Push your package to a public Github repository +### Push your package to a public Github repository This is only required if you want to publish your integration in the LangChain documentation. @@ -270,6 +185,319 @@ This is only required if you want to publish your integration in the LangChain d 2. Push your code to the repository. 3. Confirm that your repository is viewable by the public (e.g. in a private browsing window, where you're not logged into Github). +## Implementing LangChain components + +LangChain components are subclasses of base classes in [langchain-core](/docs/concepts/architecture/#langchain-core). +Examples include [chat models](/docs/concepts/chat_models/), +[vector stores](/docs/concepts/vectorstores/), [tools](/docs/concepts/tools/), +[embedding models](/docs/concepts/embedding_models/) and [retrievers](/docs/concepts/retrievers/). + +Your integration package will typically implement a subclass of at least one of these +components. Expand the tabs below to see details on each. + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import CodeBlock from '@theme/CodeBlock'; + + + + + + Refer to the [Custom Chat Model Guide](/docs/how_to/custom_chat_model) guide for + detail on a starter chat model [implementation](/docs/how_to/custom_chat_model/#implementation). + + You can start from the following template or langchain-cli command: + + ```bash + langchain-cli integration new \ + --name parrot-link \ + --name-class ParrotLink \ + --src integration_template/chat_models.py \ + --dst langchain_parrot_link/chat_models.py + ``` + +
      + Example chat model code + +import ChatModelSource from '../../../../src/theme/integration_template/integration_template/chat_models.py'; + + + { + ChatModelSource.replaceAll('__ModuleName__', 'ParrotLink') + .replaceAll('__package_name__', 'langchain-parrot-link') + .replaceAll('__MODULE_NAME__', 'PARROT_LINK') + .replaceAll('__module_name__', 'langchain_parrot_link') + } + + +
      + +
      + + + Your vector store implementation will depend on your chosen database technology. + `langchain-core` includes a minimal + [in-memory vector store](https://python.langchain.com/api_reference/core/vectorstores/langchain_core.vectorstores.in_memory.InMemoryVectorStore.html) + that we can use as a guide. You can access the code [here](https://github.com/langchain-ai/langchain/blob/master/libs/core/langchain_core/vectorstores/in_memory.py). + + All vector stores must inherit from the [VectorStore](https://python.langchain.com/api_reference/core/vectorstores/langchain_core.vectorstores.base.VectorStore.html) + base class. This interface consists of methods for writing, deleting and searching + for documents in the vector store. + + `VectorStore` supports a variety of synchronous and asynchronous search types (e.g., + nearest-neighbor or maximum marginal relevance), as well as interfaces for adding + documents to the store. See the [API Reference](https://python.langchain.com/api_reference/core/vectorstores/langchain_core.vectorstores.base.VectorStore.html) + for all supported methods. The required methods are tabulated below: + + | Method/Property | Description | + |------------------------ |------------------------------------------------------| + | `add_documents` | Add documents to the vector store. | + | `delete` | Delete selected documents from vector store (by IDs) | + | `get_by_ids` | Get selected documents from vector store (by IDs) | + | `similarity_search` | Get documents most similar to a query. | + | `embeddings` (property) | Embeddings object for vector store. | + | `from_texts` | Instantiate vector store via adding texts. | + + Note that `InMemoryVectorStore` implements some optional search types, as well as + convenience methods for loading and dumping the object to a file, but this is not + necessary for all implementations. + + :::tip + + The [in-memory vector store](https://github.com/langchain-ai/langchain/blob/master/libs/core/langchain_core/vectorstores/in_memory.py) + is tested against the standard tests in the LangChain Github repository. + + ::: + +
      + Example vector store code + +import VectorstoreSource from '../../../../src/theme/integration_template/integration_template/vectorstores.py'; + + + { + VectorstoreSource.replaceAll('__ModuleName__', 'ParrotLink') + .replaceAll('__package_name__', 'langchain-parrot-link') + .replaceAll('__MODULE_NAME__', 'PARROT_LINK') + .replaceAll('__module_name__', 'langchain_parrot_link') + } + + +
      + +
      + + +Embeddings are used to convert `str` objects from `Document.page_content` fields +into a vector representation (represented as a list of floats). + +The `Embeddings` class must inherit from the [Embeddings](https://python.langchain.com/api_reference/core/embeddings/langchain_core.embeddings.embeddings.Embeddings.html#langchain_core.embeddings.embeddings.Embeddings) +base class. This interface has 5 methods that can be implemented. + +| Method/Property | Description | +|------------------------ |------------------------------------------------------| +| `__init__` | Initialize the embeddings object. (optional) | +| `embed_query` | Embed a list of texts. (required) | +| `embed_documents` | Embed a list of documents. (required) | +| `aembed_query` | Asynchronously embed a list of texts. (optional) | +| `aembed_documents` | Asynchronously embed a list of documents. (optional) | + +### Constructor + +The `__init__` constructor is optional but common, but can be used to set up any necessary attributes +that a user can pass in when initializing the embeddings object. Common attributes include + +- `model` - the id of the model to use for embeddings + +### Embedding queries vs documents + +The `embed_query` and `embed_documents` methods are required. These methods both operate +on string inputs (the accessing of `Document.page_content` attributes) is handled +by the VectorStore using the embedding model for legacy reasons. + +`embed_query` takes in a single string and returns a single embedding as a list of floats. +If your model has different modes for embedding queries vs the underlying documents, you can +implement this method to handle that. + +`embed_documents` takes in a list of strings and returns a list of embeddings as a list of lists of floats. + +### Implementation + +You can start from the following template or langchain-cli command: + +```bash +langchain-cli integration new \ + --name parrot-link \ + --name-class ParrotLink \ + --src integration_template/embeddings.py \ + --dst langchain_parrot_link/embeddings.py +``` + +
      + Example embeddings code + +import EmbeddingsSource from '/src/theme/integration_template/integration_template/embeddings.py'; + + + { + EmbeddingsSource.replaceAll('__ModuleName__', 'ParrotLink') + .replaceAll('__package_name__', 'langchain-parrot-link') + .replaceAll('__MODULE_NAME__', 'PARROT_LINK') + .replaceAll('__module_name__', 'langchain_parrot_link') + } + + +
      + +
      + + +Tools are used in 2 main ways: + +1. To define an "input schema" or "args schema" to pass to a chat model's tool calling +feature along with a text request, such that the chat model can generate a "tool call", +or parameters to call the tool with. +2. To take a "tool call" as generated above, and take some action and return a response +that can be passed back to the chat model as a ToolMessage. + +The `Tools` class must inherit from the [BaseTool](https://python.langchain.com/api_reference/core/tools/langchain_core.tools.base.BaseTool.html#langchain_core.tools.base.BaseTool) base class. This interface has 3 properties and 2 methods that should be implemented in a +subclass. + +| Method/Property | Description | +|------------------------ |------------------------------------------------------| +| `name` | Name of the tool (passed to the LLM too). | +| `description` | Description of the tool (passed to the LLM too). | +| `args_schema` | Define the schema for the tool's input arguments. | +| `_run` | Run the tool with the given arguments. | +| `_arun` | Asynchronously run the tool with the given arguments.| + +### Properties + +`name`, `description`, and `args_schema` are all properties that should be implemented +in the subclass. `name` and `description` are strings that are used to identify the tool +and provide a description of what the tool does. Both of these are passed to the LLM, +and users may override these values depending on the LLM they are using as a form of +"prompt engineering." Giving these a concise and LLM-usable name and description is +important for the initial user experience of the tool. + +`args_schema` is a Pydantic `BaseModel` that defines the schema for the tool's input +arguments. This is used to validate the input arguments to the tool, and to provide +a schema for the LLM to fill out when calling the tool. Similar to the `name` and +`description` of the overall Tool class, the fields' names (the variable name) and +description (part of `Field(..., description="description")`) are passed to the LLM, +and the values in these fields should be concise and LLM-usable. + +### Run Methods + +`_run` is the main method that should be implemented in the subclass. This method +takes in the arguments from `args_schema` and runs the tool, returning a string +response. This method is usually called in a LangGraph [`ToolNode`](https://langchain-ai.github.io/langgraph/how-tos/tool-calling/), and can also be called in a legacy +`langchain.agents.AgentExecutor`. + +`_arun` is optional because by default, `_run` will be run in an async executor. +However, if your tool is calling any apis or doing any async work, you should implement +this method to run the tool asynchronously in addition to `_run`. + +### Implementation + +You can start from the following template or langchain-cli command: + +```bash +langchain-cli integration new \ + --name parrot-link \ + --name-class ParrotLink \ + --src integration_template/tools.py \ + --dst langchain_parrot_link/tools.py +``` + +
      + Example tool code + +import ToolSource from '/src/theme/integration_template/integration_template/tools.py'; + + + { + ToolSource.replaceAll('__ModuleName__', 'ParrotLink') + .replaceAll('__package_name__', 'langchain-parrot-link') + .replaceAll('__MODULE_NAME__', 'PARROT_LINK') + .replaceAll('__module_name__', 'langchain_parrot_link') + } + + +
      + +
      + + +Retrievers are used to retrieve documents from APIs, databases, or other sources +based on a query. The `Retriever` class must inherit from the [BaseRetriever](https://python.langchain.com/api_reference/core/retrievers/langchain_core.retrievers.BaseRetriever.html) base class. This interface has 1 attribute and 2 methods that should be implemented in a subclass. + +| Method/Property | Description | +|------------------------ |------------------------------------------------------| +| `k` | Default number of documents to retrieve (configurable). | +| `_get_relevant_documents`| Retrieve documents based on a query. | +| `_aget_relevant_documents`| Asynchronously retrieve documents based on a query. | + +### Attributes + +`k` is an attribute that should be implemented in the subclass. This attribute +can simply be defined at the top of the class with a default value like +`k: int = 5`. This attribute is the default number of documents to retrieve +from the retriever, and can be overridden by the user when constructing or calling +the retriever. + +### Methods + +`_get_relevant_documents` is the main method that should be implemented in the subclass. + +This method takes in a query and returns a list of `Document` objects, which have 2 +main properties: + +- `page_content` - the text content of the document +- `metadata` - a dictionary of metadata about the document + +Retrievers are typically directly invoked by a user, e.g. as +`MyRetriever(k=4).invoke("query")`, which will automatically call `_get_relevant_documents` +under the hood. + +`_aget_relevant_documents` is optional because by default, `_get_relevant_documents` will +be run in an async executor. However, if your retriever is calling any apis or doing +any async work, you should implement this method to run the retriever asynchronously +in addition to `_get_relevant_documents` for performance reasons. + +### Implementation + +You can start from the following template or langchain-cli command: + +```bash +langchain-cli integration new \ + --name parrot-link \ + --name-class ParrotLink \ + --src integration_template/retrievers.py \ + --dst langchain_parrot_link/retrievers.py +``` + +
      + Example retriever code + +import RetrieverSource from '/src/theme/integration_template/integration_template/retrievers.py'; + + + { + RetrieverSource.replaceAll('__ModuleName__', 'ParrotLink') + .replaceAll('__package_name__', 'langchain-parrot-link') + .replaceAll('__MODULE_NAME__', 'PARROT_LINK') + .replaceAll('__module_name__', 'langchain_parrot_link') + } + + +
      + +
      +
      + +--- + ## Next Steps Now that you've implemented your package, you can move on to [testing your integration](../standard_tests) for your integration and successfully run them. diff --git a/docs/docs/contributing/how_to/integrations/standard_tests.ipynb b/docs/docs/contributing/how_to/integrations/standard_tests.ipynb deleted file mode 100644 index 8bee8a45dd200..0000000000000 --- a/docs/docs/contributing/how_to/integrations/standard_tests.ipynb +++ /dev/null @@ -1,497 +0,0 @@ -{ - "cells": [ - { - "cell_type": "raw", - "metadata": { - "vscode": { - "languageId": "raw" - } - }, - "source": [ - "---\n", - "pagination_next: contributing/how_to/integrations/publish\n", - "pagination_prev: contributing/how_to/integrations/package\n", - "---" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# How to add standard tests to an integration\n", - "\n", - "When creating either a custom class for yourself or to publish in a LangChain integration, it is important to add standard tests to ensure it works as expected. This guide will show you how to add standard tests to a custom chat model, and you can **[Skip to the test templates](#standard-test-templates-per-component)** for implementing tests for each integration type.\n", - "\n", - "## Setup\n", - "\n", - "If you're coming from the [previous guide](../package), you have already installed these dependencies, and you can skip this section.\n", - "\n", - "First, let's install 2 dependencies:\n", - "\n", - "- `langchain-core` will define the interfaces we want to import to define our custom tool.\n", - "- `langchain-tests` will provide the standard tests we want to use. Recommended to pin to the latest version: \n", - "\n", - ":::note\n", - "\n", - "Because added tests in new versions of `langchain-tests` can break your CI/CD pipelines, we recommend pinning the \n", - "version of `langchain-tests` to avoid unexpected changes.\n", - "\n", - ":::\n", - "\n", - "import Tabs from '@theme/Tabs';\n", - "import TabItem from '@theme/TabItem';\n", - "\n", - "\n", - " \n", - "If you followed the [previous guide](../package), you should already have these dependencies installed!\n", - "\n", - "```bash\n", - "poetry add langchain-core\n", - "poetry add --group test pytest pytest-socket pytest-asyncio langchain-tests==\n", - "poetry install --with test\n", - "```\n", - " \n", - " \n", - "```bash\n", - "pip install -U langchain-core pytest pytest-socket pytest-asyncio langchain-tests\n", - "\n", - "# install current package in editable mode\n", - "pip install --editable .\n", - "```\n", - " \n", - "" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's say we're publishing a package, `langchain_parrot_link`, that exposes the chat model from the [guide on implementing the package](../package). We can add the standard tests to the package by following the steps below." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And we'll assume you've structured your package the same way as the main LangChain\n", - "packages:\n", - "\n", - "```plaintext\n", - "langchain-parrot-link/\n", - "├── langchain_parrot_link/\n", - "│ ├── __init__.py\n", - "│ └── chat_models.py\n", - "├── tests/\n", - "│ ├── __init__.py\n", - "│ └── test_chat_models.py\n", - "├── pyproject.toml\n", - "└── README.md\n", - "```\n", - "\n", - "## Add and configure standard tests\n", - "\n", - "There are 2 namespaces in the `langchain-tests` package: \n", - "\n", - "- [unit tests](../../../concepts/testing.mdx#unit-tests) (`langchain_tests.unit_tests`): designed to be used to test the component in isolation and without access to external services\n", - "- [integration tests](../../../concepts/testing.mdx#unit-tests) (`langchain_tests.integration_tests`): designed to be used to test the component with access to external services (in particular, the external service that the component is designed to interact with).\n", - "\n", - "Both types of tests are implemented as [`pytest` class-based test suites](https://docs.pytest.org/en/7.1.x/getting-started.html#group-multiple-tests-in-a-class).\n", - "\n", - "By subclassing the base classes for each type of standard test (see below), you get all of the standard tests for that type, and you\n", - "can override the properties that the test suite uses to configure the tests.\n", - "\n", - "### Standard chat model tests\n", - "\n", - "Here's how you would configure the standard unit tests for the custom chat model:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# title=\"tests/unit_tests/test_chat_models.py\"\n", - "from typing import Tuple, Type\n", - "\n", - "from langchain_parrot_link.chat_models import ChatParrotLink\n", - "from langchain_tests.unit_tests import ChatModelUnitTests\n", - "\n", - "\n", - "class TestChatParrotLinkUnit(ChatModelUnitTests):\n", - " @property\n", - " def chat_model_class(self) -> Type[ChatParrotLink]:\n", - " return ChatParrotLink\n", - "\n", - " @property\n", - " def chat_model_params(self) -> dict:\n", - " return {\n", - " \"model\": \"bird-brain-001\",\n", - " \"temperature\": 0,\n", - " \"parrot_buffer_length\": 50,\n", - " }" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# title=\"tests/integration_tests/test_chat_models.py\"\n", - "from typing import Type\n", - "\n", - "from langchain_parrot_link.chat_models import ChatParrotLink\n", - "from langchain_tests.integration_tests import ChatModelIntegrationTests\n", - "\n", - "\n", - "class TestChatParrotLinkIntegration(ChatModelIntegrationTests):\n", - " @property\n", - " def chat_model_class(self) -> Type[ChatParrotLink]:\n", - " return ChatParrotLink\n", - "\n", - " @property\n", - " def chat_model_params(self) -> dict:\n", - " return {\n", - " \"model\": \"bird-brain-001\",\n", - " \"temperature\": 0,\n", - " \"parrot_buffer_length\": 50,\n", - " }" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "and you would run these with the following commands from your project root\n", - "\n", - "\n", - " \n", - "\n", - "```bash\n", - "# run unit tests without network access\n", - "poetry run pytest --disable-socket --allow-unix-socket --asyncio-mode=auto tests/unit_tests\n", - "\n", - "# run integration tests\n", - "poetry run pytest --asyncio-mode=auto tests/integration_tests\n", - "```\n", - "\n", - " \n", - " \n", - "\n", - "```bash\n", - "# run unit tests without network access\n", - "pytest --disable-socket --allow-unix-socket --asyncio-mode=auto tests/unit_tests\n", - "\n", - "# run integration tests\n", - "pytest --asyncio-mode=auto tests/integration_tests\n", - "```\n", - "\n", - " \n", - "" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Test suite information and troubleshooting\n", - "\n", - "For a full list of the standard test suites that are available, as well as\n", - "information on which tests are included and how to troubleshoot common issues,\n", - "see the [Standard Tests API Reference](https://python.langchain.com/api_reference/standard_tests/index.html).\n", - "\n", - "An increasing number of troubleshooting guides are being added to this documentation,\n", - "and if you're interested in contributing, feel free to add docstrings to tests in \n", - "[Github](https://github.com/langchain-ai/langchain/tree/master/libs/standard-tests/langchain_tests)!" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Standard test templates per component:\n", - "\n", - "Above, we implement the **unit** and **integration** standard tests for a tool. Below are the templates for implementing the standard tests for each component:\n", - "\n", - "
      \n", - " Chat Models\n", - "

      Note: The standard tests for chat models are implemented in the example in the main body of this guide too.

      " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# title=\"tests/unit_tests/test_chat_models.py\"\n", - "from typing import Type\n", - "\n", - "from langchain_parrot_link.chat_models import ChatParrotLink\n", - "from langchain_tests.unit_tests import ChatModelUnitTests\n", - "\n", - "\n", - "class TestChatParrotLinkUnit(ChatModelUnitTests):\n", - " @property\n", - " def chat_model_class(self) -> Type[ChatParrotLink]:\n", - " return ChatParrotLink\n", - "\n", - " @property\n", - " def chat_model_params(self) -> dict:\n", - " return {\n", - " \"model\": \"bird-brain-001\",\n", - " \"temperature\": 0,\n", - " \"parrot_buffer_length\": 50,\n", - " }" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# title=\"tests/integration_tests/test_chat_models.py\"\n", - "from typing import Type\n", - "\n", - "from langchain_parrot_link.chat_models import ChatParrotLink\n", - "from langchain_tests.integration_tests import ChatModelIntegrationTests\n", - "\n", - "\n", - "class TestChatParrotLinkIntegration(ChatModelIntegrationTests):\n", - " @property\n", - " def chat_model_class(self) -> Type[ChatParrotLink]:\n", - " return ChatParrotLink\n", - "\n", - " @property\n", - " def chat_model_params(self) -> dict:\n", - " return {\n", - " \"model\": \"bird-brain-001\",\n", - " \"temperature\": 0,\n", - " \"parrot_buffer_length\": 50,\n", - " }" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
      \n", - "
      \n", - " Embedding Models" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# title=\"tests/unit_tests/test_embeddings.py\"\n", - "from typing import Tuple, Type\n", - "\n", - "from langchain_parrot_link.embeddings import ParrotLinkEmbeddings\n", - "from langchain_tests.unit_tests import EmbeddingsUnitTests\n", - "\n", - "\n", - "class TestParrotLinkEmbeddingsUnit(EmbeddingsUnitTests):\n", - " @property\n", - " def embeddings_class(self) -> Type[ParrotLinkEmbeddings]:\n", - " return ParrotLinkEmbeddings\n", - "\n", - " @property\n", - " def embedding_model_params(self) -> dict:\n", - " return {\"model\": \"nest-embed-001\", \"temperature\": 0}" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# title=\"tests/integration_tests/test_embeddings.py\"\n", - "from typing import Type\n", - "\n", - "from langchain_parrot_link.embeddings import ParrotLinkEmbeddings\n", - "from langchain_tests.integration_tests import EmbeddingsIntegrationTests\n", - "\n", - "\n", - "class TestParrotLinkEmbeddingsIntegration(EmbeddingsIntegrationTests):\n", - " @property\n", - " def embeddings_class(self) -> Type[ParrotLinkEmbeddings]:\n", - " return ParrotLinkEmbeddings\n", - "\n", - " @property\n", - " def embedding_model_params(self) -> dict:\n", - " return {\"model\": \"nest-embed-001\"}" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
      \n", - "
      \n", - " Tools/Toolkits" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# title=\"tests/unit_tests/test_tools.py\"\n", - "from typing import Type\n", - "\n", - "from langchain_parrot_link.tools import ParrotMultiplyTool\n", - "from langchain_tests.unit_tests import ToolsUnitTests\n", - "\n", - "\n", - "class TestParrotMultiplyToolUnit(ToolsUnitTests):\n", - " @property\n", - " def tool_constructor(self) -> Type[ParrotMultiplyTool]:\n", - " return ParrotMultiplyTool\n", - "\n", - " @property\n", - " def tool_constructor_params(self) -> dict:\n", - " # if your tool constructor instead required initialization arguments like\n", - " # `def __init__(self, some_arg: int):`, you would return those here\n", - " # as a dictionary, e.g.: `return {'some_arg': 42}`\n", - " return {}\n", - "\n", - " @property\n", - " def tool_invoke_params_example(self) -> dict:\n", - " \"\"\"\n", - " Returns a dictionary representing the \"args\" of an example tool call.\n", - "\n", - " This should NOT be a ToolCall dict - i.e. it should not\n", - " have {\"name\", \"id\", \"args\"} keys.\n", - " \"\"\"\n", - " return {\"a\": 2, \"b\": 3}" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# title=\"tests/integration_tests/test_tools.py\"\n", - "from typing import Type\n", - "\n", - "from langchain_parrot_link.tools import ParrotMultiplyTool\n", - "from langchain_tests.integration_tests import ToolsIntegrationTests\n", - "\n", - "\n", - "class TestParrotMultiplyToolIntegration(ToolsIntegrationTests):\n", - " @property\n", - " def tool_constructor(self) -> Type[ParrotMultiplyTool]:\n", - " return ParrotMultiplyTool\n", - "\n", - " @property\n", - " def tool_constructor_params(self) -> dict:\n", - " # if your tool constructor instead required initialization arguments like\n", - " # `def __init__(self, some_arg: int):`, you would return those here\n", - " # as a dictionary, e.g.: `return {'some_arg': 42}`\n", - " return {}\n", - "\n", - " @property\n", - " def tool_invoke_params_example(self) -> dict:\n", - " \"\"\"\n", - " Returns a dictionary representing the \"args\" of an example tool call.\n", - "\n", - " This should NOT be a ToolCall dict - i.e. it should not\n", - " have {\"name\", \"id\", \"args\"} keys.\n", - " \"\"\"\n", - " return {\"a\": 2, \"b\": 3}" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
      \n", - "
      \n", - " Vector Stores" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# title=\"tests/integration_tests/test_vectorstores_sync.py\"\n", - "\n", - "from typing import AsyncGenerator, Generator\n", - "\n", - "import pytest\n", - "from langchain_core.vectorstores import VectorStore\n", - "from langchain_parrot_link.vectorstores import ParrotVectorStore\n", - "from langchain_standard_tests.integration_tests.vectorstores import (\n", - " AsyncReadWriteTestSuite,\n", - " ReadWriteTestSuite,\n", - ")\n", - "\n", - "\n", - "class TestSync(ReadWriteTestSuite):\n", - " @pytest.fixture()\n", - " def vectorstore(self) -> Generator[VectorStore, None, None]: # type: ignore\n", - " \"\"\"Get an empty vectorstore for unit tests.\"\"\"\n", - " store = ParrotVectorStore()\n", - " # note: store should be EMPTY at this point\n", - " # if you need to delete data, you may do so here\n", - " try:\n", - " yield store\n", - " finally:\n", - " # cleanup operations, or deleting data\n", - " pass\n", - "\n", - "\n", - "class TestAsync(AsyncReadWriteTestSuite):\n", - " @pytest.fixture()\n", - " async def vectorstore(self) -> AsyncGenerator[VectorStore, None]: # type: ignore\n", - " \"\"\"Get an empty vectorstore for unit tests.\"\"\"\n", - " store = ParrotVectorStore()\n", - " # note: store should be EMPTY at this point\n", - " # if you need to delete data, you may do so here\n", - " try:\n", - " yield store\n", - " finally:\n", - " # cleanup operations, or deleting data\n", - " pass" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
      " - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": ".venv", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.4" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/docs/contributing/how_to/integrations/standard_tests.mdx b/docs/docs/contributing/how_to/integrations/standard_tests.mdx new file mode 100644 index 0000000000000..5462b966a6404 --- /dev/null +++ b/docs/docs/contributing/how_to/integrations/standard_tests.mdx @@ -0,0 +1,393 @@ +--- +pagination_next: contributing/how_to/integrations/publish +pagination_prev: contributing/how_to/integrations/package +--- +# How to add standard tests to an integration + +When creating either a custom class for yourself or to publish in a LangChain integration, it is important to add standard tests to ensure it works as expected. This guide will show you how to add standard tests to each integration type. + +## Setup + +First, let's install 2 dependencies: + +- `langchain-core` will define the interfaces we want to import to define our custom tool. +- `langchain-tests` will provide the standard tests we want to use, as well as pytest plugins necessary to run them. Recommended to pin to the latest version: + +:::note + +Because added tests in new versions of `langchain-tests` can break your CI/CD pipelines, we recommend pinning the +version of `langchain-tests` to avoid unexpected changes. + +::: + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + + + +If you followed the [previous guide](../package), you should already have these dependencies installed! + +```bash +poetry add langchain-core +poetry add --group test langchain-tests== +poetry install --with test +``` + + +```bash +pip install -U langchain-core langchain-tests + +# install current package in editable mode +pip install --editable . +``` + + + +## Add and configure standard tests + +There are 2 namespaces in the `langchain-tests` package: + +- [unit tests](../../../concepts/testing.mdx#unit-tests) (`langchain_tests.unit_tests`): designed to be used to test the component in isolation and without access to external services +- [integration tests](../../../concepts/testing.mdx#integration-tests) (`langchain_tests.integration_tests`): designed to be used to test the component with access to external services (in particular, the external service that the component is designed to interact with). + +Both types of tests are implemented as [`pytest` class-based test suites](https://docs.pytest.org/en/7.1.x/getting-started.html#group-multiple-tests-in-a-class). + +By subclassing the base classes for each type of standard test (see below), you get all of the standard tests for that type, and you +can override the properties that the test suite uses to configure the tests. + +In order to run the tests in the same way as this guide, we recommend subclassing these +classes in test files under two test subdirectories: + +- `tests/unit_tests` for unit tests +- `tests/integration_tests` for integration tests + +### Implementing standard tests + +import CodeBlock from '@theme/CodeBlock'; + +In the following tabs, we show how to implement the standard tests for +each component type: + + + + + +To configure standard tests for a chat model, we subclass `ChatModelUnitTests` and `ChatModelIntegrationTests`. On each subclass, we override the following `@property` methods to specify the chat model to be tested and the chat model's configuration: + +| Property | Description | +| --- | --- | +| `chat_model_class` | The class for the chat model to be tested | +| `chat_model_params` | The parameters to pass to the chat +model's constructor | + +Additionally, chat model standard tests test a range of behaviors, from the most basic requirements (generating a response to a query) to optional capabilities like multi-modal support and tool-calling. For a test run to be successful: + +1. If a feature is intended to be supported by the model, it should pass; +2. If a feature is not intended to be supported by the model, it should be skipped. + +Tests for "optional" capabilities are controlled via a set of properties that can be overridden on the test model subclass. + +You can see the **entire list of configurable capabilities** in the API references for +[unit tests](https://python.langchain.com/api_reference/standard_tests/unit_tests/langchain_tests.unit_tests.chat_models.ChatModelUnitTests.html) +and [integration tests](https://python.langchain.com/api_reference/standard_tests/integration_tests/langchain_tests.integration_tests.chat_models.ChatModelIntegrationTests.html). + +For example, to enable integration tests for image inputs, we can implement + +```python +@property +def supports_image_inputs(self) -> bool: + return True +``` + +on the integration test class. + +:::note + +Details on what tests are run, how each test can be skipped, and troubleshooting tips for each test can be found in the API references. See details: + +- [Unit tests API reference](https://python.langchain.com/api_reference/standard_tests/unit_tests/langchain_tests.unit_tests.chat_models.ChatModelUnitTests.html) +- [Integration tests API reference](https://python.langchain.com/api_reference/standard_tests/integration_tests/langchain_tests.integration_tests.chat_models.ChatModelIntegrationTests.html) + +::: + +Unit test example: + +import ChatUnitSource from '../../../../src/theme/integration_template/tests/unit_tests/test_chat_models.py'; + + +{ + ChatUnitSource.replaceAll('__ModuleName__', 'ParrotLink') + .replaceAll('__package_name__', 'langchain-parrot-link') + .replaceAll('__MODULE_NAME__', 'PARROT_LINK') + .replaceAll('__module_name__', 'langchain_parrot_link') +} + + +Integration test example: + + +import ChatIntegrationSource from '../../../../src/theme/integration_template/tests/integration_tests/test_chat_models.py'; + + +{ + ChatIntegrationSource.replaceAll('__ModuleName__', 'ParrotLink') + .replaceAll('__package_name__', 'langchain-parrot-link') + .replaceAll('__MODULE_NAME__', 'PARROT_LINK') + .replaceAll('__module_name__', 'langchain_parrot_link') +} + + + + + + +Here's how you would configure the standard tests for a typical vector store (using +`ParrotVectorStore` as a placeholder): + +Vector store tests do not have optional capabilities to be configured at this time. + +import VectorStoreIntegrationSource from '../../../../src/theme/integration_template/tests/integration_tests/test_vectorstores.py'; + + +{ + VectorStoreIntegrationSource.replaceAll('__ModuleName__', 'Parrot') + .replaceAll('__package_name__', 'langchain-parrot-link') + .replaceAll('__MODULE_NAME__', 'PARROT') + .replaceAll('__module_name__', 'langchain_parrot_link') +} + + +Configuring the tests consists of implementing pytest fixtures for setting up an +empty vector store and tearing down the vector store after the test run ends. + +| Fixture | Description | +| --- | --- | +| `vectorstore` | A generator that yields an empty vector store for unit tests. The vector store is cleaned up after the test run ends. | + +For example, below is the `VectorStoreIntegrationTests` class for the [Chroma](https://python.langchain.com/docs/integrations/vectorstores/chroma/) +integration: + +```python +from typing import Generator + +import pytest +from langchain_core.vectorstores import VectorStore +from langchain_tests.integration_tests.vectorstores import VectorStoreIntegrationTests + +from langchain_chroma import Chroma + + +class TestChromaStandard(VectorStoreIntegrationTests): + @pytest.fixture() + def vectorstore(self) -> Generator[VectorStore, None, None]: # type: ignore + """Get an empty vectorstore for unit tests.""" + store = Chroma(embedding_function=self.get_embeddings()) + try: + yield store + finally: + store.delete_collection() + pass + +``` + +Note that before the initial `yield`, we instantiate the vector store with an +[embeddings](/docs/concepts/embedding_models/) object. This is a pre-defined +["fake" embeddings model](https://python.langchain.com/api_reference/standard_tests/integration_tests/langchain_tests.integration_tests.vectorstores.VectorStoreIntegrationTests.html#langchain_tests.integration_tests.vectorstores.VectorStoreIntegrationTests.get_embeddings) +that will generate short, arbitrary vectors for documents. You can use a different +embeddings object if desired. + +In the `finally` block, we call whatever integration-specific logic is needed to +bring the vector store to a clean state. This logic is executed in between each test +(e.g., even if tests fail). + +:::note + +Details on what tests are run and troubleshooting tips for each test can be found in the [API reference](https://python.langchain.com/api_reference/standard_tests/integration_tests/langchain_tests.integration_tests.vectorstores.VectorStoreIntegrationTests.html). + +::: + + + + + +To configure standard tests for an embeddings model, we subclass `EmbeddingsUnitTests` and `EmbeddingsIntegrationTests`. On each subclass, we override the following `@property` methods to specify the embeddings model to be tested and the embeddings model's configuration: + +| Property | Description | +| --- | --- | +| `embeddings_class` | The class for the embeddings model to be tested | +| `embedding_model_params` | The parameters to pass to the embeddings model's constructor | + +:::note + +Details on what tests are run, how each test can be skipped, and troubleshooting tips for each test can be found in the API references. See details: + +- [Unit tests API reference](https://python.langchain.com/api_reference/standard_tests/unit_tests/langchain_tests.unit_tests.embeddings.EmbeddingsUnitTests.html) +- [Integration tests API reference](https://python.langchain.com/api_reference/standard_tests/integration_tests/langchain_tests.integration_tests.embeddings.EmbeddingsIntegrationTests.html) + +::: + +Unit test example: + +import EmbeddingsUnitSource from '../../../../src/theme/integration_template/tests/unit_tests/test_embeddings.py'; + + +{ + EmbeddingsUnitSource.replaceAll('__ModuleName__', 'ParrotLink') + .replaceAll('__package_name__', 'langchain-parrot-link') + .replaceAll('__MODULE_NAME__', 'PARROT_LINK') + .replaceAll('__module_name__', 'langchain_parrot_link') +} + + +Integration test example: + + +```python title="tests/integration_tests/test_embeddings.py" +from typing import Type + +from langchain_parrot_link.embeddings import ParrotLinkEmbeddings +from langchain_tests.integration_tests import EmbeddingsIntegrationTests + + +class TestParrotLinkEmbeddingsIntegration(EmbeddingsIntegrationTests): + @property + def embeddings_class(self) -> Type[ParrotLinkEmbeddings]: + return ParrotLinkEmbeddings + + @property + def embedding_model_params(self) -> dict: + return {"model": "nest-embed-001"} +``` + +import EmbeddingsIntegrationSource from '../../../../src/theme/integration_template/tests/integration_tests/test_embeddings.py'; + + +{ + EmbeddingsIntegrationSource.replaceAll('__ModuleName__', 'ParrotLink') + .replaceAll('__package_name__', 'langchain-parrot-link') + .replaceAll('__MODULE_NAME__', 'PARROT_LINK') + .replaceAll('__module_name__', 'langchain_parrot_link') +} + + + + + +To configure standard tests for a tool, we subclass `ToolsUnitTests` and +`ToolsIntegrationTests`. On each subclass, we override the following `@property` methods +to specify the tool to be tested and the tool's configuration: + +| Property | Description | +| --- | --- | +| `tool_constructor` | The constructor for the tool to be tested, or an instantiated tool. | +| `tool_constructor_params` | The parameters to pass to the tool (optional). | +| `tool_invoke_params_example` | An example of the parameters to pass to the tool's `invoke` method. | + +If you are testing a tool class and pass a class like `MyTool` to `tool_constructor`, you can pass the parameters to the constructor in `tool_constructor_params`. + +If you are testing an instantiated tool, you can pass the instantiated tool to `tool_constructor` and do not +override `tool_constructor_params`. + +:::note + +Details on what tests are run, how each test can be skipped, and troubleshooting tips for each test can be found in the API references. See details: + +- [Unit tests API reference](https://python.langchain.com/api_reference/standard_tests/unit_tests/langchain_tests.unit_tests.tools.ToolsUnitTests.html) +- [Integration tests API reference](https://python.langchain.com/api_reference/standard_tests/integration_tests/langchain_tests.integration_tests.tools.ToolsIntegrationTests.html) + +::: + +import ToolsUnitSource from '../../../../src/theme/integration_template/tests/unit_tests/test_tools.py'; + + +{ + ToolsUnitSource.replaceAll('__ModuleName__', 'Parrot') + .replaceAll('__package_name__', 'langchain-parrot-link') + .replaceAll('__MODULE_NAME__', 'PARROT') + .replaceAll('__module_name__', 'langchain_parrot_link') +} + + +import ToolsIntegrationSource from '../../../../src/theme/integration_template/tests/integration_tests/test_tools.py'; + + +{ + ToolsIntegrationSource.replaceAll('__ModuleName__', 'Parrot') + .replaceAll('__package_name__', 'langchain-parrot-link') + .replaceAll('__MODULE_NAME__', 'PARROT') + .replaceAll('__module_name__', 'langchain_parrot_link') +} + + + + + + +To configure standard tests for a retriever, we subclass `RetrieversUnitTests` and +`RetrieversIntegrationTests`. On each subclass, we override the following `@property` methods + +| Property | Description | +| --- | --- | +| `retriever_constructor` | The class for the retriever to be tested | +| `retriever_constructor_params` | The parameters to pass to the retriever's constructor | +| `retriever_query_example` | An example of the query to pass to the retriever's `invoke` method | + +:::note + +Details on what tests are run and troubleshooting tips for each test can be found in the [API reference](https://python.langchain.com/api_reference/standard_tests/integration_tests/langchain_tests.integration_tests.retrievers.RetrieversIntegrationTests.html). + +::: + +import RetrieverIntegrationSource from '../../../../src/theme/integration_template/tests/integration_tests/test_retrievers.py'; + + +{ + RetrieverIntegrationSource.replaceAll('__ModuleName__', 'Parrot') + .replaceAll('__package_name__', 'langchain-parrot-link') + .replaceAll('__MODULE_NAME__', 'PARROT') + .replaceAll('__module_name__', 'langchain_parrot_link') +} + + + + + +--- + +### Running the tests + +You can run these with the following commands from your project root + + + + +```bash +# run unit tests without network access +poetry run pytest --disable-socket --allow-unix-socket --asyncio-mode=auto tests/unit_tests + +# run integration tests +poetry run pytest --asyncio-mode=auto tests/integration_tests +``` + + + + +```bash +# run unit tests without network access +pytest --disable-socket --allow-unix-socket --asyncio-mode=auto tests/unit_tests + +# run integration tests +pytest --asyncio-mode=auto tests/integration_tests +``` + + + + +## Test suite information and troubleshooting + +For a full list of the standard test suites that are available, as well as +information on which tests are included and how to troubleshoot common issues, +see the [Standard Tests API Reference](https://python.langchain.com/api_reference/standard_tests/index.html). + +You can see troubleshooting guides under the individual test suites listed in that API Reference. For example, +[here is the guide for `ChatModelIntegrationTests.test_usage_metadata`](https://python.langchain.com/api_reference/standard_tests/integration_tests/langchain_tests.integration_tests.chat_models.ChatModelIntegrationTests.html#langchain_tests.integration_tests.chat_models.ChatModelIntegrationTests.test_usage_metadata). diff --git a/docs/docs/how_to/agent_executor.ipynb b/docs/docs/how_to/agent_executor.ipynb index c52c126a066c3..3c9ab38bcdf0e 100644 --- a/docs/docs/how_to/agent_executor.ipynb +++ b/docs/docs/how_to/agent_executor.ipynb @@ -802,7 +802,7 @@ "That's a wrap! In this quick start we covered how to create a simple agent. Agents are a complex topic, and there's lot to learn! \n", "\n", ":::important\n", - "This section covered building with LangChain Agents. LangChain Agents are fine for getting started, but past a certain point you will likely want flexibility and control that they do not offer. For working with more advanced agents, we'd reccommend checking out [LangGraph](/docs/concepts/architecture/#langgraph)\n", + "This section covered building with LangChain Agents. They are fine for getting started, but past a certain point you will likely want flexibility and control which they do not offer. To develop more advanced agents, we recommend checking out [LangGraph](/docs/concepts/architecture/#langgraph)\n", ":::\n", "\n", "If you want to continue using LangChain agents, some good advanced guides are:\n", diff --git a/docs/docs/how_to/caching_embeddings.ipynb b/docs/docs/how_to/caching_embeddings.ipynb index 01187ef9ba9b4..6f5e8c7d6912b 100644 --- a/docs/docs/how_to/caching_embeddings.ipynb +++ b/docs/docs/how_to/caching_embeddings.ipynb @@ -23,7 +23,7 @@ "**Attention**:\n", "\n", "- Be sure to set the `namespace` parameter to avoid collisions of the same text embedded using different embeddings models.\n", - "- `CacheBackedEmbeddings` does not cache query embeddings by default. To enable query caching, one need to specify a `query_embedding_cache`." + "- `CacheBackedEmbeddings` does not cache query embeddings by default. To enable query caching, one needs to specify a `query_embedding_cache`." ] }, { diff --git a/docs/docs/how_to/callbacks_runtime.ipynb b/docs/docs/how_to/callbacks_runtime.ipynb index ff04fcde58894..aa9048b69284e 100644 --- a/docs/docs/how_to/callbacks_runtime.ipynb +++ b/docs/docs/how_to/callbacks_runtime.ipynb @@ -15,7 +15,7 @@ "\n", ":::\n", "\n", - "In many cases, it is advantageous to pass in handlers instead when running the object. When we pass through [`CallbackHandlers`](https://python.langchain.com/api_reference/core/callbacks/langchain_core.callbacks.base.BaseCallbackHandler.html#langchain-core-callbacks-base-basecallbackhandler) using the `callbacks` keyword arg when executing an run, those callbacks will be issued by all nested objects involved in the execution. For example, when a handler is passed through to an Agent, it will be used for all callbacks related to the agent and all the objects involved in the agent's execution, in this case, the Tools and LLM.\n", + "In many cases, it is advantageous to pass in handlers instead when running the object. When we pass through [`CallbackHandlers`](https://python.langchain.com/api_reference/core/callbacks/langchain_core.callbacks.base.BaseCallbackHandler.html#langchain-core-callbacks-base-basecallbackhandler) using the `callbacks` keyword arg when executing a run, those callbacks will be issued by all nested objects involved in the execution. For example, when a handler is passed through to an Agent, it will be used for all callbacks related to the agent and all the objects involved in the agent's execution, in this case, the Tools and LLM.\n", "\n", "This prevents us from having to manually attach the handlers to each individual nested object. Here's an example:" ] diff --git a/docs/docs/how_to/chat_model_rate_limiting.ipynb b/docs/docs/how_to/chat_model_rate_limiting.ipynb index 32bbaf3779cea..ccb0131b3dbcb 100644 --- a/docs/docs/how_to/chat_model_rate_limiting.ipynb +++ b/docs/docs/how_to/chat_model_rate_limiting.ipynb @@ -37,7 +37,7 @@ "\n", "Langchain comes with a built-in in memory rate limiter. This rate limiter is thread safe and can be shared by multiple threads in the same process.\n", "\n", - "The provided rate limiter can only limit the number of requests per unit time. It will not help if you need to also limited based on the size\n", + "The provided rate limiter can only limit the number of requests per unit time. It will not help if you need to also limit based on the size\n", "of the requests." ] }, diff --git a/docs/docs/how_to/custom_tools.ipynb b/docs/docs/how_to/custom_tools.ipynb index 8046b7b00e4a4..8becb03c7c91c 100644 --- a/docs/docs/how_to/custom_tools.ipynb +++ b/docs/docs/how_to/custom_tools.ipynb @@ -162,7 +162,7 @@ "\n", "@tool\n", "def multiply_by_max(\n", - " a: Annotated[str, \"scale factor\"],\n", + " a: Annotated[int, \"scale factor\"],\n", " b: Annotated[List[int], \"list of ints over which to take maximum\"],\n", ") -> int:\n", " \"\"\"Multiply a by the maximum of b.\"\"\"\n", @@ -294,7 +294,7 @@ "metadata": {}, "source": [ ":::caution\n", - "By default, `@tool(parse_docstring=True)` will raise `ValueError` if the docstring does not parse correctly. See [API Reference](https://python.langchain.com/api_reference/core/tools/langchain_core.tools.tool.html) for detail and examples.\n", + "By default, `@tool(parse_docstring=True)` will raise `ValueError` if the docstring does not parse correctly. See [API Reference](https://python.langchain.com/api_reference/core/tools/langchain_core.tools.convert.tool.html) for detail and examples.\n", ":::" ] }, diff --git a/docs/docs/how_to/functions.ipynb b/docs/docs/how_to/functions.ipynb index c5b781f2edbca..9a37eda942f31 100644 --- a/docs/docs/how_to/functions.ipynb +++ b/docs/docs/how_to/functions.ipynb @@ -121,7 +121,7 @@ "source": [ "## The convenience `@chain` decorator\n", "\n", - "You can also turn an arbitrary function into a chain by adding a `@chain` decorator. This is functionaly equivalent to wrapping the function in a `RunnableLambda` constructor as shown above. Here's an example:" + "You can also turn an arbitrary function into a chain by adding a `@chain` decorator. This is functionally equivalent to wrapping the function in a `RunnableLambda` constructor as shown above. Here's an example:" ] }, { diff --git a/docs/docs/how_to/graph_mapping.ipynb b/docs/docs/how_to/graph_mapping.ipynb deleted file mode 100644 index 146f479e27d32..0000000000000 --- a/docs/docs/how_to/graph_mapping.ipynb +++ /dev/null @@ -1,459 +0,0 @@ -{ - "cells": [ - { - "cell_type": "raw", - "id": "5e61b0f2-15b9-4241-9ab5-ff0f3f732232", - "metadata": {}, - "source": [ - "---\n", - "sidebar_position: 1\n", - "---" - ] - }, - { - "cell_type": "markdown", - "id": "846ef4f4-ee38-4a42-a7d3-1a23826e4830", - "metadata": {}, - "source": [ - "# How to map values to a graph database\n", - "\n", - "In this guide we'll go over strategies to improve graph database query generation by mapping values from user inputs to database.\n", - "When using the built-in graph chains, the LLM is aware of the graph schema, but has no information about the values of properties stored in the database.\n", - "Therefore, we can introduce a new step in graph database QA system to accurately map values.\n", - "\n", - "## Setup\n", - "\n", - "First, get required packages and set environment variables:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "18294435-182d-48da-bcab-5b8945b6d9cf", - "metadata": {}, - "outputs": [], - "source": [ - "%pip install --upgrade --quiet langchain langchain-neo4j langchain-openai neo4j" - ] - }, - { - "cell_type": "markdown", - "id": "d86dd771-4001-4a34-8680-22e9b50e1e88", - "metadata": {}, - "source": [ - "We default to OpenAI models in this guide, but you can swap them out for the model provider of your choice." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "9346f8e9-78bf-4667-b3d3-72807a73b718", - "metadata": {}, - "outputs": [ - { - "name": "stdin", - "output_type": "stream", - "text": [ - " ········\n" - ] - } - ], - "source": [ - "import getpass\n", - "import os\n", - "\n", - "os.environ[\"OPENAI_API_KEY\"] = getpass.getpass()\n", - "\n", - "# Uncomment the below to use LangSmith. Not required.\n", - "# os.environ[\"LANGCHAIN_API_KEY\"] = getpass.getpass()\n", - "# os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"" - ] - }, - { - "cell_type": "markdown", - "id": "271c8a23-e51c-4ead-a76e-cf21107db47e", - "metadata": {}, - "source": [ - "Next, we need to define Neo4j credentials.\n", - "Follow [these installation steps](https://neo4j.com/docs/operations-manual/current/installation/) to set up a Neo4j database." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "a2a3bb65-05c7-4daf-bac2-b25ae7fe2751", - "metadata": {}, - "outputs": [], - "source": [ - "os.environ[\"NEO4J_URI\"] = \"bolt://localhost:7687\"\n", - "os.environ[\"NEO4J_USERNAME\"] = \"neo4j\"\n", - "os.environ[\"NEO4J_PASSWORD\"] = \"password\"" - ] - }, - { - "cell_type": "markdown", - "id": "50fa4510-29b7-49b6-8496-5e86f694e81f", - "metadata": {}, - "source": [ - "The below example will create a connection with a Neo4j database and will populate it with example data about movies and their actors." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "4ee9ef7a-eef9-4289-b9fd-8fbc31041688", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[]" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from langchain_neo4j import Neo4jGraph\n", - "\n", - "graph = Neo4jGraph()\n", - "\n", - "# Import movie information\n", - "\n", - "movies_query = \"\"\"\n", - "LOAD CSV WITH HEADERS FROM \n", - "'https://raw.githubusercontent.com/tomasonjo/blog-datasets/main/movies/movies_small.csv'\n", - "AS row\n", - "MERGE (m:Movie {id:row.movieId})\n", - "SET m.released = date(row.released),\n", - " m.title = row.title,\n", - " m.imdbRating = toFloat(row.imdbRating)\n", - "FOREACH (director in split(row.director, '|') | \n", - " MERGE (p:Person {name:trim(director)})\n", - " MERGE (p)-[:DIRECTED]->(m))\n", - "FOREACH (actor in split(row.actors, '|') | \n", - " MERGE (p:Person {name:trim(actor)})\n", - " MERGE (p)-[:ACTED_IN]->(m))\n", - "FOREACH (genre in split(row.genres, '|') | \n", - " MERGE (g:Genre {name:trim(genre)})\n", - " MERGE (m)-[:IN_GENRE]->(g))\n", - "\"\"\"\n", - "\n", - "graph.query(movies_query)" - ] - }, - { - "cell_type": "markdown", - "id": "0cb0ea30-ca55-4f35-aad6-beb57453de66", - "metadata": {}, - "source": [ - "## Detecting entities in the user input\n", - "We have to extract the types of entities/values we want to map to a graph database. In this example, we are dealing with a movie graph, so we can map movies and people to the database." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "e1a19424-6046-40c2-81d1-f3b88193a293", - "metadata": {}, - "outputs": [], - "source": [ - "from typing import List, Optional\n", - "\n", - "from langchain_core.prompts import ChatPromptTemplate\n", - "from langchain_openai import ChatOpenAI\n", - "from pydantic import BaseModel, Field\n", - "\n", - "llm = ChatOpenAI(model=\"gpt-3.5-turbo\", temperature=0)\n", - "\n", - "\n", - "class Entities(BaseModel):\n", - " \"\"\"Identifying information about entities.\"\"\"\n", - "\n", - " names: List[str] = Field(\n", - " ...,\n", - " description=\"All the person or movies appearing in the text\",\n", - " )\n", - "\n", - "\n", - "prompt = ChatPromptTemplate.from_messages(\n", - " [\n", - " (\n", - " \"system\",\n", - " \"You are extracting person and movies from the text.\",\n", - " ),\n", - " (\n", - " \"human\",\n", - " \"Use the given format to extract information from the following \"\n", - " \"input: {question}\",\n", - " ),\n", - " ]\n", - ")\n", - "\n", - "\n", - "entity_chain = prompt | llm.with_structured_output(Entities)" - ] - }, - { - "cell_type": "markdown", - "id": "9c14084c-37a7-4a9c-a026-74e12961c781", - "metadata": {}, - "source": [ - "We can test the entity extraction chain." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "bbfe0d8f-982e-46e6-88fb-8a4f0d850b07", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Entities(names=['Casino'])" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "entities = entity_chain.invoke({\"question\": \"Who played in Casino movie?\"})\n", - "entities" - ] - }, - { - "cell_type": "markdown", - "id": "a8afbf13-05d0-4383-8050-f88b8c2f6fab", - "metadata": {}, - "source": [ - "We will utilize a simple `CONTAINS` clause to match entities to database. In practice, you might want to use a fuzzy search or a fulltext index to allow for minor misspellings." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "6f92929f-74fb-4db2-b7e1-eb1e9d386a67", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'Casino maps to Casino Movie in database\\n'" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "match_query = \"\"\"MATCH (p:Person|Movie)\n", - "WHERE p.name CONTAINS $value OR p.title CONTAINS $value\n", - "RETURN coalesce(p.name, p.title) AS result, labels(p)[0] AS type\n", - "LIMIT 1\n", - "\"\"\"\n", - "\n", - "\n", - "def map_to_database(entities: Entities) -> Optional[str]:\n", - " result = \"\"\n", - " for entity in entities.names:\n", - " response = graph.query(match_query, {\"value\": entity})\n", - " try:\n", - " result += f\"{entity} maps to {response[0]['result']} {response[0]['type']} in database\\n\"\n", - " except IndexError:\n", - " pass\n", - " return result\n", - "\n", - "\n", - "map_to_database(entities)" - ] - }, - { - "cell_type": "markdown", - "id": "f66c6756-6efb-4b1e-9b5d-87ed914a5212", - "metadata": {}, - "source": [ - "## Custom Cypher generating chain\n", - "\n", - "We need to define a custom Cypher prompt that takes the entity mapping information along with the schema and the user question to construct a Cypher statement.\n", - "We will be using the LangChain expression language to accomplish that." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "8ef3e21d-f1c2-45e2-9511-4920d1cf6e7e", - "metadata": {}, - "outputs": [], - "source": [ - "from langchain_core.output_parsers import StrOutputParser\n", - "from langchain_core.runnables import RunnablePassthrough\n", - "\n", - "# Generate Cypher statement based on natural language input\n", - "cypher_template = \"\"\"Based on the Neo4j graph schema below, write a Cypher query that would answer the user's question:\n", - "{schema}\n", - "Entities in the question map to the following database values:\n", - "{entities_list}\n", - "Question: {question}\n", - "Cypher query:\"\"\"\n", - "\n", - "cypher_prompt = ChatPromptTemplate.from_messages(\n", - " [\n", - " (\n", - " \"system\",\n", - " \"Given an input question, convert it to a Cypher query. No pre-amble.\",\n", - " ),\n", - " (\"human\", cypher_template),\n", - " ]\n", - ")\n", - "\n", - "cypher_response = (\n", - " RunnablePassthrough.assign(names=entity_chain)\n", - " | RunnablePassthrough.assign(\n", - " entities_list=lambda x: map_to_database(x[\"names\"]),\n", - " schema=lambda _: graph.get_schema,\n", - " )\n", - " | cypher_prompt\n", - " | llm.bind(stop=[\"\\nCypherResult:\"])\n", - " | StrOutputParser()\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "1f0011e3-9660-4975-af2a-486b1bc3b954", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'MATCH (:Movie {title: \"Casino\"})<-[:ACTED_IN]-(actor)\\nRETURN actor.name'" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "cypher = cypher_response.invoke({\"question\": \"Who played in Casino movie?\"})\n", - "cypher" - ] - }, - { - "cell_type": "markdown", - "id": "38095678-611f-4847-a4de-e51ef7ef727c", - "metadata": {}, - "source": [ - "## Generating answers based on database results\n", - "\n", - "Now that we have a chain that generates the Cypher statement, we need to execute the Cypher statement against the database and send the database results back to an LLM to generate the final answer.\n", - "Again, we will be using LCEL." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "id": "d1fa97c0-1c9c-41d3-9ee1-5f1905d17434", - "metadata": {}, - "outputs": [], - "source": [ - "from langchain_neo4j.chains.graph_qa.cypher_utils import (\n", - " CypherQueryCorrector,\n", - " Schema,\n", - ")\n", - "\n", - "graph.refresh_schema()\n", - "# Cypher validation tool for relationship directions\n", - "corrector_schema = [\n", - " Schema(el[\"start\"], el[\"type\"], el[\"end\"])\n", - " for el in graph.structured_schema.get(\"relationships\")\n", - "]\n", - "cypher_validation = CypherQueryCorrector(corrector_schema)\n", - "\n", - "# Generate natural language response based on database results\n", - "response_template = \"\"\"Based on the the question, Cypher query, and Cypher response, write a natural language response:\n", - "Question: {question}\n", - "Cypher query: {query}\n", - "Cypher Response: {response}\"\"\"\n", - "\n", - "response_prompt = ChatPromptTemplate.from_messages(\n", - " [\n", - " (\n", - " \"system\",\n", - " \"Given an input question and Cypher response, convert it to a natural\"\n", - " \" language answer. No pre-amble.\",\n", - " ),\n", - " (\"human\", response_template),\n", - " ]\n", - ")\n", - "\n", - "chain = (\n", - " RunnablePassthrough.assign(query=cypher_response)\n", - " | RunnablePassthrough.assign(\n", - " response=lambda x: graph.query(cypher_validation(x[\"query\"])),\n", - " )\n", - " | response_prompt\n", - " | llm\n", - " | StrOutputParser()\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "id": "918146e5-7918-46d2-a774-53f9547d8fcb", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'Robert De Niro, James Woods, Joe Pesci, and Sharon Stone played in the movie \"Casino\".'" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "chain.invoke({\"question\": \"Who played in Casino movie?\"})" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "c7ba75cd-8399-4e54-a6f8-8a411f159f56", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.18" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/docs/docs/how_to/graph_prompting.ipynb b/docs/docs/how_to/graph_prompting.ipynb deleted file mode 100644 index db4922fb3a2da..0000000000000 --- a/docs/docs/how_to/graph_prompting.ipynb +++ /dev/null @@ -1,548 +0,0 @@ -{ - "cells": [ - { - "cell_type": "raw", - "metadata": {}, - "source": [ - "---\n", - "sidebar_position: 2\n", - "---" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# How to best prompt for Graph-RAG\n", - "\n", - "In this guide we'll go over prompting strategies to improve graph database query generation. We'll largely focus on methods for getting relevant database-specific information in your prompt.\n", - "\n", - "## Setup\n", - "\n", - "First, get required packages and set environment variables:" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Note: you may need to restart the kernel to use updated packages.\n" - ] - } - ], - "source": [ - "%pip install --upgrade --quiet langchain langchain-neo4j langchain-openai neo4j" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We default to OpenAI models in this guide, but you can swap them out for the model provider of your choice." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [ - { - "name": "stdin", - "output_type": "stream", - "text": [ - " ········\n" - ] - } - ], - "source": [ - "import getpass\n", - "import os\n", - "\n", - "os.environ[\"OPENAI_API_KEY\"] = getpass.getpass()\n", - "\n", - "# Uncomment the below to use LangSmith. Not required.\n", - "# os.environ[\"LANGCHAIN_API_KEY\"] = getpass.getpass()\n", - "# os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Next, we need to define Neo4j credentials.\n", - "Follow [these installation steps](https://neo4j.com/docs/operations-manual/current/installation/) to set up a Neo4j database." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "os.environ[\"NEO4J_URI\"] = \"bolt://localhost:7687\"\n", - "os.environ[\"NEO4J_USERNAME\"] = \"neo4j\"\n", - "os.environ[\"NEO4J_PASSWORD\"] = \"password\"" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The below example will create a connection with a Neo4j database and will populate it with example data about movies and their actors." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[]" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from langchain_neo4j import Neo4jGraph\n", - "\n", - "graph = Neo4jGraph()\n", - "\n", - "# Import movie information\n", - "\n", - "movies_query = \"\"\"\n", - "LOAD CSV WITH HEADERS FROM \n", - "'https://raw.githubusercontent.com/tomasonjo/blog-datasets/main/movies/movies_small.csv'\n", - "AS row\n", - "MERGE (m:Movie {id:row.movieId})\n", - "SET m.released = date(row.released),\n", - " m.title = row.title,\n", - " m.imdbRating = toFloat(row.imdbRating)\n", - "FOREACH (director in split(row.director, '|') | \n", - " MERGE (p:Person {name:trim(director)})\n", - " MERGE (p)-[:DIRECTED]->(m))\n", - "FOREACH (actor in split(row.actors, '|') | \n", - " MERGE (p:Person {name:trim(actor)})\n", - " MERGE (p)-[:ACTED_IN]->(m))\n", - "FOREACH (genre in split(row.genres, '|') | \n", - " MERGE (g:Genre {name:trim(genre)})\n", - " MERGE (m)-[:IN_GENRE]->(g))\n", - "\"\"\"\n", - "\n", - "graph.query(movies_query)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Filtering graph schema\n", - "\n", - "At times, you may need to focus on a specific subset of the graph schema while generating Cypher statements.\n", - "Let's say we are dealing with the following graph schema:" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Node properties are the following:\n", - "Movie {imdbRating: FLOAT, id: STRING, released: DATE, title: STRING},Person {name: STRING},Genre {name: STRING}\n", - "Relationship properties are the following:\n", - "\n", - "The relationships are the following:\n", - "(:Movie)-[:IN_GENRE]->(:Genre),(:Person)-[:DIRECTED]->(:Movie),(:Person)-[:ACTED_IN]->(:Movie)\n" - ] - } - ], - "source": [ - "graph.refresh_schema()\n", - "print(graph.schema)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's say we want to exclude the _Genre_ node from the schema representation we pass to an LLM.\n", - "We can achieve that using the `exclude` parameter of the GraphCypherQAChain chain." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "from langchain_neo4j import GraphCypherQAChain\n", - "from langchain_openai import ChatOpenAI\n", - "\n", - "llm = ChatOpenAI(model=\"gpt-3.5-turbo\", temperature=0)\n", - "chain = GraphCypherQAChain.from_llm(\n", - " graph=graph,\n", - " llm=llm,\n", - " exclude_types=[\"Genre\"],\n", - " verbose=True,\n", - " allow_dangerous_requests=True,\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Node properties are the following:\n", - "Movie {imdbRating: FLOAT, id: STRING, released: DATE, title: STRING},Person {name: STRING}\n", - "Relationship properties are the following:\n", - "\n", - "The relationships are the following:\n", - "(:Person)-[:DIRECTED]->(:Movie),(:Person)-[:ACTED_IN]->(:Movie)\n" - ] - } - ], - "source": [ - "print(chain.graph_schema)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Few-shot examples\n", - "\n", - "Including examples of natural language questions being converted to valid Cypher queries against our database in the prompt will often improve model performance, especially for complex queries.\n", - "\n", - "Let's say we have the following examples:" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "examples = [\n", - " {\n", - " \"question\": \"How many artists are there?\",\n", - " \"query\": \"MATCH (a:Person)-[:ACTED_IN]->(:Movie) RETURN count(DISTINCT a)\",\n", - " },\n", - " {\n", - " \"question\": \"Which actors played in the movie Casino?\",\n", - " \"query\": \"MATCH (m:Movie {{title: 'Casino'}})<-[:ACTED_IN]-(a) RETURN a.name\",\n", - " },\n", - " {\n", - " \"question\": \"How many movies has Tom Hanks acted in?\",\n", - " \"query\": \"MATCH (a:Person {{name: 'Tom Hanks'}})-[:ACTED_IN]->(m:Movie) RETURN count(m)\",\n", - " },\n", - " {\n", - " \"question\": \"List all the genres of the movie Schindler's List\",\n", - " \"query\": \"MATCH (m:Movie {{title: 'Schindler\\\\'s List'}})-[:IN_GENRE]->(g:Genre) RETURN g.name\",\n", - " },\n", - " {\n", - " \"question\": \"Which actors have worked in movies from both the comedy and action genres?\",\n", - " \"query\": \"MATCH (a:Person)-[:ACTED_IN]->(:Movie)-[:IN_GENRE]->(g1:Genre), (a)-[:ACTED_IN]->(:Movie)-[:IN_GENRE]->(g2:Genre) WHERE g1.name = 'Comedy' AND g2.name = 'Action' RETURN DISTINCT a.name\",\n", - " },\n", - " {\n", - " \"question\": \"Which directors have made movies with at least three different actors named 'John'?\",\n", - " \"query\": \"MATCH (d:Person)-[:DIRECTED]->(m:Movie)<-[:ACTED_IN]-(a:Person) WHERE a.name STARTS WITH 'John' WITH d, COUNT(DISTINCT a) AS JohnsCount WHERE JohnsCount >= 3 RETURN d.name\",\n", - " },\n", - " {\n", - " \"question\": \"Identify movies where directors also played a role in the film.\",\n", - " \"query\": \"MATCH (p:Person)-[:DIRECTED]->(m:Movie), (p)-[:ACTED_IN]->(m) RETURN m.title, p.name\",\n", - " },\n", - " {\n", - " \"question\": \"Find the actor with the highest number of movies in the database.\",\n", - " \"query\": \"MATCH (a:Actor)-[:ACTED_IN]->(m:Movie) RETURN a.name, COUNT(m) AS movieCount ORDER BY movieCount DESC LIMIT 1\",\n", - " },\n", - "]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can create a few-shot prompt with them like so:" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "from langchain_core.prompts import FewShotPromptTemplate, PromptTemplate\n", - "\n", - "example_prompt = PromptTemplate.from_template(\n", - " \"User input: {question}\\nCypher query: {query}\"\n", - ")\n", - "prompt = FewShotPromptTemplate(\n", - " examples=examples[:5],\n", - " example_prompt=example_prompt,\n", - " prefix=\"You are a Neo4j expert. Given an input question, create a syntactically correct Cypher query to run.\\n\\nHere is the schema information\\n{schema}.\\n\\nBelow are a number of examples of questions and their corresponding Cypher queries.\",\n", - " suffix=\"User input: {question}\\nCypher query: \",\n", - " input_variables=[\"question\", \"schema\"],\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "You are a Neo4j expert. Given an input question, create a syntactically correct Cypher query to run.\n", - "\n", - "Here is the schema information\n", - "foo.\n", - "\n", - "Below are a number of examples of questions and their corresponding Cypher queries.\n", - "\n", - "User input: How many artists are there?\n", - "Cypher query: MATCH (a:Person)-[:ACTED_IN]->(:Movie) RETURN count(DISTINCT a)\n", - "\n", - "User input: Which actors played in the movie Casino?\n", - "Cypher query: MATCH (m:Movie {title: 'Casino'})<-[:ACTED_IN]-(a) RETURN a.name\n", - "\n", - "User input: How many movies has Tom Hanks acted in?\n", - "Cypher query: MATCH (a:Person {name: 'Tom Hanks'})-[:ACTED_IN]->(m:Movie) RETURN count(m)\n", - "\n", - "User input: List all the genres of the movie Schindler's List\n", - "Cypher query: MATCH (m:Movie {title: 'Schindler\\'s List'})-[:IN_GENRE]->(g:Genre) RETURN g.name\n", - "\n", - "User input: Which actors have worked in movies from both the comedy and action genres?\n", - "Cypher query: MATCH (a:Person)-[:ACTED_IN]->(:Movie)-[:IN_GENRE]->(g1:Genre), (a)-[:ACTED_IN]->(:Movie)-[:IN_GENRE]->(g2:Genre) WHERE g1.name = 'Comedy' AND g2.name = 'Action' RETURN DISTINCT a.name\n", - "\n", - "User input: How many artists are there?\n", - "Cypher query: \n" - ] - } - ], - "source": [ - "print(prompt.format(question=\"How many artists are there?\", schema=\"foo\"))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Dynamic few-shot examples\n", - "\n", - "If we have enough examples, we may want to only include the most relevant ones in the prompt, either because they don't fit in the model's context window or because the long tail of examples distracts the model. And specifically, given any input we want to include the examples most relevant to that input.\n", - "\n", - "We can do just this using an ExampleSelector. In this case we'll use a [SemanticSimilarityExampleSelector](https://python.langchain.com/api_reference/core/example_selectors/langchain_core.example_selectors.semantic_similarity.SemanticSimilarityExampleSelector.html), which will store the examples in the vector database of our choosing. At runtime it will perform a similarity search between the input and our examples, and return the most semantically similar ones: " - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "from langchain_core.example_selectors import SemanticSimilarityExampleSelector\n", - "from langchain_neo4j import Neo4jVector\n", - "from langchain_openai import OpenAIEmbeddings\n", - "\n", - "example_selector = SemanticSimilarityExampleSelector.from_examples(\n", - " examples,\n", - " OpenAIEmbeddings(),\n", - " Neo4jVector,\n", - " k=5,\n", - " input_keys=[\"question\"],\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[{'query': 'MATCH (a:Person)-[:ACTED_IN]->(:Movie) RETURN count(DISTINCT a)',\n", - " 'question': 'How many artists are there?'},\n", - " {'query': \"MATCH (a:Person {{name: 'Tom Hanks'}})-[:ACTED_IN]->(m:Movie) RETURN count(m)\",\n", - " 'question': 'How many movies has Tom Hanks acted in?'},\n", - " {'query': \"MATCH (a:Person)-[:ACTED_IN]->(:Movie)-[:IN_GENRE]->(g1:Genre), (a)-[:ACTED_IN]->(:Movie)-[:IN_GENRE]->(g2:Genre) WHERE g1.name = 'Comedy' AND g2.name = 'Action' RETURN DISTINCT a.name\",\n", - " 'question': 'Which actors have worked in movies from both the comedy and action genres?'},\n", - " {'query': \"MATCH (d:Person)-[:DIRECTED]->(m:Movie)<-[:ACTED_IN]-(a:Person) WHERE a.name STARTS WITH 'John' WITH d, COUNT(DISTINCT a) AS JohnsCount WHERE JohnsCount >= 3 RETURN d.name\",\n", - " 'question': \"Which directors have made movies with at least three different actors named 'John'?\"},\n", - " {'query': 'MATCH (a:Actor)-[:ACTED_IN]->(m:Movie) RETURN a.name, COUNT(m) AS movieCount ORDER BY movieCount DESC LIMIT 1',\n", - " 'question': 'Find the actor with the highest number of movies in the database.'}]" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "example_selector.select_examples({\"question\": \"how many artists are there?\"})" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To use it, we can pass the ExampleSelector directly in to our FewShotPromptTemplate:" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "prompt = FewShotPromptTemplate(\n", - " example_selector=example_selector,\n", - " example_prompt=example_prompt,\n", - " prefix=\"You are a Neo4j expert. Given an input question, create a syntactically correct Cypher query to run.\\n\\nHere is the schema information\\n{schema}.\\n\\nBelow are a number of examples of questions and their corresponding Cypher queries.\",\n", - " suffix=\"User input: {question}\\nCypher query: \",\n", - " input_variables=[\"question\", \"schema\"],\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "You are a Neo4j expert. Given an input question, create a syntactically correct Cypher query to run.\n", - "\n", - "Here is the schema information\n", - "foo.\n", - "\n", - "Below are a number of examples of questions and their corresponding Cypher queries.\n", - "\n", - "User input: How many artists are there?\n", - "Cypher query: MATCH (a:Person)-[:ACTED_IN]->(:Movie) RETURN count(DISTINCT a)\n", - "\n", - "User input: How many movies has Tom Hanks acted in?\n", - "Cypher query: MATCH (a:Person {name: 'Tom Hanks'})-[:ACTED_IN]->(m:Movie) RETURN count(m)\n", - "\n", - "User input: Which actors have worked in movies from both the comedy and action genres?\n", - "Cypher query: MATCH (a:Person)-[:ACTED_IN]->(:Movie)-[:IN_GENRE]->(g1:Genre), (a)-[:ACTED_IN]->(:Movie)-[:IN_GENRE]->(g2:Genre) WHERE g1.name = 'Comedy' AND g2.name = 'Action' RETURN DISTINCT a.name\n", - "\n", - "User input: Which directors have made movies with at least three different actors named 'John'?\n", - "Cypher query: MATCH (d:Person)-[:DIRECTED]->(m:Movie)<-[:ACTED_IN]-(a:Person) WHERE a.name STARTS WITH 'John' WITH d, COUNT(DISTINCT a) AS JohnsCount WHERE JohnsCount >= 3 RETURN d.name\n", - "\n", - "User input: Find the actor with the highest number of movies in the database.\n", - "Cypher query: MATCH (a:Actor)-[:ACTED_IN]->(m:Movie) RETURN a.name, COUNT(m) AS movieCount ORDER BY movieCount DESC LIMIT 1\n", - "\n", - "User input: how many artists are there?\n", - "Cypher query: \n" - ] - } - ], - "source": [ - "print(prompt.format(question=\"how many artists are there?\", schema=\"foo\"))" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [], - "source": [ - "llm = ChatOpenAI(model=\"gpt-3.5-turbo\", temperature=0)\n", - "chain = GraphCypherQAChain.from_llm(\n", - " graph=graph,\n", - " llm=llm,\n", - " cypher_prompt=prompt,\n", - " verbose=True,\n", - " allow_dangerous_requests=True,\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "\n", - "\u001b[1m> Entering new GraphCypherQAChain chain...\u001b[0m\n", - "Generated Cypher:\n", - "\u001b[32;1m\u001b[1;3mMATCH (a:Person)-[:ACTED_IN]->(:Movie) RETURN count(DISTINCT a)\u001b[0m\n", - "Full Context:\n", - "\u001b[32;1m\u001b[1;3m[{'count(DISTINCT a)': 967}]\u001b[0m\n", - "\n", - "\u001b[1m> Finished chain.\u001b[0m\n" - ] - }, - { - "data": { - "text/plain": [ - "{'query': 'How many actors are in the graph?',\n", - " 'result': 'There are 967 actors in the graph.'}" - ] - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "chain.invoke(\"How many actors are in the graph?\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.1" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/docs/how_to/graph_semantic.ipynb b/docs/docs/how_to/graph_semantic.ipynb index 4cd717d861297..f2832853e1ca0 100644 --- a/docs/docs/how_to/graph_semantic.ipynb +++ b/docs/docs/how_to/graph_semantic.ipynb @@ -31,20 +31,12 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "id": "ffdd48f6-bd05-4e5c-b846-d41183398a55", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Note: you may need to restart the kernel to use updated packages.\n" - ] - } - ], + "outputs": [], "source": [ - "%pip install --upgrade --quiet langchain langchain-neo4j langchain-openai neo4j" + "%pip install --upgrade --quiet langchain langchain-neo4j langchain-openai" ] }, { @@ -57,7 +49,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 1, "id": "eb11c4a8-c00c-4c2d-9309-74a6acfff91c", "metadata": {}, "outputs": [ @@ -91,7 +83,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 2, "id": "ef59a3af-31a8-4ad8-8eb9-132aca66956e", "metadata": {}, "outputs": [], @@ -111,7 +103,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 3, "id": "c84b1449-6fcd-4140-b591-cb45e8dce207", "metadata": {}, "outputs": [ @@ -121,7 +113,7 @@ "[]" ] }, - "execution_count": 4, + "execution_count": 3, "metadata": {}, "output_type": "execute_result" } @@ -129,7 +121,7 @@ "source": [ "from langchain_neo4j import Neo4jGraph\n", "\n", - "graph = Neo4jGraph()\n", + "graph = Neo4jGraph(refresh_schema=False)\n", "\n", "# Import movie information\n", "\n", @@ -170,26 +162,15 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 4, "id": "d1dc1c8c-f343-4024-924b-a8a86cf5f1af", "metadata": {}, "outputs": [], "source": [ - "from typing import Optional, Type\n", - "\n", - "from langchain_core.callbacks import (\n", - " AsyncCallbackManagerForToolRun,\n", - " CallbackManagerForToolRun,\n", - ")\n", - "from langchain_core.tools import BaseTool\n", - "\n", - "# Import things that are needed generically\n", - "from pydantic import BaseModel, Field\n", - "\n", "description_query = \"\"\"\n", "MATCH (m:Movie|Person)\n", "WHERE m.title CONTAINS $candidate OR m.name CONTAINS $candidate\n", - "MATCH (m)-[r:ACTED_IN|HAS_GENRE]-(t)\n", + "MATCH (m)-[r:ACTED_IN|IN_GENRE]-(t)\n", "WITH m, type(r) as type, collect(coalesce(t.name, t.title)) as names\n", "WITH m, type+\": \"+reduce(s=\"\", n IN names | s + n + \", \") as types\n", "WITH m, collect(types) as contexts\n", @@ -220,20 +201,14 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 5, "id": "f4cde772-0d05-475d-a2f0-b53e1669bd13", "metadata": {}, "outputs": [], "source": [ "from typing import Optional, Type\n", "\n", - "from langchain_core.callbacks import (\n", - " AsyncCallbackManagerForToolRun,\n", - " CallbackManagerForToolRun,\n", - ")\n", "from langchain_core.tools import BaseTool\n", - "\n", - "# Import things that are needed generically\n", "from pydantic import BaseModel, Field\n", "\n", "\n", @@ -251,7 +226,6 @@ " def _run(\n", " self,\n", " entity: str,\n", - " run_manager: Optional[CallbackManagerForToolRun] = None,\n", " ) -> str:\n", " \"\"\"Use the tool.\"\"\"\n", " return get_information(entity)\n", @@ -259,7 +233,6 @@ " async def _arun(\n", " self,\n", " entity: str,\n", - " run_manager: Optional[AsyncCallbackManagerForToolRun] = None,\n", " ) -> str:\n", " \"\"\"Use the tool asynchronously.\"\"\"\n", " return get_information(entity)" @@ -270,74 +243,103 @@ "id": "ff4820aa-2b57-4558-901f-6d984b326738", "metadata": {}, "source": [ - "## OpenAI Agent\n", + "## LangGraph Agent\n", + "\n", + "We will implement a straightforward ReAct agent using LangGraph.\n", + "\n", + "The agent consists of an LLM and tools step. As we interact with the agent, we will first call the LLM to decide if we should use tools. Then we will run a loop:\n", "\n", - "LangChain expression language makes it very convenient to define an agent to interact with a graph database over the semantic layer." + "If the agent said to take an action (i.e. call tool), we’ll run the tools and pass the results back to the agent.\n", + "If the agent did not ask to run tools, we will finish (respond to the user).\n", + "\n", + "The code implementation is as straightforward as it gets. First we bind the tools to the LLM and define the assistant step." ] }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 6, "id": "6e959ac2-537d-4358-a43b-e3a47f68e1d6", "metadata": {}, "outputs": [], "source": [ - "from typing import List, Tuple\n", - "\n", - "from langchain.agents import AgentExecutor\n", - "from langchain.agents.format_scratchpad import format_to_openai_function_messages\n", - "from langchain.agents.output_parsers import OpenAIFunctionsAgentOutputParser\n", - "from langchain_core.messages import AIMessage, HumanMessage\n", - "from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder\n", - "from langchain_core.utils.function_calling import convert_to_openai_function\n", + "from langchain_core.messages import HumanMessage, SystemMessage\n", "from langchain_openai import ChatOpenAI\n", + "from langgraph.graph import MessagesState\n", + "\n", + "llm = ChatOpenAI(model=\"gpt-4o\")\n", "\n", - "llm = ChatOpenAI(model=\"gpt-3.5-turbo\", temperature=0)\n", "tools = [InformationTool()]\n", + "llm_with_tools = llm.bind_tools(tools)\n", "\n", - "llm_with_tools = llm.bind(functions=[convert_to_openai_function(t) for t in tools])\n", - "\n", - "prompt = ChatPromptTemplate.from_messages(\n", - " [\n", - " (\n", - " \"system\",\n", - " \"You are a helpful assistant that finds information about movies \"\n", - " \" and recommends them. If tools require follow up questions, \"\n", - " \"make sure to ask the user for clarification. Make sure to include any \"\n", - " \"available options that need to be clarified in the follow up questions \"\n", - " \"Do only the things the user specifically requested. \",\n", - " ),\n", - " MessagesPlaceholder(variable_name=\"chat_history\"),\n", - " (\"user\", \"{input}\"),\n", - " MessagesPlaceholder(variable_name=\"agent_scratchpad\"),\n", - " ]\n", + "# System message\n", + "sys_msg = SystemMessage(\n", + " content=\"You are a helpful assistant tasked with finding and explaining relevant information about movies.\"\n", ")\n", "\n", "\n", - "def _format_chat_history(chat_history: List[Tuple[str, str]]):\n", - " buffer = []\n", - " for human, ai in chat_history:\n", - " buffer.append(HumanMessage(content=human))\n", - " buffer.append(AIMessage(content=ai))\n", - " return buffer\n", - "\n", - "\n", - "agent = (\n", - " {\n", - " \"input\": lambda x: x[\"input\"],\n", - " \"chat_history\": lambda x: _format_chat_history(x[\"chat_history\"])\n", - " if x.get(\"chat_history\")\n", - " else [],\n", - " \"agent_scratchpad\": lambda x: format_to_openai_function_messages(\n", - " x[\"intermediate_steps\"]\n", - " ),\n", - " }\n", - " | prompt\n", - " | llm_with_tools\n", - " | OpenAIFunctionsAgentOutputParser()\n", + "# Node\n", + "def assistant(state: MessagesState):\n", + " return {\"messages\": [llm_with_tools.invoke([sys_msg] + state[\"messages\"])]}" + ] + }, + { + "cell_type": "markdown", + "id": "087e745b-6ce6-4f1f-9a52-a6de597971bf", + "metadata": {}, + "source": [ + "Next we define the LangGraph flow." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "6763e87b-a6d2-44ed-9000-7440b51b325f", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from IPython.display import Image, display\n", + "from langgraph.graph import END, START, StateGraph\n", + "from langgraph.prebuilt import ToolNode, tools_condition\n", + "\n", + "# Graph\n", + "builder = StateGraph(MessagesState)\n", + "\n", + "# Define nodes: these do the work\n", + "builder.add_node(\"assistant\", assistant)\n", + "builder.add_node(\"tools\", ToolNode(tools))\n", + "\n", + "# Define edges: these determine how the control flow moves\n", + "builder.add_edge(START, \"assistant\")\n", + "builder.add_conditional_edges(\n", + " \"assistant\",\n", + " # If the latest message (result) from assistant is a tool call -> tools_condition routes to tools\n", + " # If the latest message (result) from assistant is a not a tool call -> tools_condition routes to END\n", + " tools_condition,\n", ")\n", + "builder.add_edge(\"tools\", \"assistant\")\n", + "react_graph = builder.compile()\n", "\n", - "agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)" + "# Show\n", + "display(Image(react_graph.get_graph(xray=True).draw_mermaid_png()))" + ] + }, + { + "cell_type": "markdown", + "id": "c038c048-5a1a-4d6a-a904-387a85e3e549", + "metadata": {}, + "source": [ + "Let's test the workflow now with an example question." ] }, { @@ -350,42 +352,48 @@ "name": "stdout", "output_type": "stream", "text": [ + "================================\u001b[1m Human Message \u001b[0m=================================\n", "\n", + "Who played in the Casino?\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "Tool Calls:\n", + " Information (call_j4usgFStGtBM16fuguRaeoGc)\n", + " Call ID: call_j4usgFStGtBM16fuguRaeoGc\n", + " Args:\n", + " entity: Casino\n", + "=================================\u001b[1m Tool Message \u001b[0m=================================\n", + "Name: Information\n", "\n", - "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n", - "\u001b[32;1m\u001b[1;3m\n", - "Invoking: `Information` with `{'entity': 'Casino'}`\n", - "\n", - "\n", - "\u001b[0m\u001b[36;1m\u001b[1;3mtype:Movie\n", + "type:Movie\n", "title: Casino\n", "year: 1995-11-22\n", - "ACTED_IN: Joe Pesci, Robert De Niro, Sharon Stone, James Woods\n", - "\u001b[0m\u001b[32;1m\u001b[1;3mThe movie \"Casino\" starred Joe Pesci, Robert De Niro, Sharon Stone, and James Woods.\u001b[0m\n", + "ACTED_IN: Robert De Niro, Joe Pesci, Sharon Stone, James Woods\n", + "IN_GENRE: Drama, Crime\n", + "\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", "\n", - "\u001b[1m> Finished chain.\u001b[0m\n" + "The movie \"Casino,\" released in 1995, features the following actors:\n", + "\n", + "- Robert De Niro\n", + "- Joe Pesci\n", + "- Sharon Stone\n", + "- James Woods\n", + "\n", + "The film is in the Drama and Crime genres.\n" ] - }, - { - "data": { - "text/plain": [ - "{'input': 'Who played in Casino?',\n", - " 'output': 'The movie \"Casino\" starred Joe Pesci, Robert De Niro, Sharon Stone, and James Woods.'}" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" } ], "source": [ - "agent_executor.invoke({\"input\": \"Who played in Casino?\"})" + "input_messages = [HumanMessage(content=\"Who played in the Casino?\")]\n", + "messages = react_graph.invoke({\"messages\": input_messages})\n", + "for m in messages[\"messages\"]:\n", + " m.pretty_print()" ] }, { "cell_type": "code", "execution_count": null, - "id": "c2759973-de8a-4624-8930-c90a21d6caa3", + "id": "a9fe9d6b-2eb6-47e5-a085-ca35dc5e3654", "metadata": {}, "outputs": [], "source": [] @@ -407,7 +415,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.1" + "version": "3.11.5" } }, "nbformat": 4, diff --git a/docs/docs/how_to/index.mdx b/docs/docs/how_to/index.mdx index b432569bf66bc..1ce6cc2737a57 100644 --- a/docs/docs/how_to/index.mdx +++ b/docs/docs/how_to/index.mdx @@ -316,9 +316,7 @@ For a high-level tutorial, check out [this guide](/docs/tutorials/sql_qa/). You can use an LLM to do question answering over graph databases. For a high-level tutorial, check out [this guide](/docs/tutorials/graph/). -- [How to: map values to a database](/docs/how_to/graph_mapping) - [How to: add a semantic layer over the database](/docs/how_to/graph_semantic) -- [How to: improve results with prompting](/docs/how_to/graph_prompting) - [How to: construct knowledge graphs](/docs/how_to/graph_constructing) ### Summarization diff --git a/docs/docs/how_to/indexing.ipynb b/docs/docs/how_to/indexing.ipynb index e3e6ec8aef6d7..4e6fed96abdcd 100644 --- a/docs/docs/how_to/indexing.ipynb +++ b/docs/docs/how_to/indexing.ipynb @@ -39,19 +39,20 @@ "| None | ✅ | ✅ | ❌ | ❌ | - |\n", "| Incremental | ✅ | ✅ | ❌ | ✅ | Continuously |\n", "| Full | ✅ | ❌ | ✅ | ✅ | At end of indexing |\n", + "| Scoped_Full | ✅ | ✅ | ❌ | ✅ | At end of indexing |\n", "\n", "\n", "`None` does not do any automatic clean up, allowing the user to manually do clean up of old content. \n", "\n", - "`incremental` and `full` offer the following automated clean up:\n", + "`incremental`, `full` and `scoped_full` offer the following automated clean up:\n", "\n", - "* If the content of the source document or derived documents has **changed**, both `incremental` or `full` modes will clean up (delete) previous versions of the content.\n", - "* If the source document has been **deleted** (meaning it is not included in the documents currently being indexed), the `full` cleanup mode will delete it from the vector store correctly, but the `incremental` mode will not.\n", + "* If the content of the source document or derived documents has **changed**, all 3 modes will clean up (delete) previous versions of the content.\n", + "* If the source document has been **deleted** (meaning it is not included in the documents currently being indexed), the `full` cleanup mode will delete it from the vector store correctly, but the `incremental` and `scoped_full` mode will not.\n", "\n", "When content is mutated (e.g., the source PDF file was revised) there will be a period of time during indexing when both the new and old versions may be returned to the user. This happens after the new content was written, but before the old version was deleted.\n", "\n", "* `incremental` indexing minimizes this period of time as it is able to do clean up continuously, as it writes.\n", - "* `full` mode does the clean up after all batches have been written.\n", + "* `full` and `scoped_full` mode does the clean up after all batches have been written.\n", "\n", "## Requirements\n", "\n", @@ -64,7 +65,7 @@ " \n", "## Caution\n", "\n", - "The record manager relies on a time-based mechanism to determine what content can be cleaned up (when using `full` or `incremental` cleanup modes).\n", + "The record manager relies on a time-based mechanism to determine what content can be cleaned up (when using `full` or `incremental` or `scoped_full` cleanup modes).\n", "\n", "If two tasks run back-to-back, and the first task finishes before the clock time changes, then the second task may not be able to clean up content.\n", "\n", diff --git a/docs/docs/how_to/output_parser_custom.ipynb b/docs/docs/how_to/output_parser_custom.ipynb index d77e1ff9c6ae7..1949e3dd067b7 100644 --- a/docs/docs/how_to/output_parser_custom.ipynb +++ b/docs/docs/how_to/output_parser_custom.ipynb @@ -12,7 +12,7 @@ "There are two ways to implement a custom parser:\n", "\n", "1. Using `RunnableLambda` or `RunnableGenerator` in [LCEL](/docs/concepts/lcel/) -- we strongly recommend this for most use cases\n", - "2. By inherting from one of the base classes for out parsing -- this is the hard way of doing things\n", + "2. By inheriting from one of the base classes for out parsing -- this is the hard way of doing things\n", "\n", "The difference between the two approaches are mostly superficial and are mainly in terms of which callbacks are triggered (e.g., `on_chain_start` vs. `on_parser_start`), and how a runnable lambda vs. a parser might be visualized in a tracing platform like LangSmith." ] @@ -200,7 +200,7 @@ "id": "24067447-8a5a-4d6b-86a3-4b9cc4b4369b", "metadata": {}, "source": [ - "## Inherting from Parsing Base Classes" + "## Inheriting from Parsing Base Classes" ] }, { @@ -208,7 +208,7 @@ "id": "9713f547-b2e4-48eb-807f-a0f6f6d0e7e0", "metadata": {}, "source": [ - "Another approach to implement a parser is by inherting from `BaseOutputParser`, `BaseGenerationOutputParser` or another one of the base parsers depending on what you need to do.\n", + "Another approach to implement a parser is by inheriting from `BaseOutputParser`, `BaseGenerationOutputParser` or another one of the base parsers depending on what you need to do.\n", "\n", "In general, we **do not** recommend this approach for most use cases as it results in more code to write without significant benefits.\n", "\n", diff --git a/docs/docs/how_to/passthrough.ipynb b/docs/docs/how_to/passthrough.ipynb index ab4c22d06478b..9a0ea5afb7082 100644 --- a/docs/docs/how_to/passthrough.ipynb +++ b/docs/docs/how_to/passthrough.ipynb @@ -29,7 +29,7 @@ ":::\n", "\n", "\n", - "When composing chains with several steps, sometimes you will want to pass data from previous steps unchanged for use as input to a later step. The [`RunnablePassthrough`](https://python.langchain.com/api_reference/core/runnables/langchain_core.runnables.passthrough.RunnablePassthrough.html) class allows you to do just this, and is typically is used in conjuction with a [RunnableParallel](/docs/how_to/parallel/) to pass data through to a later step in your constructed chains.\n", + "When composing chains with several steps, sometimes you will want to pass data from previous steps unchanged for use as input to a later step. The [`RunnablePassthrough`](https://python.langchain.com/api_reference/core/runnables/langchain_core.runnables.passthrough.RunnablePassthrough.html) class allows you to do just this, and is typically is used in conjunction with a [RunnableParallel](/docs/how_to/parallel/) to pass data through to a later step in your constructed chains.\n", "\n", "See the example below:" ] diff --git a/docs/docs/how_to/sql_large_db.ipynb b/docs/docs/how_to/sql_large_db.ipynb index 53f4bf6224d8f..154bda4dc9b24 100644 --- a/docs/docs/how_to/sql_large_db.ipynb +++ b/docs/docs/how_to/sql_large_db.ipynb @@ -55,7 +55,7 @@ "* Run `.read Chinook_Sqlite.sql`\n", "* Test `SELECT * FROM Artist LIMIT 10;`\n", "\n", - "Now, `Chinhook.db` is in our directory and we can interface with it using the SQLAlchemy-driven [SQLDatabase](https://python.langchain.com/api_reference/community/utilities/langchain_community.utilities.sql_database.SQLDatabase.html) class:" + "Now, `Chinook.db` is in our directory and we can interface with it using the SQLAlchemy-driven [SQLDatabase](https://python.langchain.com/api_reference/community/utilities/langchain_community.utilities.sql_database.SQLDatabase.html) class:" ] }, { diff --git a/docs/docs/how_to/sql_prompting.ipynb b/docs/docs/how_to/sql_prompting.ipynb index 831a7bca13a51..5908ccd14dd57 100644 --- a/docs/docs/how_to/sql_prompting.ipynb +++ b/docs/docs/how_to/sql_prompting.ipynb @@ -51,7 +51,7 @@ "* Run `.read Chinook_Sqlite.sql`\n", "* Test `SELECT * FROM Artist LIMIT 10;`\n", "\n", - "Now, `Chinhook.db` is in our directory and we can interface with it using the SQLAlchemy-driven `SQLDatabase` class:" + "Now, `Chinook.db` is in our directory and we can interface with it using the SQLAlchemy-driven `SQLDatabase` class:" ] }, { diff --git a/docs/docs/how_to/sql_query_checking.ipynb b/docs/docs/how_to/sql_query_checking.ipynb index e15609d7ba4df..ab1a875fdf61e 100644 --- a/docs/docs/how_to/sql_query_checking.ipynb +++ b/docs/docs/how_to/sql_query_checking.ipynb @@ -54,7 +54,7 @@ "* Run `.read Chinook_Sqlite.sql`\n", "* Test `SELECT * FROM Artist LIMIT 10;`\n", "\n", - "Now, `Chinhook.db` is in our directory and we can interface with it using the SQLAlchemy-driven `SQLDatabase` class:" + "Now, `Chinook.db` is in our directory and we can interface with it using the SQLAlchemy-driven `SQLDatabase` class:" ] }, { diff --git a/docs/docs/integrations/callbacks/uptrain.ipynb b/docs/docs/integrations/callbacks/uptrain.ipynb index 7dcf97c517517..51619215d09a0 100644 --- a/docs/docs/integrations/callbacks/uptrain.ipynb +++ b/docs/docs/integrations/callbacks/uptrain.ipynb @@ -336,7 +336,7 @@ "\n", "The **MultiQueryRetriever** is used to tackle the problem that the RAG pipeline might not return the best set of documents based on the query. It generates multiple queries that mean the same as the original query and then fetches documents for each.\n", "\n", - "To evluate this retriever, UpTrain will run the following evaluation:\n", + "To evaluate this retriever, UpTrain will run the following evaluation:\n", "- **[Multi Query Accuracy](https://docs.uptrain.ai/predefined-evaluations/query-quality/multi-query-accuracy)**: Checks if the multi-queries generated mean the same as the original query." ] }, diff --git a/docs/docs/integrations/chat/ibm_watsonx.ipynb b/docs/docs/integrations/chat/ibm_watsonx.ipynb index a3ef2d572397d..e6755d9a4b797 100644 --- a/docs/docs/integrations/chat/ibm_watsonx.ipynb +++ b/docs/docs/integrations/chat/ibm_watsonx.ipynb @@ -36,7 +36,7 @@ "### Integration details\n", "| Class | Package | Local | Serializable | [JS support](https://js.langchain.com/docs/integrations/chat/ibm/) | Package downloads | Package latest |\n", "| :--- | :--- | :---: | :---: | :---: | :---: | :---: |\n", - "| [ChatWatsonx](https://python.langchain.com/api_reference/ibm/chat_models/langchain_ibm.chat_models.ChatWatsonx.html#langchain_ibm.chat_models.ChatWatsonx) | [langchain-ibm](https://python.langchain.com/api_reference/ibm/index.html) | ❌ | ❌ | ✅ | ![PyPI - Downloads](https://img.shields.io/pypi/dm/langchain-ibm?style=flat-square&label=%20) | ![PyPI - Version](https://img.shields.io/pypi/v/langchain-ibm?style=flat-square&label=%20) |\n", + "| [ChatWatsonx](https://python.langchain.com/api_reference/ibm/chat_models/langchain_ibm.chat_models.ChatWatsonx.html) | [langchain-ibm](https://python.langchain.com/api_reference/ibm/index.html) | ❌ | ❌ | ✅ | ![PyPI - Downloads](https://img.shields.io/pypi/dm/langchain-ibm?style=flat-square&label=%20) | ![PyPI - Version](https://img.shields.io/pypi/v/langchain-ibm?style=flat-square&label=%20) |\n", "\n", "### Model features\n", "| [Tool calling](/docs/how_to/tool_calling/) | [Structured output](/docs/how_to/structured_output/) | JSON mode | Image input | Audio input | Video input | [Token-level streaming](/docs/how_to/chat_streaming/) | Native async | [Token usage](/docs/how_to/chat_token_usage_tracking/) | [Logprobs](/docs/how_to/logprobs/) |\n", diff --git a/docs/docs/integrations/chat/ollama.ipynb b/docs/docs/integrations/chat/ollama.ipynb index 1d9b5de040354..daf179fe7f09c 100644 --- a/docs/docs/integrations/chat/ollama.ipynb +++ b/docs/docs/integrations/chat/ollama.ipynb @@ -34,8 +34,8 @@ "\n", "### Model features\n", "| [Tool calling](/docs/how_to/tool_calling/) | [Structured output](/docs/how_to/structured_output/) | JSON mode | [Image input](/docs/how_to/multimodal_inputs/) | Audio input | Video input | [Token-level streaming](/docs/how_to/chat_streaming/) | Native async | [Token usage](/docs/how_to/chat_token_usage_tracking/) | [Logprobs](/docs/how_to/logprobs/) |\n", - "| :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: |\n", - "| ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ | \n", + "| :---: |:----------------------------------------------------:| :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: |\n", + "| ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ✅ | ❌ | ❌ |\n", "\n", "## Setup\n", "\n", @@ -96,6 +96,20 @@ "%pip install -qU langchain-ollama" ] }, + { + "metadata": {}, + "cell_type": "markdown", + "source": "Make sure you're using the latest Ollama version for structured outputs. Update by running:", + "id": "b18bd692076f7cf7" + }, + { + "metadata": {}, + "cell_type": "code", + "outputs": [], + "execution_count": null, + "source": "%pip install -U ollama", + "id": "b7a05cba95644c2e" + }, { "cell_type": "markdown", "id": "a38cde65-254d-4219-a441-068766c0d4b5", diff --git a/docs/docs/integrations/chat/snowflake.ipynb b/docs/docs/integrations/chat/snowflake.ipynb index 650648ffb7acc..92295d85685d1 100644 --- a/docs/docs/integrations/chat/snowflake.ipynb +++ b/docs/docs/integrations/chat/snowflake.ipynb @@ -22,24 +22,16 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Note: you may need to restart the kernel to use updated packages.\n" - ] - } - ], + "outputs": [], "source": [ "%pip install --upgrade --quiet snowflake-snowpark-python" ] }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -73,14 +65,14 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from langchain_community.chat_models import ChatSnowflakeCortex\n", "from langchain_core.messages import HumanMessage, SystemMessage\n", "\n", - "# By default, we'll be using the cortex provided model: `snowflake-arctic`, with function: `complete`\n", + "# By default, we'll be using the cortex provided model: `mistral-large`, with function: `complete`\n", "chat = ChatSnowflakeCortex()" ] }, @@ -92,16 +84,16 @@ "\n", "```python\n", "chat = ChatSnowflakeCortex(\n", - " # change default cortex model and function\n", - " model=\"snowflake-arctic\",\n", + " # Change the default cortex model and function\n", + " model=\"mistral-large\",\n", " cortex_function=\"complete\",\n", "\n", - " # change default generation parameters\n", + " # Change the default generation parameters\n", " temperature=0,\n", " max_tokens=10,\n", " top_p=0.95,\n", "\n", - " # specify snowflake credentials\n", + " # Specify your Snowflake Credentials\n", " account=\"YOUR_SNOWFLAKE_ACCOUNT\",\n", " username=\"YOUR_SNOWFLAKE_USERNAME\",\n", " password=\"YOUR_SNOWFLAKE_PASSWORD\",\n", @@ -117,28 +109,13 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Calling the model\n", - "We can now call the model using the `invoke` or `generate` method.\n", - "\n", - "#### Generation" + "### Calling the chat model\n", + "We can now call the chat model using the `invoke` or `stream` methods." ] }, { - "cell_type": "code", - "execution_count": 9, + "cell_type": "markdown", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "AIMessage(content=\" Large language models are artificial intelligence systems designed to understand, generate, and manipulate human language. These models are typically based on deep learning techniques and are trained on vast amounts of text data to learn patterns and structures in language. They can perform a wide range of language-related tasks, such as language translation, text generation, sentiment analysis, and answering questions. Some well-known large language models include Google's BERT, OpenAI's GPT series, and Facebook's RoBERTa. These models have shown remarkable performance in various natural language processing tasks, and their applications continue to expand as research in AI progresses.\", response_metadata={'completion_tokens': 131, 'prompt_tokens': 29, 'total_tokens': 160}, id='run-5435bd0a-83fd-4295-b237-66cbd1b5c0f3-0')" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], "source": [ "messages = [\n", " SystemMessage(content=\"You are a friendly assistant.\"),\n", @@ -151,14 +128,31 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Streaming\n", - "`ChatSnowflakeCortex` doesn't support streaming as of now. Support for streaming will be coming in the later versions!" + "### Stream" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Sample input prompt\n", + "messages = [\n", + " SystemMessage(content=\"You are a friendly assistant.\"),\n", + " HumanMessage(content=\"What are large language models?\"),\n", + "]\n", + "\n", + "# Invoke the stream method and print each chunk as it arrives\n", + "print(\"Stream Method Response:\")\n", + "for chunk in chat._stream(messages):\n", + " print(chunk.message.content)" ] } ], "metadata": { "kernelspec": { - "display_name": ".venv", + "display_name": "langchain", "language": "python", "name": "python3" }, @@ -172,7 +166,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.9" + "version": "3.9.20" } }, "nbformat": 4, diff --git a/docs/docs/integrations/graphs/memgraph.ipynb b/docs/docs/integrations/graphs/memgraph.ipynb index 4dc8d33be4b86..1040d43f9e50f 100644 --- a/docs/docs/integrations/graphs/memgraph.ipynb +++ b/docs/docs/integrations/graphs/memgraph.ipynb @@ -2,24 +2,26 @@ "cells": [ { "cell_type": "markdown", - "id": "311b3061", "metadata": {}, "source": [ "# Memgraph\n", "\n", - ">[Memgraph](https://github.com/memgraph/memgraph) is the open-source graph database, compatible with `Neo4j`.\n", - ">The database is using the `Cypher` graph query language, \n", - ">\n", - ">[Cypher](https://en.wikipedia.org/wiki/Cypher_(query_language)) is a declarative graph query language that allows for expressive and efficient data querying in a property graph.\n", - "\n", - "This notebook shows how to use LLMs to provide a natural language interface to a [Memgraph](https://github.com/memgraph/memgraph) database.\n", + "Memgraph is an open-source graph database, tuned for dynamic analytics environments and compatible with Neo4j. To query the database, Memgraph uses Cypher - the most widely adopted, fully-specified, and open query language for property graph databases.\n", "\n", + "This notebook will show you how to [query Memgraph with natural language](#natural-language-querying) and how to [construct a knowledge graph](#constructing-knowledge-graph) from your unstructured data. \n", "\n", + "But first, make sure to [set everything up](#setting-up)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ "## Setting up\n", "\n", - "To complete this tutorial, you will need [Docker](https://www.docker.com/get-started/) and [Python 3.x](https://www.python.org/) installed.\n", + "To go over this guide, you will need [Docker](https://www.docker.com/get-started/) and [Python 3.x](https://www.python.org/) installed.\n", "\n", - "Ensure you have a running Memgraph instance. To quickly run Memgraph Platform (Memgraph database + MAGE library + Memgraph Lab) for the first time, do the following:\n", + "To quickly run **Memgraph Platform** (Memgraph database + MAGE library + Memgraph Lab) for the first time, do the following:\n", "\n", "On Linux/MacOS:\n", "```\n", @@ -31,89 +33,90 @@ "iwr https://windows.memgraph.com | iex\n", "```\n", "\n", - "Both commands run a script that downloads a Docker Compose file to your system, builds and starts `memgraph-mage` and `memgraph-lab` Docker services in two separate containers. \n", + "Both commands run a script that downloads a Docker Compose file to your system, builds and starts `memgraph-mage` and `memgraph-lab` Docker services in two separate containers. Now you have Memgraph up and running! Read more about the installation process on [Memgraph documentation](https://memgraph.com/docs/getting-started/install-memgraph).\n", "\n", - "Read more about the installation process on [Memgraph documentation](https://memgraph.com/docs/getting-started/install-memgraph).\n", + "To use LangChain, install and import all the necessary packages. We'll use the package manager [pip](https://pip.pypa.io/en/stable/installation/), along with the `--user` flag, to ensure proper permissions. If you've installed Python 3.4 or a later version, `pip` is included by default. You can install all the required packages using the following command:\n", "\n", - "Now you can start playing with `Memgraph`!" - ] - }, - { - "cell_type": "markdown", - "id": "45ee105e", - "metadata": {}, - "source": [ - "Begin by installing and importing all the necessary packages. We'll use the package manager called [pip](https://pip.pypa.io/en/stable/installation/), along with the `--user` flag, to ensure proper permissions. If you've installed Python 3.4 or a later version, pip is included by default. You can install all the required packages using the following command:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "fd6b9672", - "metadata": {}, - "outputs": [], - "source": [ - "pip install langchain langchain-neo4j langchain-openai neo4j gqlalchemy --user" + "```\n", + "pip install langchain langchain-openai neo4j --user\n", + "```\n", + "\n", + "You can either run the provided code blocks in this notebook or use a separate Python file to experiment with Memgraph and LangChain." ] }, { "cell_type": "markdown", - "id": "ec969a02", "metadata": {}, "source": [ - "You can either run the provided code blocks in this notebook or use a separate Python file to experiment with Memgraph and LangChain." + "## Natural language querying\n", + "\n", + "Memgraph's integration with LangChain includes natural language querying. To utilized it, first do all the necessary imports. We will discuss them as they appear in the code.\n", + "\n", + "First, instantiate `MemgraphGraph`. This object holds the connection to the running Memgraph instance. Make sure to set up all the environment variables properly." ] }, { "cell_type": "code", - "execution_count": null, - "id": "8206f90d", + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import os\n", "\n", - "from gqlalchemy import Memgraph\n", + "from langchain_community.chains.graph_qa.memgraph import MemgraphQAChain\n", "from langchain_community.graphs import MemgraphGraph\n", "from langchain_core.prompts import PromptTemplate\n", - "from langchain_neo4j import GraphCypherQAChain\n", - "from langchain_openai import ChatOpenAI" + "from langchain_openai import ChatOpenAI\n", + "\n", + "url = os.environ.get(\"MEMGRAPH_URI\", \"bolt://localhost:7687\")\n", + "username = os.environ.get(\"MEMGRAPH_USERNAME\", \"\")\n", + "password = os.environ.get(\"MEMGRAPH_PASSWORD\", \"\")\n", + "\n", + "graph = MemgraphGraph(\n", + " url=url, username=username, password=password, refresh_schema=False\n", + ")" ] }, { "cell_type": "markdown", - "id": "95ba37a4", "metadata": {}, "source": [ - "We're utilizing the Python library [GQLAlchemy](https://github.com/memgraph/gqlalchemy) to establish a connection between our Memgraph database and Python script. You can establish the connection to a running Memgraph instance with the Neo4j driver as well, since it's compatible with Memgraph. To execute queries with GQLAlchemy, we can set up a Memgraph instance as follows:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b90c9cf8", - "metadata": {}, - "outputs": [], - "source": [ - "memgraph = Memgraph(host=\"127.0.0.1\", port=7687)" + "The `refresh_schema` is initially set to `False` because there is still no data in the database and we want to avoid unnecessary database calls. " ] }, { "cell_type": "markdown", - "id": "4c379d16", "metadata": {}, "source": [ - "## Populating the database\n", - "You can effortlessly populate your new, empty database using the Cypher query language. Don't worry if you don't grasp every line just yet, you can learn Cypher from the documentation [here](https://memgraph.com/docs/cypher-manual/). Running the following script will execute a seeding query on the database, giving us data about a video game, including details like the publisher, available platforms, and genres. This data will serve as a basis for our work." + "### Populating the database\n", + "\n", + "To populate the database, first make sure it's empty. The most efficient way to do that is to switch to the in-memory analytical storage mode, drop the graph and go back to the in-memory transactional mode. Learn more about Memgraph's [storage modes](https://memgraph.com/docs/fundamentals/storage-memory-usage#storage-modes).\n", + "\n", + "The data we'll add to the database is about video games of different genres available on various platforms and related to publishers." ] }, { "cell_type": "code", - "execution_count": null, - "id": "11922bdf", - "metadata": {}, - "outputs": [], - "source": [ + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[]" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Drop graph\n", + "graph.query(\"STORAGE MODE IN_MEMORY_ANALYTICAL\")\n", + "graph.query(\"DROP GRAPH\")\n", + "graph.query(\"STORAGE MODE IN_MEMORY_TRANSACTIONAL\")\n", + "\n", "# Creating and executing the seeding query\n", "query = \"\"\"\n", " MERGE (g:Game {name: \"Baldur's Gate 3\"})\n", @@ -131,576 +134,660 @@ " MERGE (g)-[:PUBLISHED_BY]->(p);\n", "\"\"\"\n", "\n", - "memgraph.execute(query)" + "graph.query(query)" ] }, { "cell_type": "markdown", - "id": "378db965", "metadata": {}, "source": [ - "## Refresh graph schema" + "Notice how the `graph` object holds the `query` method. That method executes query in Memgraph and it is also used by the `MemgraphQAChain` to query the database." ] }, { "cell_type": "markdown", - "id": "d6b37df3", "metadata": {}, "source": [ - "You're all set to instantiate the Memgraph-LangChain graph using the following script. This interface will allow us to query our database using LangChain, automatically creating the required graph schema for generating Cypher queries through LLM." + "### Refresh graph schema\n", + "\n", + "Since the new data is created in Memgraph, it is necessary to refresh the schema. The generated schema will be used by the `MemgraphQAChain` to instruct LLM to better generate Cypher queries. " ] }, { "cell_type": "code", - "execution_count": null, - "id": "f38bbe83", + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ - "graph = MemgraphGraph(url=\"bolt://localhost:7687\", username=\"\", password=\"\")" + "graph.refresh_schema()" ] }, { "cell_type": "markdown", - "id": "846c32a8", "metadata": {}, "source": [ - "If necessary, you can manually refresh the graph schema as follows." + "To familiarize yourself with the data and verify the updated graph schema, you can print it using the following statement:" ] }, { "cell_type": "code", - "execution_count": null, - "id": "b561026e", - "metadata": {}, - "outputs": [], - "source": [ - "graph.refresh_schema()" - ] - }, - { - "cell_type": "markdown", - "id": "c51b7948", - "metadata": {}, - "source": [ - "To familiarize yourself with the data and verify the updated graph schema, you can print it using the following statement." + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Node labels and properties (name and type) are:\n", + "- labels: (:Platform)\n", + " properties:\n", + " - name: string\n", + "- labels: (:Genre)\n", + " properties:\n", + " - name: string\n", + "- labels: (:Game)\n", + " properties:\n", + " - name: string\n", + "- labels: (:Publisher)\n", + " properties:\n", + " - name: string\n", + "\n", + "Nodes are connected with the following relationships:\n", + "(:Game)-[:HAS_GENRE]->(:Genre)\n", + "(:Game)-[:PUBLISHED_BY]->(:Publisher)\n", + "(:Game)-[:AVAILABLE_ON]->(:Platform)\n", + "\n" + ] + } + ], + "source": [ + "print(graph.get_schema)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Querying the database\n", + "\n", + "To interact with the OpenAI API, you must configure your API key as an environment variable. This ensures proper authorization for your requests. You can find more information on obtaining your API key [here](https://help.openai.com/en/articles/4936850-where-do-i-find-my-secret-api-key). To configure the API key, you can use Python [os](https://docs.python.org/3/library/os.html) package:\n", + "\n", + "```\n", + "os.environ[\"OPENAI_API_KEY\"] = \"your-key-here\"\n", + "```\n", + "\n", + "Run the above code snippet if you're running the code within the Jupyter notebook. \n", + "\n", + "Next, create `MemgraphQAChain`, which will be utilized in the question-answering process based on your graph data. The `temperature parameter` is set to zero to ensure predictable and consistent answers. You can set `verbose` parameter to `True` to receive more detailed messages regarding query generation." ] }, { "cell_type": "code", - "execution_count": null, - "id": "f2e0ec3e", + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ - "print(graph.schema)" - ] - }, - { - "cell_type": "markdown", - "id": "a0c2a556", - "metadata": {}, - "source": [ - "```\n", - "Node properties are the following:\n", - "Node name: 'Game', Node properties: [{'property': 'name', 'type': 'str'}]\n", - "Node name: 'Platform', Node properties: [{'property': 'name', 'type': 'str'}]\n", - "Node name: 'Genre', Node properties: [{'property': 'name', 'type': 'str'}]\n", - "Node name: 'Publisher', Node properties: [{'property': 'name', 'type': 'str'}]\n", - "\n", - "Relationship properties are the following:\n", - "\n", - "The relationships are the following:\n", - "['(:Game)-[:AVAILABLE_ON]->(:Platform)']\n", - "['(:Game)-[:HAS_GENRE]->(:Genre)']\n", - "['(:Game)-[:PUBLISHED_BY]->(:Publisher)']\n", - "```" + "chain = MemgraphQAChain.from_llm(\n", + " ChatOpenAI(temperature=0),\n", + " graph=graph,\n", + " model_name=\"gpt-4-turbo\",\n", + " allow_dangerous_requests=True,\n", + ")" ] }, { "cell_type": "markdown", - "id": "44d3a1da", "metadata": {}, "source": [ - "## Querying the database" + "Now you can start asking questions!" ] }, { - "cell_type": "markdown", - "id": "8aedfd63", + "cell_type": "code", + "execution_count": 7, "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "MATCH (:Game{name: \"Baldur's Gate 3\"})-[:AVAILABLE_ON]->(platform:Platform)\n", + "RETURN platform.name\n", + "Baldur's Gate 3 is available on PlayStation 5, Mac OS, Windows, and Xbox Series X/S.\n" + ] + } + ], "source": [ - "To interact with the OpenAI API, you must configure your API key as an environment variable using the Python [os](https://docs.python.org/3/library/os.html) package. This ensures proper authorization for your requests. You can find more information on obtaining your API key [here](https://help.openai.com/en/articles/4936850-where-do-i-find-my-secret-api-key)." + "response = chain.invoke(\"Which platforms is Baldur's Gate 3 available on?\")\n", + "print(response[\"result\"])" ] }, { "cell_type": "code", - "execution_count": null, - "id": "b8385c72", + "execution_count": 8, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "MATCH (:Game{name: \"Baldur's Gate 3\"})-[:AVAILABLE_ON]->(:Platform{name: \"Windows\"})\n", + "RETURN \"Yes\"\n", + "Yes, Baldur's Gate 3 is available on Windows.\n" + ] + } + ], "source": [ - "os.environ[\"OPENAI_API_KEY\"] = \"your-key-here\"" + "response = chain.invoke(\"Is Baldur's Gate 3 available on Windows?\")\n", + "print(response[\"result\"])" ] }, { "cell_type": "markdown", - "id": "5a74565a", "metadata": {}, "source": [ - "You should create the graph chain using the following script, which will be utilized in the question-answering process based on your graph data. While it defaults to GPT-3.5-turbo, you might also consider experimenting with other models like [GPT-4](https://help.openai.com/en/articles/7102672-how-can-i-access-gpt-4) for notably improved Cypher queries and outcomes. We'll utilize the OpenAI chat, utilizing the key you previously configured. We'll set the temperature to zero, ensuring predictable and consistent answers. Additionally, we'll use our Memgraph-LangChain graph and set the verbose parameter, which defaults to False, to True to receive more detailed messages regarding query generation." + "### Chain modifiers\n", + "\n", + "To modify the behavior of your chain and obtain more context or additional information, you can modify the chain's parameters.\n", + "\n", + "#### Return direct query results\n", + "The `return_direct` modifier specifies whether to return the direct results of the executed Cypher query or the processed natural language response." ] }, { "cell_type": "code", - "execution_count": null, - "id": "4a3a5f2e", - "metadata": {}, - "outputs": [], + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "MATCH (g:Game {name: \"Baldur's Gate 3\"})-[:PUBLISHED_BY]->(p:Publisher)\n", + "RETURN p.name\n", + "[{'p.name': 'Larian Studios'}]\n" + ] + } + ], "source": [ - "chain = GraphCypherQAChain.from_llm(\n", + "# Return the result of querying the graph directly\n", + "chain = MemgraphQAChain.from_llm(\n", " ChatOpenAI(temperature=0),\n", " graph=graph,\n", - " verbose=True,\n", - " model_name=\"gpt-3.5-turbo\",\n", + " return_direct=True,\n", " allow_dangerous_requests=True,\n", - ")" + " model_name=\"gpt-4-turbo\",\n", + ")\n", + "\n", + "response = chain.invoke(\"Which studio published Baldur's Gate 3?\")\n", + "print(response[\"result\"])" ] }, { "cell_type": "markdown", - "id": "949de4f3", "metadata": {}, "source": [ - "Now you can start asking questions!" + "#### Return query intermediate steps\n", + "The `return_intermediate_steps` chain modifier enhances the returned response by including the intermediate steps of the query in addition to the initial query result." ] }, { "cell_type": "code", - "execution_count": null, - "id": "b7aea263", - "metadata": {}, - "outputs": [], + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "MATCH (:Game {name: \"Baldur's Gate 3\"})-[:HAS_GENRE]->(:Genre {name: \"Adventure\"})\n", + "RETURN \"Yes\"\n", + "Intermediate steps: [{'query': 'MATCH (:Game {name: \"Baldur\\'s Gate 3\"})-[:HAS_GENRE]->(:Genre {name: \"Adventure\"})\\nRETURN \"Yes\"'}, {'context': [{'\"Yes\"': 'Yes'}]}]\n", + "Final response: Yes.\n" + ] + } + ], "source": [ - "response = chain.run(\"Which platforms is Baldur's Gate 3 available on?\")\n", - "print(response)" + "# Return all the intermediate steps of query execution\n", + "chain = MemgraphQAChain.from_llm(\n", + " ChatOpenAI(temperature=0),\n", + " graph=graph,\n", + " allow_dangerous_requests=True,\n", + " return_intermediate_steps=True,\n", + " model_name=\"gpt-4-turbo\",\n", + ")\n", + "\n", + "response = chain.invoke(\"Is Baldur's Gate 3 an Adventure game?\")\n", + "print(f\"Intermediate steps: {response['intermediate_steps']}\")\n", + "print(f\"Final response: {response['result']}\")" ] }, { "cell_type": "markdown", - "id": "a06a8164", "metadata": {}, "source": [ - "```\n", - "> Entering new GraphCypherQAChain chain...\n", - "Generated Cypher:\n", - "MATCH (g:Game {name: 'Baldur\\'s Gate 3'})-[:AVAILABLE_ON]->(p:Platform)\n", - "RETURN p.name\n", - "Full Context:\n", - "[{'p.name': 'PlayStation 5'}, {'p.name': 'Mac OS'}, {'p.name': 'Windows'}, {'p.name': 'Xbox Series X/S'}]\n", + "#### Limit the number of query results\n", "\n", - "> Finished chain.\n", - "Baldur's Gate 3 is available on PlayStation 5, Mac OS, Windows, and Xbox Series X/S.\n", - "```" + "The `top_k` modifier can be used when you want to restrict the maximum number of query results." ] }, { "cell_type": "code", - "execution_count": null, - "id": "59d298d5", - "metadata": {}, - "outputs": [], - "source": [ - "response = chain.run(\"Is Baldur's Gate 3 available on Windows?\")\n", - "print(response)" - ] - }, - { - "cell_type": "markdown", - "id": "99dd783c", - "metadata": {}, + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "MATCH (:Game {name: \"Baldur's Gate 3\"})-[:HAS_GENRE]->(g:Genre)\n", + "RETURN g.name;\n", + "Adventure, Role-Playing Game\n" + ] + } + ], "source": [ - "```\n", - "> Entering new GraphCypherQAChain chain...\n", - "Generated Cypher:\n", - "MATCH (:Game {name: 'Baldur\\'s Gate 3'})-[:AVAILABLE_ON]->(:Platform {name: 'Windows'})\n", - "RETURN true\n", - "Full Context:\n", - "[{'true': True}]\n", + "# Limit the maximum number of results returned by query\n", + "chain = MemgraphQAChain.from_llm(\n", + " ChatOpenAI(temperature=0),\n", + " graph=graph,\n", + " top_k=2,\n", + " allow_dangerous_requests=True,\n", + " model_name=\"gpt-4-turbo\",\n", + ")\n", "\n", - "> Finished chain.\n", - "Yes, Baldur's Gate 3 is available on Windows.\n", - "```" - ] - }, - { - "cell_type": "markdown", - "id": "08620465", - "metadata": {}, - "source": [ - "## Chain modifiers" - ] - }, - { - "cell_type": "markdown", - "id": "6603e6c8", - "metadata": {}, - "source": [ - "To modify the behavior of your chain and obtain more context or additional information, you can modify the chain's parameters." + "response = chain.invoke(\"What genres are associated with Baldur's Gate 3?\")\n", + "print(response[\"result\"])" ] }, { "cell_type": "markdown", - "id": "8d187a83", "metadata": {}, "source": [ - "#### Return direct query results\n", - "The `return_direct` modifier specifies whether to return the direct results of the executed Cypher query or the processed natural language response." + "### Advanced querying\n", + "\n", + "As the complexity of your solution grows, you might encounter different use-cases that require careful handling. Ensuring your application's scalability is essential to maintain a smooth user flow without any hitches.\n", + "\n", + "Let's instantiate our chain once again and attempt to ask some questions that users might potentially ask." ] }, { "cell_type": "code", - "execution_count": null, - "id": "0533847d", - "metadata": {}, - "outputs": [], - "source": [ - "# Return the result of querying the graph directly\n", - "chain = GraphCypherQAChain.from_llm(\n", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "MATCH (:Game{name: \"Baldur's Gate 3\"})-[:AVAILABLE_ON]->(:Platform{name: \"PS5\"})\n", + "RETURN \"Yes\"\n", + "I don't know the answer.\n" + ] + } + ], + "source": [ + "chain = MemgraphQAChain.from_llm(\n", " ChatOpenAI(temperature=0),\n", " graph=graph,\n", - " verbose=True,\n", - " return_direct=True,\n", + " model_name=\"gpt-4-turbo\",\n", " allow_dangerous_requests=True,\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "afbe96fb", - "metadata": {}, - "outputs": [], - "source": [ - "response = chain.run(\"Which studio published Baldur's Gate 3?\")\n", - "print(response)" + ")\n", + "\n", + "response = chain.invoke(\"Is Baldur's Gate 3 available on PS5?\")\n", + "print(response[\"result\"])" ] }, { "cell_type": "markdown", - "id": "94b32b6e", "metadata": {}, "source": [ - "```\n", - "> Entering new GraphCypherQAChain chain...\n", - "Generated Cypher:\n", - "MATCH (:Game {name: 'Baldur\\'s Gate 3'})-[:PUBLISHED_BY]->(p:Publisher)\n", - "RETURN p.name\n", - "\n", - "> Finished chain.\n", - "[{'p.name': 'Larian Studios'}]\n", - "```" + "The generated Cypher query looks fine, but we didn't receive any information in response. This illustrates a common challenge when working with LLMs - the misalignment between how users phrase queries and how data is stored. In this case, the difference between user perception and the actual data storage can cause mismatches. Prompt refinement, the process of honing the model's prompts to better grasp these distinctions, is an efficient solution that tackles this issue. Through prompt refinement, the model gains increased proficiency in generating precise and pertinent queries, leading to the successful retrieval of the desired data." ] }, { "cell_type": "markdown", - "id": "5c97ab3a", "metadata": {}, "source": [ - "#### Return query intermediate steps\n", - "The `return_intermediate_steps` chain modifier enhances the returned response by including the intermediate steps of the query in addition to the initial query result." + "#### Prompt refinement\n", + "\n", + "To address this, we can adjust the initial Cypher prompt of the QA chain. This involves adding guidance to the LLM on how users can refer to specific platforms, such as PS5 in our case. We achieve this using the LangChain [PromptTemplate](/docs/how_to#prompt-templates), creating a modified initial prompt. This modified prompt is then supplied as an argument to our refined `MemgraphQAChain` instance." ] }, { "cell_type": "code", - "execution_count": null, - "id": "82f673c8", - "metadata": {}, - "outputs": [], - "source": [ - "# Return all the intermediate steps of query execution\n", - "chain = GraphCypherQAChain.from_llm(\n", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "MATCH (:Game{name: \"Baldur's Gate 3\"})-[:AVAILABLE_ON]->(:Platform{name: \"PlayStation 5\"})\n", + "RETURN \"Yes\"\n", + "Yes, Baldur's Gate 3 is available on PS5.\n" + ] + } + ], + "source": [ + "MEMGRAPH_GENERATION_TEMPLATE = \"\"\"Your task is to directly translate natural language inquiry into precise and executable Cypher query for Memgraph database. \n", + "You will utilize a provided database schema to understand the structure, nodes and relationships within the Memgraph database.\n", + "Instructions: \n", + "- Use provided node and relationship labels and property names from the\n", + "schema which describes the database's structure. Upon receiving a user\n", + "question, synthesize the schema to craft a precise Cypher query that\n", + "directly corresponds to the user's intent. \n", + "- Generate valid executable Cypher queries on top of Memgraph database. \n", + "Any explanation, context, or additional information that is not a part \n", + "of the Cypher query syntax should be omitted entirely. \n", + "- Use Memgraph MAGE procedures instead of Neo4j APOC procedures. \n", + "- Do not include any explanations or apologies in your responses. \n", + "- Do not include any text except the generated Cypher statement.\n", + "- For queries that ask for information or functionalities outside the direct\n", + "generation of Cypher queries, use the Cypher query format to communicate\n", + "limitations or capabilities. For example: RETURN \"I am designed to generate\n", + "Cypher queries based on the provided schema only.\"\n", + "Schema: \n", + "{schema}\n", + "\n", + "With all the above information and instructions, generate Cypher query for the\n", + "user question. \n", + "If the user asks about PS5, Play Station 5 or PS 5, that is the platform called PlayStation 5.\n", + "\n", + "The question is:\n", + "{question}\"\"\"\n", + "\n", + "MEMGRAPH_GENERATION_PROMPT = PromptTemplate(\n", + " input_variables=[\"schema\", \"question\"], template=MEMGRAPH_GENERATION_TEMPLATE\n", + ")\n", + "\n", + "chain = MemgraphQAChain.from_llm(\n", " ChatOpenAI(temperature=0),\n", + " cypher_prompt=MEMGRAPH_GENERATION_PROMPT,\n", " graph=graph,\n", - " verbose=True,\n", - " return_intermediate_steps=True,\n", + " model_name=\"gpt-4-turbo\",\n", " allow_dangerous_requests=True,\n", - ")" + ")\n", + "\n", + "response = chain.invoke(\"Is Baldur's Gate 3 available on PS5?\")\n", + "print(response[\"result\"])" ] }, { - "cell_type": "code", - "execution_count": null, - "id": "d87e0976", + "cell_type": "markdown", "metadata": {}, - "outputs": [], "source": [ - "response = chain(\"Is Baldur's Gate 3 an Adventure game?\")\n", - "print(f\"Intermediate steps: {response['intermediate_steps']}\")\n", - "print(f\"Final response: {response['result']}\")" + "Now, with the revised initial Cypher prompt that includes guidance on platform naming, we are obtaining accurate and relevant results that align more closely with user queries. \n", + "\n", + "This approach allows for further improvement of your QA chain. You can effortlessly integrate extra prompt refinement data into your chain, thereby enhancing the overall user experience of your app." ] }, { "cell_type": "markdown", - "id": "df12b3da", "metadata": {}, "source": [ - "```\n", - "> Entering new GraphCypherQAChain chain...\n", - "Generated Cypher:\n", - "MATCH (g:Game {name: 'Baldur\\'s Gate 3'})-[:HAS_GENRE]->(genre:Genre {name: 'Adventure'})\n", - "RETURN g, genre\n", - "Full Context:\n", - "[{'g': {'name': \"Baldur's Gate 3\"}, 'genre': {'name': 'Adventure'}}]\n", + "## Constructing knowledge graph\n", + "\n", + "Transforming unstructured data to structured is not an easy or straightforward task. This guide will show how LLMs can be utilized to help us there and how to construct a knowledge graph in Memgraph. After knowledge graph is created, you can use it for your GraphRAG application.\n", "\n", - "> Finished chain.\n", - "Intermediate steps: [{'query': \"MATCH (g:Game {name: 'Baldur\\\\'s Gate 3'})-[:HAS_GENRE]->(genre:Genre {name: 'Adventure'})\\nRETURN g, genre\"}, {'context': [{'g': {'name': \"Baldur's Gate 3\"}, 'genre': {'name': 'Adventure'}}]}]\n", - "Final response: Yes, Baldur's Gate 3 is an Adventure game.\n", - "```" + "The steps of constructing a knowledge graph from the text are:\n", + "\n", + "- [Extracting structured information from text](#extracting-structured-information-from-text): LLM is used to extract structured graph information from text in a form of nodes and relationships.\n", + "- [Storing into Memgraph](#storing-into-memgraph): Storing the extracted structured graph information into Memgraph." ] }, { "cell_type": "markdown", - "id": "41124485", "metadata": {}, "source": [ - "#### Limit the number of query results\n", - "The `top_k` modifier can be used when you want to restrict the maximum number of query results." + "### Extracting structured information from text\n", + "\n", + "Besides all the imports in the [setup section](#setting-up), import `LLMGraphTransformer` and `Document` which will be used to extract structured information from text." ] }, { "cell_type": "code", - "execution_count": null, - "id": "7340fc87", + "execution_count": 14, "metadata": {}, "outputs": [], "source": [ - "# Limit the maximum number of results returned by query\n", - "chain = GraphCypherQAChain.from_llm(\n", - " ChatOpenAI(temperature=0),\n", - " graph=graph,\n", - " verbose=True,\n", - " top_k=2,\n", - " allow_dangerous_requests=True,\n", - ")" + "from langchain_core.documents import Document\n", + "from langchain_experimental.graph_transformers import LLMGraphTransformer" ] }, { - "cell_type": "code", - "execution_count": null, - "id": "3a17cdc6", + "cell_type": "markdown", "metadata": {}, - "outputs": [], "source": [ - "response = chain.run(\"What genres are associated with Baldur's Gate 3?\")\n", - "print(response)" + "Below is an example text about Charles Darwin ([source](https://en.wikipedia.org/wiki/Charles_Darwin)) from which knowledge graph will be constructed." ] }, { - "cell_type": "markdown", - "id": "dcff33ed", + "cell_type": "code", + "execution_count": 15, "metadata": {}, + "outputs": [], "source": [ - "```\n", - "> Entering new GraphCypherQAChain chain...\n", - "Generated Cypher:\n", - "MATCH (:Game {name: 'Baldur\\'s Gate 3'})-[:HAS_GENRE]->(g:Genre)\n", - "RETURN g.name\n", - "Full Context:\n", - "[{'g.name': 'Adventure'}, {'g.name': 'Role-Playing Game'}]\n", - "\n", - "> Finished chain.\n", - "Baldur's Gate 3 is associated with the genres Adventure and Role-Playing Game.\n", - "```" + "text = \"\"\"\n", + " Charles Robert Darwin was an English naturalist, geologist, and biologist,\n", + " widely known for his contributions to evolutionary biology. His proposition that\n", + " all species of life have descended from a common ancestor is now generally\n", + " accepted and considered a fundamental scientific concept. In a joint\n", + " publication with Alfred Russel Wallace, he introduced his scientific theory that\n", + " this branching pattern of evolution resulted from a process he called natural\n", + " selection, in which the struggle for existence has a similar effect to the\n", + " artificial selection involved in selective breeding. Darwin has been\n", + " described as one of the most influential figures in human history and was\n", + " honoured by burial in Westminster Abbey.\n", + "\"\"\"" ] }, { "cell_type": "markdown", - "id": "2eb524a1", "metadata": {}, "source": [ - "# Advanced querying" + "The next step is to initialize `LLMGraphTransformer` from the desired LLM and convert the document to the graph structure. " ] }, { - "cell_type": "markdown", - "id": "113be997", + "cell_type": "code", + "execution_count": 16, "metadata": {}, + "outputs": [], "source": [ - "As the complexity of your solution grows, you might encounter different use-cases that require careful handling. Ensuring your application's scalability is essential to maintain a smooth user flow without any hitches." + "llm = ChatOpenAI(temperature=0, model_name=\"gpt-4-turbo\")\n", + "llm_transformer = LLMGraphTransformer(llm=llm)\n", + "documents = [Document(page_content=text)]\n", + "graph_documents = llm_transformer.convert_to_graph_documents(documents)" ] }, { "cell_type": "markdown", - "id": "e0b2db17", "metadata": {}, "source": [ - "Let's instantiate our chain once again and attempt to ask some questions that users might potentially ask." + "Under the hood, LLM extracts important entities from the text and returns them as a list of nodes and relationships. Here's how it looks like:" ] }, { "cell_type": "code", - "execution_count": null, - "id": "fc544d0b", + "execution_count": 17, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[GraphDocument(nodes=[Node(id='Charles Robert Darwin', type='Person', properties={}), Node(id='English', type='Nationality', properties={}), Node(id='Naturalist', type='Profession', properties={}), Node(id='Geologist', type='Profession', properties={}), Node(id='Biologist', type='Profession', properties={}), Node(id='Evolutionary Biology', type='Field', properties={}), Node(id='Common Ancestor', type='Concept', properties={}), Node(id='Scientific Concept', type='Concept', properties={}), Node(id='Alfred Russel Wallace', type='Person', properties={}), Node(id='Natural Selection', type='Concept', properties={}), Node(id='Selective Breeding', type='Concept', properties={}), Node(id='Westminster Abbey', type='Location', properties={})], relationships=[Relationship(source=Node(id='Charles Robert Darwin', type='Person', properties={}), target=Node(id='English', type='Nationality', properties={}), type='NATIONALITY', properties={}), Relationship(source=Node(id='Charles Robert Darwin', type='Person', properties={}), target=Node(id='Naturalist', type='Profession', properties={}), type='PROFESSION', properties={}), Relationship(source=Node(id='Charles Robert Darwin', type='Person', properties={}), target=Node(id='Geologist', type='Profession', properties={}), type='PROFESSION', properties={}), Relationship(source=Node(id='Charles Robert Darwin', type='Person', properties={}), target=Node(id='Biologist', type='Profession', properties={}), type='PROFESSION', properties={}), Relationship(source=Node(id='Charles Robert Darwin', type='Person', properties={}), target=Node(id='Evolutionary Biology', type='Field', properties={}), type='CONTRIBUTION', properties={}), Relationship(source=Node(id='Common Ancestor', type='Concept', properties={}), target=Node(id='Scientific Concept', type='Concept', properties={}), type='BASIS', properties={}), Relationship(source=Node(id='Charles Robert Darwin', type='Person', properties={}), target=Node(id='Alfred Russel Wallace', type='Person', properties={}), type='COLLABORATION', properties={}), Relationship(source=Node(id='Natural Selection', type='Concept', properties={}), target=Node(id='Selective Breeding', type='Concept', properties={}), type='COMPARISON', properties={}), Relationship(source=Node(id='Charles Robert Darwin', type='Person', properties={}), target=Node(id='Westminster Abbey', type='Location', properties={}), type='BURIAL', properties={})], source=Document(metadata={}, page_content='\\n Charles Robert Darwin was an English naturalist, geologist, and biologist,\\n widely known for his contributions to evolutionary biology. His proposition that\\n all species of life have descended from a common ancestor is now generally\\n accepted and considered a fundamental scientific concept. In a joint\\n publication with Alfred Russel Wallace, he introduced his scientific theory that\\n this branching pattern of evolution resulted from a process he called natural\\n selection, in which the struggle for existence has a similar effect to the\\n artificial selection involved in selective breeding. Darwin has been\\n described as one of the most influential figures in human history and was\\n honoured by burial in Westminster Abbey.\\n'))]\n" + ] + } + ], "source": [ - "chain = GraphCypherQAChain.from_llm(\n", - " ChatOpenAI(temperature=0),\n", - " graph=graph,\n", - " verbose=True,\n", - " model_name=\"gpt-3.5-turbo\",\n", - " allow_dangerous_requests=True,\n", - ")" + "print(graph_documents)" ] }, { - "cell_type": "code", - "execution_count": null, - "id": "e2abde2d", + "cell_type": "markdown", "metadata": {}, - "outputs": [], "source": [ - "response = chain.run(\"Is Baldur's Gate 3 available on PS5?\")\n", - "print(response)" + "### Storing into Memgraph\n", + "\n", + "Once you have the data ready in a format of `GraphDocument`, that is, nodes and relationships, you can use `add_graph_documents` method to import it into Memgraph. That method transforms the list of `graph_documents` into appropriate Cypher queries that need to be executed in Memgraph. Once that's done, a knowledge graph is stored in Memgraph. " ] }, { - "cell_type": "markdown", - "id": "cf22dc48", + "cell_type": "code", + "execution_count": 18, "metadata": {}, + "outputs": [], "source": [ - "```\n", - "> Entering new GraphCypherQAChain chain...\n", - "Generated Cypher:\n", - "MATCH (g:Game {name: 'Baldur\\'s Gate 3'})-[:AVAILABLE_ON]->(p:Platform {name: 'PS5'})\n", - "RETURN g.name, p.name\n", - "Full Context:\n", - "[]\n", + "# Empty the database\n", + "graph.query(\"STORAGE MODE IN_MEMORY_ANALYTICAL\")\n", + "graph.query(\"DROP GRAPH\")\n", + "graph.query(\"STORAGE MODE IN_MEMORY_TRANSACTIONAL\")\n", "\n", - "> Finished chain.\n", - "I'm sorry, but I don't have the information to answer your question.\n", - "```" + "# Create KG\n", + "graph.add_graph_documents(graph_documents)" ] }, { "cell_type": "markdown", - "id": "293aa1c9", "metadata": {}, "source": [ - "The generated Cypher query looks fine, but we didn't receive any information in response. This illustrates a common challenge when working with LLMs - the misalignment between how users phrase queries and how data is stored. In this case, the difference between user perception and the actual data storage can cause mismatches. Prompt refinement, the process of honing the model's prompts to better grasp these distinctions, is an efficient solution that tackles this issue. Through prompt refinement, the model gains increased proficiency in generating precise and pertinent queries, leading to the successful retrieval of the desired data." + "Here is how the graph looks like in Memgraph Lab (check on `localhost:3000`):\n", + "\n", + "![memgraph-kg](../../../static/img/memgraph_kg.png)\n", + "\n", + "In case you tried this out and got a different graph, that is expected behavior. The graph construction process is non-deterministic, since LLM which is used to generate nodes and relationships from unstructured data in non-deterministic.\n", + "\n", + "### Additional options\n", + "\n", + "Additionally, you have the flexibility to define specific types of nodes and relationships for extraction according to your requirements." ] }, { - "cell_type": "markdown", - "id": "a87b2f1b", - "metadata": {}, - "source": [ - "### Prompt refinement" + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Nodes:[Node(id='Charles Robert Darwin', type='Person', properties={}), Node(id='English', type='Nationality', properties={}), Node(id='Evolutionary Biology', type='Concept', properties={}), Node(id='Natural Selection', type='Concept', properties={}), Node(id='Alfred Russel Wallace', type='Person', properties={})]\n", + "Relationships:[Relationship(source=Node(id='Charles Robert Darwin', type='Person', properties={}), target=Node(id='English', type='Nationality', properties={}), type='NATIONALITY', properties={}), Relationship(source=Node(id='Charles Robert Darwin', type='Person', properties={}), target=Node(id='Evolutionary Biology', type='Concept', properties={}), type='INVOLVED_IN', properties={}), Relationship(source=Node(id='Charles Robert Darwin', type='Person', properties={}), target=Node(id='Natural Selection', type='Concept', properties={}), type='INVOLVED_IN', properties={}), Relationship(source=Node(id='Charles Robert Darwin', type='Person', properties={}), target=Node(id='Alfred Russel Wallace', type='Person', properties={}), type='COLLABORATES_WITH', properties={})]\n" + ] + } + ], + "source": [ + "llm_transformer_filtered = LLMGraphTransformer(\n", + " llm=llm,\n", + " allowed_nodes=[\"Person\", \"Nationality\", \"Concept\"],\n", + " allowed_relationships=[\"NATIONALITY\", \"INVOLVED_IN\", \"COLLABORATES_WITH\"],\n", + ")\n", + "graph_documents_filtered = llm_transformer_filtered.convert_to_graph_documents(\n", + " documents\n", + ")\n", + "\n", + "print(f\"Nodes:{graph_documents_filtered[0].nodes}\")\n", + "print(f\"Relationships:{graph_documents_filtered[0].relationships}\")" ] }, { "cell_type": "markdown", - "id": "8edb9976", "metadata": {}, "source": [ - "To address this, we can adjust the initial Cypher prompt of the QA chain. This involves adding guidance to the LLM on how users can refer to specific platforms, such as PS5 in our case. We achieve this using the LangChain [PromptTemplate](/docs/how_to#prompt-templates), creating a modified initial prompt. This modified prompt is then supplied as an argument to our refined Memgraph-LangChain instance." + "Here's how the graph would like in such case:\n", + "\n", + "![memgraph-kg-2](../../../static/img/memgraph_kg_2.png)\n", + "\n", + "Your graph can also have `__Entity__` labels on all nodes which will be indexed for faster retrieval. " ] }, { "cell_type": "code", - "execution_count": null, - "id": "312dad05", + "execution_count": 20, "metadata": {}, "outputs": [], "source": [ - "CYPHER_GENERATION_TEMPLATE = \"\"\"\n", - "Task:Generate Cypher statement to query a graph database.\n", - "Instructions:\n", - "Use only the provided relationship types and properties in the schema.\n", - "Do not use any other relationship types or properties that are not provided.\n", - "Schema:\n", - "{schema}\n", - "Note: Do not include any explanations or apologies in your responses.\n", - "Do not respond to any questions that might ask anything else than for you to construct a Cypher statement.\n", - "Do not include any text except the generated Cypher statement.\n", - "If the user asks about PS5, Play Station 5 or PS 5, that is the platform called PlayStation 5.\n", - "\n", - "The question is:\n", - "{question}\n", - "\"\"\"\n", + "# Drop graph\n", + "graph.query(\"STORAGE MODE IN_MEMORY_ANALYTICAL\")\n", + "graph.query(\"DROP GRAPH\")\n", + "graph.query(\"STORAGE MODE IN_MEMORY_TRANSACTIONAL\")\n", "\n", - "CYPHER_GENERATION_PROMPT = PromptTemplate(\n", - " input_variables=[\"schema\", \"question\"], template=CYPHER_GENERATION_TEMPLATE\n", - ")" + "# Store to Memgraph with Entity label\n", + "graph.add_graph_documents(graph_documents, baseEntityLabel=True)" ] }, { - "cell_type": "code", - "execution_count": null, - "id": "2c297245", + "cell_type": "markdown", "metadata": {}, - "outputs": [], "source": [ - "chain = GraphCypherQAChain.from_llm(\n", - " ChatOpenAI(temperature=0),\n", - " cypher_prompt=CYPHER_GENERATION_PROMPT,\n", - " graph=graph,\n", - " verbose=True,\n", - " model_name=\"gpt-3.5-turbo\",\n", - " allow_dangerous_requests=True,\n", - ")" + "Here's how the graph would look like:\n", + "\n", + "![memgraph-kg-3](../../../static/img/memgraph_kg_3.png)\n", + "\n", + "There is also an option to include the source of the information that's obtained in the graph. To do that, set `include_source` to `True` and then the source document is stored and it is linked to the nodes in the graph using the `MENTIONS` relationship." ] }, { "cell_type": "code", - "execution_count": null, - "id": "7efb11a0", + "execution_count": 21, "metadata": {}, "outputs": [], "source": [ - "response = chain.run(\"Is Baldur's Gate 3 available on PS5?\")\n", - "print(response)" - ] - }, - { - "cell_type": "markdown", - "id": "289db07f", - "metadata": {}, - "source": [ - "```\n", - "> Entering new GraphCypherQAChain chain...\n", - "Generated Cypher:\n", - "MATCH (g:Game {name: 'Baldur\\'s Gate 3'})-[:AVAILABLE_ON]->(p:Platform {name: 'PlayStation 5'})\n", - "RETURN g.name, p.name\n", - "Full Context:\n", - "[{'g.name': \"Baldur's Gate 3\", 'p.name': 'PlayStation 5'}]\n", + "# Drop graph\n", + "graph.query(\"STORAGE MODE IN_MEMORY_ANALYTICAL\")\n", + "graph.query(\"DROP GRAPH\")\n", + "graph.query(\"STORAGE MODE IN_MEMORY_TRANSACTIONAL\")\n", "\n", - "> Finished chain.\n", - "Yes, Baldur's Gate 3 is available on PlayStation 5.\n", - "```" + "# Store to Memgraph with source included\n", + "graph.add_graph_documents(graph_documents, include_source=True)" ] }, { "cell_type": "markdown", - "id": "84b5f6af", "metadata": {}, "source": [ - "Now, with the revised initial Cypher prompt that includes guidance on platform naming, we are obtaining accurate and relevant results that align more closely with user queries. " + "The constructed graph would look like this:\n", + "\n", + "![memgraph-kg-4](../../../static/img/memgraph_kg_4.png)\n", + "\n", + "Notice how the content of the source is stored and `id` property is generated since the document didn't have any `id`.\n", + "You can combine having both `__Entity__` label and document source. Still, be aware that both take up memory, especially source included due to long strings for content.\n", + "\n", + "In the end, you can query the knowledge graph, as explained in the section before:" ] }, { - "cell_type": "markdown", - "id": "a21108ad", - "metadata": {}, - "source": [ - "This approach allows for further improvement of your QA chain. You can effortlessly integrate extra prompt refinement data into your chain, thereby enhancing the overall user experience of your app." + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "MATCH (:Person {id: \"Charles Robert Darwin\"})-[:COLLABORATION]->(collaborator)\n", + "RETURN collaborator;\n", + "Alfred Russel Wallace\n" + ] + } + ], + "source": [ + "chain = MemgraphQAChain.from_llm(\n", + " ChatOpenAI(temperature=0),\n", + " graph=graph,\n", + " model_name=\"gpt-4-turbo\",\n", + " allow_dangerous_requests=True,\n", + ")\n", + "print(chain.invoke(\"Who Charles Robert Darwin collaborated with?\")[\"result\"])" ] } ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "langchain", "language": "python", "name": "python3" }, @@ -714,9 +801,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.12" + "version": "3.9.19" } }, "nbformat": 4, - "nbformat_minor": 5 + "nbformat_minor": 2 } diff --git a/docs/docs/integrations/providers/aerospike.mdx b/docs/docs/integrations/providers/aerospike.mdx new file mode 100644 index 0000000000000..9dfbaa68091de --- /dev/null +++ b/docs/docs/integrations/providers/aerospike.mdx @@ -0,0 +1,24 @@ +# Aerospike + +>[Aerospike](https://aerospike.com/docs/vector) is a high-performance, distributed database known for its speed and scalability, now with support for vector storage and search, enabling retrieval and search of embedding vectors for machine learning and AI applications. +> See the documentation for Aerospike Vector Search (AVS) [here](https://aerospike.com/docs/vector). + +## Installation and Setup + +Install the AVS Python SDK and AVS langchain vector store: + +```bash +pip install aerospike-vector-search langchain-community + +See the documentation for the Ptyhon SDK [here](https://aerospike-vector-search-python-client.readthedocs.io/en/latest/index.html). +The documentation for the AVS langchain vector store is [here](https://python.langchain.com/api_reference/community/vectorstores/langchain_community.vectorstores.aerospike.Aerospike.html). + +## Vector Store + +To import this vectorstore: + +```python +from langchain_community.vectorstores import Aerospike + +See a usage example [here](https://python.langchain.com/docs/integrations/vectorstores/aerospike/). + diff --git a/docs/docs/integrations/providers/alibaba_cloud.mdx b/docs/docs/integrations/providers/alibaba_cloud.mdx index 74c3045a6424f..baf0fe9fe0768 100644 --- a/docs/docs/integrations/providers/alibaba_cloud.mdx +++ b/docs/docs/integrations/providers/alibaba_cloud.mdx @@ -89,3 +89,11 @@ See [installation instructions and a usage example](/docs/integrations/vectorsto ```python from langchain_community.vectorstores import Hologres ``` + +### Tablestore + +See [installation instructions and a usage example](/docs/integrations/vectorstores/tablestore). + +```python +from langchain_community.vectorstores import TablestoreVectorStore +``` \ No newline at end of file diff --git a/docs/docs/integrations/providers/jina.mdx b/docs/docs/integrations/providers/jina.mdx index 5262889c37dda..66fa2464b1f0d 100644 --- a/docs/docs/integrations/providers/jina.mdx +++ b/docs/docs/integrations/providers/jina.mdx @@ -2,6 +2,10 @@ >[Jina AI](https://jina.ai/about-us) is a search AI company. `Jina` helps businesses and developers unlock multimodal data with a better search. +:::caution +For proper compatibility, please ensure you are using the `openai` SDK at version **0.x**. +::: + ## Installation and Setup - Get a Jina AI API token from [here](https://jina.ai/embeddings/) and set it as an environment variable (`JINA_API_TOKEN`) diff --git a/docs/docs/integrations/providers/linkup.mdx b/docs/docs/integrations/providers/linkup.mdx new file mode 100644 index 0000000000000..ee7f595321746 --- /dev/null +++ b/docs/docs/integrations/providers/linkup.mdx @@ -0,0 +1,39 @@ +# Linkup + +> [Linkup](https://www.linkup.so/) provides an API to connect LLMs to the web and the Linkup Premium Partner sources. + +## Installation and Setup + +To use the Linkup provider, you first need a valid API key, which you can find by signing-up [here](https://app.linkup.so/sign-up). +You will also need the `langchain-linkup` package, which you can install using pip: + +```bash +pip install langchain-linkup +``` + +## Retriever + +See a [usage example](/docs/integrations/retrievers/linkup_search). + +```python +from langchain_linkup import LinkupSearchRetriever + +retriever = LinkupSearchRetriever( + depth="deep", # "standard" or "deep" + linkup_api_key=None, # API key can be passed here or set as the LINKUP_API_KEY environment variable +) +``` + +## Tools + +See a [usage example](/docs/integrations/tools/linkup_search). + +```python +from langchain_linkup import LinkupSearchTool + +tool = LinkupSearchTool( + depth="deep", # "standard" or "deep" + output_type="searchResults", # "searchResults", "sourcedAnswer" or "structured" + linkup_api_key=None, # API key can be passed here or set as the LINKUP_API_KEY environment variable +) +``` diff --git a/docs/docs/integrations/providers/localai.mdx b/docs/docs/integrations/providers/localai.mdx index 1424420b53338..d7c2929ccc25e 100644 --- a/docs/docs/integrations/providers/localai.mdx +++ b/docs/docs/integrations/providers/localai.mdx @@ -6,6 +6,10 @@ > audio (and not only) locally or on-prem with consumer grade hardware, > supporting multiple model families and architectures. +:::caution +For proper compatibility, please ensure you are using the `openai` SDK at version **0.x**. +::: + ## Installation and Setup We have to install several python packages: diff --git a/docs/docs/integrations/providers/microsoft.mdx b/docs/docs/integrations/providers/microsoft.mdx index a63c2fe898ffc..518d4869d47f2 100644 --- a/docs/docs/integrations/providers/microsoft.mdx +++ b/docs/docs/integrations/providers/microsoft.mdx @@ -343,6 +343,31 @@ See a [usage example](/docs/integrations/memory/postgres_chat_message_history/). Since Azure Database for PostgreSQL is open-source Postgres, you can use the [LangChain's Postgres support](/docs/integrations/vectorstores/pgvector/) to connect to Azure Database for PostgreSQL. +### Azure SQL Database + +>[Azure SQL Database](https://learn.microsoft.com/azure/azure-sql/database/sql-database-paas-overview?view=azuresql) is a robust service that combines scalability, security, and high availability, providing all the benefits of a modern database solution. It also provides a dedicated Vector data type & built-in functions that simplifies the storage and querying of vector embeddings directly within a relational database. This eliminates the need for separate vector databases and related integrations, increasing the security of your solutions while reducing the overall complexity. + +By leveraging your current SQL Server databases for vector search, you can enhance data capabilities while minimizing expenses and avoiding the challenges of transitioning to new systems. + +##### Installation and Setup + +See [detail configuration instructions](/docs/integrations/vectorstores/sqlserver). + +We need to install the `langchain-sqlserver` python package. + +```bash +!pip install langchain-sqlserver==0.1.1 +``` + +##### Deploy Azure SQL DB on Microsoft Azure + +[Sign Up](https://learn.microsoft.com/azure/azure-sql/database/free-offer?view=azuresql) for free to get started today. + +See a [usage example](/docs/integrations/vectorstores/sqlserver). + +```python +from langchain_sqlserver import SQLServer_VectorStore +``` ### Azure AI Search diff --git a/docs/docs/integrations/providers/robocorp.mdx b/docs/docs/integrations/providers/robocorp.mdx index 4573db24b45e4..edafdf75dd27f 100644 --- a/docs/docs/integrations/providers/robocorp.mdx +++ b/docs/docs/integrations/providers/robocorp.mdx @@ -1,4 +1,4 @@ -# Robocorp +# Sema4 (fka Robocorp) >[Robocorp](https://robocorp.com/) helps build and operate Python workers that run seamlessly anywhere at any scale diff --git a/docs/docs/integrations/providers/scrapegraph.mdx b/docs/docs/integrations/providers/scrapegraph.mdx new file mode 100644 index 0000000000000..93507ef3a88d8 --- /dev/null +++ b/docs/docs/integrations/providers/scrapegraph.mdx @@ -0,0 +1,41 @@ +# ScrapeGraph AI + +>[ScrapeGraph AI](https://scrapegraphai.com) is a service that provides AI-powered web scraping capabilities. +>It offers tools for extracting structured data, converting webpages to markdown, and processing local HTML content +>using natural language prompts. + +## Installation and Setup + +Install the required packages: + +```bash +pip install langchain-scrapegraph +``` + +Set up your API key: + +```bash +export SGAI_API_KEY="your-scrapegraph-api-key" +``` + +## Tools + +See a [usage example](/docs/integrations/tools/scrapegraph). + +There are four tools available: + +```python +from langchain_scrapegraph.tools import ( + SmartScraperTool, # Extract structured data from websites + MarkdownifyTool, # Convert webpages to markdown + LocalScraperTool, # Process local HTML content + GetCreditsTool, # Check remaining API credits +) +``` + +Each tool serves a specific purpose: + +- `SmartScraperTool`: Extract structured data from websites given a URL, prompt and optional output schema +- `MarkdownifyTool`: Convert any webpage to clean markdown format +- `LocalScraperTool`: Extract structured data from a local HTML file given a prompt and optional output schema +- `GetCreditsTool`: Check your remaining ScrapeGraph AI credits diff --git a/docs/docs/integrations/providers/unstructured.mdx b/docs/docs/integrations/providers/unstructured.mdx index 312a28d6f6815..8425ff1faa226 100644 --- a/docs/docs/integrations/providers/unstructured.mdx +++ b/docs/docs/integrations/providers/unstructured.mdx @@ -22,7 +22,7 @@ dependencies running. - To run everything locally, install the open-source python package with `pip install unstructured` along with `pip install langchain-community` and use the same `UnstructuredLoader` as mentioned above. - - You can install document specific dependencies with extras, e.g. `pip install "unstructured[docx]"`. + - You can install document specific dependencies with extras, e.g. `pip install "unstructured[docx]"`. Learn more about extras [here](https://docs.unstructured.io/open-source/installation/full-installation). - To install the dependencies for all document types, use `pip install "unstructured[all-docs]"`. - Install the following system dependencies if they are not already available on your system with e.g. `brew install` for Mac. Depending on what document types you're parsing, you may not need all of these. diff --git a/docs/docs/integrations/providers/upstage.ipynb b/docs/docs/integrations/providers/upstage.ipynb index 8e5cb60310931..6f33bd71af53b 100644 --- a/docs/docs/integrations/providers/upstage.ipynb +++ b/docs/docs/integrations/providers/upstage.ipynb @@ -8,7 +8,7 @@ "\n", ">[Upstage](https://upstage.ai) is a leading artificial intelligence (AI) company specializing in delivering above-human-grade performance LLM components.\n", ">\n", - ">**Solar Mini Chat** is a fast yet powerful advanced large language model focusing on English and Korean. It has been specifically fine-tuned for multi-turn chat purposes, showing enhanced performance across a wide range of natural language processing tasks, like multi-turn conversation or tasks that require an understanding of long contexts, such as RAG (Retrieval-Augmented Generation), compared to other models of a similar size. This fine-tuning equips it with the ability to handle longer conversations more effectively, making it particularly adept for interactive applications.\n", + ">**Solar Pro** is an enterprise-grade LLM optimized for single-GPU deployment, excelling in instruction-following and processing structured formats like HTML and Markdown. It supports English, Korean, and Japanese with top multilingual performance and offers domain expertise in finance, healthcare, and legal.\n", "\n", ">Other than Solar, Upstage also offers features for real-world RAG (retrieval-augmented generation), such as **Document Parse** and **Groundedness Check**. \n" ] @@ -21,12 +21,12 @@ "\n", "| API | Description | Import | Example usage |\n", "| --- | --- | --- | --- |\n", - "| Chat | Build assistants using Solar Mini Chat | `from langchain_upstage import ChatUpstage` | [Go](../../chat/upstage) |\n", + "| Chat | Build assistants using Solar Chat | `from langchain_upstage import ChatUpstage` | [Go](../../chat/upstage) |\n", "| Text Embedding | Embed strings to vectors | `from langchain_upstage import UpstageEmbeddings` | [Go](../../text_embedding/upstage) |\n", "| Groundedness Check | Verify groundedness of assistant's response | `from langchain_upstage import UpstageGroundednessCheck` | [Go](../../tools/upstage_groundedness_check) |\n", "| Document Parse | Serialize documents with tables and figures | `from langchain_upstage import UpstageDocumentParseLoader` | [Go](../../document_loaders/upstage) |\n", "\n", - "See [documentations](https://developers.upstage.ai/) for more details about the features." + "See [documentations](https://console.upstage.ai/docs/getting-started/overview) for more details about the models and features." ] }, { diff --git a/docs/docs/integrations/providers/wandb.mdx b/docs/docs/integrations/providers/wandb.mdx new file mode 100644 index 0000000000000..8eb85116ff02f --- /dev/null +++ b/docs/docs/integrations/providers/wandb.mdx @@ -0,0 +1,42 @@ +# Weights & Biases + +>[Weights & Biases](https://wandb.ai/) is provider of the AI developer platform to train and +> fine-tune AI models and develop AI applications. + +`Weights & Biase` products can be used to log metrics and artifacts during training, +and to trace the execution of your code. + +There are several main ways to use `Weights & Biases` products within LangChain: +- with `wandb_tracing_enabled` +- with `Weave` lightweight toolkit +- with `WandbCallbackHandler` (deprecated) + + +## wandb_tracing_enabled + +See a [usage example](/docs/integrations/providers/wandb_tracing). + +See in the [W&B documentation](https://docs.wandb.ai/guides/integrations/langchain). + +```python +from langchain_community.callbacks import wandb_tracing_enabled +``` + +## Weave + +See in the [W&B documentation](https://weave-docs.wandb.ai/guides/integrations/langchain). + + +## WandbCallbackHandler + +**Note:** the `WandbCallbackHandler` is being deprecated in favour of the `wandb_tracing_enabled`. + +See a [usage example](/docs/integrations/providers/wandb_tracking). + +See in the [W&B documentation](https://docs.wandb.ai/guides/integrations/langchain). + +```python +from langchain_community.callbacks import WandbCallbackHandler +``` + + diff --git a/docs/docs/integrations/providers/wandb_tracing.ipynb b/docs/docs/integrations/providers/wandb_tracing.ipynb index 89b0dd8d3b727..ce6480d1a0d06 100644 --- a/docs/docs/integrations/providers/wandb_tracing.ipynb +++ b/docs/docs/integrations/providers/wandb_tracing.ipynb @@ -5,7 +5,7 @@ "id": "5371a9bb", "metadata": {}, "source": [ - "# WandB Tracing\n", + "# Weights & Biases tracing\n", "\n", "There are two recommended ways to trace your LangChains:\n", "\n", @@ -165,7 +165,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.3" + "version": "3.10.12" } }, "nbformat": 4, diff --git a/docs/docs/integrations/providers/wandb_tracking.ipynb b/docs/docs/integrations/providers/wandb_tracking.ipynb index 5f034619867a1..ea49435e24efd 100644 --- a/docs/docs/integrations/providers/wandb_tracking.ipynb +++ b/docs/docs/integrations/providers/wandb_tracking.ipynb @@ -6,19 +6,24 @@ "id": "e43f4ea0", "metadata": {}, "source": [ - "# Weights & Biases\n", + "# Weights & Biases tracking\n", "\n", - "This notebook goes over how to track your LangChain experiments into one centralized Weights and Biases dashboard. To learn more about prompt engineering and the callback please refer to this Report which explains both alongside the resultant dashboards you can expect to see.\n", + "This notebook goes over how to track your LangChain experiments into one centralized `Weights and Biases` dashboard. \n", "\n", + "To learn more about prompt engineering and the callback please refer to this notebook which explains both alongside the resultant dashboards you can expect to see:\n", "\n", "\"Open\n", "\n", "\n", - "[View Report](https://wandb.ai/a-sh0ts/langchain_callback_demo/reports/Prompt-Engineering-LLMs-with-LangChain-and-W-B--VmlldzozNjk1NTUw#👋-how-to-build-a-callback-in-langchain-for-better-prompt-engineering\n", - ") \n", + "View a detailed description and examples in the [W&B article](https://wandb.ai/a-sh0ts/langchain_callback_demo/reports/Prompt-Engineering-LLMs-with-LangChain-and-W-B--VmlldzozNjk1NTUw#👋-how-to-build-a-callback-in-langchain-for-better-prompt-engineering\n", + "). \n", "\n", "\n", - "**Note**: _the `WandbCallbackHandler` is being deprecated in favour of the `WandbTracer`_ . In future please use the `WandbTracer` as it is more flexible and allows for more granular logging. To know more about the `WandbTracer` refer to the [agent_with_wandb_tracing](/docs/integrations/providers/wandb_tracing) notebook or use the following [colab notebook](http://wandb.me/prompts-quickstart). To know more about Weights & Biases Prompts refer to the following [prompts documentation](https://docs.wandb.ai/guides/prompts)." + "**Note**: _the `WandbCallbackHandler` is being deprecated in favour of the `WandbTracer`_ . In future please use the `WandbTracer` as it is more flexible and allows for more granular logging. \n", + "\n", + "To know more about the `WandbTracer` refer to the [agent_with_wandb_tracing](/docs/integrations/providers/wandb_tracing) notebook or use the following [colab notebook](http://wandb.me/prompts-quickstart). \n", + "\n", + "To know more about Weights & Biases Prompts refer to the following [prompts documentation](https://docs.wandb.ai/guides/prompts)." ] }, { @@ -248,6 +253,38 @@ "The `flush_tracker` function is used to log LangChain sessions to Weights & Biases. It takes in the LangChain module or agent, and logs at minimum the prompts and generations alongside the serialized form of the LangChain module to the specified Weights & Biases project. By default we reset the session as opposed to concluding the session outright." ] }, + { + "cell_type": "markdown", + "id": "483eefd4-633e-4686-8730-944705fe8a80", + "metadata": { + "execution": { + "iopub.execute_input": "2024-11-12T18:20:31.003316Z", + "iopub.status.busy": "2024-11-12T18:20:31.003152Z", + "iopub.status.idle": "2024-11-12T18:20:31.006033Z", + "shell.execute_reply": "2024-11-12T18:20:31.005546Z", + "shell.execute_reply.started": "2024-11-12T18:20:31.003303Z" + } + }, + "source": [ + "## Usage Scenarios" + ] + }, + { + "cell_type": "markdown", + "id": "066adc04-8180-4936-8d75-5c3660b61de7", + "metadata": { + "execution": { + "iopub.execute_input": "2024-11-12T18:20:45.826326Z", + "iopub.status.busy": "2024-11-12T18:20:45.825714Z", + "iopub.status.idle": "2024-11-12T18:20:45.830483Z", + "shell.execute_reply": "2024-11-12T18:20:45.830037Z", + "shell.execute_reply.started": "2024-11-12T18:20:45.826279Z" + } + }, + "source": [ + "### With LLM" + ] + }, { "cell_type": "code", "execution_count": 4, @@ -373,6 +410,14 @@ "wandb_callback.flush_tracker(llm, name=\"simple_sequential\")" ] }, + { + "cell_type": "markdown", + "id": "7dcfc24b-f0b0-48ec-89ce-beeb2fb763d5", + "metadata": {}, + "source": [ + "### Within Chains" + ] + }, { "cell_type": "code", "execution_count": 5, @@ -524,6 +569,14 @@ "wandb_callback.flush_tracker(synopsis_chain, name=\"agent\")" ] }, + { + "cell_type": "markdown", + "id": "533cd4c9-56e2-4ad9-a83e-4653bfcf322f", + "metadata": {}, + "source": [ + "### With Agents" + ] + }, { "cell_type": "code", "execution_count": 7, @@ -646,7 +699,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.1" + "version": "3.10.12" } }, "nbformat": 4, diff --git a/docs/docs/integrations/retrievers/ibm_watsonx_ranker.ipynb b/docs/docs/integrations/retrievers/ibm_watsonx_ranker.ipynb index 66b804e2dcd4d..d58cb2e4d9a55 100644 --- a/docs/docs/integrations/retrievers/ibm_watsonx_ranker.ipynb +++ b/docs/docs/integrations/retrievers/ibm_watsonx_ranker.ipynb @@ -35,9 +35,9 @@ "\n", "### Integration details\n", "\n", - "| Class | Package | JS support | Package downloads | Package latest |\n", + "| Class | Package | [JS support](https://js.langchain.com/docs/integrations/document_compressors/ibm/) | Package downloads | Package latest |\n", "| :--- | :--- | :---: | :---: | :---: |\n", - "| [WatsonxRerank](https://python.langchain.com/api_reference/ibm/chat_models/langchain_ibm.rerank.WatsonxRerank.html) | [langchain-ibm](https://python.langchain.com/api_reference/ibm/index.html) | ❌ | ![PyPI - Downloads](https://img.shields.io/pypi/dm/langchain-ibm?style=flat-square&label=%20) | ![PyPI - Version](https://img.shields.io/pypi/v/langchain-ibm?style=flat-square&label=%20) |" + "| [WatsonxRerank](https://python.langchain.com/api_reference/ibm/rerank/langchain_ibm.rerank.WatsonxRerank.html) | [langchain-ibm](https://python.langchain.com/api_reference/ibm/index.html) | ✅ | ![PyPI - Downloads](https://img.shields.io/pypi/dm/langchain-ibm?style=flat-square&label=%20) | ![PyPI - Version](https://img.shields.io/pypi/v/langchain-ibm?style=flat-square&label=%20) |" ] }, { @@ -445,7 +445,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "langchain_ibm", "language": "python", "name": "python3" }, diff --git a/docs/docs/integrations/retrievers/linkup_search.ipynb b/docs/docs/integrations/retrievers/linkup_search.ipynb new file mode 100644 index 0000000000000..4ca53214917ce --- /dev/null +++ b/docs/docs/integrations/retrievers/linkup_search.ipynb @@ -0,0 +1,270 @@ +{ + "cells": [ + { + "cell_type": "raw", + "id": "afaf8039", + "metadata": {}, + "source": [ + "---\n", + "sidebar_label: LinkupSearchRetriever\n", + "---" + ] + }, + { + "cell_type": "markdown", + "id": "e49f1e0d", + "metadata": {}, + "source": [ + "# LinkupSearchRetriever\n", + "\n", + "> [Linkup](https://www.linkup.so/) provides an API to connect LLMs to the web and the Linkup Premium Partner sources.\n", + "\n", + "This will help you getting started with the LinkupSearchRetriever [retriever](/docs/concepts/retrievers/). For detailed documentation of all LinkupSearchRetriever features and configurations head to the [API reference](https://python.langchain.com/api_reference/linkup/retrievers/linkup_langchain.search_retriever.LinkupSearchRetriever.html).\n", + "\n", + "### Integration details\n", + "\n", + "| Retriever | Source | Package |\n", + "| :--- | :--- | :---: |\n", + "[LinkupSearchRetriever](https://python.langchain.com/api_reference/linkup/retrievers/linkup_langchain.search_retriever.LinkupSearchRetriever.html) | Web and partner sources | langchain-linkup |\n", + "\n", + "## Setup\n", + "\n", + "To use the Linkup provider, you need a valid API key, which you can find by signing-up [here](https://app.linkup.so/sign-up). You can then set it up as the `LINKUP_API_KEY` environment variable. For the chain example below, you also need to set an OpenAI API key as `OPENAI_API_KEY` environment variable, which you can also do here:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0c6cab32-8f55-473d-b5bc-72673ea4da61", + "metadata": {}, + "outputs": [], + "source": [ + "# import os\n", + "# os.environ[\"LINKUP_API_KEY\"] = \"\" # Fill with your API key\n", + "# os.environ[\"OPENAI_API_KEY\"] = \"\" # Fill with your API key" + ] + }, + { + "cell_type": "markdown", + "id": "72ee0c4b-9764-423a-9dbf-95129e185210", + "metadata": {}, + "source": [ + "If you want to get automated tracing from individual queries, you can also set your [LangSmith](https://docs.smith.langchain.com/) API key by uncommenting below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a15d341e-3e26-4ca3-830b-5aab30ed66de", + "metadata": {}, + "outputs": [], + "source": [ + "# os.environ[\"LANGSMITH_API_KEY\"] = getpass.getpass(\"Enter your LangSmith API key: \")\n", + "# os.environ[\"LANGSMITH_TRACING\"] = \"true\"" + ] + }, + { + "cell_type": "markdown", + "id": "0730d6a1-c893-4840-9817-5e5251676d5d", + "metadata": {}, + "source": [ + "### Installation\n", + "\n", + "This retriever lives in the `langchain-linkup` package:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "652d6238-1f87-422a-b135-f5abbb8652fc", + "metadata": {}, + "outputs": [], + "source": [ + "%pip install -qU langchain-linkup" + ] + }, + { + "cell_type": "markdown", + "id": "a38cde65-254d-4219-a441-068766c0d4b5", + "metadata": {}, + "source": [ + "## Instantiation\n", + "\n", + "Now we can instantiate our retriever:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "70cc8e65-2a02-408a-bbc6-8ef649057d82", + "metadata": {}, + "outputs": [], + "source": [ + "from langchain_linkup import LinkupSearchRetriever\n", + "\n", + "retriever = LinkupSearchRetriever(\n", + " depth=\"deep\", # \"standard\" or \"deep\"\n", + " linkup_api_key=None, # API key can be passed here or set as the LINKUP_API_KEY environment variable\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "5c5f2839-4020-424e-9fc9-07777eede442", + "metadata": {}, + "source": [ + "## Usage" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "51a60dbe-9f2e-4e04-bb62-23968f17164a", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[Document(metadata={'name': 'US presidential election results 2024: Harris vs. Trump | Live maps ...', 'url': 'https://www.reuters.com/graphics/USA-ELECTION/RESULTS/zjpqnemxwvx/'}, page_content='Updated results from the 2024 election for the US president. Reuters live coverage of the 2024 US President, Senate, House and state governors races.'),\n", + " Document(metadata={'name': 'Election 2024: Presidential results - CNN', 'url': 'https://www.cnn.com/election/2024/results/president'}, page_content='View maps and real-time results for the 2024 US presidential election matchup between former President Donald Trump and Vice President Kamala Harris. For more ...'),\n", + " Document(metadata={'name': 'Presidential Election 2024 Live Results: Donald Trump wins - NBC News', 'url': 'https://www.nbcnews.com/politics/2024-elections/president-results'}, page_content='View live election results from the 2024 presidential race as Kamala Harris and Donald Trump face off. See the map of votes by state as results are tallied.'),\n", + " Document(metadata={'name': '2024 President Election - Live Results | RealClearPolitics', 'url': 'https://www.realclearpolitics.com/elections/live_results/2024/president/'}, page_content='Latest Election 2024 Results • President • United States • Tuesday November 3rd • Presidential Election Details'),\n", + " Document(metadata={'name': 'Live: Presidential Election Results 2024 : NPR', 'url': 'https://apps.npr.org/2024-election-results/'}, page_content='Presidential race ratings are based on NPR analysis. Maps do not shade in until 50% of the estimated vote is in for a given state, to mitigate flutuations in early returns . 2024 General Election Results'),\n", + " Document(metadata={'name': '2024 US Presidential Election Results: Live Map - Bloomberg.com', 'url': 'https://www.bloomberg.com/graphics/2024-us-election-results/'}, page_content='US Presidential Election Results November 5, 2024. Bloomberg News is reporting live election results in the presidential race between Democratic Vice President Kamala Harris and her Republican ...'),\n", + " Document(metadata={'name': 'Presidential Election Results 2024: Electoral Votes & Map by State ...', 'url': 'https://www.politico.com/2024-election/results/president/'}, page_content='Live 2024 Presidential election results, maps and electoral votes by state. POLITICO’s real-time coverage of 2024 races for President, Senate, House and Governor.'),\n", + " Document(metadata={'name': 'US Presidential Election Results 2024 - BBC News', 'url': 'https://www.bbc.com/news/election/2024/us/results'}, page_content='Kamala Harris of the Democrat party has 74,498,303 votes (48.3%) Donald Trump of the Republican party has 76,989,499 votes (49.9%) This map of the US states was filled in as presidential results ...'),\n", + " Document(metadata={'name': 'Election Results 2024: Live Map - Races by State - POLITICO', 'url': 'https://www.politico.com/2024-election/results/'}, page_content='Live 2024 election results and maps by state. POLITICO’s real-time coverage of 2024 races for President, Senate, House and Governor.'),\n", + " Document(metadata={'name': '2024 U.S. Presidential Election: Live Results and Maps - USA TODAY', 'url': 'https://www.usatoday.com/elections/results/2024-11-05/president'}, page_content='See who is winning in the Nov. 5, 2024 U.S. Presidential election nationwide with real-time results and state-by-state maps.'),\n", + " Document(metadata={'name': 'Presidential Election 2024 Live Results: Donald Trump winsNBC News LogoSearchSearchNBC News LogoMSNBC LogoToday Logo', 'url': 'https://www.nbcnews.com/politics/2024-elections/president-results'}, page_content=\"Profile\\n\\nSections\\n\\nLocal\\n\\ntv\\n\\nFeatured\\n\\nMore From NBC\\n\\nFollow NBC News\\n\\nnews Alerts\\n\\nThere are no new alerts at this time\\n\\n2024 President Results: Trump wins\\n==================================\\n\\nDonald Trump has secured more than the 270 Electoral College votes needed to secure the presidency, NBC News projects.\\n\\nRaces to watch\\n--------------\\n\\nAll Presidential races\\n----------------------\\n\\nElection Night Coverage\\n-----------------------\\n\\n### China competition should be top priority for Trump, Sullivan says, as Biden and Xi prepare for final meeting\\n\\n### Jim Himes says 'truth and analysis are not what drive’ Gabbard and Gaetz\\n\\n### Trump praises RFK Jr. in Mar-a-Lago remarks\\n\\n### Trump announces North Dakota Gov. Doug Burgum as his pick for interior secretary\\n\\n### House Ethics Committee cancels meeting at which Gaetz probe was on the agenda\\n\\n### Trump picks former Rep. Doug Collins for veterans affairs secretary\\n\\n### Trump to nominate his criminal defense lawyer for deputy attorney general\\n\\n### From ‘brilliant’ to ‘dangerous’: Mixed reactions roll in after Trump picks RFK Jr. for top health post\\n\\n### Donald Trump Jr. says he played key role in RFK Jr., Tulsi Gabbard picks\\n\\n### Jared Polis offers surprising words of support for RFK Jr. pick for HHS secretary\\n\\nNational early voting\\n---------------------\\n\\n### 88,233,886 mail-in and early in-person votes cast nationally\\n\\n### 65,676,748 mail-in and early in-person votes requested nationally\\n\\nPast Presidential Elections\\n---------------------------\\n\\n### Vote Margin by State in the 2020 Presidential Election\\n\\nCircle size represents the number electoral votes in that state.\\n\\nThe expected vote is the total number of votes that are expected in a given race once all votes are counted. This number is an estimate and is based on several different factors, including information on the number of votes cast early as well as information provided to our vote reporters on Election Day from county election officials. The figure can change as NBC News gathers new information.\\n\\n**Source**: [National Election Pool (NEP)](https://www.nbcnews.com/politics/2024-elections/how-election-data-is-collected )\\n\\n2024 election results\\n---------------------\\n\\nElection Night Coverage\\n-----------------------\\n\\n### China competition should be top priority for Trump, Sullivan says, as Biden and Xi prepare for final meeting\\n\\n### Jim Himes says 'truth and analysis are not what drive’ Gabbard and Gaetz\\n\\n### Trump praises RFK Jr. in Mar-a-Lago remarks\\n\\n©\\xa02024 NBCUniversal Media, LLC\")]" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "query = \"Who won the latest US presidential elections?\"\n", + "\n", + "retriever.invoke(query)" + ] + }, + { + "cell_type": "markdown", + "id": "dfe8aad4-8626-4330-98a9-7ea1ca5d2e0e", + "metadata": {}, + "source": [ + "## Use within a chain\n", + "\n", + "Like other retrievers, LinkupSearchRetriever can be incorporated into LLM applications via [chains](/docs/how_to/sequence/).\n", + "\n", + "We will need a LLM or chat model:\n", + "\n", + "```{=mdx}\n", + "import ChatModelTabs from \"@theme/ChatModelTabs\";\n", + "\n", + "\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25b647a3-f8f2-4541-a289-7a241e43f9df", + "metadata": {}, + "outputs": [], + "source": [ + "# | output: false\n", + "# | echo: false\n", + "\n", + "from langchain_openai import ChatOpenAI\n", + "\n", + "llm = ChatOpenAI(model=\"gpt-3.5-turbo-0125\", temperature=0)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "23e11cc9-abd6-4855-a7eb-799f45ca01ae", + "metadata": {}, + "outputs": [], + "source": [ + "from langchain_core.output_parsers import StrOutputParser\n", + "from langchain_core.prompts import ChatPromptTemplate\n", + "from langchain_core.runnables import RunnablePassthrough\n", + "\n", + "prompt = ChatPromptTemplate.from_template(\n", + " \"\"\"Answer the question based only on the context provided.\n", + "\n", + "Context: {context}\n", + "\n", + "Question: {question}\"\"\"\n", + ")\n", + "\n", + "\n", + "def format_docs(docs):\n", + " return \"\\n\\n\".join(doc.page_content for doc in docs)\n", + "\n", + "\n", + "chain = (\n", + " {\"context\": retriever | format_docs, \"question\": RunnablePassthrough()}\n", + " | prompt\n", + " | llm\n", + " | StrOutputParser()\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "d47c37dd-5c11-416c-a3b6-bec413cd70e8", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'The 3 latest US presidential elections were won by Joe Biden in 2020, Donald Trump in 2016, and Barack Obama in 2012.'" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "chain.invoke(\"Who won the 3 latest US presidential elections?\")" + ] + }, + { + "cell_type": "markdown", + "id": "3a5bb5ca-c3ae-4a58-be67-2cd18574b9a3", + "metadata": {}, + "source": [ + "## API reference\n", + "\n", + "For detailed documentation of all LinkupSearchRetriever features and configurations head to the [API reference](https://python.langchain.com/api_reference/linkup/retrievers/linkup_langchain.search_retriever.LinkupSearchRetriever.html)." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/docs/integrations/retrievers/self_query/activeloop_deeplake_self_query.ipynb b/docs/docs/integrations/retrievers/self_query/activeloop_deeplake_self_query.ipynb index d5584362a89ae..3ddb37100092f 100644 --- a/docs/docs/integrations/retrievers/self_query/activeloop_deeplake_self_query.ipynb +++ b/docs/docs/integrations/retrievers/self_query/activeloop_deeplake_self_query.ipynb @@ -194,7 +194,7 @@ "metadata": {}, "outputs": [], "source": [ - "from langchain.chains.query_constructor.base import AttributeInfo\n", + "from langchain.chains.query_constructor.schema import AttributeInfo\n", "from langchain.retrievers.self_query.base import SelfQueryRetriever\n", "from langchain_openai import OpenAI\n", "\n", diff --git a/docs/docs/integrations/retrievers/self_query/astradb.ipynb b/docs/docs/integrations/retrievers/self_query/astradb.ipynb index a170a306f1b6a..a69b22006a367 100644 --- a/docs/docs/integrations/retrievers/self_query/astradb.ipynb +++ b/docs/docs/integrations/retrievers/self_query/astradb.ipynb @@ -146,7 +146,7 @@ "metadata": {}, "outputs": [], "source": [ - "from langchain.chains.query_constructor.base import AttributeInfo\n", + "from langchain.chains.query_constructor.schema import AttributeInfo\n", "from langchain.retrievers.self_query.base import SelfQueryRetriever\n", "from langchain_openai import OpenAI\n", "\n", diff --git a/docs/docs/integrations/retrievers/self_query/chroma_self_query.ipynb b/docs/docs/integrations/retrievers/self_query/chroma_self_query.ipynb index f9f5725afbe2c..c12336bb78275 100644 --- a/docs/docs/integrations/retrievers/self_query/chroma_self_query.ipynb +++ b/docs/docs/integrations/retrievers/self_query/chroma_self_query.ipynb @@ -164,7 +164,7 @@ }, "outputs": [], "source": [ - "from langchain.chains.query_constructor.base import AttributeInfo\n", + "from langchain.chains.query_constructor.schema import AttributeInfo\n", "from langchain.retrievers.self_query.base import SelfQueryRetriever\n", "from langchain_openai import OpenAI\n", "\n", diff --git a/docs/docs/integrations/retrievers/self_query/dashvector.ipynb b/docs/docs/integrations/retrievers/self_query/dashvector.ipynb index a5c856d9555f7..3e7bcda942754 100644 --- a/docs/docs/integrations/retrievers/self_query/dashvector.ipynb +++ b/docs/docs/integrations/retrievers/self_query/dashvector.ipynb @@ -185,7 +185,7 @@ }, "outputs": [], "source": [ - "from langchain.chains.query_constructor.base import AttributeInfo\n", + "from langchain.chains.query_constructor.schema import AttributeInfo\n", "from langchain.retrievers.self_query.base import SelfQueryRetriever\n", "from langchain_community.llms import Tongyi\n", "\n", diff --git a/docs/docs/integrations/retrievers/self_query/databricks_vector_search.ipynb b/docs/docs/integrations/retrievers/self_query/databricks_vector_search.ipynb index 1f88a518785e7..d969db98281bc 100644 --- a/docs/docs/integrations/retrievers/self_query/databricks_vector_search.ipynb +++ b/docs/docs/integrations/retrievers/self_query/databricks_vector_search.ipynb @@ -282,7 +282,7 @@ }, "outputs": [], "source": [ - "from langchain.chains.query_constructor.base import AttributeInfo\n", + "from langchain.chains.query_constructor.schema import AttributeInfo\n", "from langchain.retrievers.self_query.base import SelfQueryRetriever\n", "from langchain_openai import OpenAI\n", "\n", diff --git a/docs/docs/integrations/retrievers/self_query/dingo.ipynb b/docs/docs/integrations/retrievers/self_query/dingo.ipynb index 95bda4d1513e9..12f675823b2dc 100644 --- a/docs/docs/integrations/retrievers/self_query/dingo.ipynb +++ b/docs/docs/integrations/retrievers/self_query/dingo.ipynb @@ -196,7 +196,7 @@ "metadata": {}, "outputs": [], "source": [ - "from langchain.chains.query_constructor.base import AttributeInfo\n", + "from langchain.chains.query_constructor.schema import AttributeInfo\n", "from langchain.retrievers.self_query.base import SelfQueryRetriever\n", "from langchain_openai import OpenAI\n", "\n", diff --git a/docs/docs/integrations/retrievers/self_query/hanavector_self_query.ipynb b/docs/docs/integrations/retrievers/self_query/hanavector_self_query.ipynb index 0bdef3836c8a0..e2a0d8a28f25d 100644 --- a/docs/docs/integrations/retrievers/self_query/hanavector_self_query.ipynb +++ b/docs/docs/integrations/retrievers/self_query/hanavector_self_query.ipynb @@ -125,7 +125,7 @@ "metadata": {}, "outputs": [], "source": [ - "from langchain.chains.query_constructor.base import AttributeInfo\n", + "from langchain.chains.query_constructor.schema import AttributeInfo\n", "from langchain.retrievers.self_query.base import SelfQueryRetriever\n", "from langchain_community.query_constructors.hanavector import HanaTranslator\n", "from langchain_openai import ChatOpenAI\n", diff --git a/docs/docs/integrations/retrievers/self_query/milvus_self_query.ipynb b/docs/docs/integrations/retrievers/self_query/milvus_self_query.ipynb index c43ab4cf2c4e6..21414461733de 100644 --- a/docs/docs/integrations/retrievers/self_query/milvus_self_query.ipynb +++ b/docs/docs/integrations/retrievers/self_query/milvus_self_query.ipynb @@ -119,7 +119,7 @@ "metadata": {}, "outputs": [], "source": [ - "from langchain.chains.query_constructor.base import AttributeInfo\n", + "from langchain.chains.query_constructor.schema import AttributeInfo\n", "from langchain.retrievers.self_query.base import SelfQueryRetriever\n", "from langchain_openai import OpenAI\n", "\n", diff --git a/docs/docs/integrations/retrievers/self_query/mongodb_atlas.ipynb b/docs/docs/integrations/retrievers/self_query/mongodb_atlas.ipynb index 045a224acdf0c..c7471e29effbc 100644 --- a/docs/docs/integrations/retrievers/self_query/mongodb_atlas.ipynb +++ b/docs/docs/integrations/retrievers/self_query/mongodb_atlas.ipynb @@ -160,7 +160,7 @@ "metadata": {}, "outputs": [], "source": [ - "from langchain.chains.query_constructor.base import AttributeInfo\n", + "from langchain.chains.query_constructor.schema import AttributeInfo\n", "from langchain.retrievers.self_query.base import SelfQueryRetriever\n", "from langchain_openai import OpenAI\n", "\n", diff --git a/docs/docs/integrations/retrievers/self_query/myscale_self_query.ipynb b/docs/docs/integrations/retrievers/self_query/myscale_self_query.ipynb index a29517693669b..c4c90888d3781 100644 --- a/docs/docs/integrations/retrievers/self_query/myscale_self_query.ipynb +++ b/docs/docs/integrations/retrievers/self_query/myscale_self_query.ipynb @@ -165,7 +165,7 @@ }, "outputs": [], "source": [ - "from langchain.chains.query_constructor.base import AttributeInfo\n", + "from langchain.chains.query_constructor.schema import AttributeInfo\n", "from langchain.retrievers.self_query.base import SelfQueryRetriever\n", "from langchain_openai import ChatOpenAI\n", "\n", diff --git a/docs/docs/integrations/retrievers/self_query/neo4j_self_query.ipynb b/docs/docs/integrations/retrievers/self_query/neo4j_self_query.ipynb index 0d2f14521277b..1730222283f0b 100644 --- a/docs/docs/integrations/retrievers/self_query/neo4j_self_query.ipynb +++ b/docs/docs/integrations/retrievers/self_query/neo4j_self_query.ipynb @@ -168,7 +168,7 @@ "metadata": {}, "outputs": [], "source": [ - "from langchain.chains.query_constructor.base import AttributeInfo\n", + "from langchain.chains.query_constructor.schema import AttributeInfo\n", "from langchain.retrievers.self_query.base import SelfQueryRetriever\n", "from langchain_openai import OpenAI\n", "\n", diff --git a/docs/docs/integrations/retrievers/self_query/opensearch_self_query.ipynb b/docs/docs/integrations/retrievers/self_query/opensearch_self_query.ipynb index 8795bbd1d1a38..dfc68614eadab 100644 --- a/docs/docs/integrations/retrievers/self_query/opensearch_self_query.ipynb +++ b/docs/docs/integrations/retrievers/self_query/opensearch_self_query.ipynb @@ -135,7 +135,7 @@ }, "outputs": [], "source": [ - "from langchain.chains.query_constructor.base import AttributeInfo\n", + "from langchain.chains.query_constructor.schema import AttributeInfo\n", "from langchain.retrievers.self_query.base import SelfQueryRetriever\n", "from langchain_openai import OpenAI\n", "\n", diff --git a/docs/docs/integrations/retrievers/self_query/pgvector_self_query.ipynb b/docs/docs/integrations/retrievers/self_query/pgvector_self_query.ipynb index 8d27022fe2582..d05734063fcdc 100644 --- a/docs/docs/integrations/retrievers/self_query/pgvector_self_query.ipynb +++ b/docs/docs/integrations/retrievers/self_query/pgvector_self_query.ipynb @@ -141,7 +141,7 @@ }, "outputs": [], "source": [ - "from langchain.chains.query_constructor.base import AttributeInfo\n", + "from langchain.chains.query_constructor.schema import AttributeInfo\n", "from langchain.retrievers.self_query.base import SelfQueryRetriever\n", "from langchain_openai import OpenAI\n", "\n", diff --git a/docs/docs/integrations/retrievers/self_query/pinecone.ipynb b/docs/docs/integrations/retrievers/self_query/pinecone.ipynb index 670c7db91f0f3..8c8bc331d7d9e 100644 --- a/docs/docs/integrations/retrievers/self_query/pinecone.ipynb +++ b/docs/docs/integrations/retrievers/self_query/pinecone.ipynb @@ -190,7 +190,7 @@ "metadata": {}, "outputs": [], "source": [ - "from langchain.chains.query_constructor.base import AttributeInfo\n", + "from langchain.chains.query_constructor.schema import AttributeInfo\n", "from langchain.retrievers.self_query.base import SelfQueryRetriever\n", "from langchain_openai import OpenAI\n", "\n", diff --git a/docs/docs/integrations/retrievers/self_query/qdrant_self_query.ipynb b/docs/docs/integrations/retrievers/self_query/qdrant_self_query.ipynb index 50b162b6928cd..a012caa99d3c1 100644 --- a/docs/docs/integrations/retrievers/self_query/qdrant_self_query.ipynb +++ b/docs/docs/integrations/retrievers/self_query/qdrant_self_query.ipynb @@ -144,7 +144,7 @@ }, "outputs": [], "source": [ - "from langchain.chains.query_constructor.base import AttributeInfo\n", + "from langchain.chains.query_constructor.schema import AttributeInfo\n", "from langchain.retrievers.self_query.base import SelfQueryRetriever\n", "from langchain_openai import OpenAI\n", "\n", diff --git a/docs/docs/integrations/retrievers/self_query/redis_self_query.ipynb b/docs/docs/integrations/retrievers/self_query/redis_self_query.ipynb index 55e4f6fa350e4..5f72e7738b5cb 100644 --- a/docs/docs/integrations/retrievers/self_query/redis_self_query.ipynb +++ b/docs/docs/integrations/retrievers/self_query/redis_self_query.ipynb @@ -194,7 +194,7 @@ }, "outputs": [], "source": [ - "from langchain.chains.query_constructor.base import AttributeInfo\n", + "from langchain.chains.query_constructor.schema import AttributeInfo\n", "from langchain.retrievers.self_query.base import SelfQueryRetriever\n", "from langchain_openai import OpenAI\n", "\n", diff --git a/docs/docs/integrations/retrievers/self_query/supabase_self_query.ipynb b/docs/docs/integrations/retrievers/self_query/supabase_self_query.ipynb index 8884216448788..8468979c4e81a 100644 --- a/docs/docs/integrations/retrievers/self_query/supabase_self_query.ipynb +++ b/docs/docs/integrations/retrievers/self_query/supabase_self_query.ipynb @@ -308,7 +308,7 @@ }, "outputs": [], "source": [ - "from langchain.chains.query_constructor.base import AttributeInfo\n", + "from langchain.chains.query_constructor.schema import AttributeInfo\n", "from langchain.retrievers.self_query.base import SelfQueryRetriever\n", "from langchain_openai import OpenAI\n", "\n", diff --git a/docs/docs/integrations/retrievers/self_query/tencentvectordb.ipynb b/docs/docs/integrations/retrievers/self_query/tencentvectordb.ipynb index a59546e1308fd..4d6362085ec28 100644 --- a/docs/docs/integrations/retrievers/self_query/tencentvectordb.ipynb +++ b/docs/docs/integrations/retrievers/self_query/tencentvectordb.ipynb @@ -218,7 +218,7 @@ }, "outputs": [], "source": [ - "from langchain.chains.query_constructor.base import AttributeInfo\n", + "from langchain.chains.query_constructor.schema import AttributeInfo\n", "from langchain.retrievers.self_query.base import SelfQueryRetriever\n", "from langchain_openai import ChatOpenAI\n", "\n", diff --git a/docs/docs/integrations/retrievers/self_query/timescalevector_self_query.ipynb b/docs/docs/integrations/retrievers/self_query/timescalevector_self_query.ipynb index f78a2f107cd6b..4e495264d7260 100644 --- a/docs/docs/integrations/retrievers/self_query/timescalevector_self_query.ipynb +++ b/docs/docs/integrations/retrievers/self_query/timescalevector_self_query.ipynb @@ -249,7 +249,7 @@ }, "outputs": [], "source": [ - "from langchain.chains.query_constructor.base import AttributeInfo\n", + "from langchain.chains.query_constructor.schema import AttributeInfo\n", "from langchain.retrievers.self_query.base import SelfQueryRetriever\n", "from langchain_openai import OpenAI\n", "\n", diff --git a/docs/docs/integrations/retrievers/self_query/vectara_self_query.ipynb b/docs/docs/integrations/retrievers/self_query/vectara_self_query.ipynb index fe98c8f12a624..ab3dbe4f301a6 100644 --- a/docs/docs/integrations/retrievers/self_query/vectara_self_query.ipynb +++ b/docs/docs/integrations/retrievers/self_query/vectara_self_query.ipynb @@ -91,7 +91,7 @@ "os.environ[\"VECTARA_CORPUS_ID\"] = \"\"\n", "os.environ[\"VECTARA_CUSTOMER_ID\"] = \"\"\n", "\n", - "from langchain.chains.query_constructor.base import AttributeInfo\n", + "from langchain.chains.query_constructor.schema import AttributeInfo\n", "from langchain.retrievers.self_query.base import SelfQueryRetriever\n", "from langchain_community.vectorstores import Vectara\n", "from langchain_openai.chat_models import ChatOpenAI" diff --git a/docs/docs/integrations/retrievers/self_query/weaviate_self_query.ipynb b/docs/docs/integrations/retrievers/self_query/weaviate_self_query.ipynb index df5fd9d03e080..b3729e193ea12 100644 --- a/docs/docs/integrations/retrievers/self_query/weaviate_self_query.ipynb +++ b/docs/docs/integrations/retrievers/self_query/weaviate_self_query.ipynb @@ -115,7 +115,7 @@ }, "outputs": [], "source": [ - "from langchain.chains.query_constructor.base import AttributeInfo\n", + "from langchain.chains.query_constructor.schema import AttributeInfo\n", "from langchain.retrievers.self_query.base import SelfQueryRetriever\n", "from langchain_openai import OpenAI\n", "\n", diff --git a/docs/docs/integrations/retrievers/weaviate-hybrid.ipynb b/docs/docs/integrations/retrievers/weaviate-hybrid.ipynb deleted file mode 100644 index 9592435b918b1..0000000000000 --- a/docs/docs/integrations/retrievers/weaviate-hybrid.ipynb +++ /dev/null @@ -1,297 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "ce0f17b9", - "metadata": {}, - "source": [ - "# Weaviate Hybrid Search\n", - "\n", - ">[Weaviate](https://weaviate.io/developers/weaviate) is an open-source vector database.\n", - "\n", - ">[Hybrid search](https://weaviate.io/blog/hybrid-search-explained) is a technique that combines multiple search algorithms to improve the accuracy and relevance of search results. It uses the best features of both keyword-based search algorithms with vector search techniques.\n", - "\n", - ">The `Hybrid search in Weaviate` uses sparse and dense vectors to represent the meaning and context of search queries and documents.\n", - "\n", - "This notebook shows how to use `Weaviate hybrid search` as a LangChain retriever." - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "c307b082", - "metadata": {}, - "source": [ - "Set up the retriever:" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "bba863a2-977c-4add-b5f4-bfc33a80eae5", - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "%pip install --upgrade --quiet weaviate-client" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "c10dd962", - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "\n", - "import weaviate\n", - "\n", - "WEAVIATE_URL = os.getenv(\"WEAVIATE_URL\")\n", - "auth_client_secret = (weaviate.AuthApiKey(api_key=os.getenv(\"WEAVIATE_API_KEY\")),)\n", - "client = weaviate.Client(\n", - " url=WEAVIATE_URL,\n", - " additional_headers={\n", - " \"X-Openai-Api-Key\": os.getenv(\"OPENAI_API_KEY\"),\n", - " },\n", - ")\n", - "\n", - "# client.schema.delete_all()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f47a2bfe", - "metadata": {}, - "outputs": [], - "source": [ - "from langchain_community.retrievers import (\n", - " WeaviateHybridSearchRetriever,\n", - ")\n", - "from langchain_core.documents import Document" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "f2eff08e", - "metadata": {}, - "outputs": [], - "source": [ - "retriever = WeaviateHybridSearchRetriever(\n", - " client=client,\n", - " index_name=\"LangChain\",\n", - " text_key=\"text\",\n", - " attributes=[],\n", - " create_schema_if_missing=True,\n", - ")" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "b68debff", - "metadata": {}, - "source": [ - "Add some data:" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "cd8a7b17", - "metadata": {}, - "outputs": [], - "source": [ - "docs = [\n", - " Document(\n", - " metadata={\n", - " \"title\": \"Embracing The Future: AI Unveiled\",\n", - " \"author\": \"Dr. Rebecca Simmons\",\n", - " },\n", - " page_content=\"A comprehensive analysis of the evolution of artificial intelligence, from its inception to its future prospects. Dr. Simmons covers ethical considerations, potentials, and threats posed by AI.\",\n", - " ),\n", - " Document(\n", - " metadata={\n", - " \"title\": \"Symbiosis: Harmonizing Humans and AI\",\n", - " \"author\": \"Prof. Jonathan K. Sterling\",\n", - " },\n", - " page_content=\"Prof. Sterling explores the potential for harmonious coexistence between humans and artificial intelligence. The book discusses how AI can be integrated into society in a beneficial and non-disruptive manner.\",\n", - " ),\n", - " Document(\n", - " metadata={\"title\": \"AI: The Ethical Quandary\", \"author\": \"Dr. Rebecca Simmons\"},\n", - " page_content=\"In her second book, Dr. Simmons delves deeper into the ethical considerations surrounding AI development and deployment. It is an eye-opening examination of the dilemmas faced by developers, policymakers, and society at large.\",\n", - " ),\n", - " Document(\n", - " metadata={\n", - " \"title\": \"Conscious Constructs: The Search for AI Sentience\",\n", - " \"author\": \"Dr. Samuel Cortez\",\n", - " },\n", - " page_content=\"Dr. Cortez takes readers on a journey exploring the controversial topic of AI consciousness. The book provides compelling arguments for and against the possibility of true AI sentience.\",\n", - " ),\n", - " Document(\n", - " metadata={\n", - " \"title\": \"Invisible Routines: Hidden AI in Everyday Life\",\n", - " \"author\": \"Prof. Jonathan K. Sterling\",\n", - " },\n", - " page_content=\"In his follow-up to 'Symbiosis', Prof. Sterling takes a look at the subtle, unnoticed presence and influence of AI in our everyday lives. It reveals how AI has become woven into our routines, often without our explicit realization.\",\n", - " ),\n", - "]" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "3c5970db", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['3a27b0a5-8dbb-4fee-9eba-8b6bc2c252be',\n", - " 'eeb9fd9b-a3ac-4d60-a55b-a63a25d3b907',\n", - " '7ebbdae7-1061-445f-a046-1989f2343d8f',\n", - " 'c2ab315b-3cab-467f-b23a-b26ed186318d',\n", - " 'b83765f2-e5d2-471f-8c02-c3350ade4c4f']" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "retriever.add_documents(docs)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "6e030694", - "metadata": {}, - "source": [ - "Do a hybrid search:" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "bf7dbb98", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[Document(page_content='In her second book, Dr. Simmons delves deeper into the ethical considerations surrounding AI development and deployment. It is an eye-opening examination of the dilemmas faced by developers, policymakers, and society at large.', metadata={}),\n", - " Document(page_content='A comprehensive analysis of the evolution of artificial intelligence, from its inception to its future prospects. Dr. Simmons covers ethical considerations, potentials, and threats posed by AI.', metadata={}),\n", - " Document(page_content=\"In his follow-up to 'Symbiosis', Prof. Sterling takes a look at the subtle, unnoticed presence and influence of AI in our everyday lives. It reveals how AI has become woven into our routines, often without our explicit realization.\", metadata={}),\n", - " Document(page_content='Prof. Sterling explores the potential for harmonious coexistence between humans and artificial intelligence. The book discusses how AI can be integrated into society in a beneficial and non-disruptive manner.', metadata={})]" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "retriever.invoke(\"the ethical implications of AI\")" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "d0c5bb4d", - "metadata": {}, - "source": [ - "Do a hybrid search with where filter:" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "b2bc87c1", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[Document(page_content='Prof. Sterling explores the potential for harmonious coexistence between humans and artificial intelligence. The book discusses how AI can be integrated into society in a beneficial and non-disruptive manner.', metadata={}),\n", - " Document(page_content=\"In his follow-up to 'Symbiosis', Prof. Sterling takes a look at the subtle, unnoticed presence and influence of AI in our everyday lives. It reveals how AI has become woven into our routines, often without our explicit realization.\", metadata={})]" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "retriever.invoke(\n", - " \"AI integration in society\",\n", - " where_filter={\n", - " \"path\": [\"author\"],\n", - " \"operator\": \"Equal\",\n", - " \"valueString\": \"Prof. Jonathan K. Sterling\",\n", - " },\n", - ")" - ] - }, - { - "cell_type": "markdown", - "id": "5ae2899e", - "metadata": {}, - "source": [ - "Do a hybrid search with scores:" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "4fffd0af", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[Document(page_content='Prof. Sterling explores the potential for harmonious coexistence between humans and artificial intelligence. The book discusses how AI can be integrated into society in a beneficial and non-disruptive manner.', metadata={'_additional': {'explainScore': '(bm25)\\n(hybrid) Document eeb9fd9b-a3ac-4d60-a55b-a63a25d3b907 contributed 0.00819672131147541 to the score\\n(hybrid) Document eeb9fd9b-a3ac-4d60-a55b-a63a25d3b907 contributed 0.00819672131147541 to the score', 'score': '0.016393442'}}),\n", - " Document(page_content=\"In his follow-up to 'Symbiosis', Prof. Sterling takes a look at the subtle, unnoticed presence and influence of AI in our everyday lives. It reveals how AI has become woven into our routines, often without our explicit realization.\", metadata={'_additional': {'explainScore': '(bm25)\\n(hybrid) Document b83765f2-e5d2-471f-8c02-c3350ade4c4f contributed 0.0078125 to the score\\n(hybrid) Document b83765f2-e5d2-471f-8c02-c3350ade4c4f contributed 0.008064516129032258 to the score', 'score': '0.015877016'}}),\n", - " Document(page_content='In her second book, Dr. Simmons delves deeper into the ethical considerations surrounding AI development and deployment. It is an eye-opening examination of the dilemmas faced by developers, policymakers, and society at large.', metadata={'_additional': {'explainScore': '(bm25)\\n(hybrid) Document 7ebbdae7-1061-445f-a046-1989f2343d8f contributed 0.008064516129032258 to the score\\n(hybrid) Document 7ebbdae7-1061-445f-a046-1989f2343d8f contributed 0.0078125 to the score', 'score': '0.015877016'}}),\n", - " Document(page_content='A comprehensive analysis of the evolution of artificial intelligence, from its inception to its future prospects. Dr. Simmons covers ethical considerations, potentials, and threats posed by AI.', metadata={'_additional': {'explainScore': '(vector) [-0.0071824766 -0.0006682752 0.001723625 -0.01897258 -0.0045127636 0.0024410256 -0.020503938 0.013768672 0.009520169 -0.037972264]... \\n(hybrid) Document 3a27b0a5-8dbb-4fee-9eba-8b6bc2c252be contributed 0.007936507936507936 to the score', 'score': '0.007936508'}})]" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "retriever.invoke(\n", - " \"AI integration in society\",\n", - " score=True,\n", - ")" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.12" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/docs/docs/integrations/text_embedding/ibm_watsonx.ipynb b/docs/docs/integrations/text_embedding/ibm_watsonx.ipynb index b3ebe32f2e09c..a5720b91144f8 100644 --- a/docs/docs/integrations/text_embedding/ibm_watsonx.ipynb +++ b/docs/docs/integrations/text_embedding/ibm_watsonx.ipynb @@ -327,7 +327,7 @@ ], "metadata": { "kernelspec": { - "display_name": "langchain", + "display_name": "langchain_ibm", "language": "python", "name": "python3" }, diff --git a/docs/docs/integrations/text_embedding/model2vec.ipynb b/docs/docs/integrations/text_embedding/model2vec.ipynb new file mode 100644 index 0000000000000..d8ef5d357050a --- /dev/null +++ b/docs/docs/integrations/text_embedding/model2vec.ipynb @@ -0,0 +1,201 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "e8712110", + "metadata": {}, + "source": [ + "## Overview\n", + "\n", + "Model2Vec is a technique to turn any sentence transformer into a really small static model\n", + "[model2vec](https://github.com/MinishLab/model2vec) can be used to generate embeddings." + ] + }, + { + "cell_type": "markdown", + "id": "266dd424", + "metadata": {}, + "source": [ + "## Setup\n", + "\n", + "```bash\n", + "pip install -U langchain-community\n", + "```\n" + ] + }, + { + "cell_type": "markdown", + "id": "78ab91a6", + "metadata": {}, + "source": [ + "## Instantiation" + ] + }, + { + "cell_type": "markdown", + "id": "d06e7719", + "metadata": {}, + "source": [ + "Ensure that `model2vec` is installed\n", + "\n", + "```bash\n", + "pip install -U model2vec\n", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "f8ea1ed5", + "metadata": {}, + "source": [ + "## Indexing and Retrieval" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "d25dc22d-b656-46c6-a42d-eace958590cd", + "metadata": { + "ExecuteTime": { + "end_time": "2023-05-24T15:13:17.176956Z", + "start_time": "2023-05-24T15:13:15.399076Z" + }, + "execution": { + "iopub.execute_input": "2024-03-29T15:39:19.252281Z", + "iopub.status.busy": "2024-03-29T15:39:19.252101Z", + "iopub.status.idle": "2024-03-29T15:39:19.339106Z", + "shell.execute_reply": "2024-03-29T15:39:19.338614Z", + "shell.execute_reply.started": "2024-03-29T15:39:19.252260Z" + } + }, + "outputs": [], + "source": [ + "from langchain_community.embeddings import Model2vecEmbeddings" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "8397b91f-a1f9-4be6-a699-fedaada7c37a", + "metadata": { + "ExecuteTime": { + "end_time": "2023-05-24T15:13:17.193751Z", + "start_time": "2023-05-24T15:13:17.182053Z" + }, + "execution": { + "iopub.execute_input": "2024-03-29T15:39:19.901573Z", + "iopub.status.busy": "2024-03-29T15:39:19.900935Z", + "iopub.status.idle": "2024-03-29T15:39:19.906540Z", + "shell.execute_reply": "2024-03-29T15:39:19.905345Z", + "shell.execute_reply.started": "2024-03-29T15:39:19.901529Z" + } + }, + "outputs": [], + "source": [ + "embeddings = Model2vecEmbeddings(\"minishlab/potion-base-8M\")" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "abcf98b7-424c-4691-a1cd-862c3d53be11", + "metadata": { + "ExecuteTime": { + "end_time": "2023-05-24T15:13:17.844903Z", + "start_time": "2023-05-24T15:13:17.198751Z" + }, + "execution": { + "iopub.execute_input": "2024-03-29T15:39:20.434581Z", + "iopub.status.busy": "2024-03-29T15:39:20.433117Z", + "iopub.status.idle": "2024-03-29T15:39:22.178650Z", + "shell.execute_reply": "2024-03-29T15:39:22.176058Z", + "shell.execute_reply.started": "2024-03-29T15:39:20.434501Z" + }, + "scrolled": true + }, + "outputs": [], + "source": [ + "query_text = \"This is a test query.\"\n", + "query_result = embeddings.embed_query(query_text)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "98897454-b280-4ee1-bbb9-2c6c15342f87", + "metadata": { + "ExecuteTime": { + "end_time": "2023-05-24T15:13:18.605339Z", + "start_time": "2023-05-24T15:13:17.845906Z" + }, + "execution": { + "iopub.execute_input": "2024-03-29T15:39:28.164009Z", + "iopub.status.busy": "2024-03-29T15:39:28.161759Z", + "iopub.status.idle": "2024-03-29T15:39:30.217232Z", + "shell.execute_reply": "2024-03-29T15:39:30.215348Z", + "shell.execute_reply.started": "2024-03-29T15:39:28.163876Z" + }, + "scrolled": true + }, + "outputs": [], + "source": [ + "document_text = \"This is a test document.\"\n", + "document_result = embeddings.embed_documents([document_text])" + ] + }, + { + "cell_type": "markdown", + "id": "11bac134", + "metadata": {}, + "source": [ + "## Direct Usage\n", + "\n", + "Here's how you would directly make use of `model2vec`\n", + "\n", + "```python\n", + "from model2vec import StaticModel\n", + "\n", + "# Load a model from the HuggingFace hub (in this case the potion-base-8M model)\n", + "model = StaticModel.from_pretrained(\"minishlab/potion-base-8M\")\n", + "\n", + "# Make embeddings\n", + "embeddings = model.encode([\"It's dangerous to go alone!\", \"It's a secret to everybody.\"])\n", + "\n", + "# Make sequences of token embeddings\n", + "token_embeddings = model.encode_as_sequence([\"It's dangerous to go alone!\", \"It's a secret to everybody.\"])\n", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "d81e21aa", + "metadata": {}, + "source": [ + "## API Reference\n", + "\n", + "For more information check out the model2vec github [repo](https://github.com/MinishLab/model2vec)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/docs/integrations/tools/linkup_search.ipynb b/docs/docs/integrations/tools/linkup_search.ipynb new file mode 100644 index 0000000000000..83126f0e3cbde --- /dev/null +++ b/docs/docs/integrations/tools/linkup_search.ipynb @@ -0,0 +1,303 @@ +{ + "cells": [ + { + "cell_type": "raw", + "id": "10238e62-3465-4973-9279-606cbb7ccf16", + "metadata": {}, + "source": [ + "---\n", + "sidebar_label: LinkupSearchTool\n", + "---" + ] + }, + { + "cell_type": "markdown", + "id": "a6f91f20", + "metadata": {}, + "source": [ + "# LinkupSearchTool\n", + "\n", + "> [Linkup](https://www.linkup.so/) provides an API to connect LLMs to the web and the Linkup Premium Partner sources.\n", + "\n", + "This notebook provides a quick overview for getting started with LinkupSearchTool [tool](/docs/concepts/tools/). For detailed documentation of all LinkupSearchTool features and configurations head to the [API reference](https://python.langchain.com/api_reference/linkup/tools/linkup_langchain.search_tool.LinkupSearchTool.html).\n", + "\n", + "## Overview\n", + "\n", + "### Integration details\n", + "\n", + "| Class | Package | Serializable | [JS support](https://js.langchain.com/docs/integrations/tools/linkup_search) | Package latest |\n", + "| :--- | :--- | :---: | :---: | :---: |\n", + "| [LinkupSearchTool](https://python.langchain.com/api_reference/linkup/tools/linkup_langchain.search_tool.LinkupSearchTool.html) | [langchain-linkup](https://python.langchain.com/api_reference/linkup/index.html) | ❌ | ❌ | ![PyPI - Version](https://img.shields.io/pypi/v/langchain-linkup?style=flat-square&label=%20) |\n", + "\n", + "## Setup\n", + "\n", + "To use the Linkup provider, you need a valid API key, which you can find by signing-up [here](https://app.linkup.so/sign-up). To run the following examples you will also need an OpenAI API key." + ] + }, + { + "cell_type": "markdown", + "id": "fa3318d2-108e-41d1-81b3-01ba4f47e952", + "metadata": {}, + "source": [ + "### Installation\n", + "\n", + "This tool lives in the `langchain-linkup` package:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f85b4089", + "metadata": {}, + "outputs": [], + "source": [ + "%pip install -qU langchain-linkup" + ] + }, + { + "cell_type": "markdown", + "id": "b15e9266", + "metadata": {}, + "source": [ + "### Credentials" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e0b178a2-8816-40ca-b57c-ccdd86dde9c9", + "metadata": {}, + "outputs": [], + "source": [ + "import getpass\n", + "import os\n", + "\n", + "# if not os.environ.get(\"LINKUP_API_KEY\"):\n", + "# os.environ[\"LINKUP_API_KEY\"] = getpass.getpass(\"LINKUP API key:\\n\")" + ] + }, + { + "cell_type": "markdown", + "id": "bc5ab717-fd27-4c59-b912-bdd099541478", + "metadata": {}, + "source": [ + "It's also helpful (but not needed) to set up [LangSmith](https://smith.langchain.com/) for best-in-class observability:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a6c2f136-6367-4f1f-825d-ae741e1bf281", + "metadata": {}, + "outputs": [], + "source": [ + "# os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n", + "# os.environ[\"LANGCHAIN_API_KEY\"] = getpass.getpass()" + ] + }, + { + "cell_type": "markdown", + "id": "1c97218f-f366-479d-8bf7-fe9f2f6df73f", + "metadata": {}, + "source": [ + "## Instantiation\n", + "\n", + "Here we show how to instantiate an instance of the LinkupSearchTool tool, with " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8b3ddfe9-ca79-494c-a7ab-1f56d9407a64", + "metadata": {}, + "outputs": [], + "source": [ + "from langchain_linkup import LinkupSearchTool\n", + "\n", + "tool = LinkupSearchTool(\n", + " depth=\"deep\", # \"standard\" or \"deep\"\n", + " output_type=\"searchResults\", # \"searchResults\", \"sourcedAnswer\" or \"structured\"\n", + " linkup_api_key=None, # API key can be passed here or set as the LINKUP_API_KEY environment variable\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "74147a1a", + "metadata": {}, + "source": [ + "## Invocation\n", + "\n", + "### Invoke directly with args\n", + "\n", + "The tool simply accepts a `query`, which is a string." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "65310a8b-eb0c-4d9e-a618-4f4abe2414fc", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "LinkupSearchResults(results=[LinkupSearchResult(name='US presidential election results 2024: Harris vs. Trump | Live maps ...', url='https://www.reuters.com/graphics/USA-ELECTION/RESULTS/zjpqnemxwvx/', content='Updated results from the 2024 election for the US president. Reuters live coverage of the 2024 US President, Senate, House and state governors races.'), LinkupSearchResult(name='Election 2024: Presidential results - CNN', url='https://www.cnn.com/election/2024/results/president', content='View maps and real-time results for the 2024 US presidential election matchup between former President Donald Trump and Vice President Kamala Harris. For more ...'), LinkupSearchResult(name='Presidential Election 2024 Live Results: Donald Trump wins - NBC News', url='https://www.nbcnews.com/politics/2024-elections/president-results', content='View live election results from the 2024 presidential race as Kamala Harris and Donald Trump face off. See the map of votes by state as results are tallied.'), LinkupSearchResult(name='Live: Presidential Election Results 2024 : NPR', url='https://apps.npr.org/2024-election-results/', content='Presidential race ratings are based on NPR analysis. Maps do not shade in until 50% of the estimated vote is in for a given state, to mitigate flutuations in early returns . 2024 General Election Results'), LinkupSearchResult(name='2024 US Presidential Election Results: Live Map - Bloomberg.com', url='https://www.bloomberg.com/graphics/2024-us-election-results/', content='US Presidential Election Results November 5, 2024. Bloomberg News is reporting live election results in the presidential race between Democratic Vice President Kamala Harris and her Republican ...'), LinkupSearchResult(name='US Presidential Election Results 2024 - BBC News', url='https://www.bbc.com/news/election/2024/us/results', content='Kamala Harris of the Democrat party has 74,470,899 votes (48.3%) Donald Trump of the Republican party has 76,971,602 votes (49.9%) This map of the US states was filled in as presidential results ...'), LinkupSearchResult(name='Election Results 2024: Live Map - Races by State - POLITICO', url='https://www.politico.com/2024-election/results/', content='Live 2024 election results and maps by state. POLITICO’s real-time coverage of 2024 races for President, Senate, House and Governor.'), LinkupSearchResult(name='Presidential Election Results 2024: Electoral Votes & Map by State ...', url='https://www.politico.com/2024-election/results/president/', content='Live 2024 Presidential election results, maps and electoral votes by state. POLITICO’s real-time coverage of 2024 races for President, Senate, House and Governor.'), LinkupSearchResult(name='2024 US Presidential Election Results: Live Map - ABC News', url='https://abcnews.go.com/Elections/2024-us-presidential-election-results-live-map/', content='View live updates on electoral votes by state for presidential candidates Joe Biden and Donald Trump on ABC News. Senate, House, and Governor Election results also available at ABCNews.com'), LinkupSearchResult(name='US Presidential Election Results 2024 - BBC News', url='https://www.bbc.co.uk/news/election/2024/us/results', content='Follow the 2024 US presidential election results as they come in with BBC News. Find out if Trump or Harris is ahead as well as detailed state-by-state results.'), LinkupSearchResult(name='Presidential Election 2024 Live Results: Donald Trump winsNBC News LogoSearchSearchNBC News LogoMSNBC LogoToday Logo', url='https://www.nbcnews.com/politics/2024-elections/president-results', content=\"Profile\\n\\nSections\\n\\nLocal\\n\\ntv\\n\\nFeatured\\n\\nMore From NBC\\n\\nFollow NBC News\\n\\nnews Alerts\\n\\nThere are no new alerts at this time\\n\\n2024 President Results: Trump wins\\n==================================\\n\\nDonald Trump has secured more than the 270 Electoral College votes needed to secure the presidency, NBC News projects.\\n\\nRaces to watch\\n--------------\\n\\nAll Presidential races\\n----------------------\\n\\nElection Night Coverage\\n-----------------------\\n\\n### China competition should be top priority for Trump, Sullivan says, as Biden and Xi prepare for final meeting\\n\\n### Jim Himes says 'truth and analysis are not what drive’ Gabbard and Gaetz\\n\\n### Trump praises RFK Jr. in Mar-a-Lago remarks\\n\\n### Trump announces North Dakota Gov. Doug Burgum as his pick for interior secretary\\n\\n### House Ethics Committee cancels meeting at which Gaetz probe was on the agenda\\n\\n### Trump picks former Rep. Doug Collins for veterans affairs secretary\\n\\n### Trump to nominate his criminal defense lawyer for deputy attorney general\\n\\n### From ‘brilliant’ to ‘dangerous’: Mixed reactions roll in after Trump picks RFK Jr. for top health post\\n\\n### Donald Trump Jr. says he played key role in RFK Jr., Tulsi Gabbard picks\\n\\n### Jared Polis offers surprising words of support for RFK Jr. pick for HHS secretary\\n\\nNational early voting\\n---------------------\\n\\n### 88,233,886 mail-in and early in-person votes cast nationally\\n\\n### 65,676,748 mail-in and early in-person votes requested nationally\\n\\nPast Presidential Elections\\n---------------------------\\n\\n### Vote Margin by State in the 2020 Presidential Election\\n\\nCircle size represents the number electoral votes in that state.\\n\\nThe expected vote is the total number of votes that are expected in a given race once all votes are counted. This number is an estimate and is based on several different factors, including information on the number of votes cast early as well as information provided to our vote reporters on Election Day from county election officials. The figure can change as NBC News gathers new information.\\n\\n**Source**: [National Election Pool (NEP)](https://www.nbcnews.com/politics/2024-elections/how-election-data-is-collected )\\n\\n2024 election results\\n---------------------\\n\\nElection Night Coverage\\n-----------------------\\n\\n### China competition should be top priority for Trump, Sullivan says, as Biden and Xi prepare for final meeting\\n\\n### Jim Himes says 'truth and analysis are not what drive’ Gabbard and Gaetz\\n\\n### Trump praises RFK Jr. in Mar-a-Lago remarks\\n\\n©\\xa02024 NBCUniversal Media, LLC\")])" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "tool.invoke({\"query\": \"Who won the latest US presidential elections?\"})" + ] + }, + { + "cell_type": "markdown", + "id": "d6e73897", + "metadata": {}, + "source": [ + "### Invoke with ToolCall\n", + "\n", + "We can also invoke the tool with a model-generated ToolCall, in which case a ToolMessage will be returned:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "f90e33a7", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "ToolMessage(content='results=[LinkupSearchResult(name=\\'US presidential election results 2024: Harris vs. Trump | Live maps ...\\', url=\\'https://www.reuters.com/graphics/USA-ELECTION/RESULTS/zjpqnemxwvx/\\', content=\\'Updated results from the 2024 election for the US president. Reuters live coverage of the 2024 US President, Senate, House and state governors races.\\'), LinkupSearchResult(name=\\'Election 2024: Presidential results - CNN\\', url=\\'https://www.cnn.com/election/2024/results/president\\', content=\\'View maps and real-time results for the 2024 US presidential election matchup between former President Donald Trump and Vice President Kamala Harris. For more ...\\'), LinkupSearchResult(name=\\'Presidential Election 2024 Live Results: Donald Trump wins - NBC News\\', url=\\'https://www.nbcnews.com/politics/2024-elections/president-results\\', content=\\'View live election results from the 2024 presidential race as Kamala Harris and Donald Trump face off. See the map of votes by state as results are tallied.\\'), LinkupSearchResult(name=\\'2024 US Presidential Election Results: Live Map - Bloomberg.com\\', url=\\'https://www.bloomberg.com/graphics/2024-us-election-results/\\', content=\\'US Presidential Election Results November 5, 2024. Bloomberg News is reporting live election results in the presidential race between Democratic Vice President Kamala Harris and her Republican ...\\'), LinkupSearchResult(name=\\'US Presidential Election Results 2024 - BBC News\\', url=\\'https://www.bbc.com/news/election/2024/us/results\\', content=\\'Kamala Harris of the Democrat party has 74,498,303 votes (48.3%) Donald Trump of the Republican party has 76,989,499 votes (49.9%) This map of the US states was filled in as presidential results ...\\'), LinkupSearchResult(name=\\'Presidential Election Results 2024: Electoral Votes & Map by State ...\\', url=\\'https://www.politico.com/2024-election/results/president/\\', content=\\'Live 2024 Presidential election results, maps and electoral votes by state. POLITICO’s real-time coverage of 2024 races for President, Senate, House and Governor.\\'), LinkupSearchResult(name=\\'2024 U.S. Election: Live Results and Maps - USA TODAY\\', url=\\'https://www.usatoday.com/elections/results/2024-11-05\\', content=\\'See who is winning races in the Nov. 5, 2024 U.S. Election with real-time results and state-by-state maps.\\'), LinkupSearchResult(name=\\'Donald Trump wins US presidency - US election 2024 complete results map\\', url=\\'https://www.aljazeera.com/us-election-2024/results/\\', content=\\'Complete, state-by-state breakdown of the 2024 US presidential, Senate, House and Governor results\\'), LinkupSearchResult(name=\\'US Presidential Election Results 2024 - BBC News\\', url=\\'https://www.bbc.co.uk/news/election/2024/us/results\\', content=\\'Follow the 2024 US presidential election results as they come in with BBC News. Find out if Trump or Harris is ahead as well as detailed state-by-state results.\\'), LinkupSearchResult(name=\\'Election Results 2024: Live Map - Races by State - POLITICO\\', url=\\'https://www.politico.com/2024-election/results/\\', content=\\'Live 2024 election results and maps by state. POLITICO’s real-time coverage of 2024 races for President, Senate, House and Governor.\\'), LinkupSearchResult(name=\\'Presidential Election 2024 Live Results: Donald Trump winsNBC News LogoSearchSearchNBC News LogoMSNBC LogoToday Logo\\', url=\\'https://www.nbcnews.com/politics/2024-elections/president-results\\', content=\"Profile\\\\n\\\\nSections\\\\n\\\\nLocal\\\\n\\\\ntv\\\\n\\\\nFeatured\\\\n\\\\nMore From NBC\\\\n\\\\nFollow NBC News\\\\n\\\\nnews Alerts\\\\n\\\\nThere are no new alerts at this time\\\\n\\\\n2024 President Results: Trump wins\\\\n==================================\\\\n\\\\nDonald Trump has secured more than the 270 Electoral College votes needed to secure the presidency, NBC News projects.\\\\n\\\\nRaces to watch\\\\n--------------\\\\n\\\\nAll Presidential races\\\\n----------------------\\\\n\\\\nElection Night Coverage\\\\n-----------------------\\\\n\\\\n### China competition should be top priority for Trump, Sullivan says, as Biden and Xi prepare for final meeting\\\\n\\\\n### Jim Himes says \\'truth and analysis are not what drive’ Gabbard and Gaetz\\\\n\\\\n### Trump praises RFK Jr. in Mar-a-Lago remarks\\\\n\\\\n### Trump announces North Dakota Gov. Doug Burgum as his pick for interior secretary\\\\n\\\\n### House Ethics Committee cancels meeting at which Gaetz probe was on the agenda\\\\n\\\\n### Trump picks former Rep. Doug Collins for veterans affairs secretary\\\\n\\\\n### Trump to nominate his criminal defense lawyer for deputy attorney general\\\\n\\\\n### From ‘brilliant’ to ‘dangerous’: Mixed reactions roll in after Trump picks RFK Jr. for top health post\\\\n\\\\n### Donald Trump Jr. says he played key role in RFK Jr., Tulsi Gabbard picks\\\\n\\\\n### Jared Polis offers surprising words of support for RFK Jr. pick for HHS secretary\\\\n\\\\nNational early voting\\\\n---------------------\\\\n\\\\n### 88,233,886 mail-in and early in-person votes cast nationally\\\\n\\\\n### 65,676,748 mail-in and early in-person votes requested nationally\\\\n\\\\nPast Presidential Elections\\\\n---------------------------\\\\n\\\\n### Vote Margin by State in the 2020 Presidential Election\\\\n\\\\nCircle size represents the number electoral votes in that state.\\\\n\\\\nThe expected vote is the total number of votes that are expected in a given race once all votes are counted. This number is an estimate and is based on several different factors, including information on the number of votes cast early as well as information provided to our vote reporters on Election Day from county election officials. The figure can change as NBC News gathers new information.\\\\n\\\\n**Source**: [National Election Pool (NEP)](https://www.nbcnews.com/politics/2024-elections/how-election-data-is-collected )\\\\n\\\\n2024 election results\\\\n---------------------\\\\n\\\\nElection Night Coverage\\\\n-----------------------\\\\n\\\\n### China competition should be top priority for Trump, Sullivan says, as Biden and Xi prepare for final meeting\\\\n\\\\n### Jim Himes says \\'truth and analysis are not what drive’ Gabbard and Gaetz\\\\n\\\\n### Trump praises RFK Jr. in Mar-a-Lago remarks\\\\n\\\\n©\\\\xa02024 NBCUniversal Media, LLC\")]', name='linkup', tool_call_id='1')" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# This is usually generated by a model, but we'll create a tool call directly for demo purposes.\n", + "model_generated_tool_call = {\n", + " \"args\": {\"query\": \"Who won the latest US presidential elections?\"},\n", + " \"id\": \"1\",\n", + " \"name\": tool.name,\n", + " \"type\": \"tool_call\",\n", + "}\n", + "tool.invoke(model_generated_tool_call)" + ] + }, + { + "cell_type": "markdown", + "id": "659f9fbd-6fcf-445f-aa8c-72d8e60154bd", + "metadata": {}, + "source": [ + "## Chaining\n", + "\n", + "We can use our tool in a chain by first binding it to a [tool-calling model](/docs/how_to/tool_calling/) and then calling it:\n", + "\n", + "import ChatModelTabs from \"@theme/ChatModelTabs\";\n", + "\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "af3123ad-7a02-40e5-b58e-7d56e23e5830", + "metadata": {}, + "outputs": [], + "source": [ + "# | output: false\n", + "# | echo: false\n", + "\n", + "# !pip install -qU langchain langchain-openai\n", + "from langchain.chat_models import init_chat_model\n", + "\n", + "llm = init_chat_model(model=\"gpt-4o\", model_provider=\"openai\")" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "fdbf35b5-3aaf-4947-9ec6-48c21533fb95", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_JcHj0XLARWRnwrrLhUoBjOV1', 'function': {'arguments': '{\"query\":\"2016 US presidential election winner\"}', 'name': 'linkup'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 10, 'prompt_tokens': 1037, 'total_tokens': 1047, 'completion_tokens_details': {'audio_tokens': 0, 'reasoning_tokens': 0, 'accepted_prediction_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-2024-08-06', 'system_fingerprint': 'fp_831e067d82', 'finish_reason': 'stop', 'logprobs': None}, id='run-cd7642ed-4509-4c96-8934-20bd0b986c3f-0', tool_calls=[{'name': 'linkup', 'args': {'query': '2016 US presidential election winner'}, 'id': 'call_JcHj0XLARWRnwrrLhUoBjOV1', 'type': 'tool_call'}], usage_metadata={'input_tokens': 1037, 'output_tokens': 10, 'total_tokens': 1047, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from langchain_core.prompts import ChatPromptTemplate\n", + "from langchain_core.runnables import RunnableConfig, chain\n", + "\n", + "prompt = ChatPromptTemplate(\n", + " [\n", + " (\"system\", \"You are a helpful assistant.\"),\n", + " (\"human\", \"{user_input}\"),\n", + " (\"placeholder\", \"{messages}\"),\n", + " ]\n", + ")\n", + "\n", + "# specifying tool_choice will force the model to call this tool.\n", + "llm_with_tools = llm.bind_tools([tool], tool_choice=tool.name)\n", + "\n", + "llm_chain = prompt | llm_with_tools\n", + "\n", + "\n", + "@chain\n", + "def tool_chain(user_input: str, config: RunnableConfig):\n", + " input_ = {\"user_input\": user_input}\n", + " ai_msg = llm_chain.invoke(input_, config=config)\n", + " tool_msgs = tool.batch(ai_msg.tool_calls, config=config)\n", + " return llm_chain.invoke({**input_, \"messages\": [ai_msg, *tool_msgs]}, config=config)\n", + "\n", + "\n", + "tool_chain.invoke(\"Who won the 2016 US presidential elections?\")" + ] + }, + { + "cell_type": "markdown", + "id": "4ac8146c", + "metadata": {}, + "source": [ + "## API reference\n", + "\n", + "For detailed documentation of all LinkupSearchTool features and configurations head to the [API reference](https://python.langchain.com/api_reference/linkup/tools/linkup_langchain.search_tool.LinkupSearchTool.html)." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/docs/integrations/tools/scrapegraph.ipynb b/docs/docs/integrations/tools/scrapegraph.ipynb new file mode 100644 index 0000000000000..ed9b37d97bd34 --- /dev/null +++ b/docs/docs/integrations/tools/scrapegraph.ipynb @@ -0,0 +1,380 @@ +{ + "cells": [ + { + "cell_type": "raw", + "id": "10238e62-3465-4973-9279-606cbb7ccf16", + "metadata": {}, + "source": [ + "---\n", + "sidebar_label: ScrapeGraph\n", + "---" + ] + }, + { + "cell_type": "markdown", + "id": "a6f91f20", + "metadata": {}, + "source": [ + "# ScrapeGraph\n", + "\n", + "This notebook provides a quick overview for getting started with ScrapeGraph [tools](/docs/integrations/tools/). For detailed documentation of all ScrapeGraph features and configurations head to the [API reference](https://python.langchain.com/docs/integrations/tools/scrapegraph).\n", + "\n", + "For more information about ScrapeGraph AI:\n", + "- [ScrapeGraph AI Website](https://scrapegraphai.com)\n", + "- [Open Source Project](https://github.com/ScrapeGraphAI/Scrapegraph-ai)\n", + "\n", + "## Overview\n", + "\n", + "### Integration details\n", + "\n", + "| Class | Package | Serializable | JS support | Package latest |\n", + "| :--- | :--- | :---: | :---: | :---: |\n", + "| [SmartScraperTool](https://python.langchain.com/docs/integrations/tools/scrapegraph) | langchain-scrapegraph | ✅ | ❌ | ![PyPI - Version](https://img.shields.io/pypi/v/langchain-scrapegraph?style=flat-square&label=%20) |\n", + "| [MarkdownifyTool](https://python.langchain.com/docs/integrations/tools/scrapegraph) | langchain-scrapegraph | ✅ | ❌ | ![PyPI - Version](https://img.shields.io/pypi/v/langchain-scrapegraph?style=flat-square&label=%20) |\n", + "| [LocalScraperTool](https://python.langchain.com/docs/integrations/tools/scrapegraph) | langchain-scrapegraph | ✅ | ❌ | ![PyPI - Version](https://img.shields.io/pypi/v/langchain-scrapegraph?style=flat-square&label=%20) |\n", + "| [GetCreditsTool](https://python.langchain.com/docs/integrations/tools/scrapegraph) | langchain-scrapegraph | ✅ | ❌ | ![PyPI - Version](https://img.shields.io/pypi/v/langchain-scrapegraph?style=flat-square&label=%20) |\n", + "\n", + "### Tool features\n", + "\n", + "| Tool | Purpose | Input | Output |\n", + "| :--- | :--- | :--- | :--- |\n", + "| SmartScraperTool | Extract structured data from websites | URL + prompt | JSON |\n", + "| MarkdownifyTool | Convert webpages to markdown | URL | Markdown text |\n", + "| LocalScraperTool | Extract data from HTML content | HTML + prompt | JSON |\n", + "| GetCreditsTool | Check API credits | None | Credit info |\n", + "\n", + "\n", + "## Setup\n", + "\n", + "The integration requires the following packages:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "f85b4089", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Note: you may need to restart the kernel to use updated packages.\n" + ] + } + ], + "source": [ + "%pip install --quiet -U langchain-scrapegraph" + ] + }, + { + "cell_type": "markdown", + "id": "b15e9266", + "metadata": {}, + "source": [ + "### Credentials\n", + "\n", + "You'll need a ScrapeGraph AI API key to use these tools. Get one at [scrapegraphai.com](https://scrapegraphai.com)." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "e0b178a2", + "metadata": {}, + "outputs": [], + "source": [ + "import getpass\n", + "import os\n", + "\n", + "if not os.environ.get(\"SGAI_API_KEY\"):\n", + " os.environ[\"SGAI_API_KEY\"] = getpass.getpass(\"ScrapeGraph AI API key:\\n\")" + ] + }, + { + "cell_type": "markdown", + "id": "bc5ab717", + "metadata": {}, + "source": [ + "It's also helpful (but not needed) to set up [LangSmith](https://smith.langchain.com/) for best-in-class observability:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a6c2f136", + "metadata": {}, + "outputs": [], + "source": [ + "os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n", + "os.environ[\"LANGCHAIN_API_KEY\"] = getpass.getpass()" + ] + }, + { + "cell_type": "markdown", + "id": "1c97218f", + "metadata": {}, + "source": [ + "## Instantiation\n", + "\n", + "Here we show how to instantiate instances of the ScrapeGraph tools:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "8b3ddfe9", + "metadata": {}, + "outputs": [], + "source": [ + "from langchain_scrapegraph.tools import (\n", + " GetCreditsTool,\n", + " LocalScraperTool,\n", + " MarkdownifyTool,\n", + " SmartScraperTool,\n", + ")\n", + "\n", + "smartscraper = SmartScraperTool()\n", + "markdownify = MarkdownifyTool()\n", + "localscraper = LocalScraperTool()\n", + "credits = GetCreditsTool()" + ] + }, + { + "cell_type": "markdown", + "id": "74147a1a", + "metadata": {}, + "source": [ + "## Invocation\n", + "\n", + "### [Invoke directly with args](/docs/concepts/tools)\n", + "\n", + "Let's try each tool individually:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "65310a8b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "SmartScraper Result: {'company_name': 'ScrapeGraphAI', 'description': \"ScrapeGraphAI is a powerful AI web scraping tool that turns entire websites into clean, structured data through a simple API. It's designed to help developers and AI companies extract valuable data from websites efficiently and transform it into formats that are ready for use in LLM applications and data analysis.\"}\n", + "\n", + "Markdownify Result (first 200 chars): [![ScrapeGraphAI Logo](https://scrapegraphai.com/images/scrapegraphai_logo.svg)ScrapeGraphAI](https://scrapegraphai.com/)\n", + "\n", + "PartnersPricingFAQ[Blog](https://scrapegraphai.com/blog)DocsLog inSign up\n", + "\n", + "Op\n", + "LocalScraper Result: {'company_name': 'Company Name', 'description': 'We are a technology company focused on AI solutions.', 'contact': {'email': 'contact@example.com', 'phone': '(555) 123-4567'}}\n", + "\n", + "Credits Info: {'remaining_credits': 49679, 'total_credits_used': 914}\n" + ] + } + ], + "source": [ + "# SmartScraper\n", + "result = smartscraper.invoke(\n", + " {\n", + " \"user_prompt\": \"Extract the company name and description\",\n", + " \"website_url\": \"https://scrapegraphai.com\",\n", + " }\n", + ")\n", + "print(\"SmartScraper Result:\", result)\n", + "\n", + "# Markdownify\n", + "markdown = markdownify.invoke({\"website_url\": \"https://scrapegraphai.com\"})\n", + "print(\"\\nMarkdownify Result (first 200 chars):\", markdown[:200])\n", + "\n", + "local_html = \"\"\"\n", + "\n", + " \n", + "

      Company Name

      \n", + "

      We are a technology company focused on AI solutions.

      \n", + "
      \n", + "

      Email: contact@example.com

      \n", + "

      Phone: (555) 123-4567

      \n", + "
      \n", + " \n", + "\n", + "\"\"\"\n", + "\n", + "# LocalScraper\n", + "result_local = localscraper.invoke(\n", + " {\n", + " \"user_prompt\": \"Make a summary of the webpage and extract the email and phone number\",\n", + " \"website_html\": local_html,\n", + " }\n", + ")\n", + "print(\"LocalScraper Result:\", result_local)\n", + "\n", + "# Check credits\n", + "credits_info = credits.invoke({})\n", + "print(\"\\nCredits Info:\", credits_info)" + ] + }, + { + "cell_type": "markdown", + "id": "d6e73897", + "metadata": {}, + "source": [ + "### [Invoke with ToolCall](/docs/concepts/tools)\n", + "\n", + "We can also invoke the tool with a model-generated ToolCall:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "f90e33a7", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "ToolMessage(content='{\"main_heading\": \"Get the data you need from any website\", \"description\": \"Easily extract and gather information with just a few lines of code with a simple api. Turn websites into clean and usable structured data.\"}', name='SmartScraper', tool_call_id='1')" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "model_generated_tool_call = {\n", + " \"args\": {\n", + " \"user_prompt\": \"Extract the main heading and description\",\n", + " \"website_url\": \"https://scrapegraphai.com\",\n", + " },\n", + " \"id\": \"1\",\n", + " \"name\": smartscraper.name,\n", + " \"type\": \"tool_call\",\n", + "}\n", + "smartscraper.invoke(model_generated_tool_call)" + ] + }, + { + "cell_type": "markdown", + "id": "659f9fbd", + "metadata": {}, + "source": [ + "## Chaining\n", + "\n", + "Let's use our tools with an LLM to analyze a website:\n", + "\n", + "import ChatModelTabs from \"@theme/ChatModelTabs\";\n", + "\n", + "" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "af3123ad", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Note: you may need to restart the kernel to use updated packages.\n" + ] + } + ], + "source": [ + "# | output: false\n", + "# | echo: false\n", + "\n", + "# %pip install -qU langchain langchain-openai\n", + "from langchain.chat_models import init_chat_model\n", + "\n", + "llm = init_chat_model(model=\"gpt-4o\", model_provider=\"openai\")" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "fdbf35b5", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "AIMessage(content='ScrapeGraph AI is an AI-powered web scraping tool that efficiently extracts and converts website data into structured formats via a simple API. It caters to developers, data scientists, and AI researchers, offering features like easy integration, support for dynamic content, and scalability for large projects. It supports various website types, including business, e-commerce, and educational sites. Contact: contact@scrapegraphai.com.', additional_kwargs={'tool_calls': [{'id': 'call_shkRPyjyAtfjH9ffG5rSy9xj', 'function': {'arguments': '{\"user_prompt\":\"Extract details about the products, services, and key features offered by ScrapeGraph AI, as well as any unique selling points or innovations mentioned on the website.\",\"website_url\":\"https://scrapegraphai.com\"}', 'name': 'SmartScraper'}, 'type': 'function'}], 'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 47, 'prompt_tokens': 480, 'total_tokens': 527, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-2024-08-06', 'system_fingerprint': 'fp_c7ca0ebaca', 'finish_reason': 'stop', 'logprobs': None}, id='run-45a12c86-d499-4273-8c59-0db926799bc7-0', tool_calls=[{'name': 'SmartScraper', 'args': {'user_prompt': 'Extract details about the products, services, and key features offered by ScrapeGraph AI, as well as any unique selling points or innovations mentioned on the website.', 'website_url': 'https://scrapegraphai.com'}, 'id': 'call_shkRPyjyAtfjH9ffG5rSy9xj', 'type': 'tool_call'}], usage_metadata={'input_tokens': 480, 'output_tokens': 47, 'total_tokens': 527, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from langchain_core.prompts import ChatPromptTemplate\n", + "from langchain_core.runnables import RunnableConfig, chain\n", + "\n", + "prompt = ChatPromptTemplate(\n", + " [\n", + " (\n", + " \"system\",\n", + " \"You are a helpful assistant that can use tools to extract structured information from websites.\",\n", + " ),\n", + " (\"human\", \"{user_input}\"),\n", + " (\"placeholder\", \"{messages}\"),\n", + " ]\n", + ")\n", + "\n", + "llm_with_tools = llm.bind_tools([smartscraper], tool_choice=smartscraper.name)\n", + "llm_chain = prompt | llm_with_tools\n", + "\n", + "\n", + "@chain\n", + "def tool_chain(user_input: str, config: RunnableConfig):\n", + " input_ = {\"user_input\": user_input}\n", + " ai_msg = llm_chain.invoke(input_, config=config)\n", + " tool_msgs = smartscraper.batch(ai_msg.tool_calls, config=config)\n", + " return llm_chain.invoke({**input_, \"messages\": [ai_msg, *tool_msgs]}, config=config)\n", + "\n", + "\n", + "tool_chain.invoke(\n", + " \"What does ScrapeGraph AI do? Extract this information from their website https://scrapegraphai.com\"\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "4ac8146c", + "metadata": {}, + "source": [ + "## API reference\n", + "\n", + "For detailed documentation of all ScrapeGraph features and configurations head to the Langchain API reference: https://python.langchain.com/docs/integrations/tools/scrapegraph\n", + "\n", + "Or to the official SDK repo: https://github.com/ScrapeGraphAI/langchain-scrapegraph" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.9" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/docs/integrations/tools/sql_database.ipynb b/docs/docs/integrations/tools/sql_database.ipynb index 0582040343746..cc95f06063669 100644 --- a/docs/docs/integrations/tools/sql_database.ipynb +++ b/docs/docs/integrations/tools/sql_database.ipynb @@ -192,10 +192,10 @@ { "data": { "text/plain": [ - "[QuerySQLDataBaseTool(description=\"Input to this tool is a detailed and correct SQL query, output is a result from the database. If the query is not correct, an error message will be returned. If an error is returned, rewrite the query, check the query, and try again. If you encounter an issue with Unknown column 'xxxx' in 'field list', use sql_db_schema to query the correct table fields.\", db=),\n", - " InfoSQLDatabaseTool(description='Input to this tool is a comma-separated list of tables, output is the schema and sample rows for those tables. Be sure that the tables actually exist by calling sql_db_list_tables first! Example Input: table1, table2, table3', db=),\n", - " ListSQLDatabaseTool(db=),\n", - " QuerySQLCheckerTool(description='Use this tool to double check if your query is correct before executing it. Always use this tool before executing a query with sql_db_query!', db=, llm=ChatOpenAI(client=, async_client=, temperature=0.0, openai_api_key=SecretStr('**********'), openai_proxy=''), llm_chain=LLMChain(prompt=PromptTemplate(input_variables=['dialect', 'query'], template='\\n{query}\\nDouble check the {dialect} query above for common mistakes, including:\\n- Using NOT IN with NULL values\\n- Using UNION when UNION ALL should have been used\\n- Using BETWEEN for exclusive ranges\\n- Data type mismatch in predicates\\n- Properly quoting identifiers\\n- Using the correct number of arguments for functions\\n- Casting to the correct data type\\n- Using the proper columns for joins\\n\\nIf there are any of the above mistakes, rewrite the query. If there are no mistakes, just reproduce the original query.\\n\\nOutput the final SQL query only.\\n\\nSQL Query: '), llm=ChatOpenAI(client=, async_client=, temperature=0.0, openai_api_key=SecretStr('**********'), openai_proxy='')))]" + "[QuerySQLDatabaseTool(description=\"Input to this tool is a detailed and correct SQL query, output is a result from the database. If the query is not correct, an error message will be returned. If an error is returned, rewrite the query, check the query, and try again. If you encounter an issue with Unknown column 'xxxx' in 'field list', use sql_db_schema to query the correct table fields.\", db=),\n", + " InfoSQLDatabaseTool(description='Input to this tool is a comma-separated list of tables, output is the schema and sample rows for those tables. Be sure that the tables actually exist by calling sql_db_list_tables first! Example Input: table1, table2, table3', db=),\n", + " ListSQLDatabaseTool(db=),\n", + " QuerySQLCheckerTool(description='Use this tool to double check if your query is correct before executing it. Always use this tool before executing a query with sql_db_query!', db=, llm=ChatOpenAI(client=, async_client=, root_client=, root_async_client=, temperature=0.0, model_kwargs={}, openai_api_key=SecretStr('**********')), llm_chain=LLMChain(verbose=False, prompt=PromptTemplate(input_variables=['dialect', 'query'], input_types={}, partial_variables={}, template='\\n{query}\\nDouble check the {dialect} query above for common mistakes, including:\\n- Using NOT IN with NULL values\\n- Using UNION when UNION ALL should have been used\\n- Using BETWEEN for exclusive ranges\\n- Data type mismatch in predicates\\n- Properly quoting identifiers\\n- Using the correct number of arguments for functions\\n- Casting to the correct data type\\n- Using the proper columns for joins\\n\\nIf there are any of the above mistakes, rewrite the query. If there are no mistakes, just reproduce the original query.\\n\\nOutput the final SQL query only.\\n\\nSQL Query: '), llm=ChatOpenAI(client=, async_client=, root_client=, root_async_client=, temperature=0.0, model_kwargs={}, openai_api_key=SecretStr('**********')), output_parser=StrOutputParser(), llm_kwargs={}))]" ] }, "execution_count": 4, @@ -226,7 +226,7 @@ " InfoSQLDatabaseTool,\n", " ListSQLDatabaseTool,\n", " QuerySQLCheckerTool,\n", - " QuerySQLDataBaseTool,\n", + " QuerySQLDatabaseTool,\n", ")" ] }, @@ -242,7 +242,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 6, "id": "eda12f8b-be90-4697-ac84-2ece9e2d1708", "metadata": {}, "outputs": [ @@ -265,7 +265,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 7, "id": "3470ae96-e5e5-4717-a6d6-d7d28c7b7347", "metadata": {}, "outputs": [], @@ -283,7 +283,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 8, "id": "48bca92c-9b4b-4d5c-bcce-1b239c9e901c", "metadata": {}, "outputs": [], @@ -305,7 +305,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 9, "id": "39e6d2bf-3194-4aba-854b-63faf919157b", "metadata": {}, "outputs": [ @@ -318,8 +318,8 @@ "Which country's customers spent the most?\n", "==================================\u001b[1m Ai Message \u001b[0m==================================\n", "Tool Calls:\n", - " sql_db_list_tables (call_eiheSxiL0s90KE50XyBnBtJY)\n", - " Call ID: call_eiheSxiL0s90KE50XyBnBtJY\n", + " sql_db_list_tables (call_EBPjyfzqXzFutDn8BklYACLj)\n", + " Call ID: call_EBPjyfzqXzFutDn8BklYACLj\n", " Args:\n", "=================================\u001b[1m Tool Message \u001b[0m=================================\n", "Name: sql_db_list_tables\n", @@ -327,8 +327,8 @@ "Album, Artist, Customer, Employee, Genre, Invoice, InvoiceLine, MediaType, Playlist, PlaylistTrack, Track\n", "==================================\u001b[1m Ai Message \u001b[0m==================================\n", "Tool Calls:\n", - " sql_db_schema (call_YKwGWt4UUVmxxY7vjjBDzFLJ)\n", - " Call ID: call_YKwGWt4UUVmxxY7vjjBDzFLJ\n", + " sql_db_schema (call_kGcnKpxRVFIY8dPjYIJbRoVU)\n", + " Call ID: call_kGcnKpxRVFIY8dPjYIJbRoVU\n", " Args:\n", " table_names: Customer, Invoice, InvoiceLine\n", "=================================\u001b[1m Tool Message \u001b[0m=================================\n", @@ -405,14 +405,14 @@ "*/\n", "==================================\u001b[1m Ai Message \u001b[0m==================================\n", "Tool Calls:\n", - " sql_db_query (call_7WBDcMxl1h7MnI05njx1q8V9)\n", - " Call ID: call_7WBDcMxl1h7MnI05njx1q8V9\n", + " sql_db_query (call_cTfI7OrY64FzJaDd49ILFWw7)\n", + " Call ID: call_cTfI7OrY64FzJaDd49ILFWw7\n", " Args:\n", " query: SELECT c.Country, SUM(i.Total) AS TotalSpent FROM Customer c JOIN Invoice i ON c.CustomerId = i.CustomerId GROUP BY c.Country ORDER BY TotalSpent DESC LIMIT 1\n", "=================================\u001b[1m Tool Message \u001b[0m=================================\n", "Name: sql_db_query\n", "\n", - "[('USA', 523.0600000000003)]\n", + "[('USA', 523.06)]\n", "==================================\u001b[1m Ai Message \u001b[0m==================================\n", "\n", "Customers from the USA spent the most, with a total amount spent of $523.06.\n" @@ -440,7 +440,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 10, "id": "23c1235c-6d18-43e4-98ab-85b426b53d94", "metadata": {}, "outputs": [ @@ -453,8 +453,8 @@ "Who are the top 3 best selling artists?\n", "==================================\u001b[1m Ai Message \u001b[0m==================================\n", "Tool Calls:\n", - " sql_db_query (call_9F6Bp2vwsDkeLW6FsJFqLiet)\n", - " Call ID: call_9F6Bp2vwsDkeLW6FsJFqLiet\n", + " sql_db_query (call_xAkvYiRFM7nCMKXsDNvk1OMx)\n", + " Call ID: call_xAkvYiRFM7nCMKXsDNvk1OMx\n", " Args:\n", " query: SELECT artist_name, SUM(quantity) AS total_sold FROM sales GROUP BY artist_name ORDER BY total_sold DESC LIMIT 3\n", "=================================\u001b[1m Tool Message \u001b[0m=================================\n", @@ -465,8 +465,8 @@ "(Background on this error at: https://sqlalche.me/e/20/e3q8)\n", "==================================\u001b[1m Ai Message \u001b[0m==================================\n", "Tool Calls:\n", - " sql_db_list_tables (call_Gx5adzWnrBDIIxzUDzsn83zO)\n", - " Call ID: call_Gx5adzWnrBDIIxzUDzsn83zO\n", + " sql_db_list_tables (call_K4Zvbowsq7XPgGFepbvc5G7i)\n", + " Call ID: call_K4Zvbowsq7XPgGFepbvc5G7i\n", " Args:\n", "=================================\u001b[1m Tool Message \u001b[0m=================================\n", "Name: sql_db_list_tables\n", @@ -474,8 +474,8 @@ "Album, Artist, Customer, Employee, Genre, Invoice, InvoiceLine, MediaType, Playlist, PlaylistTrack, Track\n", "==================================\u001b[1m Ai Message \u001b[0m==================================\n", "Tool Calls:\n", - " sql_db_schema (call_ftywrZgEgGWLrnk9dYC0xtZv)\n", - " Call ID: call_ftywrZgEgGWLrnk9dYC0xtZv\n", + " sql_db_schema (call_tUztueSK7VO2klZ99xT4ZVhM)\n", + " Call ID: call_tUztueSK7VO2klZ99xT4ZVhM\n", " Args:\n", " table_names: Artist, Album, InvoiceLine\n", "=================================\u001b[1m Tool Message \u001b[0m=================================\n", @@ -534,8 +534,8 @@ "*/\n", "==================================\u001b[1m Ai Message \u001b[0m==================================\n", "Tool Calls:\n", - " sql_db_query (call_i6n3lmS7E2ZivN758VOayTiy)\n", - " Call ID: call_i6n3lmS7E2ZivN758VOayTiy\n", + " sql_db_query (call_tVtLQIRPmCM6pukgpHFfq86A)\n", + " Call ID: call_tVtLQIRPmCM6pukgpHFfq86A\n", " Args:\n", " query: SELECT Artist.Name AS artist_name, SUM(InvoiceLine.Quantity) AS total_sold FROM Artist JOIN Album ON Artist.ArtistId = Album.ArtistId JOIN Track ON Album.AlbumId = Track.AlbumId JOIN InvoiceLine ON Track.TrackId = InvoiceLine.TrackId GROUP BY Artist.Name ORDER BY total_sold DESC LIMIT 3\n", "=================================\u001b[1m Tool Message \u001b[0m=================================\n", @@ -614,7 +614,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.12" + "version": "3.10.4" } }, "nbformat": 4, diff --git a/docs/docs/integrations/vectorstores/aerospike.ipynb b/docs/docs/integrations/vectorstores/aerospike.ipynb index b2ad324b0554f..17f9fda39f930 100644 --- a/docs/docs/integrations/vectorstores/aerospike.ipynb +++ b/docs/docs/integrations/vectorstores/aerospike.ipynb @@ -29,8 +29,8 @@ "metadata": {}, "outputs": [], "source": [ - "PROXIMUS_HOST = \"\"\n", - "PROXIMUS_PORT = 5000" + "AVS_HOST = \"\"\n", + "AVS_PORT = 5000" ] }, { @@ -51,7 +51,7 @@ }, "outputs": [], "source": [ - "!pip install --upgrade --quiet aerospike-vector-search==0.6.1 langchain-community sentence-transformers langchain" + "!pip install --upgrade --quiet aerospike-vector-search==3.0.1 langchain-community sentence-transformers langchain" ] }, { @@ -369,7 +369,7 @@ "from langchain_community.vectorstores import Aerospike\n", "\n", "# Here we are using the AVS host and port you configured earlier\n", - "seed = HostPort(host=PROXIMUS_HOST, port=PROXIMUS_PORT)\n", + "seed = HostPort(host=AVS_HOST, port=AVS_PORT)\n", "\n", "# The namespace of where to place our vectors. This should match the vector configured in your docstore.conf file.\n", "NAMESPACE = \"test\"\n", @@ -401,7 +401,7 @@ " vector_field=VECTOR_KEY,\n", " vector_distance_metric=MODEL_DISTANCE_CALC,\n", " dimensions=MODEL_DIM,\n", - " index_meta_data={\n", + " index_labels={\n", " \"model\": \"miniLM-L6-v2\",\n", " \"date\": \"05/04/2024\",\n", " \"dim\": str(MODEL_DIM),\n", diff --git a/docs/docs/integrations/vectorstores/azure_cosmos_db.ipynb b/docs/docs/integrations/vectorstores/azure_cosmos_db.ipynb index f4d93afa0138d..cdf70e4f42a83 100644 --- a/docs/docs/integrations/vectorstores/azure_cosmos_db.ipynb +++ b/docs/docs/integrations/vectorstores/azure_cosmos_db.ipynb @@ -38,9 +38,6 @@ "name": "stdout", "output_type": "stream", "text": [ - "\r\n", - "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m23.2.1\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m23.3.2\u001b[0m\r\n", - "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\r\n", "Note: you may need to restart the kernel to use updated packages.\n" ] } @@ -74,7 +71,7 @@ "id": "f2e66b097c6ce2e3", "metadata": {}, "source": [ - "We want to use `OpenAIEmbeddings` so we need to set up our Azure OpenAI API Key alongside other environment variables. " + "We want to use `AzureOpenAIEmbeddings` so we need to set up our Azure OpenAI API Key alongside other environment variables. " ] }, { @@ -90,15 +87,10 @@ "outputs": [], "source": [ "# Set up the OpenAI Environment Variables\n", - "os.environ[\"OPENAI_API_TYPE\"] = \"azure\"\n", - "os.environ[\"OPENAI_API_VERSION\"] = \"2023-05-15\"\n", - "os.environ[\"OPENAI_API_BASE\"] = (\n", - " \"YOUR_OPEN_AI_ENDPOINT\" # https://example.openai.azure.com/\n", - ")\n", - "os.environ[\"OPENAI_API_KEY\"] = \"YOUR_OPENAI_API_KEY\"\n", - "os.environ[\"OPENAI_EMBEDDINGS_DEPLOYMENT\"] = (\n", - " \"smart-agent-embedding-ada\" # the deployment name for the embedding model\n", - ")\n", + "\n", + "os.environ[\"AZURE_OPENAI_API_KEY\"] = \"YOUR_AZURE_OPENAI_API_KEY\"\n", + "os.environ[\"AZURE_OPENAI_ENDPOINT\"] = \"YOUR_AZURE_OPENAI_ENDPOINT\"\n", + "os.environ[\"AZURE_OPENAI_API_VERSION\"] = \"2023-05-15\"\n", "os.environ[\"OPENAI_EMBEDDINGS_MODEL_NAME\"] = \"text-embedding-ada-002\" # the model name" ] }, @@ -130,7 +122,7 @@ " CosmosDBSimilarityType,\n", " CosmosDBVectorSearchType,\n", ")\n", - "from langchain_openai import OpenAIEmbeddings\n", + "from langchain_openai import AzureOpenAIEmbeddings\n", "from langchain_text_splitters import CharacterTextSplitter\n", "\n", "SOURCE_FILE_NAME = \"../../how_to/state_of_the_union.txt\"\n", @@ -147,14 +139,35 @@ "model_name = os.getenv(\"OPENAI_EMBEDDINGS_MODEL_NAME\", \"text-embedding-ada-002\")\n", "\n", "\n", - "openai_embeddings: OpenAIEmbeddings = OpenAIEmbeddings(\n", - " deployment=model_deployment, model=model_name, chunk_size=1\n", + "openai_embeddings: AzureOpenAIEmbeddings = AzureOpenAIEmbeddings(\n", + " model=model_name, chunk_size=1\n", ")" ] }, { "cell_type": "code", "execution_count": 5, + "id": "f6c6ed80-7b91-4833-bab5-c9b2b5edcdec", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Document(metadata={'source': '../../how_to/state_of_the_union.txt'}, page_content='Madam Speaker, Madam Vice President, our First Lady and Second Gentleman. Members of Congress and the Cabinet. Justices of the Supreme Court. My fellow Americans. \\n\\nLast year COVID-19 kept us apart. This year we are finally together again. \\n\\nTonight, we meet as Democrats Republicans and Independents. But most importantly as Americans. \\n\\nWith a duty to one another to the American people to the Constitution. \\n\\nAnd with an unwavering resolve that freedom will always triumph over tyranny. \\n\\nSix days ago, Russia’s Vladimir Putin sought to shake the foundations of the free world thinking he could make it bend to his menacing ways. But he badly miscalculated. \\n\\nHe thought he could roll into Ukraine and the world would roll over. Instead he met a wall of strength he never imagined. \\n\\nHe met the Ukrainian people. \\n\\nFrom President Zelenskyy to every Ukrainian, their fearlessness, their courage, their determination, inspires the world.')" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "docs[0]" + ] + }, + { + "cell_type": "code", + "execution_count": 6, "id": "39ae6058c2f7fdf1", "metadata": { "ExecuteTime": { @@ -166,14 +179,10 @@ { "data": { "text/plain": [ - "{'raw': {'defaultShard': {'numIndexesBefore': 1,\n", - " 'numIndexesAfter': 2,\n", - " 'createdCollectionAutomatically': False,\n", - " 'ok': 1}},\n", - " 'ok': 1}" + "'\\n# DiskANN vectorstore\\nmaxDegree = 40\\ndimensions = 1536\\nsimilarity_algorithm = CosmosDBSimilarityType.COS\\nkind = CosmosDBVectorSearchType.VECTOR_DISKANN\\nlBuild = 20\\n\\nvectorstore.create_index(\\n dimensions=dimensions,\\n similarity=similarity_algorithm,\\n kind=kind ,\\n max_degree=maxDegree,\\n l_build=lBuild,\\n )\\n\\n# -----------------------------------------------------------\\n\\n# HNSW vectorstore\\ndimensions = 1536\\nsimilarity_algorithm = CosmosDBSimilarityType.COS\\nkind = CosmosDBVectorSearchType.VECTOR_HNSW\\nm = 16\\nef_construction = 64\\n\\nvectorstore.create_index(\\n dimensions=dimensions,\\n similarity=similarity_algorithm,\\n kind=kind ,\\n m=m,\\n ef_construction=ef_construction,\\n )\\n'" ] }, - "execution_count": 5, + "execution_count": 6, "metadata": {}, "output_type": "execute_result" } @@ -212,12 +221,46 @@ "\n", "vectorstore.create_index(\n", " num_lists, dimensions, similarity_algorithm, kind, m, ef_construction\n", - ")" + ")\n", + "\n", + "\"\"\"\n", + "# DiskANN vectorstore\n", + "maxDegree = 40\n", + "dimensions = 1536\n", + "similarity_algorithm = CosmosDBSimilarityType.COS\n", + "kind = CosmosDBVectorSearchType.VECTOR_DISKANN\n", + "lBuild = 20\n", + "\n", + "vectorstore.create_index(\n", + " dimensions=dimensions,\n", + " similarity=similarity_algorithm,\n", + " kind=kind ,\n", + " max_degree=maxDegree,\n", + " l_build=lBuild,\n", + " )\n", + "\n", + "# -----------------------------------------------------------\n", + "\n", + "# HNSW vectorstore\n", + "dimensions = 1536\n", + "similarity_algorithm = CosmosDBSimilarityType.COS\n", + "kind = CosmosDBVectorSearchType.VECTOR_HNSW\n", + "m = 16\n", + "ef_construction = 64\n", + "\n", + "vectorstore.create_index(\n", + " dimensions=dimensions,\n", + " similarity=similarity_algorithm,\n", + " kind=kind ,\n", + " m=m,\n", + " ef_construction=ef_construction,\n", + " )\n", + "\"\"\"" ] }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 7, "id": "32c68d3246adc21f", "metadata": { "ExecuteTime": { @@ -234,7 +277,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 8, "id": "8feeeb4364efb204", "metadata": { "ExecuteTime": { @@ -271,7 +314,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 9, "id": "3c218ab6f59301f7", "metadata": { "ExecuteTime": { @@ -308,7 +351,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 10, "id": "fd67e4d92c9ab32f", "metadata": { "ExecuteTime": { @@ -352,10 +395,106 @@ "Azure Cosmos DB for MongoDB supports pre-filtering with $lt, $lte, $eq, $neq, $gte, $gt, $in, $nin, and $regex. To use this feature, enable \"filtering vector search\" in the \"Preview Features\" tab of your Azure Subscription. Learn more about preview features [here](https://learn.microsoft.com/azure/cosmos-db/mongodb/vcore/vector-search#filtered-vector-search-preview)." ] }, + { + "cell_type": "code", + "execution_count": 29, + "id": "19c43de6-47f9-45f0-a422-8d852a5d191f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'raw': {'defaultShard': {'numIndexesBefore': 3,\n", + " 'numIndexesAfter': 4,\n", + " 'createdCollectionAutomatically': False,\n", + " 'ok': 1}},\n", + " 'ok': 1}" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# create a filter index\n", + "vectorstore.create_filter_index(\n", + " property_to_filter=\"metadata.source\", index_name=\"filter_index\"\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "c7031279-dfb8-43f2-a7a8-d10a3786023b", + "metadata": {}, + "outputs": [], + "source": [ + "query = \"What did the president say about Ketanji Brown Jackson\"\n", + "docs = vectorstore.similarity_search(\n", + " query, pre_filter={\"metadata.source\": {\"$ne\": \"filter content\"}}\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "3860be72-d293-43b9-a727-425f166ff6c6", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "4" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len(docs)" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "id": "b7fb9800-b1cf-4315-af9d-e8c572d3e05f", + "metadata": {}, + "outputs": [], + "source": [ + "docs = vectorstore.similarity_search(\n", + " query,\n", + " pre_filter={\"metadata.source\": {\"$ne\": \"../../how_to/state_of_the_union.txt\"}},\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "id": "dba9d39e-6220-4fad-84fa-e123aa7ca6e4", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len(docs)" + ] + }, { "cell_type": "code", "execution_count": null, - "id": "50bb4346", + "id": "25ea7250-6e8f-48e6-aac9-196effbdc8d8", "metadata": {}, "outputs": [], "source": [] diff --git a/docs/docs/integrations/vectorstores/sap_hanavector.ipynb b/docs/docs/integrations/vectorstores/sap_hanavector.ipynb index 90a0ed5e668e6..ded2118134bc2 100644 --- a/docs/docs/integrations/vectorstores/sap_hanavector.ipynb +++ b/docs/docs/integrations/vectorstores/sap_hanavector.ipynb @@ -22,7 +22,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": { "tags": [] }, @@ -41,7 +41,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 1, "metadata": { "ExecuteTime": { "end_time": "2023-09-09T08:02:16.802456Z", @@ -64,7 +64,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 9, "metadata": { "ExecuteTime": { "end_time": "2023-09-09T08:02:28.174088Z", @@ -73,8 +73,10 @@ }, "outputs": [], "source": [ + "from dotenv import load_dotenv\n", "from hdbcli import dbapi\n", "\n", + "load_dotenv()\n", "# Use connection settings from the environment\n", "connection = dbapi.connect(\n", " address=os.environ.get(\"HANA_DB_ADDRESS\"),\n", @@ -102,14 +104,22 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 10, "metadata": { "ExecuteTime": { "end_time": "2023-09-09T08:02:25.452472Z", "start_time": "2023-09-09T08:02:25.441563Z" } }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Number of document chunks: 88\n" + ] + } + ], "source": [ "from langchain_community.document_loaders import TextLoader\n", "from langchain_community.vectorstores.hanavector import HanaDB\n", @@ -134,7 +144,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 11, "metadata": { "ExecuteTime": { "end_time": "2023-09-09T08:04:16.696625Z", @@ -157,9 +167,20 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 12, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "[]" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# Delete already existing documents from the table\n", "db.delete(filter={})\n", @@ -178,9 +199,24 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 13, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--------------------------------------------------------------------------------\n", + "One of the most serious constitutional responsibilities a President has is nominating someone to serve on the United States Supreme Court. \n", + "\n", + "And I did that 4 days ago, when I nominated Circuit Court of Appeals Judge Ketanji Brown Jackson. One of our nation’s top legal minds, who will continue Justice Breyer’s legacy of excellence.\n", + "--------------------------------------------------------------------------------\n", + "As I said last year, especially to our younger transgender Americans, I will always have your back as your President, so you can be yourself and reach your God-given potential. \n", + "\n", + "While it often appears that we never agree, that isn’t true. I signed 80 bipartisan bills into law last year. From preventing government shutdowns to protecting Asian-Americans from still-too-common hate crimes to reforming military justice.\n" + ] + } + ], "source": [ "query = \"What did the president say about Ketanji Brown Jackson\"\n", "docs = db.similarity_search(query, k=2)\n", @@ -199,9 +235,24 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 14, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--------------------------------------------------------------------------------\n", + "One of the most serious constitutional responsibilities a President has is nominating someone to serve on the United States Supreme Court. \n", + "\n", + "And I did that 4 days ago, when I nominated Circuit Court of Appeals Judge Ketanji Brown Jackson. One of our nation’s top legal minds, who will continue Justice Breyer’s legacy of excellence.\n", + "--------------------------------------------------------------------------------\n", + "As I said last year, especially to our younger transgender Americans, I will always have your back as your President, so you can be yourself and reach your God-given potential. \n", + "\n", + "While it often appears that we never agree, that isn’t true. I signed 80 bipartisan bills into law last year. From preventing government shutdowns to protecting Asian-Americans from still-too-common hate crimes to reforming military justice.\n" + ] + } + ], "source": [ "from langchain_community.vectorstores.utils import DistanceStrategy\n", "\n", @@ -235,7 +286,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 15, "metadata": { "ExecuteTime": { "end_time": "2023-09-09T08:05:23.276819Z", @@ -246,7 +297,24 @@ "outputs_hidden": false } }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--------------------------------------------------------------------------------\n", + "One of the most serious constitutional responsibilities a President has is nominating someone to serve on the United States Supreme Court. \n", + "\n", + "And I did that 4 days ago, when I nominated Circuit Court of Appeals Judge Ketanji Brown Jackson. One of our nation’s top legal minds, who will continue Justice Breyer’s legacy of excellence.\n", + "--------------------------------------------------------------------------------\n", + "Groups of citizens blocking tanks with their bodies. Everyone from students to retirees teachers turned soldiers defending their homeland. \n", + "\n", + "In this struggle as President Zelenskyy said in his speech to the European Parliament “Light will win over darkness.” The Ukrainian Ambassador to the United States is here tonight. \n", + "\n", + "Let each of us here tonight in this Chamber send an unmistakable signal to Ukraine and to the world.\n" + ] + } + ], "source": [ "docs = db.max_marginal_relevance_search(query, k=2, fetch_k=20)\n", "for doc in docs:\n", @@ -254,6 +322,86 @@ " print(doc.page_content)" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Creating an HNSW Vector Index\n", + "\n", + "A vector index can significantly speed up top-k nearest neighbor queries for vectors. Users can create a Hierarchical Navigable Small World (HNSW) vector index using the `create_hnsw_index` function.\n", + "\n", + "For more information about creating an index at the database level, please refer to the [official documentation](https://help.sap.com/docs/hana-cloud-database/sap-hana-cloud-sap-hana-database-vector-engine-guide/create-vector-index-statement-data-definition).\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--------------------------------------------------------------------------------\n", + "One of the most serious constitutional responsibilities a President has is nominating someone to serve on the United States Supreme Court. \n", + "\n", + "And I did that 4 days ago, when I nominated Circuit Court of Appeals Judge Ketanji Brown Jackson. One of our nation’s top legal minds, who will continue Justice Breyer’s legacy of excellence.\n", + "--------------------------------------------------------------------------------\n", + "Groups of citizens blocking tanks with their bodies. Everyone from students to retirees teachers turned soldiers defending their homeland. \n", + "\n", + "In this struggle as President Zelenskyy said in his speech to the European Parliament “Light will win over darkness.” The Ukrainian Ambassador to the United States is here tonight. \n", + "\n", + "Let each of us here tonight in this Chamber send an unmistakable signal to Ukraine and to the world.\n" + ] + } + ], + "source": [ + "# HanaDB instance uses cosine similarity as default:\n", + "db_cosine = HanaDB(\n", + " embedding=embeddings, connection=connection, table_name=\"STATE_OF_THE_UNION\"\n", + ")\n", + "\n", + "# Attempting to create the HNSW index with default parameters\n", + "db_cosine.create_hnsw_index() # If no other parameters are specified, the default values will be used\n", + "# Default values: m=64, ef_construction=128, ef_search=200\n", + "# The default index name will be: STATE_OF_THE_UNION_COSINE_SIMILARITY_IDX (verify this naming pattern in HanaDB class)\n", + "\n", + "\n", + "# Creating a HanaDB instance with L2 distance as the similarity function and defined values\n", + "db_l2 = HanaDB(\n", + " embedding=embeddings,\n", + " connection=connection,\n", + " table_name=\"STATE_OF_THE_UNION\",\n", + " distance_strategy=DistanceStrategy.EUCLIDEAN_DISTANCE, # Specify L2 distance\n", + ")\n", + "\n", + "# This will create an index based on L2 distance strategy.\n", + "db_l2.create_hnsw_index(\n", + " index_name=\"STATE_OF_THE_UNION_L2_index\",\n", + " m=100, # Max number of neighbors per graph node (valid range: 4 to 1000)\n", + " ef_construction=200, # Max number of candidates during graph construction (valid range: 1 to 100000)\n", + " ef_search=500, # Min number of candidates during the search (valid range: 1 to 100000)\n", + ")\n", + "\n", + "# Use L2 index to perform MMR\n", + "docs = db_l2.max_marginal_relevance_search(query, k=2, fetch_k=20)\n", + "for doc in docs:\n", + " print(\"-\" * 80)\n", + " print(doc.page_content)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "**Key Points**:\n", + "- **Similarity Function**: The similarity function for the index is **cosine similarity** by default. If you want to use a different similarity function (e.g., `L2` distance), you need to specify it when initializing the `HanaDB` instance.\n", + "- **Default Parameters**: In the `create_hnsw_index` function, if the user does not provide custom values for parameters like `m`, `ef_construction`, or `ef_search`, the default values (e.g., `m=64`, `ef_construction=128`, `ef_search=200`) will be used automatically. These values ensure the index is created with reasonable performance without requiring user intervention.\n", + "\n" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -263,9 +411,20 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 19, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "db = HanaDB(\n", " connection=connection, embedding=embeddings, table_name=\"LANGCHAIN_DEMO_BASIC\"\n", @@ -284,9 +443,20 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 20, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "[]" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "docs = [Document(page_content=\"Some text\"), Document(page_content=\"Other docs\")]\n", "db.add_documents(docs)" @@ -301,9 +471,20 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 21, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "[]" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "docs = [\n", " Document(\n", @@ -327,9 +508,19 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 22, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--------------------------------------------------------------------------------\n", + "foo\n", + "{'start': 100, 'end': 150, 'doc_name': 'foo.txt', 'quality': 'bad'}\n" + ] + } + ], "source": [ "docs = db.similarity_search(\"foobar\", k=2, filter={\"quality\": \"bad\"})\n", "# With filtering on \"quality\"==\"bad\", only one document should be returned\n", @@ -348,9 +539,17 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 23, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0\n" + ] + } + ], "source": [ "db.delete(filter={\"quality\": \"bad\"})\n", "\n", @@ -385,7 +584,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 24, "metadata": {}, "outputs": [], "source": [ @@ -433,9 +632,30 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 25, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Filter: {'id': {'$ne': 1}}\n", + "{'name': 'bob', 'is_active': False, 'id': 2, 'height': 5.7}\n", + "{'name': 'jane', 'is_active': True, 'id': 3, 'height': 2.4}\n", + "Filter: {'id': {'$gt': 1}}\n", + "{'name': 'bob', 'is_active': False, 'id': 2, 'height': 5.7}\n", + "{'name': 'jane', 'is_active': True, 'id': 3, 'height': 2.4}\n", + "Filter: {'id': {'$gte': 1}}\n", + "{'name': 'adam', 'is_active': True, 'id': 1, 'height': 10.0}\n", + "{'name': 'bob', 'is_active': False, 'id': 2, 'height': 5.7}\n", + "{'name': 'jane', 'is_active': True, 'id': 3, 'height': 2.4}\n", + "Filter: {'id': {'$lt': 1}}\n", + "\n", + "Filter: {'id': {'$lte': 1}}\n", + "{'name': 'adam', 'is_active': True, 'id': 1, 'height': 10.0}\n" + ] + } + ], "source": [ "advanced_filter = {\"id\": {\"$ne\": 1}}\n", "print(f\"Filter: {advanced_filter}\")\n", @@ -467,9 +687,24 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 26, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Filter: {'id': {'$between': (1, 2)}}\n", + "{'name': 'adam', 'is_active': True, 'id': 1, 'height': 10.0}\n", + "{'name': 'bob', 'is_active': False, 'id': 2, 'height': 5.7}\n", + "Filter: {'name': {'$in': ['adam', 'bob']}}\n", + "{'name': 'adam', 'is_active': True, 'id': 1, 'height': 10.0}\n", + "{'name': 'bob', 'is_active': False, 'id': 2, 'height': 5.7}\n", + "Filter: {'name': {'$nin': ['adam', 'bob']}}\n", + "{'name': 'jane', 'is_active': True, 'id': 3, 'height': 2.4}\n" + ] + } + ], "source": [ "advanced_filter = {\"id\": {\"$between\": (1, 2)}}\n", "print(f\"Filter: {advanced_filter}\")\n", @@ -493,9 +728,21 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 27, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Filter: {'name': {'$like': 'a%'}}\n", + "{'name': 'adam', 'is_active': True, 'id': 1, 'height': 10.0}\n", + "Filter: {'name': {'$like': '%a%'}}\n", + "{'name': 'adam', 'is_active': True, 'id': 1, 'height': 10.0}\n", + "{'name': 'jane', 'is_active': True, 'id': 3, 'height': 2.4}\n" + ] + } + ], "source": [ "advanced_filter = {\"name\": {\"$like\": \"a%\"}}\n", "print(f\"Filter: {advanced_filter}\")\n", @@ -515,9 +762,25 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 28, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Filter: {'$or': [{'id': 1}, {'name': 'bob'}]}\n", + "{'name': 'adam', 'is_active': True, 'id': 1, 'height': 10.0}\n", + "{'name': 'bob', 'is_active': False, 'id': 2, 'height': 5.7}\n", + "Filter: {'$and': [{'id': 1}, {'id': 2}]}\n", + "\n", + "Filter: {'$or': [{'id': 1}, {'id': 2}, {'id': 3}]}\n", + "{'name': 'adam', 'is_active': True, 'id': 1, 'height': 10.0}\n", + "{'name': 'bob', 'is_active': False, 'id': 2, 'height': 5.7}\n", + "{'name': 'jane', 'is_active': True, 'id': 3, 'height': 2.4}\n" + ] + } + ], "source": [ "advanced_filter = {\"$or\": [{\"id\": 1}, {\"name\": \"bob\"}]}\n", "print(f\"Filter: {advanced_filter}\")\n", @@ -541,7 +804,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 29, "metadata": {}, "outputs": [], "source": [ @@ -574,7 +837,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 30, "metadata": {}, "outputs": [], "source": [ @@ -635,9 +898,21 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 32, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Answer from LLM:\n", + "================\n", + "The United States has set up joint patrols with Mexico and Guatemala to catch more human traffickers. This collaboration is part of the efforts to address immigration issues and secure the borders in the region.\n", + "================\n", + "Number of used source document chunks: 5\n" + ] + } + ], "source": [ "question = \"What about Mexico and Guatemala?\"\n", "\n", @@ -679,9 +954,19 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 34, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Answer from LLM:\n", + "================\n", + "Mexico and Guatemala are involved in joint patrols to catch human traffickers.\n" + ] + } + ], "source": [ "question = \"What about other countries?\"\n", "\n", @@ -711,9 +996,20 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 35, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "[]" + ] + }, + "execution_count": 35, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# Access the vector DB with a new table\n", "db = HanaDB(\n", @@ -742,9 +1038,19 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 36, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "('VEC_META', 'NCLOB')\n", + "('VEC_TEXT', 'NCLOB')\n", + "('VEC_VECTOR', 'REAL_VECTOR')\n" + ] + } + ], "source": [ "cur = connection.cursor()\n", "cur.execute(\n", @@ -795,12 +1101,23 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 39, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "None\n", + "Some other text\n", + "{\"start\": 400, \"end\": 450, \"doc_name\": \"other.txt\"}\n", + "\n" + ] + } + ], "source": [ - "# Create a new table \"MY_OWN_TABLE\" with three \"standard\" columns and one additional column\n", - "my_own_table_name = \"MY_OWN_TABLE\"\n", + "# Create a new table \"MY_OWN_TABLE_ADD\" with three \"standard\" columns and one additional column\n", + "my_own_table_name = \"MY_OWN_TABLE_ADD\"\n", "cur = connection.cursor()\n", "cur.execute(\n", " (\n", @@ -851,9 +1168,20 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 40, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--------------------------------------------------------------------------------\n", + "Some other text\n", + "--------------------------------------------------------------------------------\n", + "Some more text\n" + ] + } + ], "source": [ "docs = [\n", " Document(\n", @@ -886,9 +1214,20 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 41, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Filters on this value are very performant\n", + "Some other text\n", + "{\"start\": 400, \"end\": 450, \"doc_name\": \"other.txt\", \"CUSTOMTEXT\": \"Filters on this value are very performant\"}\n", + "\n" + ] + } + ], "source": [ "# Create a new table \"PERFORMANT_CUSTOMTEXT_FILTER\" with three \"standard\" columns and one additional column\n", "my_own_table_name = \"PERFORMANT_CUSTOMTEXT_FILTER\"\n", @@ -952,9 +1291,20 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 42, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--------------------------------------------------------------------------------\n", + "Some other text\n", + "--------------------------------------------------------------------------------\n", + "Some more text\n" + ] + } + ], "source": [ "docs = [\n", " Document(\n", @@ -994,7 +1344,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.9" + "version": "3.10.14" } }, "nbformat": 4, diff --git a/docs/docs/integrations/vectorstores/sqlserver.ipynb b/docs/docs/integrations/vectorstores/sqlserver.ipynb new file mode 100644 index 0000000000000..2e6ee2a33c950 --- /dev/null +++ b/docs/docs/integrations/vectorstores/sqlserver.ipynb @@ -0,0 +1,959 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": { + "azdata_cell_guid": "3fe4f4a9-8810-428c-90cb-147ad8563025", + "language": "python" + }, + "source": [ + "# SQLServer " + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": { + "azdata_cell_guid": "f791e7da-9710-4f15-93f0-6ea61840a25f", + "language": "python" + }, + "source": [ + ">Azure SQL provides a dedicated [Vector data type](https:\\learn.microsoft.com\\sql\\t-sql\\data-types\\vector-data-type?view=azuresqldb-current&viewFallbackFrom=sql-server-ver16&tabs=csharp-sample) that simplifies the creation, storage, and querying of vector embeddings directly within a relational database. This eliminates the need for separate vector databases and related integrations, increasing the security of your solutions while reducing the overall complexity.\n", + "\n", + "Azure SQL is a robust service that combines scalability, security, and high availability, providing all the benefits of a modern database solution. It leverages a sophisticated query optimizer and enterprise features to perform vector similarity searches alongside traditional SQL queries, enhancing data analysis and decision-making. \n", + " \n", + "Read more on using [Intelligent applications with Azure SQL Database](https://learn.microsoft.com/azure/azure-sql/database/ai-artificial-intelligence-intelligent-applications?view=azuresql)\n", + "\n", + "This notebook shows you how to leverage this integrated SQL [vector database](https://devblogs.microsoft.com/azure-sql/exciting-announcement-public-preview-of-native-vector-support-in-azure-sql-database/) to store documents and perform vector search queries using Cosine (cosine distance), L2 (Euclidean distance), and IP (inner product) to locate documents close to the query vectors" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": { + "azdata_cell_guid": "320f08b1-2fac-46fe-8e3a-273b6bf6ca8d", + "language": "python" + }, + "source": [ + "## Setup\n", + " \n", + "Install the `langchain-sqlserver` python package.\n", + "\n", + "The code lives in an integration package called:[langchain-sqlserver](https:\\github.com\\langchain-ai\\langchain-azure\\tree\\main\\libs\\sqlserver)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "azdata_cell_guid": "5fa6ff09-79d5-4023-9005-91a217f91a5b", + "language": "python" + }, + "outputs": [], + "source": [ + "!pip install langchain-sqlserver==0.1.1" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Credentials\n", + "\n", + "There are no credentials needed to run this notebook, just make sure you downloaded the `langchain_sqlserver` package\n", + "If you want to get best in-class automated tracing of your model calls you can also set your [LangSmith](https://docs.smith.langchain.com/) API key by uncommenting below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# os.environ[\"LANGSMITH_API_KEY\"] = getpass.getpass(\"Enter your LangSmith API key: \")\n", + "# os.environ[\"LANGSMITH_TRACING\"] = \"true\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Initialization" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "azdata_cell_guid": "4113da9c-b0fe-4e01-bc06-cafe05634fb6", + "language": "python" + }, + "outputs": [], + "source": [ + "from langchain_sqlserver import SQLServer_VectorStore" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": { + "azdata_cell_guid": "458deaef-f985-4efe-957c-7840509fdfa3", + "language": "python" + }, + "source": [ + "Find your Azure SQL DB connection string in the Azure portal under your database settings\n", + "\n", + "For more info: [Connect to Azure SQL DB - Python](https:\\learn.microsoft.com\\en-us\\azure\\azure-sql\\database\\connect-query-python?view=azuresql)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "azdata_cell_guid": "d3439463-899e-48aa-88a1-ba6bdedbdc9d", + "language": "python" + }, + "outputs": [], + "source": [ + "import os\n", + "\n", + "import pyodbc\n", + "\n", + "# Define your SQLServer Connection String\n", + "_CONNECTION_STRING = (\n", + " \"Driver={ODBC Driver 18 for SQL Server};\"\n", + " \"Server=.database.windows.net,1433;\"\n", + " \"Database=test;\"\n", + " \"TrustServerCertificate=yes;\"\n", + " \"Connection Timeout=60;\"\n", + " \"LongAsMax=yes;\"\n", + ")\n", + "\n", + "# Connection string can vary:\n", + "# \"mssql+pyodbc://:/?driver=ODBC+Driver+18+for+SQL+Server\" -> With Username and Password specified\n", + "# \"mssql+pyodbc:///?driver=ODBC+Driver+18+for+SQL+Server&Trusted_connection=yes\" -> Uses Trusted connection\n", + "# \"mssql+pyodbc:///?driver=ODBC+Driver+18+for+SQL+Server\" -> Uses EntraID connection\n", + "# \"mssql+pyodbc:///?driver=ODBC+Driver+18+for+SQL+Server&Trusted_connection=no\" -> Uses EntraID connection" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": { + "azdata_cell_guid": "dcbdafc3-71ec-4e73-b768-ffb49dae2aee", + "language": "python" + }, + "source": [ + "In this example we use Azure OpenAI to generate embeddings , however you can use different embeddings provided in LangChain.\n", + "\n", + "You can deploy a version of Azure OpenAI instance on Azure Portal following this [guide](https:\\learn.microsoft.com\\en-us\\azure\\ai-services\\openai\\how-to\\create-resource?pivots=web-portal). Once you have your instance running, make sure you have the name of your instance and key. You can find the key in the Azure Portal, under the \"Keys and Endpoint\" section of your instance." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "azdata_cell_guid": "a65110ff-cfa4-498c-bb7a-d937c04872c0", + "language": "python" + }, + "outputs": [], + "source": [ + "!pip install langchain-openai" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "azdata_cell_guid": "3bd306b1-f346-4c01-93f4-039827e4f2e6", + "language": "python" + }, + "outputs": [], + "source": [ + "# Import the necessary Libraries\n", + "from langchain_openai import AzureChatOpenAI, AzureOpenAIEmbeddings\n", + "\n", + "# Set your AzureOpenAI details\n", + "azure_endpoint = \"https://.openai.azure.com/\"\n", + "azure_deployment_name_embedding = \"text-embedding-3-small\"\n", + "azure_deployment_name_chatcompletion = \"chatcompletion\"\n", + "azure_api_version = \"2023-05-15\"\n", + "azure_api_key = \"YOUR_KEY\"\n", + "\n", + "\n", + "# Use AzureChatOpenAI for chat completions\n", + "llm = AzureChatOpenAI(\n", + " azure_endpoint=azure_endpoint,\n", + " azure_deployment=azure_deployment_name_chatcompletion,\n", + " openai_api_version=azure_api_version,\n", + " openai_api_key=azure_api_key,\n", + ")\n", + "\n", + "# Use AzureOpenAIEmbeddings for embeddings\n", + "embeddings = AzureOpenAIEmbeddings(\n", + " azure_endpoint=azure_endpoint,\n", + " azure_deployment=azure_deployment_name_embedding,\n", + " openai_api_version=azure_api_version,\n", + " openai_api_key=azure_api_key,\n", + ")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": { + "azdata_cell_guid": "f1f10145-06db-4cab-853f-9eb3b6fa8ada", + "language": "python" + }, + "source": [ + "## Manage vector store  " + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "azdata_cell_guid": "c4033f67-bea2-4859-af4d-b41f3b929978", + "language": "python" + }, + "outputs": [], + "source": [ + "from langchain_community.vectorstores.utils import DistanceStrategy\n", + "from langchain_sqlserver import SQLServer_VectorStore\n", + "\n", + "# Initialize the vector store\n", + "vector_store = SQLServer_VectorStore(\n", + " connection_string=_CONNECTION_STRING,\n", + " distance_strategy=DistanceStrategy.COSINE, # optional, if not provided, defaults to COSINE\n", + " embedding_function=embeddings, # you can use different embeddings provided in LangChain\n", + " embedding_length=1536,\n", + " table_name=\"langchain_test_table\", # using table with a custom name\n", + ")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": { + "azdata_cell_guid": "525f611b-2bd5-4fd4-9192-93d588c5ad0b", + "language": "python" + }, + "source": [ + "### Add items to vector store" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "azdata_cell_guid": "6410813d-0ff1-44dd-b6bb-32fd74772e4f", + "language": "python" + }, + "outputs": [], + "source": [ + "## we will use some artificial data for this example\n", + "query = [\n", + " \"I have bought several of the Vitality canned dog food products and have found them all to be of good quality. The product looks more like a stew than a processed meat and it smells better. My Labrador is finicky and she appreciates this product better than most.\",\n", + " \"The candy is just red , No flavor . Just plan and chewy . I would never buy them again\",\n", + " \"Arrived in 6 days and were so stale i could not eat any of the 6 bags!!\",\n", + " \"Got these on sale for roughly 25 cents per cup, which is half the price of my local grocery stores, plus they rarely stock the spicy flavors. These things are a GREAT snack for my office where time is constantly crunched and sometimes you can't escape for a real meal. This is one of my favorite flavors of Instant Lunch and will be back to buy every time it goes on sale.\",\n", + " \"If you are looking for a less messy version of licorice for the children, then be sure to try these! They're soft, easy to chew, and they don't get your hands all sticky and gross in the car, in the summer, at the beach, etc. We love all the flavos and sometimes mix these in with the chocolate to have a very nice snack! Great item, great price too, highly recommend!\",\n", + " \"We had trouble finding this locally - delivery was fast, no more hunting up and down the flour aisle at our local grocery stores.\",\n", + " \"Too much of a good thing? We worked this kibble in over time, slowly shifting the percentage of Felidae to national junk-food brand until the bowl was all natural. By this time, the cats couldn't keep it in or down. What a mess. We've moved on.\",\n", + " \"Hey, the description says 360 grams - that is roughly 13 ounces at under $4.00 per can. No way - that is the approximate price for a 100 gram can.\",\n", + " \"The taste of these white cheddar flat breads is like a regular cracker - which is not bad, except that I bought them because I wanted a cheese taste.

      What was a HUGE disappointment? How misleading the packaging of the box is. The photo on the box (I bought these in store) makes it look like it is full of long flatbreads (expanding the length and width of the box). Wrong! The plastic tray that holds the crackers is about 2\"\n", + " \" smaller all around - leaving you with about 15 or so small flatbreads.

      What is also bad about this is that the company states they use biodegradable and eco-friendly packaging. FAIL! They used a HUGE box for a ridiculously small amount of crackers. Not ecofriendly at all.

      Would I buy these again? No - I feel ripped off. The other crackers (like Sesame Tarragon) give you a little
      more bang for your buck and have more flavor.\",\n", + " \"I have used this product in smoothies for my son and he loves it. Additionally, I use this oil in the shower as a skin conditioner and it has made my skin look great. Some of the stretch marks on my belly has disappeared quickly. Highly recommend!!!\",\n", + " \"Been taking Coconut Oil for YEARS. This is the best on the retail market. I wish it was in glass, but this is the one.\",\n", + "]\n", + "\n", + "query_metadata = [\n", + " {\"id\": 1, \"summary\": \"Good Quality Dog Food\"},\n", + " {\"id\": 8, \"summary\": \"Nasty No flavor\"},\n", + " {\"id\": 4, \"summary\": \"stale product\"},\n", + " {\"id\": 11, \"summary\": \"Great value and convenient ramen\"},\n", + " {\"id\": 5, \"summary\": \"Great for the kids!\"},\n", + " {\"id\": 2, \"summary\": \"yum falafel\"},\n", + " {\"id\": 9, \"summary\": \"Nearly killed the cats\"},\n", + " {\"id\": 6, \"summary\": \"Price cannot be correct\"},\n", + " {\"id\": 3, \"summary\": \"Taste is neutral, quantity is DECEITFUL!\"},\n", + " {\"id\": 7, \"summary\": \"This stuff is great\"},\n", + " {\"id\": 10, \"summary\": \"The reviews don't lie\"},\n", + "]" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "azdata_cell_guid": "03e8161a-6cdd-415d-8261-b6b99982726c", + "language": "python" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[1, 8, 4, 11, 5, 2, 9, 6, 3, 7, 10]" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "vector_store.add_texts(texts=query, metadatas=query_metadata)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": { + "azdata_cell_guid": "a2838ad1-64a1-409e-b97d-7883b42a0b33", + "language": "python" + }, + "source": [ + "## Query vector store\n", + "Once your vector store has been created and the relevant documents have been added you will most likely wish to query it during the running of your chain or agent.\n", + "\n", + "Performing a simple similarity search can be done as follows:" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": { + "azdata_cell_guid": "1baa2857-167e-4873-ad9c-e67649ef39bf", + "language": "python" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[Document(metadata={'id': 1, 'summary': 'Good Quality Dog Food'}, page_content='I have bought several of the Vitality canned dog food products and have found them all to be of good quality. The product looks more like a stew than a processed meat and it smells better. My Labrador is finicky and she appreciates this product better than most.'), Document(metadata={'id': 7, 'summary': 'This stuff is great'}, page_content='I have used this product in smoothies for my son and he loves it. Additionally, I use this oil in the shower as a skin conditioner and it has made my skin look great. Some of the stretch marks on my belly has disappeared quickly. Highly recommend!!!'), Document(metadata={'id': 5, 'summary': 'Great for the kids!'}, page_content=\"If you are looking for a less messy version of licorice for the children, then be sure to try these! They're soft, easy to chew, and they don't get your hands all sticky and gross in the car, in the summer, at the beach, etc. We love all the flavos and sometimes mix these in with the chocolate to have a very nice snack! Great item, great price too, highly recommend!\")]\n" + ] + } + ], + "source": [ + "# Perform a similarity search between the embedding of the query and the embeddings of the documents\n", + "simsearch_result = vector_store.similarity_search(\"Good reviews\", k=3)\n", + "print(simsearch_result)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": { + "azdata_cell_guid": "f92f0a1b-19aa-46d1-ad1a-c2e52f9114d0", + "language": "python" + }, + "source": [ + "### Filtering Support:\n", + "\n", + "The vectorstore supports a set of filters that can be applied against the metadata fields of the documents.This feature enables developers and data analysts to refine their queries, ensuring that the search results are accurately aligned with their needs. By applying filters based on specific metadata attributes, users can limit the scope of their searches, concentrating only on the most relevant data subsets." + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": { + "azdata_cell_guid": "24fabd60-0b29-4ed9-9d5e-38c68fe05dfa", + "language": "python" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[Document(metadata={'id': 7, 'summary': 'This stuff is great'}, page_content='I have used this product in smoothies for my son and he loves it. Additionally, I use this oil in the shower as a skin conditioner and it has made my skin look great. Some of the stretch marks on my belly has disappeared quickly. Highly recommend!!!'), Document(metadata={'id': 5, 'summary': 'Great for the kids!'}, page_content=\"If you are looking for a less messy version of licorice for the children, then be sure to try these! They're soft, easy to chew, and they don't get your hands all sticky and gross in the car, in the summer, at the beach, etc. We love all the flavos and sometimes mix these in with the chocolate to have a very nice snack! Great item, great price too, highly recommend!\"), Document(metadata={'id': 3, 'summary': 'Taste is neutral, quantity is DECEITFUL!'}, page_content='The taste of these white cheddar flat breads is like a regular cracker - which is not bad, except that I bought them because I wanted a cheese taste.

      What was a HUGE disappointment? How misleading the packaging of the box is. The photo on the box (I bought these in store) makes it look like it is full of long flatbreads (expanding the length and width of the box). Wrong! The plastic tray that holds the crackers is about 2 smaller all around - leaving you with about 15 or so small flatbreads.

      What is also bad about this is that the company states they use biodegradable and eco-friendly packaging. FAIL! They used a HUGE box for a ridiculously small amount of crackers. Not ecofriendly at all.

      Would I buy these again? No - I feel ripped off. The other crackers (like Sesame Tarragon) give you a little
      more bang for your buck and have more flavor.')]\n" + ] + } + ], + "source": [ + "# hybrid search -> filter for cases where id not equal to 1.\n", + "hybrid_simsearch_result = vector_store.similarity_search(\n", + " \"Good reviews\", k=3, filter={\"id\": {\"$ne\": 1}}\n", + ")\n", + "print(hybrid_simsearch_result)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": { + "azdata_cell_guid": "449c4cde-e303-4856-8deb-6e6ad56f9501", + "language": "python" + }, + "source": [ + "### Similarity Search with Score:\n", + "If you want to execute a similarity search and receive the corresponding scores you can run:" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": { + "azdata_cell_guid": "382fa5d4-6da1-46c1-987f-6d0ec050be99", + "language": "python", + "tags": [ + "hide_input" + ] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[(Document(metadata={'id': 3, 'summary': 'Taste is neutral, quantity is DECEITFUL!'}, page_content='The taste of these white cheddar flat breads is like a regular cracker - which is not bad, except that I bought them because I wanted a cheese taste.

      What was a HUGE disappointment? How misleading the packaging of the box is. The photo on the box (I bought these in store) makes it look like it is full of long flatbreads (expanding the length and width of the box). Wrong! The plastic tray that holds the crackers is about 2 smaller all around - leaving you with about 15 or so small flatbreads.

      What is also bad about this is that the company states they use biodegradable and eco-friendly packaging. FAIL! They used a HUGE box for a ridiculously small amount of crackers. Not ecofriendly at all.

      Would I buy these again? No - I feel ripped off. The other crackers (like Sesame Tarragon) give you a little
      more bang for your buck and have more flavor.'), 0.651870006770711), (Document(metadata={'id': 8, 'summary': 'Nasty No flavor'}, page_content='The candy is just red , No flavor . Just plan and chewy . I would never buy them again'), 0.6908952973052638), (Document(metadata={'id': 4, 'summary': 'stale product'}, page_content='Arrived in 6 days and were so stale i could not eat any of the 6 bags!!'), 0.7360955776468822), (Document(metadata={'id': 1, 'summary': 'Good Quality Dog Food'}, page_content='I have bought several of the Vitality canned dog food products and have found them all to be of good quality. The product looks more like a stew than a processed meat and it smells better. My Labrador is finicky and she appreciates this product better than most.'), 0.7408823529514486), (Document(metadata={'id': 9, 'summary': 'Nearly killed the cats'}, page_content=\"Too much of a good thing? We worked this kibble in over time, slowly shifting the percentage of Felidae to national junk-food brand until the bowl was all natural. By this time, the cats couldn't keep it in or down. What a mess. We've moved on.\"), 0.782995248991772), (Document(metadata={'id': 7, 'summary': 'This stuff is great'}, page_content='I have used this product in smoothies for my son and he loves it. Additionally, I use this oil in the shower as a skin conditioner and it has made my skin look great. Some of the stretch marks on my belly has disappeared quickly. Highly recommend!!!'), 0.7912681479906212), (Document(metadata={'id': 2, 'summary': 'yum falafel'}, page_content='We had trouble finding this locally - delivery was fast, no more hunting up and down the flour aisle at our local grocery stores.'), 0.809213468778896), (Document(metadata={'id': 10, 'summary': \"The reviews don't lie\"}, page_content='Been taking Coconut Oil for YEARS. This is the best on the retail market. I wish it was in glass, but this is the one.'), 0.8281482301097155), (Document(metadata={'id': 5, 'summary': 'Great for the kids!'}, page_content=\"If you are looking for a less messy version of licorice for the children, then be sure to try these! They're soft, easy to chew, and they don't get your hands all sticky and gross in the car, in the summer, at the beach, etc. We love all the flavos and sometimes mix these in with the chocolate to have a very nice snack! Great item, great price too, highly recommend!\"), 0.8283754326400574), (Document(metadata={'id': 6, 'summary': 'Price cannot be correct'}, page_content='Hey, the description says 360 grams - that is roughly 13 ounces at under $4.00 per can. No way - that is the approximate price for a 100 gram can.'), 0.8323967822635847), (Document(metadata={'id': 11, 'summary': 'Great value and convenient ramen'}, page_content=\"Got these on sale for roughly 25 cents per cup, which is half the price of my local grocery stores, plus they rarely stock the spicy flavors. These things are a GREAT snack for my office where time is constantly crunched and sometimes you can't escape for a real meal. This is one of my favorite flavors of Instant Lunch and will be back to buy every time it goes on sale.\"), 0.8387189489406939)]\n" + ] + } + ], + "source": [ + "simsearch_with_score_result = vector_store.similarity_search_with_score(\n", + " \"Not a very good product\", k=12\n", + ")\n", + "print(simsearch_with_score_result)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": { + "azdata_cell_guid": "620e29bd-02f8-4dc7-91a2-52537cb08886", + "language": "python" + }, + "source": [ + "For a full list of the different searches you can execute on a Azure SQL vector store, please refer to the [API reference](https://python.langchain.com/api_reference/sqlserver/index.html)." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": { + "azdata_cell_guid": "ff48b371-b94f-4a3a-bd66-cce856baf6c4", + "language": "python" + }, + "source": [ + "### Similarity Search when you already have embeddings you want to search on" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "azdata_cell_guid": "35afb4cd-0682-4525-9ba8-625fecc59bb4", + "language": "python", + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[Document(metadata={'id': 8, 'summary': 'Nasty No flavor'}, page_content='The candy is just red , No flavor . Just plan and chewy . I would never buy them again'), Document(metadata={'id': 4, 'summary': 'stale product'}, page_content='Arrived in 6 days and were so stale i could not eat any of the 6 bags!!'), Document(metadata={'id': 3, 'summary': 'Taste is neutral, quantity is DECEITFUL!'}, page_content='The taste of these white cheddar flat breads is like a regular cracker - which is not bad, except that I bought them because I wanted a cheese taste.

      What was a HUGE disappointment? How misleading the packaging of the box is. The photo on the box (I bought these in store) makes it look like it is full of long flatbreads (expanding the length and width of the box). Wrong! The plastic tray that holds the crackers is about 2 smaller all around - leaving you with about 15 or so small flatbreads.

      What is also bad about this is that the company states they use biodegradable and eco-friendly packaging. FAIL! They used a HUGE box for a ridiculously small amount of crackers. Not ecofriendly at all.

      Would I buy these again? No - I feel ripped off. The other crackers (like Sesame Tarragon) give you a little
      more bang for your buck and have more flavor.'), Document(metadata={'id': 6, 'summary': 'Price cannot be correct'}, page_content='Hey, the description says 360 grams - that is roughly 13 ounces at under $4.00 per can. No way - that is the approximate price for a 100 gram can.')]\n" + ] + } + ], + "source": [ + "# if you already have embeddings you want to search on\n", + "simsearch_by_vector = vector_store.similarity_search_by_vector(\n", + " [-0.0033353185281157494, -0.017689190804958344, -0.01590404286980629, ...]\n", + ")\n", + "print(simsearch_by_vector)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "azdata_cell_guid": "8a7083fd-ddb2-4187-a315-744b7a623178", + "language": "python" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[(Document(metadata={'id': 8, 'summary': 'Nasty No flavor'}, page_content='The candy is just red , No flavor . Just plan and chewy . I would never buy them again'), 0.9648153551769503), (Document(metadata={'id': 4, 'summary': 'stale product'}, page_content='Arrived in 6 days and were so stale i could not eat any of the 6 bags!!'), 0.9655108580341948), (Document(metadata={'id': 3, 'summary': 'Taste is neutral, quantity is DECEITFUL!'}, page_content='The taste of these white cheddar flat breads is like a regular cracker - which is not bad, except that I bought them because I wanted a cheese taste.

      What was a HUGE disappointment? How misleading the packaging of the box is. The photo on the box (I bought these in store) makes it look like it is full of long flatbreads (expanding the length and width of the box). Wrong! The plastic tray that holds the crackers is about 2 smaller all around - leaving you with about 15 or so small flatbreads.

      What is also bad about this is that the company states they use biodegradable and eco-friendly packaging. FAIL! They used a HUGE box for a ridiculously small amount of crackers. Not ecofriendly at all.

      Would I buy these again? No - I feel ripped off. The other crackers (like Sesame Tarragon) give you a little
      more bang for your buck and have more flavor.'), 0.9840511208615808), (Document(metadata={'id': 6, 'summary': 'Price cannot be correct'}, page_content='Hey, the description says 360 grams - that is roughly 13 ounces at under $4.00 per can. No way - that is the approximate price for a 100 gram can.'), 0.9915737524649991)]\n" + ] + } + ], + "source": [ + "# Similarity Search with Score if you already have embeddings you want to search on\n", + "simsearch_by_vector_with_score = vector_store.similarity_search_by_vector_with_score(\n", + " [-0.0033353185281157494, -0.017689190804958344, -0.01590404286980629, ...]\n", + ")\n", + "print(simsearch_by_vector_with_score)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Delete items from vector store" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": { + "azdata_cell_guid": "01f30a69-76cb-4137-bb80-1061abc095be", + "language": "python" + }, + "source": [ + "### Delete Row by ID" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": { + "azdata_cell_guid": "1b42828c-0850-4d89-a1b5-a463bae0f143", + "language": "python" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 35, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# delete row by id\n", + "vector_store.delete([\"3\", \"7\"])" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": { + "azdata_cell_guid": "51b9a47e-a17a-4427-8abe-90d87fd63389", + "language": "python" + }, + "source": [ + "### Drop Vector Store" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "azdata_cell_guid": "cc9a281a-d204-4830-83d0-fcdd890c7f9c", + "language": "python" + }, + "outputs": [], + "source": [ + "# drop vectorstore\n", + "vector_store.drop()" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": { + "azdata_cell_guid": "2d1b942b-f1ca-4fb5-abb7-bb2855631962", + "language": "python" + }, + "source": [ + "## Load a Document from Azure Blob Storage" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": { + "azdata_cell_guid": "cab89a29-e5e3-44b6-8f29-b4470d26f5d4", + "language": "python" + }, + "source": [ + "Below is example of loading a file from Azure Blob Storage container into the SQL Vector store after splitting the document into chunks.\n", + "[Azure Blog Storage](https://learn.microsoft.com/en-us/azure/storage/blobs/storage-blobs-introduction) is Microsoft's object storage solution for the cloud. Blob Storage is optimized for storing massive amounts of unstructured data. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "azdata_cell_guid": "6cff6a17-89b6-4d73-a92d-cf289dea4294", + "language": "python" + }, + "outputs": [], + "source": [ + "pip install azure-storage-blob" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": { + "azdata_cell_guid": "d9127900-0942-48f1-bd4d-081c7fa3fcae", + "language": "python" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Number of split documents: 528\n" + ] + } + ], + "source": [ + "from langchain.document_loaders import AzureBlobStorageFileLoader\n", + "from langchain.text_splitter import RecursiveCharacterTextSplitter\n", + "from langchain_core.documents import Document\n", + "\n", + "# Define your connection string and blob details\n", + "conn_str = \"DefaultEndpointsProtocol=https;AccountName=;AccountKey===;EndpointSuffix=core.windows.net\"\n", + "container_name = \" 100\n", + " else doc.page_content\n", + " for doc in response[\"context\"]\n", + " ],\n", + " }\n", + "\n", + " # Create a DataFrame\n", + " df = pd.DataFrame(data)\n", + "\n", + " # Print the table\n", + " print(\"\\nSources:\")\n", + " print(df.to_markdown(index=False))" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "azdata_cell_guid": "3cab0661-2351-4164-952f-67670addd99b", + "language": "python", + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Answer: When Harry first learned that he was a wizard, he felt quite sure there had been a horrible mistake. He struggled to believe it because he had spent his life being bullied and mistreated by the Dursleys. If he was really a wizard, he wondered why he hadn't been able to use magic to defend himself. This disbelief and surprise were evident when he gasped, “I’m a what?”\n", + "\n", + "Sources:\n", + "| Doc ID | Content |\n", + "|:--------------------------------------------|:------------------------------------------------------|\n", + "| 01 Harry Potter and the Sorcerers Stone.txt | Harry was wondering what a wizard did once he’d fi... |\n", + "| 01 Harry Potter and the Sorcerers Stone.txt | Harry realized his mouth was open and closed it qu... |\n", + "| 01 Harry Potter and the Sorcerers Stone.txt | “Most of us reckon he’s still out there somewhere ... |\n", + "| 01 Harry Potter and the Sorcerers Stone.txt | “Ah, go boil yer heads, both of yeh,” said Hagrid.... |\n", + "\n" + ] + } + ], + "source": [ + "# Define the user query\n", + "user_query = \"How did Harry feel when he first learnt that he was a Wizard?\"\n", + "\n", + "# Call the function to get the answer and sources\n", + "get_answer_and_sources(user_query)" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "azdata_cell_guid": "1e1939d8-671f-4063-906c-89ee6813f12b", + "language": "python" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Yes, Harry had a pet owl named Hedwig. He decided to call her Hedwig after finding the name in a book titled *A History of Magic*.\n", + "\n", + "Sources:\n", + "| Doc ID | Content |\n", + "|:--------------------------------------------|:------------------------------------------------------|\n", + "| 01 Harry Potter and the Sorcerers Stone.txt | Harry sank down next to the bowl of peas. “What di... |\n", + "| 01 Harry Potter and the Sorcerers Stone.txt | Harry kept to his room, with his new owl for compa... |\n", + "| 01 Harry Potter and the Sorcerers Stone.txt | As the snake slid swiftly past him, Harry could ha... |\n", + "| 01 Harry Potter and the Sorcerers Stone.txt | Ron reached inside his jacket and pulled out a fat... |\n", + "\n" + ] + } + ], + "source": [ + "# Define the user query\n", + "user_query = \"Did Harry have a pet? What was it\"\n", + "\n", + "# Call the function to get the answer and sources\n", + "get_answer_and_sources(user_query)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": { + "azdata_cell_guid": "d1f01a01-1e1d-4af6-95a3-82bad34419fe" + }, + "source": [ + "## API reference \n", + "\n", + "For detailed documentation of SQLServer Vectorstore features and configurations head to the API reference: [https://python.langchain.com/api\\_reference/sqlserver/index.html](https:\\python.langchain.com\\api_reference\\sqlserver\\index.html)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": { + "azdata_cell_guid": "f04dd9d6-d4f2-4425-9c6c-2275ff65c594" + }, + "source": [ + "## Related\n", + "- Vector store [conceptual guide](https://python.langchain.com/docs/concepts/vectorstores/)\n", + "- Vector store [how-to guides](https://python.langchain.com/docs/how_to/#vector-stores)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.9" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/docs/integrations/vectorstores/tablestore.ipynb b/docs/docs/integrations/vectorstores/tablestore.ipynb new file mode 100644 index 0000000000000..0c82261d97f2d --- /dev/null +++ b/docs/docs/integrations/vectorstores/tablestore.ipynb @@ -0,0 +1,385 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + } + }, + "source": [ + "# TablestoreVectorStore\n", + "\n", + "> [Tablestore](https://www.aliyun.com/product/ots) is a fully managed NoSQL cloud database service that enables storage of a massive amount of structured\n", + "and semi-structured data.\n", + "\n", + "This notebook shows how to use functionality related to the `Tablestore` vector database.\n", + "\n", + "To use Tablestore, you must create an instance.\n", + "Here are the [creating instance instructions](https://help.aliyun.com/zh/tablestore/getting-started/manage-the-wide-column-model-in-the-tablestore-console)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + } + }, + "source": "## Setup" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%pip install --upgrade --quiet langchain-community tablestore" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "## Initialization" + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "ExecuteTime": { + "end_time": "2024-08-20T11:10:04.469458Z", + "start_time": "2024-08-20T11:09:49.541150Z" + }, + "pycharm": { + "is_executing": true, + "name": "#%%\n" + } + }, + "outputs": [], + "source": [ + "import getpass\n", + "import os\n", + "\n", + "os.environ[\"end_point\"] = getpass.getpass(\"Tablestore end_point:\")\n", + "os.environ[\"instance_name\"] = getpass.getpass(\"Tablestore instance_name:\")\n", + "os.environ[\"access_key_id\"] = getpass.getpass(\"Tablestore access_key_id:\")\n", + "os.environ[\"access_key_secret\"] = getpass.getpass(\"Tablestore access_key_secret:\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "Create vector store. " + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "ExecuteTime": { + "end_time": "2024-08-20T11:10:07.911086Z", + "start_time": "2024-08-20T11:10:07.351293Z" + } + }, + "outputs": [], + "source": [ + "import tablestore\n", + "from langchain_community.embeddings import FakeEmbeddings\n", + "from langchain_community.vectorstores import TablestoreVectorStore\n", + "from langchain_core.documents import Document\n", + "\n", + "test_embedding_dimension_size = 4\n", + "embeddings = FakeEmbeddings(size=test_embedding_dimension_size)\n", + "\n", + "store = TablestoreVectorStore(\n", + " embedding=embeddings,\n", + " endpoint=os.getenv(\"end_point\"),\n", + " instance_name=os.getenv(\"instance_name\"),\n", + " access_key_id=os.getenv(\"access_key_id\"),\n", + " access_key_secret=os.getenv(\"access_key_secret\"),\n", + " vector_dimension=test_embedding_dimension_size,\n", + " # metadata mapping is used to filter non-vector fields.\n", + " metadata_mappings=[\n", + " tablestore.FieldSchema(\n", + " \"type\", tablestore.FieldType.KEYWORD, index=True, enable_sort_and_agg=True\n", + " ),\n", + " tablestore.FieldSchema(\n", + " \"time\", tablestore.FieldType.LONG, index=True, enable_sort_and_agg=True\n", + " ),\n", + " ],\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "## Manage vector store" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "Create table and index." + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "ExecuteTime": { + "end_time": "2024-08-20T11:10:10.875422Z", + "start_time": "2024-08-20T11:10:10.566400Z" + } + }, + "outputs": [], + "source": [ + "store.create_table_if_not_exist()\n", + "store.create_search_index_if_not_exist()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "Add documents." + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "ExecuteTime": { + "end_time": "2024-08-20T11:10:14.974253Z", + "start_time": "2024-08-20T11:10:14.894190Z" + }, + "pycharm": { + "is_executing": true, + "name": "#%%\n" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "['1', '2', '3', '4', '5']" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "store.add_documents(\n", + " [\n", + " Document(\n", + " id=\"1\", page_content=\"1 hello world\", metadata={\"type\": \"pc\", \"time\": 2000}\n", + " ),\n", + " Document(\n", + " id=\"2\", page_content=\"abc world\", metadata={\"type\": \"pc\", \"time\": 2009}\n", + " ),\n", + " Document(\n", + " id=\"3\", page_content=\"3 text world\", metadata={\"type\": \"sky\", \"time\": 2010}\n", + " ),\n", + " Document(\n", + " id=\"4\", page_content=\"hi world\", metadata={\"type\": \"sky\", \"time\": 2030}\n", + " ),\n", + " Document(\n", + " id=\"5\", page_content=\"hi world\", metadata={\"type\": \"sky\", \"time\": 2030}\n", + " ),\n", + " ]\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + } + }, + "source": "Delete document." + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "ExecuteTime": { + "end_time": "2024-08-20T11:10:17.408739Z", + "start_time": "2024-08-20T11:10:17.269593Z" + }, + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "store.delete([\"3\"])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + } + }, + "source": "Get documents." + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "## Query vector store" + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "ExecuteTime": { + "end_time": "2024-08-20T11:10:19.379617Z", + "start_time": "2024-08-20T11:10:19.339970Z" + }, + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[Document(id='1', metadata={'embedding': '[1.3296732307905934, 0.0037521341868022385, 0.9821875819319514, 2.5644103644492393]', 'time': 2000, 'type': 'pc'}, page_content='1 hello world'),\n", + " None,\n", + " Document(id='5', metadata={'embedding': '[1.4558082172139821, -1.6441137122167426, -0.13113098640337423, -1.889685473174525]', 'time': 2030, 'type': 'sky'}, page_content='hi world')]" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "store.get_by_ids([\"1\", \"3\", \"5\"])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "Similarity search." + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "ExecuteTime": { + "end_time": "2024-08-20T11:10:21.306717Z", + "start_time": "2024-08-20T11:10:21.284244Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[Document(id='1', metadata={'embedding': [1.3296732307905934, 0.0037521341868022385, 0.9821875819319514, 2.5644103644492393], 'time': 2000, 'type': 'pc'}, page_content='1 hello world'),\n", + " Document(id='4', metadata={'embedding': [-0.3310144199800685, 0.29250046478723635, -0.0646862290377582, -0.23664360156781225], 'time': 2030, 'type': 'sky'}, page_content='hi world')]" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "store.similarity_search(query=\"hello world\", k=2)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": "Similarity search with filters. " + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "ExecuteTime": { + "end_time": "2024-08-20T11:10:23.231425Z", + "start_time": "2024-08-20T11:10:23.213046Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[Document(id='5', metadata={'embedding': [1.4558082172139821, -1.6441137122167426, -0.13113098640337423, -1.889685473174525], 'time': 2030, 'type': 'sky'}, page_content='hi world'),\n", + " Document(id='4', metadata={'embedding': [-0.3310144199800685, 0.29250046478723635, -0.0646862290377582, -0.23664360156781225], 'time': 2030, 'type': 'sky'}, page_content='hi world')]" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "store.similarity_search(\n", + " query=\"hello world\",\n", + " k=10,\n", + " tablestore_filter_query=tablestore.BoolQuery(\n", + " must_queries=[tablestore.TermQuery(field_name=\"type\", column_value=\"sky\")],\n", + " should_queries=[tablestore.RangeQuery(field_name=\"time\", range_from=2020)],\n", + " must_not_queries=[tablestore.TermQuery(field_name=\"type\", column_value=\"pc\")],\n", + " ),\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Usage for retrieval-augmented generation\n", + "\n", + "For guides on how to use this vector store for retrieval-augmented generation (RAG), see the following sections:\n", + "\n", + "- [Tutorials](/docs/tutorials/)\n", + "- [How-to: Question and answer with RAG](https://python.langchain.com/docs/how_to/#qa-with-rag)\n", + "- [Retrieval conceptual docs](https://python.langchain.com/docs/concepts/retrieval)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## API reference\n", + "\n", + "For detailed documentation of all `TablestoreVectorStore` features and configurations head to the API reference:\n", + " https://python.langchain.com/api_reference/community/vectorstores/langchain_community.vectorstores.tablestore.TablestoreVectorStore.html" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.6" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/docs/docs/integrations/vectorstores/weaviate.ipynb b/docs/docs/integrations/vectorstores/weaviate.ipynb index bd61077055351..ebe4541269f46 100644 --- a/docs/docs/integrations/vectorstores/weaviate.ipynb +++ b/docs/docs/integrations/vectorstores/weaviate.ipynb @@ -552,7 +552,7 @@ "id": "66690c78", "metadata": {}, "source": [ - "A known limitation of large languag models (LLMs) is that their training data can be outdated, or not include the specific domain knowledge that you require.\n", + "A known limitation of large language models (LLMs) is that their training data can be outdated, or not include the specific domain knowledge that you require.\n", "\n", "Take a look at the example below:" ] diff --git a/docs/docs/introduction.mdx b/docs/docs/introduction.mdx index b3edcfbd15dff..185eeb4325bf8 100644 --- a/docs/docs/introduction.mdx +++ b/docs/docs/introduction.mdx @@ -8,10 +8,10 @@ sidebar_class_name: hidden **LangChain** is a framework for developing applications powered by large language models (LLMs). LangChain simplifies every stage of the LLM application lifecycle: -- **Development**: Build your applications using LangChain's open-source [building blocks](/docs/concepts/lcel), [components](/docs/concepts), and [third-party integrations](/docs/integrations/providers/). +- **Development**: Build your applications using LangChain's open-source [components](/docs/concepts) and [third-party integrations](/docs/integrations/providers/). Use [LangGraph](/docs/concepts/architecture/#langgraph) to build stateful agents with first-class streaming and human-in-the-loop support. -- **Productionization**: Use [LangSmith](https://docs.smith.langchain.com/) to inspect, monitor and evaluate your chains, so that you can continuously optimize and deploy with confidence. -- **Deployment**: Turn your LangGraph applications into production-ready APIs and Assistants with [LangGraph Cloud](https://langchain-ai.github.io/langgraph/cloud/). +- **Productionization**: Use [LangSmith](https://docs.smith.langchain.com/) to inspect, monitor and evaluate your applications, so that you can continuously optimize and deploy with confidence. +- **Deployment**: Turn your LangGraph applications into production-ready APIs and Assistants with [LangGraph Platform](https://langchain-ai.github.io/langgraph/cloud/). import ThemedImage from '@theme/ThemedImage'; import useBaseUrl from '@docusaurus/useBaseUrl'; @@ -26,16 +26,18 @@ import useBaseUrl from '@docusaurus/useBaseUrl'; title="LangChain Framework Overview" /> -Concretely, the framework consists of the following open-source libraries: +LangChain implements a standard interface for large language models and related +technologies, such as embedding models and vector stores, and integrates with +hundreds of providers. See the [integrations](/docs/integrations/providers/) page for +more. -- **`langchain-core`**: Base abstractions and LangChain Expression Language. -- Integration packages (e.g. **`langchain-openai`**, **`langchain-anthropic`**, etc.): Important integrations have been split into lightweight packages that are co-maintained by the LangChain team and the integration developers. -- **`langchain`**: Chains, agents, and retrieval strategies that make up an application's cognitive architecture. -- **`langchain-community`**: Third-party integrations that are community maintained. -- **[LangGraph](https://langchain-ai.github.io/langgraph)**: Build robust and stateful multi-actor applications with LLMs by modeling steps as edges and nodes in a graph. Integrates smoothly with LangChain, but can be used without it. -- **[LangGraphPlatform](https://langchain-ai.github.io/langgraph/concepts/#langgraph-platform)**: Deploy LLM applications built with LangGraph to production. -- **[LangSmith](https://docs.smith.langchain.com)**: A developer platform that lets you debug, test, evaluate, and monitor LLM applications. +import ChatModelTabs from "@theme/ChatModelTabs"; + + +```python +model.invoke("Hello, world!") +``` :::note @@ -43,7 +45,20 @@ These docs focus on the Python LangChain library. [Head here](https://js.langcha ::: -## [Tutorials](/docs/tutorials) +## Architecture + +The LangChain framework consists of multiple open-source libraries. Read more in the +[Architecture](/docs/concepts/architecture/) page. + +- **`langchain-core`**: Base abstractions for chat models and other components. +- **Integration packages** (e.g. `langchain-openai`, `langchain-anthropic`, etc.): Important integrations have been split into lightweight packages that are co-maintained by the LangChain team and the integration developers. +- **`langchain`**: Chains, agents, and retrieval strategies that make up an application's cognitive architecture. +- **`langchain-community`**: Third-party integrations that are community maintained. +- **`langgraph`**: Orchestration framework for combining LangChain components into production-ready applications with persistence, streaming, and other key features. See [LangGraph documentation](https://langchain-ai.github.io/langgraph/). + +## Guides + +### [Tutorials](/docs/tutorials) If you're looking to build something specific or are more of a hands-on learner, check out our [tutorials section](/docs/tutorials). This is the best place to get started. @@ -58,7 +73,7 @@ These are the best ones to get started with: Explore the full list of LangChain tutorials [here](/docs/tutorials), and check out other [LangGraph tutorials here](https://langchain-ai.github.io/langgraph/tutorials/). To learn more about LangGraph, check out our first LangChain Academy course, *Introduction to LangGraph*, available [here](https://academy.langchain.com/courses/intro-to-langgraph). -## [How-to guides](/docs/how_to) +### [How-to guides](/docs/how_to) [Here](/docs/how_to) you’ll find short answers to “How do I….?” types of questions. These how-to guides don’t cover topics in depth – you’ll find that material in the [Tutorials](/docs/tutorials) and the [API Reference](https://python.langchain.com/api_reference/). @@ -67,20 +82,20 @@ However, these guides will help you quickly accomplish common tasks using [chat Check out [LangGraph-specific how-tos here](https://langchain-ai.github.io/langgraph/how-tos/). -## [Conceptual guide](/docs/concepts) +### [Conceptual guide](/docs/concepts) Introductions to all the key parts of LangChain you’ll need to know! [Here](/docs/concepts) you'll find high level explanations of all LangChain concepts. For a deeper dive into LangGraph concepts, check out [this page](https://langchain-ai.github.io/langgraph/concepts/). -## [Integrations](integrations/providers/index.mdx) +### [Integrations](integrations/providers/index.mdx) LangChain is part of a rich ecosystem of tools that integrate with our framework and build on top of it. If you're looking to get up and running quickly with [chat models](/docs/integrations/chat/), [vector stores](/docs/integrations/vectorstores/), or other LangChain components from a specific provider, check out our growing list of [integrations](/docs/integrations/providers/). -## [API reference](https://python.langchain.com/api_reference/) +### [API reference](https://python.langchain.com/api_reference/) Head to the reference section for full documentation of all classes and methods in the LangChain Python packages. ## Ecosystem diff --git a/docs/docs/security.md b/docs/docs/security.md deleted file mode 100644 index 08e841c89a25d..0000000000000 --- a/docs/docs/security.md +++ /dev/null @@ -1,30 +0,0 @@ -# Security - -LangChain has a large ecosystem of integrations with various external resources like local and remote file systems, APIs and databases. These integrations allow developers to create versatile applications that combine the power of LLMs with the ability to access, interact with and manipulate external resources. - -## Best practices - -When building such applications developers should remember to follow good security practices: - -* [**Limit Permissions**](https://en.wikipedia.org/wiki/Principle_of_least_privilege): Scope permissions specifically to the application's need. Granting broad or excessive permissions can introduce significant security vulnerabilities. To avoid such vulnerabilities, consider using read-only credentials, disallowing access to sensitive resources, using sandboxing techniques (such as running inside a container), specifying proxy configurations to control external requests, etc. as appropriate for your application. -* **Anticipate Potential Misuse**: Just as humans can err, so can Large Language Models (LLMs). Always assume that any system access or credentials may be used in any way allowed by the permissions they are assigned. For example, if a pair of database credentials allows deleting data, it’s safest to assume that any LLM able to use those credentials may in fact delete data. -* [**Defense in Depth**](https://en.wikipedia.org/wiki/Defense_in_depth_(computing)): No security technique is perfect. Fine-tuning and good chain design can reduce, but not eliminate, the odds that a Large Language Model (LLM) may make a mistake. It’s best to combine multiple layered security approaches rather than relying on any single layer of defense to ensure security. For example: use both read-only permissions and sandboxing to ensure that LLMs are only able to access data that is explicitly meant for them to use. - -Risks of not doing so include, but are not limited to: -* Data corruption or loss. -* Unauthorized access to confidential information. -* Compromised performance or availability of critical resources. - -Example scenarios with mitigation strategies: - -* A user may ask an agent with access to the file system to delete files that should not be deleted or read the content of files that contain sensitive information. To mitigate, limit the agent to only use a specific directory and only allow it to read or write files that are safe to read or write. Consider further sandboxing the agent by running it in a container. -* A user may ask an agent with write access to an external API to write malicious data to the API, or delete data from that API. To mitigate, give the agent read-only API keys, or limit it to only use endpoints that are already resistant to such misuse. -* A user may ask an agent with access to a database to drop a table or mutate the schema. To mitigate, scope the credentials to only the tables that the agent needs to access and consider issuing READ-ONLY credentials. - -If you're building applications that access external resources like file systems, APIs -or databases, consider speaking with your company's security team to determine how to best -design and secure your applications. - -## Reporting a vulnerability - -Please report security vulnerabilities by email to security@langchain.dev. This will ensure the issue is promptly triaged and acted upon as needed. diff --git a/docs/docs/tutorials/graph.ipynb b/docs/docs/tutorials/graph.ipynb index 4130bae5a84f3..1bd434c89cd71 100644 --- a/docs/docs/tutorials/graph.ipynb +++ b/docs/docs/tutorials/graph.ipynb @@ -15,7 +15,7 @@ "source": [ "# Build a Question Answering application over a Graph Database\n", "\n", - "In this guide we'll go over the basic ways to create a Q&A chain over a graph database. These systems will allow us to ask a question about the data in a graph database and get back a natural language answer.\n", + "In this guide we'll go over the basic ways to create a Q&A chain over a graph database. These systems will allow us to ask a question about the data in a graph database and get back a natural language answer. First, we will show a simple out-of-the-box option and then implement a more sophisticated version with LangGraph.\n", "\n", "## ⚠️ Security note ⚠️\n", "\n", @@ -45,7 +45,7 @@ "metadata": {}, "outputs": [], "source": [ - "%pip install --upgrade --quiet langchain langchain-neo4j langchain-openai neo4j" + "%pip install --upgrade --quiet langchain langchain-neo4j langchain-openai langgraph" ] }, { @@ -57,14 +57,14 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - " ········\n" + "Enter your OpenAI API key: ········\n" ] } ], @@ -90,7 +90,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -108,7 +108,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 4, "metadata": {}, "outputs": [ { @@ -117,7 +117,7 @@ "[]" ] }, - "execution_count": 3, + "execution_count": 4, "metadata": {}, "output_type": "execute_result" } @@ -162,19 +162,24 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Node properties are the following:\n", - "Movie {imdbRating: FLOAT, id: STRING, released: DATE, title: STRING},Person {name: STRING},Genre {name: STRING},Chunk {id: STRING, question: STRING, query: STRING, text: STRING, embedding: LIST}\n", - "Relationship properties are the following:\n", + "Node properties:\n", + "Person {name: STRING}\n", + "Movie {id: STRING, released: DATE, title: STRING, imdbRating: FLOAT}\n", + "Genre {name: STRING}\n", + "Chunk {id: STRING, embedding: LIST, text: STRING, question: STRING, query: STRING}\n", + "Relationship properties:\n", "\n", - "The relationships are the following:\n", - "(:Movie)-[:IN_GENRE]->(:Genre),(:Person)-[:DIRECTED]->(:Movie),(:Person)-[:ACTED_IN]->(:Movie)\n" + "The relationships:\n", + "(:Person)-[:DIRECTED]->(:Movie)\n", + "(:Person)-[:ACTED_IN]->(:Movie)\n", + "(:Movie)-[:IN_GENRE]->(:Genre)\n" ] } ], @@ -187,11 +192,65 @@ "cell_type": "markdown", "metadata": {}, "source": [ + "For more involved schema information, you can use `enhanced_schema` option." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Received notification from DBMS server: {severity: WARNING} {code: Neo.ClientNotification.Statement.FeatureDeprecationWarning} {category: DEPRECATION} {title: This feature is deprecated and will be removed in future versions.} {description: The procedure has a deprecated field. ('config' used by 'apoc.meta.graphSample' is deprecated.)} {position: line: 1, column: 1, offset: 0} for query: \"CALL apoc.meta.graphSample() YIELD nodes, relationships RETURN nodes, [rel in relationships | {name:apoc.any.property(rel, 'type'), count: apoc.any.property(rel, 'count')}] AS relationships\"\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Node properties:\n", + "- **Person**\n", + " - `name`: STRING Example: \"John Lasseter\"\n", + "- **Movie**\n", + " - `id`: STRING Example: \"1\"\n", + " - `released`: DATE Min: 1964-12-16, Max: 1996-09-15\n", + " - `title`: STRING Example: \"Toy Story\"\n", + " - `imdbRating`: FLOAT Min: 2.4, Max: 9.3\n", + "- **Genre**\n", + " - `name`: STRING Example: \"Adventure\"\n", + "- **Chunk**\n", + " - `id`: STRING Available options: ['d66006059fd78d63f3df90cc1059639a', '0e3dcb4502853979d12357690a95ec17', 'c438c6bcdcf8e4fab227f29f8e7ff204', '97fe701ec38057594464beaa2df0710e', 'b54f9286e684373498c4504b4edd9910', '5b50a72c3a4954b0ff7a0421be4f99b9', 'fb28d41771e717255f0d8f6c799ede32', '58e6f14dd2e6c6702cf333f2335c499c']\n", + " - `text`: STRING Available options: ['How many artists are there?', 'Which actors played in the movie Casino?', 'How many movies has Tom Hanks acted in?', \"List all the genres of the movie Schindler's List\", 'Which actors have worked in movies from both the c', 'Which directors have made movies with at least thr', 'Identify movies where directors also played a role', 'Find the actor with the highest number of movies i']\n", + " - `question`: STRING Available options: ['How many artists are there?', 'Which actors played in the movie Casino?', 'How many movies has Tom Hanks acted in?', \"List all the genres of the movie Schindler's List\", 'Which actors have worked in movies from both the c', 'Which directors have made movies with at least thr', 'Identify movies where directors also played a role', 'Find the actor with the highest number of movies i']\n", + " - `query`: STRING Available options: ['MATCH (a:Person)-[:ACTED_IN]->(:Movie) RETURN coun', \"MATCH (m:Movie {title: 'Casino'})<-[:ACTED_IN]-(a)\", \"MATCH (a:Person {name: 'Tom Hanks'})-[:ACTED_IN]->\", \"MATCH (m:Movie {title: 'Schindler's List'})-[:IN_G\", 'MATCH (a:Person)-[:ACTED_IN]->(:Movie)-[:IN_GENRE]', 'MATCH (d:Person)-[:DIRECTED]->(m:Movie)<-[:ACTED_I', 'MATCH (p:Person)-[:DIRECTED]->(m:Movie), (p)-[:ACT', 'MATCH (a:Actor)-[:ACTED_IN]->(m:Movie) RETURN a.na']\n", + "Relationship properties:\n", + "\n", + "The relationships:\n", + "(:Person)-[:DIRECTED]->(:Movie)\n", + "(:Person)-[:ACTED_IN]->(:Movie)\n", + "(:Movie)-[:IN_GENRE]->(:Genre)\n" + ] + } + ], + "source": [ + "enhanced_graph = Neo4jGraph(enhanced_schema=True)\n", + "print(enhanced_graph.schema)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The `enhanced_schema` option enriches property information by including details such as minimum and maximum values for floats and dates, as well as example values for string properties. This additional context helps guide the LLM toward generating more accurate and effective queries.\n", + "\n", "Great! We've got a graph database that we can query. Now let's try hooking it up to an LLM.\n", "\n", - "## Chain\n", + "## GraphQACypherChain\n", "\n", - "Let's use a simple chain that takes a question, turns it into a Cypher query, executes the query, and uses the result to answer the original question.\n", + "Let's use a simple out-of-the-box chain that takes a question, turns it into a Cypher query, executes the query, and uses the result to answer the original question.\n", "\n", "![graph_chain.webp](../../static/img/graph_chain.webp)\n", "\n", @@ -201,7 +260,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 7, "metadata": {}, "outputs": [ { @@ -212,10 +271,12 @@ "\n", "\u001b[1m> Entering new GraphCypherQAChain chain...\u001b[0m\n", "Generated Cypher:\n", - "\u001b[32;1m\u001b[1;3mMATCH (:Movie {title: \"Casino\"})<-[:ACTED_IN]-(actor:Person)\n", - "RETURN actor.name\u001b[0m\n", + "\u001b[32;1m\u001b[1;3mcypher\n", + "MATCH (p:Person)-[:ACTED_IN]->(m:Movie {title: \"Casino\"})\n", + "RETURN p.name\n", + "\u001b[0m\n", "Full Context:\n", - "\u001b[32;1m\u001b[1;3m[{'actor.name': 'Joe Pesci'}, {'actor.name': 'Robert De Niro'}, {'actor.name': 'Sharon Stone'}, {'actor.name': 'James Woods'}]\u001b[0m\n", + "\u001b[32;1m\u001b[1;3m[{'p.name': 'Robert De Niro'}, {'p.name': 'Joe Pesci'}, {'p.name': 'Sharon Stone'}, {'p.name': 'James Woods'}]\u001b[0m\n", "\n", "\u001b[1m> Finished chain.\u001b[0m\n" ] @@ -224,10 +285,10 @@ "data": { "text/plain": [ "{'query': 'What was the cast of the Casino?',\n", - " 'result': 'The cast of Casino included Joe Pesci, Robert De Niro, Sharon Stone, and James Woods.'}" + " 'result': 'Robert De Niro, Joe Pesci, Sharon Stone, and James Woods were the cast of Casino.'}" ] }, - "execution_count": 5, + "execution_count": 7, "metadata": {}, "output_type": "execute_result" } @@ -236,9 +297,9 @@ "from langchain_neo4j import GraphCypherQAChain\n", "from langchain_openai import ChatOpenAI\n", "\n", - "llm = ChatOpenAI(model=\"gpt-3.5-turbo\", temperature=0)\n", + "llm = ChatOpenAI(model=\"gpt-4o\", temperature=0)\n", "chain = GraphCypherQAChain.from_llm(\n", - " graph=graph, llm=llm, verbose=True, allow_dangerous_requests=True\n", + " graph=enhanced_graph, llm=llm, verbose=True, allow_dangerous_requests=True\n", ")\n", "response = chain.invoke({\"query\": \"What was the cast of the Casino?\"})\n", "response" @@ -248,54 +309,754 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Validating relationship direction\n", + "## Advanced implementation with LangGraph\n", + "\n", + "While the GraphCypherQAChain is effective for quick demonstrations, it may face challenges in production environments. Transitioning to LangGraph can enhance the workflow, but implementing natural language to query flows in production remains a complex task. Nevertheless, there are several strategies to significantly improve accuracy and reliability, which we will explore next.\n", + "\n", + "Here is the visualized LangGraph flow we will implement:\n", + "\n", + "![langgraph_text2cypher](../../static/img/langgraph_text2cypher.webp)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We will begin by defining the Input, Output, and Overall state of the LangGraph application." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "from operator import add\n", + "from typing import Annotated, List\n", + "\n", + "from typing_extensions import TypedDict\n", "\n", - "LLMs can struggle with relationship directions in generated Cypher statement. Since the graph schema is predefined, we can validate and optionally correct relationship directions in the generated Cypher statements by using the `validate_cypher` parameter." + "\n", + "class InputState(TypedDict):\n", + " question: str\n", + "\n", + "\n", + "class OverallState(TypedDict):\n", + " question: str\n", + " next_action: str\n", + " cypher_statement: str\n", + " cypher_errors: List[str]\n", + " database_records: List[dict]\n", + " steps: Annotated[List[str], add]\n", + "\n", + "\n", + "class OutputState(TypedDict):\n", + " answer: str\n", + " steps: List[str]\n", + " cypher_statement: str" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The first step is a simple `guardrails` step, where we validate whether the question pertains to movies or their cast. If it doesn't, we notify the user that we cannot answer any other questions. Otherwise, we move on to the Cypher generation step." ] }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "from typing import Literal\n", + "\n", + "from langchain_core.prompts import ChatPromptTemplate\n", + "from pydantic import BaseModel, Field\n", + "\n", + "guardrails_system = \"\"\"\n", + "As an intelligent assistant, your primary objective is to decide whether a given question is related to movies or not. \n", + "If the question is related to movies, output \"movie\". Otherwise, output \"end\".\n", + "To make this decision, assess the content of the question and determine if it refers to any movie, actor, director, film industry, \n", + "or related topics. Provide only the specified output: \"movie\" or \"end\".\n", + "\"\"\"\n", + "guardrails_prompt = ChatPromptTemplate.from_messages(\n", + " [\n", + " (\n", + " \"system\",\n", + " guardrails_system,\n", + " ),\n", + " (\n", + " \"human\",\n", + " (\"{question}\"),\n", + " ),\n", + " ]\n", + ")\n", + "\n", + "\n", + "class GuardrailsOutput(BaseModel):\n", + " decision: Literal[\"movie\", \"end\"] = Field(\n", + " description=\"Decision on whether the question is related to movies\"\n", + " )\n", + "\n", + "\n", + "guardrails_chain = guardrails_prompt | llm.with_structured_output(GuardrailsOutput)\n", + "\n", + "\n", + "def guardrails(state: InputState) -> OverallState:\n", + " \"\"\"\n", + " Decides if the question is related to movies or not.\n", + " \"\"\"\n", + " guardrails_output = guardrails_chain.invoke({\"question\": state.get(\"question\")})\n", + " database_records = None\n", + " if guardrails_output.decision == \"end\":\n", + " database_records = \"This questions is not about movies or their cast. Therefore I cannot answer this question.\"\n", + " return {\n", + " \"next_action\": guardrails_output.decision,\n", + " \"database_records\": database_records,\n", + " \"steps\": [\"guardrail\"],\n", + " }" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Few-shot prompting" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Converting natural language into accurate queries is challenging. One way to enhance this process is by providing relevant few-shot examples to guide the LLM in query generation. To achieve this, we will use the `SemanticSimilarityExampleSelector` to dynamically select the most relevant examples." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "from langchain_core.example_selectors import SemanticSimilarityExampleSelector\n", + "from langchain_neo4j import Neo4jVector\n", + "from langchain_openai import OpenAIEmbeddings\n", + "\n", + "examples = [\n", + " {\n", + " \"question\": \"How many artists are there?\",\n", + " \"query\": \"MATCH (a:Person)-[:ACTED_IN]->(:Movie) RETURN count(DISTINCT a)\",\n", + " },\n", + " {\n", + " \"question\": \"Which actors played in the movie Casino?\",\n", + " \"query\": \"MATCH (m:Movie {title: 'Casino'})<-[:ACTED_IN]-(a) RETURN a.name\",\n", + " },\n", + " {\n", + " \"question\": \"How many movies has Tom Hanks acted in?\",\n", + " \"query\": \"MATCH (a:Person {name: 'Tom Hanks'})-[:ACTED_IN]->(m:Movie) RETURN count(m)\",\n", + " },\n", + " {\n", + " \"question\": \"List all the genres of the movie Schindler's List\",\n", + " \"query\": \"MATCH (m:Movie {title: 'Schindler's List'})-[:IN_GENRE]->(g:Genre) RETURN g.name\",\n", + " },\n", + " {\n", + " \"question\": \"Which actors have worked in movies from both the comedy and action genres?\",\n", + " \"query\": \"MATCH (a:Person)-[:ACTED_IN]->(:Movie)-[:IN_GENRE]->(g1:Genre), (a)-[:ACTED_IN]->(:Movie)-[:IN_GENRE]->(g2:Genre) WHERE g1.name = 'Comedy' AND g2.name = 'Action' RETURN DISTINCT a.name\",\n", + " },\n", + " {\n", + " \"question\": \"Which directors have made movies with at least three different actors named 'John'?\",\n", + " \"query\": \"MATCH (d:Person)-[:DIRECTED]->(m:Movie)<-[:ACTED_IN]-(a:Person) WHERE a.name STARTS WITH 'John' WITH d, COUNT(DISTINCT a) AS JohnsCount WHERE JohnsCount >= 3 RETURN d.name\",\n", + " },\n", + " {\n", + " \"question\": \"Identify movies where directors also played a role in the film.\",\n", + " \"query\": \"MATCH (p:Person)-[:DIRECTED]->(m:Movie), (p)-[:ACTED_IN]->(m) RETURN m.title, p.name\",\n", + " },\n", + " {\n", + " \"question\": \"Find the actor with the highest number of movies in the database.\",\n", + " \"query\": \"MATCH (a:Actor)-[:ACTED_IN]->(m:Movie) RETURN a.name, COUNT(m) AS movieCount ORDER BY movieCount DESC LIMIT 1\",\n", + " },\n", + "]\n", + "\n", + "example_selector = SemanticSimilarityExampleSelector.from_examples(\n", + " examples, OpenAIEmbeddings(), Neo4jVector, k=5, input_keys=[\"question\"]\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Next, we implement the Cypher generation chain, also known as **text2cypher**. The prompt includes an enhanced graph schema, dynamically selected few-shot examples, and the user’s question. This combination enables the generation of a Cypher query to retrieve relevant information from the database." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "from langchain_core.output_parsers import StrOutputParser\n", + "\n", + "text2cypher_prompt = ChatPromptTemplate.from_messages(\n", + " [\n", + " (\n", + " \"system\",\n", + " (\n", + " \"Given an input question, convert it to a Cypher query. No pre-amble.\"\n", + " \"Do not wrap the response in any backticks or anything else. Respond with a Cypher statement only!\"\n", + " ),\n", + " ),\n", + " (\n", + " \"human\",\n", + " (\n", + " \"\"\"You are a Neo4j expert. Given an input question, create a syntactically correct Cypher query to run.\n", + "Do not wrap the response in any backticks or anything else. Respond with a Cypher statement only!\n", + "Here is the schema information\n", + "{schema}\n", + "\n", + "Below are a number of examples of questions and their corresponding Cypher queries.\n", + "\n", + "{fewshot_examples}\n", + "\n", + "User input: {question}\n", + "Cypher query:\"\"\"\n", + " ),\n", + " ),\n", + " ]\n", + ")\n", + "\n", + "text2cypher_chain = text2cypher_prompt | llm | StrOutputParser()\n", + "\n", + "\n", + "def generate_cypher(state: OverallState) -> OverallState:\n", + " \"\"\"\n", + " Generates a cypher statement based on the provided schema and user input\n", + " \"\"\"\n", + " NL = \"\\n\"\n", + " fewshot_examples = (NL * 2).join(\n", + " [\n", + " f\"Question: {el['question']}{NL}Cypher:{el['query']}\"\n", + " for el in example_selector.select_examples(\n", + " {\"question\": state.get(\"question\")}\n", + " )\n", + " ]\n", + " )\n", + " generated_cypher = text2cypher_chain.invoke(\n", + " {\n", + " \"question\": state.get(\"question\"),\n", + " \"fewshot_examples\": fewshot_examples,\n", + " \"schema\": enhanced_graph.schema,\n", + " }\n", + " )\n", + " return {\"cypher_statement\": generated_cypher, \"steps\": [\"generate_cypher\"]}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Query validation\n", + "\n", + "The next step is to validate the generated Cypher statement and ensuring that all property values are accurate. While numbers and dates typically don’t require validation, strings such as movie titles or people’s names do. In this example, we’ll use a basic `CONTAINS` clause for validation, though more advanced mapping and validation techniques can be implemented if needed.\n", + "\n", + "First, we will create a chain that detects any errors in the Cypher statement and extracts the property values it references." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "from typing import List, Optional\n", + "\n", + "validate_cypher_system = \"\"\"\n", + "You are a Cypher expert reviewing a statement written by a junior developer.\n", + "\"\"\"\n", + "\n", + "validate_cypher_user = \"\"\"You must check the following:\n", + "* Are there any syntax errors in the Cypher statement?\n", + "* Are there any missing or undefined variables in the Cypher statement?\n", + "* Are any node labels missing from the schema?\n", + "* Are any relationship types missing from the schema?\n", + "* Are any of the properties not included in the schema?\n", + "* Does the Cypher statement include enough information to answer the question?\n", + "\n", + "Examples of good errors:\n", + "* Label (:Foo) does not exist, did you mean (:Bar)?\n", + "* Property bar does not exist for label Foo, did you mean baz?\n", + "* Relationship FOO does not exist, did you mean FOO_BAR?\n", + "\n", + "Schema:\n", + "{schema}\n", + "\n", + "The question is:\n", + "{question}\n", + "\n", + "The Cypher statement is:\n", + "{cypher}\n", + "\n", + "Make sure you don't make any mistakes!\"\"\"\n", + "\n", + "validate_cypher_prompt = ChatPromptTemplate.from_messages(\n", + " [\n", + " (\n", + " \"system\",\n", + " validate_cypher_system,\n", + " ),\n", + " (\n", + " \"human\",\n", + " (validate_cypher_user),\n", + " ),\n", + " ]\n", + ")\n", + "\n", + "\n", + "class Property(BaseModel):\n", + " \"\"\"\n", + " Represents a filter condition based on a specific node property in a graph in a Cypher statement.\n", + " \"\"\"\n", + "\n", + " node_label: str = Field(\n", + " description=\"The label of the node to which this property belongs.\"\n", + " )\n", + " property_key: str = Field(description=\"The key of the property being filtered.\")\n", + " property_value: str = Field(\n", + " description=\"The value that the property is being matched against.\"\n", + " )\n", + "\n", + "\n", + "class ValidateCypherOutput(BaseModel):\n", + " \"\"\"\n", + " Represents the validation result of a Cypher query's output,\n", + " including any errors and applied filters.\n", + " \"\"\"\n", + "\n", + " errors: Optional[List[str]] = Field(\n", + " description=\"A list of syntax or semantical errors in the Cypher statement. Always explain the discrepancy between schema and Cypher statement\"\n", + " )\n", + " filters: Optional[List[Property]] = Field(\n", + " description=\"A list of property-based filters applied in the Cypher statement.\"\n", + " )\n", + "\n", + "\n", + "validate_cypher_chain = validate_cypher_prompt | llm.with_structured_output(\n", + " ValidateCypherOutput\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "LLMs often struggle with correctly determining relationship directions in generated Cypher statements. Since we have access to the schema, we can deterministically correct these directions using the **CypherQueryCorrector**. \n", + "\n", + "*Note: The `CypherQueryCorrector` is an experimental feature and doesn't support all the newest Cypher syntax.*" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "from langchain_neo4j.chains.graph_qa.cypher_utils import CypherQueryCorrector, Schema\n", + "\n", + "# Cypher query corrector is experimental\n", + "corrector_schema = [\n", + " Schema(el[\"start\"], el[\"type\"], el[\"end\"])\n", + " for el in enhanced_graph.structured_schema.get(\"relationships\")\n", + "]\n", + "cypher_query_corrector = CypherQueryCorrector(corrector_schema)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we can implement the Cypher validation step. First, we use the `EXPLAIN` method to detect any syntax errors. Next, we leverage the LLM to identify potential issues and extract the properties used for filtering. For string properties, we validate them against the database using a simple `CONTAINS` clause.\n", + "\n", + "Based on the validation results, the process can take the following paths:\n", + "\n", + "- If value mapping fails, we end the conversation and inform the user that we couldn't identify a specific property value (e.g., a person or movie title). \n", + "- If errors are found, we route the query for correction. \n", + "- If no issues are detected, we proceed to the Cypher execution step. " + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "from neo4j.exceptions import CypherSyntaxError\n", + "\n", + "\n", + "def validate_cypher(state: OverallState) -> OverallState:\n", + " \"\"\"\n", + " Validates the Cypher statements and maps any property values to the database.\n", + " \"\"\"\n", + " errors = []\n", + " mapping_errors = []\n", + " # Check for syntax errors\n", + " try:\n", + " enhanced_graph.query(f\"EXPLAIN {state.get('cypher_statement')}\")\n", + " except CypherSyntaxError as e:\n", + " errors.append(e.message)\n", + " # Experimental feature for correcting relationship directions\n", + " corrected_cypher = cypher_query_corrector(state.get(\"cypher_statement\"))\n", + " if not corrected_cypher:\n", + " errors.append(\"The generated Cypher statement doesn't fit the graph schema\")\n", + " if not corrected_cypher == state.get(\"cypher_statement\"):\n", + " print(\"Relationship direction was corrected\")\n", + " # Use LLM to find additional potential errors and get the mapping for values\n", + " llm_output = validate_cypher_chain.invoke(\n", + " {\n", + " \"question\": state.get(\"question\"),\n", + " \"schema\": enhanced_graph.schema,\n", + " \"cypher\": state.get(\"cypher_statement\"),\n", + " }\n", + " )\n", + " if llm_output.errors:\n", + " errors.extend(llm_output.errors)\n", + " if llm_output.filters:\n", + " for filter in llm_output.filters:\n", + " # Do mapping only for string values\n", + " if (\n", + " not [\n", + " prop\n", + " for prop in enhanced_graph.structured_schema[\"node_props\"][\n", + " filter.node_label\n", + " ]\n", + " if prop[\"property\"] == filter.property_key\n", + " ][0][\"type\"]\n", + " == \"STRING\"\n", + " ):\n", + " continue\n", + " mapping = enhanced_graph.query(\n", + " f\"MATCH (n:{filter.node_label}) WHERE toLower(n.`{filter.property_key}`) = toLower($value) RETURN 'yes' LIMIT 1\",\n", + " {\"value\": filter.property_value},\n", + " )\n", + " if not mapping:\n", + " print(\n", + " f\"Missing value mapping for {filter.node_label} on property {filter.property_key} with value {filter.property_value}\"\n", + " )\n", + " mapping_errors.append(\n", + " f\"Missing value mapping for {filter.node_label} on property {filter.property_key} with value {filter.property_value}\"\n", + " )\n", + " if mapping_errors:\n", + " next_action = \"end\"\n", + " elif errors:\n", + " next_action = \"correct_cypher\"\n", + " else:\n", + " next_action = \"execute_cypher\"\n", + "\n", + " return {\n", + " \"next_action\": next_action,\n", + " \"cypher_statement\": corrected_cypher,\n", + " \"cypher_errors\": errors,\n", + " \"steps\": [\"validate_cypher\"],\n", + " }" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The Cypher correction step takes the existing Cypher statement, any identified errors, and the original question to generate a corrected version of the query." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [], + "source": [ + "correct_cypher_prompt = ChatPromptTemplate.from_messages(\n", + " [\n", + " (\n", + " \"system\",\n", + " (\n", + " \"You are a Cypher expert reviewing a statement written by a junior developer. \"\n", + " \"You need to correct the Cypher statement based on the provided errors. No pre-amble.\"\n", + " \"Do not wrap the response in any backticks or anything else. Respond with a Cypher statement only!\"\n", + " ),\n", + " ),\n", + " (\n", + " \"human\",\n", + " (\n", + " \"\"\"Check for invalid syntax or semantics and return a corrected Cypher statement.\n", + "\n", + "Schema:\n", + "{schema}\n", + "\n", + "Note: Do not include any explanations or apologies in your responses.\n", + "Do not wrap the response in any backticks or anything else.\n", + "Respond with a Cypher statement only!\n", + "\n", + "Do not respond to any questions that might ask anything else than for you to construct a Cypher statement.\n", + "\n", + "The question is:\n", + "{question}\n", + "\n", + "The Cypher statement is:\n", + "{cypher}\n", + "\n", + "The errors are:\n", + "{errors}\n", + "\n", + "Corrected Cypher statement: \"\"\"\n", + " ),\n", + " ),\n", + " ]\n", + ")\n", + "\n", + "correct_cypher_chain = correct_cypher_prompt | llm | StrOutputParser()\n", + "\n", + "\n", + "def correct_cypher(state: OverallState) -> OverallState:\n", + " \"\"\"\n", + " Correct the Cypher statement based on the provided errors.\n", + " \"\"\"\n", + " corrected_cypher = correct_cypher_chain.invoke(\n", + " {\n", + " \"question\": state.get(\"question\"),\n", + " \"errors\": state.get(\"cypher_errors\"),\n", + " \"cypher\": state.get(\"cypher_statement\"),\n", + " \"schema\": enhanced_graph.schema,\n", + " }\n", + " )\n", + "\n", + " return {\n", + " \"next_action\": \"validate_cypher\",\n", + " \"cypher_statement\": corrected_cypher,\n", + " \"steps\": [\"correct_cypher\"],\n", + " }" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We need to add a step that executes the given Cypher statement. If no results are returned, we should explicitly handle this scenario, as leaving the context empty can sometimes lead to LLM hallucinations." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [], + "source": [ + "no_results = \"I couldn't find any relevant information in the database\"\n", + "\n", + "\n", + "def execute_cypher(state: OverallState) -> OverallState:\n", + " \"\"\"\n", + " Executes the given Cypher statement.\n", + " \"\"\"\n", + "\n", + " records = enhanced_graph.query(state.get(\"cypher_statement\"))\n", + " return {\n", + " \"database_records\": records if records else no_results,\n", + " \"next_action\": \"end\",\n", + " \"steps\": [\"execute_cypher\"],\n", + " }" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The final step is to generate the answer. This involves combining the initial question with the database output to produce a relevant response." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [], + "source": [ + "generate_final_prompt = ChatPromptTemplate.from_messages(\n", + " [\n", + " (\n", + " \"system\",\n", + " \"You are a helpful assistant\",\n", + " ),\n", + " (\n", + " \"human\",\n", + " (\n", + " \"\"\"Use the following results retrieved from a database to provide\n", + "a succinct, definitive answer to the user's question.\n", + "\n", + "Respond as if you are answering the question directly.\n", + "\n", + "Results: {results}\n", + "Question: {question}\"\"\"\n", + " ),\n", + " ),\n", + " ]\n", + ")\n", + "\n", + "generate_final_chain = generate_final_prompt | llm | StrOutputParser()\n", + "\n", + "\n", + "def generate_final_answer(state: OverallState) -> OutputState:\n", + " \"\"\"\n", + " Decides if the question is related to movies.\n", + " \"\"\"\n", + " final_answer = generate_final_chain.invoke(\n", + " {\"question\": state.get(\"question\"), \"results\": state.get(\"database_records\")}\n", + " )\n", + " return {\"answer\": final_answer, \"steps\": [\"generate_final_answer\"]}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Next, we will implement the LangGraph workflow, starting with defining the conditional edge functions." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [], + "source": [ + "def guardrails_condition(\n", + " state: OverallState,\n", + ") -> Literal[\"generate_cypher\", \"generate_final_answer\"]:\n", + " if state.get(\"next_action\") == \"end\":\n", + " return \"generate_final_answer\"\n", + " elif state.get(\"next_action\") == \"movie\":\n", + " return \"generate_cypher\"\n", + "\n", + "\n", + "def validate_cypher_condition(\n", + " state: OverallState,\n", + ") -> Literal[\"generate_final_answer\", \"correct_cypher\", \"execute_cypher\"]:\n", + " if state.get(\"next_action\") == \"end\":\n", + " return \"generate_final_answer\"\n", + " elif state.get(\"next_action\") == \"correct_cypher\":\n", + " return \"correct_cypher\"\n", + " elif state.get(\"next_action\") == \"execute_cypher\":\n", + " return \"execute_cypher\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's put it all together now." + ] + }, + { + "cell_type": "code", + "execution_count": 19, "metadata": {}, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "\n", - "\n", - "\u001b[1m> Entering new GraphCypherQAChain chain...\u001b[0m\n", - "Generated Cypher:\n", - "\u001b[32;1m\u001b[1;3mMATCH (:Movie {title: \"Casino\"})<-[:ACTED_IN]-(actor:Person)\n", - "RETURN actor.name\u001b[0m\n", - "Full Context:\n", - "\u001b[32;1m\u001b[1;3m[{'actor.name': 'Joe Pesci'}, {'actor.name': 'Robert De Niro'}, {'actor.name': 'Sharon Stone'}, {'actor.name': 'James Woods'}]\u001b[0m\n", - "\n", - "\u001b[1m> Finished chain.\u001b[0m\n" - ] - }, + "data": { + "image/png": "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", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from IPython.display import Image, display\n", + "from langgraph.graph import END, START, StateGraph\n", + "\n", + "langgraph = StateGraph(OverallState, input=InputState, output=OutputState)\n", + "langgraph.add_node(guardrails)\n", + "langgraph.add_node(generate_cypher)\n", + "langgraph.add_node(validate_cypher)\n", + "langgraph.add_node(correct_cypher)\n", + "langgraph.add_node(execute_cypher)\n", + "langgraph.add_node(generate_final_answer)\n", + "\n", + "langgraph.add_edge(START, \"guardrails\")\n", + "langgraph.add_conditional_edges(\n", + " \"guardrails\",\n", + " guardrails_condition,\n", + ")\n", + "langgraph.add_edge(\"generate_cypher\", \"validate_cypher\")\n", + "langgraph.add_conditional_edges(\n", + " \"validate_cypher\",\n", + " validate_cypher_condition,\n", + ")\n", + "langgraph.add_edge(\"execute_cypher\", \"generate_final_answer\")\n", + "langgraph.add_edge(\"correct_cypher\", \"validate_cypher\")\n", + "langgraph.add_edge(\"generate_final_answer\", END)\n", + "\n", + "langgraph = langgraph.compile()\n", + "\n", + "# View\n", + "display(Image(langgraph.get_graph().draw_mermaid_png()))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can now test the application by asking an irrelevant question." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ { "data": { "text/plain": [ - "{'query': 'What was the cast of the Casino?',\n", - " 'result': 'The cast of Casino included Joe Pesci, Robert De Niro, Sharon Stone, and James Woods.'}" + "{'answer': \"I'm sorry, but I cannot provide current weather information. Please check a reliable weather website or app for the latest updates on the weather in Spain.\",\n", + " 'steps': ['guardrail', 'generate_final_answer']}" ] }, - "execution_count": 6, + "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "chain = GraphCypherQAChain.from_llm(\n", - " graph=graph,\n", - " llm=llm,\n", - " verbose=True,\n", - " validate_cypher=True,\n", - " allow_dangerous_requests=True,\n", - ")\n", - "response = chain.invoke({\"query\": \"What was the cast of the Casino?\"})\n", - "response" + "langgraph.invoke({\"question\": \"What's the weather in Spain?\"})" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's now ask something relevant about the movies." + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'answer': 'The cast of \"Casino\" includes Robert De Niro, Joe Pesci, Sharon Stone, and James Woods.',\n", + " 'steps': ['guardrail',\n", + " 'generate_cypher',\n", + " 'validate_cypher',\n", + " 'execute_cypher',\n", + " 'generate_final_answer'],\n", + " 'cypher_statement': \"MATCH (m:Movie {title: 'Casino'})<-[:ACTED_IN]-(a:Person) RETURN a.name\"}" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "langgraph.invoke({\"question\": \"What was the cast of the Casino?\"})" ] }, { @@ -304,10 +1065,8 @@ "source": [ "### Next steps\n", "\n", - "For more complex query-generation, we may want to create few-shot prompts or add query-checking steps. For advanced techniques like this and more check out:\n", + "For other graph techniques like this and more check out:\n", "\n", - "* [Prompting strategies](/docs/how_to/graph_prompting): Advanced prompt engineering techniques.\n", - "* [Mapping values](/docs/how_to/graph_mapping): Techniques for mapping values from questions to database.\n", "* [Semantic layer](/docs/how_to/graph_semantic): Techniques for implementing semantic layers.\n", "* [Constructing graphs](/docs/how_to/graph_constructing): Techniques for constructing knowledge graphs." ] @@ -336,7 +1095,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.4" + "version": "3.11.5" } }, "nbformat": 4, diff --git a/docs/docs/tutorials/qa_chat_history.ipynb b/docs/docs/tutorials/qa_chat_history.ipynb index 2e0b09b2d1713..3acb3368a7bc3 100644 --- a/docs/docs/tutorials/qa_chat_history.ipynb +++ b/docs/docs/tutorials/qa_chat_history.ipynb @@ -51,8 +51,6 @@ "\n", "We will need to select three components from LangChain's suite of integrations.\n", "\n", - "A [chat model](/docs/integrations/chat/):\n", - "\n", "import ChatModelTabs from \"@theme/ChatModelTabs\";\n", "\n", "" @@ -78,8 +76,6 @@ "id": "da14773e-ac98-4a97-944b-4c6ec028d195", "metadata": {}, "source": [ - "An [embedding model](/docs/integrations/text_embedding/):\n", - "\n", "import EmbeddingTabs from \"@theme/EmbeddingTabs\";\n", "\n", "" @@ -105,8 +101,6 @@ "id": "22fdc314-b91d-4820-b0a8-873b5b6e76f5", "metadata": {}, "source": [ - "And a [vector store](/docs/integrations/vectorstores/):\n", - "\n", "import VectorStoreTabs from \"@theme/VectorStoreTabs\";\n", "\n", "" diff --git a/docs/docs/tutorials/rag.ipynb b/docs/docs/tutorials/rag.ipynb index 631b5537aa9bc..959c13cf9bc20 100644 --- a/docs/docs/tutorials/rag.ipynb +++ b/docs/docs/tutorials/rag.ipynb @@ -122,8 +122,6 @@ "\n", "We will need to select three components from LangChain's suite of integrations.\n", "\n", - "A [chat model](/docs/integrations/chat/):\n", - "\n", "import ChatModelTabs from \"@theme/ChatModelTabs\";\n", "\n", "" @@ -149,8 +147,6 @@ "id": "f1b78672-f21e-4827-843e-59514d18ca20", "metadata": {}, "source": [ - "An [embedding model](/docs/integrations/text_embedding/):\n", - "\n", "import EmbeddingTabs from \"@theme/EmbeddingTabs\";\n", "\n", "" @@ -176,8 +172,6 @@ "id": "859ffca8-055e-4f5a-95fe-55906ed1d63f", "metadata": {}, "source": [ - "And a [vector store](/docs/integrations/vectorstores/):\n", - "\n", "import VectorStoreTabs from \"@theme/VectorStoreTabs\";\n", "\n", "" diff --git a/docs/docs/tutorials/sql_qa.ipynb b/docs/docs/tutorials/sql_qa.ipynb index efd996ef238af..4f67b1624ae05 100644 --- a/docs/docs/tutorials/sql_qa.ipynb +++ b/docs/docs/tutorials/sql_qa.ipynb @@ -305,12 +305,12 @@ "metadata": {}, "outputs": [], "source": [ - "from langchain_community.tools.sql_database.tool import QuerySQLDataBaseTool\n", + "from langchain_community.tools.sql_database.tool import QuerySQLDatabaseTool\n", "\n", "\n", "def execute_query(state: State):\n", " \"\"\"Execute SQL query.\"\"\"\n", - " execute_query_tool = QuerySQLDataBaseTool(db=db)\n", + " execute_query_tool = QuerySQLDatabaseTool(db=db)\n", " return {\"result\": execute_query_tool.invoke(state[\"query\"])}" ] }, @@ -407,7 +407,7 @@ "outputs": [ { "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAKIAAAFNCAIAAAAM9SOvAAAAAXNSR0IArs4c6QAAIABJREFUeJztnXdcU9cewE+Sm5CQQSBhQ5giIioiIqAiuEFcuHDUVVtb7bDWZ33W9vlqx+u22tq+Vm197j1QZAg4UFkCFUUFHMgKkED2vEneH/FRnjKChnuRc79/8CE3957zS765555z7xkkk8kECPo6ZLwDIMACQjMUEJqhgNAMBYRmKCA0QwGCdwDtIKrTKKUGldygVRt1GiPe4ViEDZ1MQUi2HAqDTXH1ZuAdztOQek+7+fE95YNS5cNbSnd/hkZptGVTuI5UowHvsCyDxiC3NOpUMoPRYKy6q/YNZvoEMwOHs0kkEt6hgd6iuaZCdTVZzHelOXnSfYKZLG5vLGMsx2Q0PbilfHhLWXVHFTbefsgYLt4R9QLNFw42yJvRkVN5TgI6vpFYHQNquposqixWxC1zcfXBsyTHU7NUpD/41eOpK13d/WzxigEDlDI0dY+wfyg7eKQdXjHgplklR4/9UDN/vYBKg6K2n3200c2X0X8YG5fc8dHcVKtN21O/aKM39lnjSNahRgabEjmFh33WOJxJJqPp8LfVsDkGAIxNcpKK9BXFcuyzxkFz6n+EizYIsM+3NzB5icv9m8rmBi3G+WKtuSxXRqOTuU40jPPtPQwIZ+ecEmOcKdaaryWLoqbyMc60V+E1gGnQm2or1VhmiqnmW1elQ8faM5gULDPthYyczivLk2KZI6aa7xbI3fwwugdiMBhKSkrwOrxznDzp1eVqpRTtofSfBTvNaoVBItJhdlt/y5Ytn3/+OV6Hd4lvMPPBLWXPpf8U2Gl+VKYMiuBglp1W+5y1WfONhOc+3EL8Q1jCR9hdnrF7SNAs1DFYPXJVzsnJ2b59e01NjZub2+zZs+fNm7d58+aMjAwAQFhYGADgzJkzbm5uZ86cOXLkSGVlpa2tbWRk5Lp16+zt7QEAFy5c2LBhwzfffLN3797bt28vWbKkoaHh2cOtGzPHgVr3QGPdNDsBO80quYHnav12lEql+uCDD3x9fTdt2lRZWdnU1AQAWL58eUNDQ21t7SeffAIA4PP5AIDS0lJvb+/4+Pjm5uZDhw4plcqtW7e2pvPll1+uXr36zTffFAgEGo3m2cOtiy2HopJh95AVS82oLdv62TU3N2u12rFjx8bFxbVuFAgEXC5XLBaHhIS0bty4cWPr018EQXbv3q3Vam1sbMxb5s2bl5CQ0Lrzs4dbF4RKRqgkjcpAt8Wi3YGdZgqFRKFaP1l3d/fBgwfv2rWLwWAkJibSaB0WGHq9/tChQykpKUKhkE6nG43GlpYWFxcX87vh4eHWD65TGGyK0YDRAwXsqmBUG7JSav1iikQibdu2LSEhYevWrYmJiUVFRe3uZjKZ1qxZs3v37mnTpv3444/x8fEAAKPxrx5ItraYPgw1Gk3SJn1PFG/tgp3mnrsasVisDRs2HD9+nMVirV27VqVSmbe3ffhWVFSUn5+/YcOGBQsWBAcH+/v7d5lsjz67U8kMthzsbhNhp9neiYaiPdJ/z9z4cXd3T0pKUigUdXV1AAAGgyEWi1vPV4lEAgAIDAxs+7Lt2fwUTx1udZQyvWcAduUHZfPmzdjkZMOg5JwSDYm2cscovV6fmJjY1NQkEokOHz6s1WpXrVqFIIhcLk9LS2tqapLJZEKhcODAgUePHq2vr2cymVlZWTt37tTr9WFhYd7e3g8ePLhw4cLcuXO53L9ie+pwLy8v64Z987KUZU9188XoZhF2mum2lNvXZW5+dOtekJRK5ePHj7Ozs7OyshwdHTdv3uzh4QEA8Pf3l0qlqampRUVFXC43NjbW19c3OTk5OTkZRdFPP/20sbGxpKQkISGhXc1PHW71CtqlE00jJjn00I2EZ8G090hRZjPVhjJoFG5donoJUrEu57RoynIr33LpBEy7yobE2P+8/n4nmvPy8j744INnt7PZbLm8/U4X77777syZM60a5tMoFIq2Teq2DB48+ObNm89uf+ONN5KSkjpKMPdcc78QTDuFYd0XrDCjWa8zddQfSqPRNDc3dytBOzs7JpNppejax2g0CoXCbh3C4XBYLFa7bzXVajMPNCT9DdP+Mzh0+Tv9c+2UV10RODp0PsvFo41+Q1hYVrPx6QsWPcvx0DfV2OfbG7h+TsziIhg7xkezvRMtYgrvzL9rsc8aX0outUhF+rAJDthnjVt3/IYqTV5q87SV2NU28eXPSxKFFB05DZ9+cLhdIJ296MFRnD1bHqkU2PWVwYvsIw2SJj1ejvEfKicT67OONDo406Km8hBqH6yU3c6VXksWRyY4BEfiOS4S/xGRAIA/L0uuJYvDJtq7+TLc/XrdGPDnQNKke3hLea9A7iSgR03l0fHuzNorNJu5mSOpLFaI6nTBURyTCTDtEI4DAnrHMPAuQRAgE6NKGarXGqvuqIxG4BPMDI7icB17xcCDXqTZjFZtqC5Xy8R6pRRF9SaV3MrPLltaWpqbm/38/KybLNuBakCNTA7C5lKcvRkOzr3Cbiu9TnNPk5mZmZaW9tVXX+EdCKb0wVoPwbMQmqEAOs1UKtXR0RHvKLAGOs16vd7clxsqoNNMoVAYjL7QNO8W0Gk2GAxqNaZji3sD0GlGEITNxmc6HxyBTjOKoh31N+rDQKeZSqW2DqiBB+g06/X67nbs6gNAp5lMJncynK6vAp1mo9Go0+nwjgJroNMMJ9BpptFozs7OeEeBNdBp1ul0DQ0NeEeBNdBphhPoNJPJZOKedt/HaDQS97T7PsTzZiggnjcT9Fmg00x0K4AColsBQZ+F0AwF0Gmm0WhOTk54R4E10GnW6XSNjY14R4E10GmGE0IzFECnmWg3QwHRbibos0CnGUEQ84I1UAGdZhRFW1pa8I4Ca6DTDCfQaSaRSKSXZHYiKwKdZpPJBNusOjBqJvppQwHRTxsK4OzyB8v0b3PmzNFqtSaTSa1Wq9VqHo9nMpm0Wm16ejreoWEBLGdzeHh4bW1tfX29RCLRarV1dXX19fUODjjMYI4LsGieP3++eX2qVmxsbGbPno1fRJgCi2YPD4+oqKi2Vyh3d/dZs2bhGhR2wKIZALBw4UJ3d3fz/zQaLSkpCZ77JBBp9vDwGD16tPmEdnd3T0xMxDsi7IBIMwBgwYIF7u7u5lMZ71gwxcqLB2rVBlGtTqvpqfVwXxhuTPi827dvD+0/+cEtJd7BtA+FQrJ3pnIcrLl0vTXbzWl7hY9uq9z9GD226jEUsOyRx3eU9i60iDgHZwHdKmlaRzOqN57YXjtwpL0gsP2FEQm6i1qJpv1RO2W5i4OLzYunZp1r88kf60In8AjHVoTBRGas9jq1o04ptcI6XVbQXFEs57nbOAuwXvcQBiKnO+Wldm8J3HaxguamGi2diek60PDAcaDWVFihH6oVNGvVRg7PmtVCglbY9jQyxQr1J+toNvb9ZR5xwgSkTboXv1sH1+0RaCE0QwGhGQoIzVBAaIYCQjMUEJqhgNAMBYRmKCA0QwGhGQpeJs0PHlROmx6bc/Wi+aVCoSivuIt3UC8HL5NmBEFYLDZCefLQc8XrSefPn8Y7qJeDl+M5sclkIpFIAoH3gf1nWjf2/lXDzGHjHQXA4Ww2mUxTp8V88+2nrVv+/uEaqVRi/l8sFo0dPzw1LVkqlcSOCzt8ZO+nn2+KmzLq3fdeS01Ljh0XFjsurPBGHgAgaUFCS0vzqdNHY8eFJS1IaE3t9JljC1+ZMSkuasmy2f/Zu1Or1XYZ0ukzxxYvnTUpLurN1UuOHN2XOHuieYqS2HFhBw7+0TbOVW8tNf+v0Wh+/OnbmbMmTJka/cabr2RlPxlvd/HShdhxYTk5F99+99UJkyK++vqT2HFhubk5rYmcSzkVOy5MJMJ6en6sz2YSiRQ1csy165eNRiOZTG5oEOblXU1NS5439xUAwKXLmRQKJSpqjMloBADs27dr+vQ5337zC4VC4drZv/7a27/+tt2czuZ/fLX+g7dChgybM3sh9X9rPv6x59ejx/Ylzkzy8vKtrn50+Mh/amofb9zwSSfx7PnPb3/s+feIESPnJy2RSFr27d+NIF18J0aj8cNN7wmFdQsXLONyHUpKCrd8ulGjUcfHTTfv8MP2L1csX7182Zse7oKyO6Vp6WcjIkaZ37p8OTM4eAifj/XIWxwK7Zjo8enp58rKSoODh6SmJZtMprPnTv5P84XQ0HAOm2M+v4OCBq14dXXrgUMGh7b+H9g/CEEQHo8/aFCIeYtI1LT/wO5NH342JnqceQuP5/j91i/eWr2Ow+a0G4lUKtl/YHdExKgvPttq3tLYKLx0ObPz+C9fybpZWnxwf7LZ1vhxk9Vq1fETB1s1z5wxb9KkJwVM3ORpu3//WSaXcdgcmVxWVFywetX7L/DlPSc4aA4Li2CxWDlXLw4cODgtLXlK/IzzqWdKSm54enqVlpas/9vHrXuGhoZbnuyNG3koin72+abPPt9k3mLuWyNqauxIc+mtEr1ePy2hewPmcnNzUBRdsGha6xaDwcBk/tWrtW3YE8bH79z1U3Z2+vRps69evWgymWJjJnQrO6uAg2YqlRoZGX312qXw8KjGpoYli1+XSiXnUk4GBQ02l9ite9Lp3ZhcU9wsAgB8/tlWJ8f/m1rEzc2jo0NkMikAgO/Yvem1W1rEPB7/u29+abuR0qaot2X81cmVx+MPHx6Zln52+rTZFy9dGDZshJ0dt1vZWQV8atox0eMzMlJ+2/ljVGS0o6PT1KmzNn20tqrqobnEtjydtn3h2P87UCDwtvBwHs8RACAWNfXz7//UW53UkNlsjkTS4uzsamNjUUf5+LjpH//jb2VlpUVF+evXfWzBEdYHn3ZzWFgEk8m8e/f21KmzAADDwyKcHJ0rKu91q0Bj0Blisaj15dChw0kk0slTh1u3dDkFq59vPwRBzqWcevYtCoXCZnNE4idVYpPJ1Nj4ZA330NBwg8FwJvmYhRlFRoy2s+N+9sVHCIKMHBlj8eezJviczTQaLTIyuqysNGzYCPOpk5CQuGv3jrYldpcMGjQ0Myv1wME/2GzOwKDBvr7+iTOTjp84uHHTe6NGxojFolOnj3zx+Q8B/QI7SoHPd5wSP+P0mWN//3DNqJExCoX8Sk5267vhwyMz0s+FDh3uYM87cnTf48eP+vULNF9uk8+e+OXfP9QL6wL6BVZWludczf5j9zE6vf3xTgiCxIwZf/rMsdiYCba2+AxawO32SEz0eH+/gNayMW7ytNu3b3arxF75+jvNzaK9+3Zy7exXrVrr6+u/etVaJyfnkycPFxRc5/H4o0fFOvK7uO6uenMtglAzs1KLiwt8fPzd3Dxqah6b31q96n2tVvuvL//BZLKmTZ2t0WrM13Iqlfr1lz/9tnN7Vlba2bMnPDwE06bO7rwZNiAw+PSZY+PGTrb801kXK3T1Tt0jdPNj+QzqCwOoftj25aXLmSeOWXl6oRMnDv2x59/Hj6VTqd0bt2Aygr1bKld/5/+CAbwcNztfhN92/tj2OtoKh223f1+P3xIvLS1JSz+bln520cJXu+vYivR9zXPnvpKQ0M78E2QSFtXPgsLrpbdK3li5JnHmPAyy6wii0O7VWKvQfpkeRBI8N4RmKCA0QwGhGQoIzVBAaIYCQjMUEJqhgNAMBYRmKLCCZhaXgsntYRgxGk0u3lZYhtgKfph21MZq6JbKxQZxvcZotMKkqlbQ7BXIUEj0L54OwbM0VWv8Q5gvno4VNDu42HgH2V4+LnzxpAjaUlEkrX+oGhpjhWWIrTaf9p18WVme3CeYzXen0+jEtfpFMInqtDKxvv6+ctY7HfY+7hbWnDZd+Eh967pM0YJKmnpvGW4wGIxGI44dObqE704nkUxeA2yDo+yslSYsq8q1kpmZmZaW9tVXX+EdCKYQpSsUEJqhADrNVCrVxcUF7yiwBjrNer1eKISu7QedZgRBeDwe3lFgDXSaURQVi8V4R4E10GmGczV26DTr9fqmJqxneMEd6DQjCMLn8/GOAmug04yiqEgksmDHPgV0muEEOs0UCoXJtMIT3JcL6DQbDAalspeu3NxzQKeZqIJBAVEFI+izQKeZKLShgCi0oYBoUEEB0aAi6LNAp5noVgAFRLcCgj4LoRkKoNNMo9GcnLq36kEfADrNOp2usbER7yiwBjrNcEJohgLoNBPtZigg2s0EfRboNBMNKiggGlQEfRboNCMIYmdntalbXhag04yiqFQqxTsKrIFOM5VKJdrNfR+9Xk+0m/s+cHb5g2X6t6VLl5rn95NKpQqFwtPT02g0qlSqkydP4h0aFvT9NSLNODs7Z2Zmtr68c+cOAMDd3R3XoLADlkL7lVdesbd/eirb+Ph4nMLBGlg0BwcHDx06tO0VytPTc948PFdhxRJYNAMAlixZ4uDg0Ppy8uTJXC4X14iwAyLNAwcODAkJMZ/QAoEAnlMZLs0AgGXLlrm6upJIpIkTJ8JzKj9nTdtkNMklKIlE6oF4ehZP14DQwSPLysqmxs2Vt6B4h/M8UGkkOpPS3aO6125+VKYsuSSpqVDz3Ww0SkN3MyN4cWw5FKXUEBTBHjG5G7dsu6H5bqG8LFc2It6Rw6M9b5AEVkAp1T8qUzQ+Vk99zdXCMtVSzXfyZeVFirHz3V44SALrUFEsrbmnnLbSIiMWVcH0euOdfDnhuFfRb6gdh0erKJFbsrNFmpvrdDqN8YUDI7AydCal4ZHWkj0t0ixr1rv62L5wVARWxsHVRmvZ6WeRZgMK1IqXsvnRtzEagMKyZiFct0eghdAMBYRmKCA0QwGhGQoIzVBAaIYCQjMUEJqhgNAMBYRmKOizmoXC+nphHd5R9Bb6pubaupoFi6bdu1eGdyC9hb6p2YCivXxsGMbh9eAYquKSwt92/nj/frm9vcPQkOErXl3N4/G/3/pFesa5Pb8fd3JyBgB89/3n2dnpu3YednJy1mg0O3f9lJmVqtNpPT285s59ZWzsRHNSDQ3Cnbt/Kii4rlIp/fwC5s5ZFBszYdfuHYeP7E1PvW7e5+69sjdXLf7XF9sEAu8ly2YDAP75yYZ/AjBpUsKG9ZsBAPXCuh07vrtRlEej2QT0C1y+fFVg/6DOP4JGo9m1e0f2xXS1WhU6NJzH48tk0o8/+qLwRt7f1q/+afvvQUGDzHvGTRk1c8a81197u5OMlr0618fbz9vb78TJQ1qtZt7cxQcO/n70SKod58nsCZ998VHZ7Zv79522uoueOptvFOWv/+Atby/fde9/NHf2ops3i9aue0Oj0by24m0mk/XTjm8BAAWFuclnT7z77gYnJ2ej0fjhpveuX7+8cMGy99Zs9Pfvv+XTjSnnTwMAxGLR6reXFhbmJs1b/P57H/r6+ItEnc0Rw3Pgf7jxUwDAsqVvbNu6c9GC5eZE3n5nuUwufWv1upWvv6PX699ds+Lhw/udpGMO6fiJg6NHxa55Z4Ozs2vy2RNdfvDOMyoouH733u3PP/1+yyffTk1INBgM2dnp5rf0en1u7pWxYyd185u2iJ46m7f/+PXUhMR33l5vfhkWFrFk2eyCwuujR8WueXfDRx+vy8pO//mX72NjJowfNxkAcPlK1s3S4oP7k/l8RwDA+HGT1WrV8RMH4+Om/2fvbxJJy+6dhwUCbwDApEkJnWdNo9EC+gUCAAQC70GDQswb9+7bac91+PbrnxEEAQBMGB+/aPGMsykn3169rqN0cnNziooLVr7+TtK8xQCACRPibxTldfnBO8+IgiAfffg5g8Ew7zx8eGRa+tkZ0+cAAAoLcxUKxbixk7vzNVtKj2huamqsqnpYW1t99tz/jR5ubGwAAIwaGTN6VOyWTzfy+Y5r1vzd/FZubg6KogsWTWvd2WAwMJksAEBe/tXQocPNjp+bvLyrjU0N8QmjW7fo9fqmxoZODrlRnA8AmJowy4oZDRgQ3OoYADB50tR/frLh8eNHAoH3xcsX/Pz6eXv7dis7C+kRzRJpCwBgyeLXo0ePbbvdweHJOl9Tpsy8kpM9ccIUDptj3tLSIubx+N9980vb/SkIAgBoaWkeFjriBUNqbhFHRo5+fcXbbTeaf0YdIZfLWCxWd6c26DwjBp3RdvvIqDEcjl1a+tmlS1Zeu3ppwYJl3crLcnpEs/lTabWadk9BFEV//W2bra3tseMHxo2d7OvrDwBgszkSSYuzs6uNjc1T+7NY7OaWdmYL6dboHjabI5VKulUk8HmOCoVCrVa3Pf+6zLpbGVGp1PHj49IzzgUNGKRQKsbG9siFuaeqYK4ubs7OLudTz6jVavMWFEX1er35/737dj5+/OiH73cKPL23fLZRo9EAAEJDww0Gw5nkY62JtB4bOnR4UVF+23sdKIoCAOzs7PV6vVT2ZPYnYZsdbGzoAACxqKl1S2ho+K1bf94rv/Ns+h0REDAAAJCScurZt+y5DgAAkfhJ+mKxqPXTdTejyZOmikRNO375ftCgEGdnl85Dem4omzdv7nInUZ1O0qQXBHZWxLWFRCI5O7umpJy+dv2yyQTKykq3bf9Kj+qDggZVVpb/68t/zE9aMm7c5EHBIQcP7ZFKWyIiRnl7+xUU5qaln5XKJC0tzalpZ7f/+FXClEQEQby9fM+nnk7POIeiaG1t9aFDe27cyIuKimbaMk+fOSYSNTo7u94ozNvx83cajXr8+DgPd08mk5mRkVJ6u8TWlnnjRl5AvwEBAQMyLqRkZKQYDIbqmqr9+3dfupLZ+dkjEHhfvpJ5ITNVKpNIWlouZJ7Pz78mEHiPGTOezeakZ5y9d6/M29vvUdWDr7/5RNwsCg4eMmzYCF/ffh1ldPrMUXuuw5gx49vmwnPgZ19Mr6l5vGD+0i4beE8hb9aLazWBw9ld7tkjmgEAXgKfwP5BN28Wp2ecu3P3lp9vvwkTptjZcTd+uIZGs/now88RBLG3d6DT6fv27/b3C/Dx8YsZM0GhkF28mHH5SpZSpYibPH3QoBAymWxnx42MGP3wYWXGhZSionwKgsTGTPT19edy7V1d3DMzz584eUilUs6ZvTDn6kWzZhKJFBQ0OL/gWlZ2Wr2wbtTIWDdX95FRY6oeP8zIOFdQeJ3JZE2Jn9F5fYdEIkVGjK6vr71yJauwMNeWyVQo5M5OLmPGjCeTycHBIfkF148c3VdRcXfp4pXXrl8eEBg8bNgIDpvTUUbtagYAlJffeVT1YP3fPqbT6ZZ/w93SbNEYqrsF8kdlqpEznLsVRN/DfH/j44++sG6yH328DjWgX3y2tbsH1laq7uVLpr/Z9aAnWGYS6oh31qx4+LDy2e1RUWP+/sE/ezr3jAvnL2SeLyi4/u03P/doRrBr/njTF3pU/+z2p1o+PcT586f1qP7Lf20fGhLWoxnBrtl8081Cft91xLq5f/ftLxbsZQX65hMqgqcgNEMBoRkKCM1QQGiGAkIzFBCaoYDQDAWEZiggNEOBRZopFMBgd3s6UIKehkwhsR0sul1tkWY7J2pdZRd9LQiwR1SrsbG1yKBFOzl50GkMonjvdWhVqJuPRT0RLJU3JNoubU/ti0VFYE1Kc5q1KoNPsEVderox0fLju8prZ8XhcY52fBqNTlyqcaNZqK0qU+g06Pj5lvbn6d606Q1VmqKslupytS2LolK8lNOmm4DJZDKRSS/rNYjFpZLIpoEjOIOju7GIw3OuKqdRGkjkl28RBADApUuXsrKy/vnPHu8A1EPQbJ7nJ/qcvUeeY7mFXgKFajQCrQ1kNUq4Pi20QKcZQZC2i45BAnSaURRtbm7GOwqsgU4zlUp1cempoUq9Fug06/V6oVCIdxRYA51m4myGAuJshgIKhcJidWNoZ98AOs0Gg0GhUOAdBdZApxlOoNNMpVL5fD7eUWANdJr1er1IJMI7CqyBTjOZTO7u3A99AOg0G41G89xFUAGdZjiBTjONRnN2hm6uHOg063S6hobOpursk0CnGU6g00wikahUKt5RYA10mk0mU+v8mvAAnWbiQSQUEA8iCfoshGYogE4zgiAcDgfvKLAGOs0oispkMryjwBroNMMJoRkKoNNMtJuhgGg3E/RZoNNMo9GcnJzwjgJroNOs0+kaGztbMLZPAp1mOIFOM1FoQwFRaEMBhUKxtbXFOwqsgU6zwWBQqVR4R4E10GmGE+g0w9nl7zln+XvpWLlyZWFhoclkIpPJRqPR/Nfd3T05ORnv0LAAlrN5yZIldnZ2ZDLZPFrOvDEqKgrvuDACFs1RUVEBAQFttwgEgvnz5+MXEabAohkAsHjxYjs7O/P/JpMpMjLS29sb76AwAiLNbU9oDw+PpKQkvCPCDog0AwCWLl3K5/NNJlNERISXlxfe4WAHXMt0jxgxol+/fhQKZeHChXjHgikv1KCqu69+cEvVWKNVKwwahYFEAjqd0arhWR+j0Wg0GhHkJfh9c3g0VGdksCg8N5qnP90nmIlQn7P0fR7NaoWhIF1Sliels6gcZyZigyA2CEKjIFQyFG1wrDAZTajWoNeiRoNJ1qiUN6g8ApmhMRyPft2+J989zSaTKfuouLxI5hLAY/MZFOrLOkf+S4qiWS1+JLFlkaJn8pwF3Zgopxuaayp12UcbGVxbvrfd88ZJYAXkIpW0Xu49gDFqqr2Fh1iq+U6+7Nq5Ft8R7iTSS7mSSd9DWC62szPFLbVoHhWLLuk1lZr8DJlfhAfhuPfgEsBTqqmZRyyayq7rs7nqjvLSqRZBiKuVwiOwJuLHEjbTMHFRF92eujibVXI0dU8D4bjXwhNwW8Sm4kuSznfrQvO5XQ1ew6AbivJy4RzAv3VNIWnUdbJPZ5rLi+Q6PZnOsumB2AisCceVc/mUuJMdOtN85ZTY0Q+6JZteRuycmWKhvvFxh3ORdqj5/k05g0unMV6Cm4JPYTAYHlSV4B0F1jgIuMUXpR2926Hm8mIVw+6lnJD46OnPjp/5Eu8osIbNZzwo7XBxhw41V5UpOY7MbuVkMpnv+iPVAAALcklEQVREzTXdDK/bdNkC1Ou1PR0DLnT+wckUMsvBprq8/b7J7bebGx9rLp5scQroehBKVfWtM+e31gsr2Gy+i5NvbX35B2uOUhGaTqc5f+Hn4ptper3Wke8VM2phyKAJAIDL1w6WlF6Ijpp//sLPcrnI3S1wzvS/Ozk+6cVR+eBGSsaOOmE5m+Xg7xMWN+FNDpsPAPh6+3wXJ18XJ9+c3CM6vebj9efqGyovXNz9sOpPAIDAIyhh0jue7gMAAIdOfFJYfK41vI1rTzrYuwEAruUfv3T1gFTW6GDvNnTwxJiRi6jUzqqW+UXJ1/KO1QsrbWxs+/tHTJ+ylsW07zz+sntXU9J/ErfUOHDdIsMTI8Jmbv5y8pCB4+bM2GhOc9fetUmJHzOZXACATC7a8nXC3BmbhocmNLfUnTm/tfx+PhWxcXfrHzf+DU/3IADAieSvb5ZlzZm+MTn1B5G4+t03fjdv74jmapmnt3H4RN6zb1E2b97cjuYabdVdNcepi4V8WiTCbb8u53KcEia9YzQZim+mjY1e7O8zzGg07ty7prrm9piRC0IGT0BR3fkLP9vZOXu49a+qvpVfdKZFIpwx5f3BA8cV3UytuJ8/Imw6AKDifsHOve/28xseHZnk5hLw560LRTdThw+dSqEg1/KP19bfo5Aps6atHxQU6+Lk8+BRcXVN2Yhh0/x9hpXfzy8sPhcVPptCQZwdfRqaHgIAli/6Jjx0qiPfi0KmpGf9lpG9K3zYtBHDprNYDpevHhCJqwcFxXTy0a7ln6DbMMOGTnHiexeWpNQLK0KHTDL/rNuNX6tVbfv3Mg6bHz/hTQaDrdOq+/eLaGh6dKfiWnTUAhKJ1CIRnjz3NZPJ9RYMBgAUFJ+tuJ8/Z/qHarV826/LqQg9NnpxgP+I2vp7GRd3Dxwwhs1yuFN+raq6tF5YMSP+/UEDY/19wjq/C6lR6lC13n9IO9bar2Gp5CgZ6frp040/z+t06kXzPuOweQMHRD94VHyn/NrY6CWlZdkPH5VsfP+UHccRABA6eJJWp8q5fnjEsGnmA5ct/IbD5gEARkXMTU79QamSMm3tTp37NiJs5syEdeZ9AvxHfL1t3r3KXLMPChlZOPdTGxrD/G7okMnDQuLM/3u6B/3y+6qHVX/27zfCkS9g2nLlimYfrxDzu1JZU+blPxbO3jI4eKx5ix2bfzz5y+nxa21tO5w5ava0Da3fKZmCZF76Xa/XthYAz8av1sj1eu2goJjQIZNbExkycNyNkpSq6lIfryEFxWdNJlNe4emYUYsAADdvZfXzHW5ryzme/BWL6bBy2Y8UCgIAGDYk7l9bZ+UVnp4xZS0AAEV1s6f/3cszuEsXAACqDaJoUbb7Vvua9Roj1ZbWZbpSaSPdhmn+wCQSiefg3iIRAgDu3LtqMKKffzezdU+j0cCg//Ura7Vlz3UFAMhkTVqtqqHpoai5OrfwVNssJNInc18LPAe2HmXOrrTs4qWrBxqbHtJotgAAuaL9hmPF/XyDAd1/7OP9xz7+3zYTAEAqb+xEM2rQ51w/XPRnaotUSKPSTSajQtliz3XpKH4XZz9vz8EXLv1OozEihs+kIjTzL5VOZ92+c9lbMLiw+NyIYdPzi5IrH95w4nk9fFwyd8YmAMDd8msSacPGLX8VLQaDXiJ78qmpVLqFjgEAVDpFj7R/urevmYyQ9KrO7qqY4fM8NFplfUOlq7M/iurr6sv9fIaZv3EOm//Gsp/+L01yO3khFKr5R2CWNCF2xeCg2LY7sNlPFhOiURltt2dk70rL+nV0ZNKUiatkcvHewxtNpvY7rsjkIgDAq4u+49r9X1WD5+DR0ecymUy7962trr0zMXaFl+eg0rKLF3P2tpt+a/wkEunVxd+nZOw4m7rt8rUDSYn/8PMJRRDqwP6jb9+93D8gUiJtmBC7QqmS5BWe9vYcRCZTBgZGm7+roP6jpkxc3TZZus2TU8LGphs9CFCdUadu/0toX7MtGzGiXY8nCwuZcunqwd373h82JP7+oyKDAZ0YuwIAYMvgKJQt9lzXzqs5bWHQ2eZKcmt1rBP0em3WlT0jhk2fHv9e2zP+L9rUKxmMJ6esJSmbuf+oqOJ+wYI5n4QOngQAEImrLfsIrFlT18eMXPjHgfW/H/jbR+uSbWxsBwePu/Hn+fMZO4ICR3PtnCKHJ+7ev66x6ZG5xDZ/V0qV1PLYOgHVokxOB+dtu1tt2RSD3tBlukwmd0b8WipCFzbeD/ALf2/VXke+AADg7zfcaDRcyz/euqdWp+48KUe+gGvnUlCU3LqnwYCiaPsTX2t1ar1e6+EWaH6pVEoAAMb/nW00GkOuEBuNT1728w0jkUg5eUcsD0allAIA3F37P0lfJTF3Iuv8KHNDjufgPipirkajaJbUPSm3bZiPa25HhiWaX3LtnGvr7w0JHv+/8IY/evxnde0dy8PrMAAtyrZvv0bVvnxnAV0h7rr1+bjm9uGTW2YmrKNQqCQSubmlls3iUSiUYUPi8gpPnU3b3iKpd3ftXyesKC27uP6dwzRah/dbSCTS9Pj39hz8YPu/X40MTzQaDYXFKcNCJkdHtTMwgsXkujr75+QeYbN5Go0iPXsniUQWNtw3v+vnPbSgKPn4mX95ew2xZXAGBo4eFTHvyvVDu/e9P3DAGLlcdDXv2KuvfNf6K3kWgWcwgtDOZ+wYETajXliRdXkPAEDYcJ/P67CcR1H9V9vmDhk43sXZ91r+cTqdxbP3AABQEVpQ4Oiq6lsB/uHmjxkRNiM18xdziW2+Tt0pv/rbnneiRy5gMx3uVlw3Gg3LFn7d5Zf/LDqlzmV4+42j9jVTEJKLD0MuUrH5nV0b7LmuDg7uh09uaW18u7v2X73iVxqN/tqSbSnpPxXfTL9ecNKRJ4gKTzTXJDthUFDM8kXfpWX+eiblezqd5eMd4us9tKOdF87dcvjElr2HP3TkCaZOfrdOWHHl+qEpE99CEGrokLjq2js3SlLK7uUMH5owMHD0tLg1XDunnNyj9ypzOWx+cFCMHaezWwJcO6eFc7acTvn+3qENXp6D3li+Iy3r1yu5h4ODxnR0iE6n9vcJK7qZqtEoXJz9X130betvevDAcW4u/Vrr7eGhU6uqS1trf3yex1uv/Zacti3r0h+ARPJwDRwZMafzL6ojpEKVT3D7nUk67FZwM0dSVqh16d/FcooGg4FCoZj/uXXn4t7DG1cu+6mfb9jzBUrw3Cia1apGydz32i9vOjzDAodzbmR1UfVoaHr08643BvQf5ebST49qS29n06h0R57nC8eMBXfuXW3TxPo/3n5tp7OTD+YRvRCKJlXI6A7bh511Erp2Vlz72OTo02H3QZlMlJ2zt+xejkQqZNDZ3l5DxkUvNd907P3odBqFsrndt+w4Tl1eYnoVGoWu8V7j4k0dDhfqoi/YjnX3A2MEZApcQ61eOmpuCkcm2PkM7PDmdBf+xi90arrfWbcEAtyRNyn5rkgnjrvWHDCU7e5DFT9qsXZsBNZBo9A1V7VMXtxFb+2uS+NR03h8Z1JjJWG614FqDY3lTa98KOhyT4suumMSeUwm2nS//QoLAS4oxOqH+TULP/Akk7seI9GNMVT5ac1VFXqOC8eG2fXDK4IepblaalSrZ7/rbuH+3RsRWXVXmX1ERGPaOPnZIzYvU5OjzyCqkjaUN4+I44VNsHSc3HOOby7Lk93OVShlBibPluPMpDEQYmxVj2LQG+RNaoVIadCjXv1toxN5ZEr3vvDnn62g/qG6okQprNI2VqlpdAqVQaEyKCaUGMhuNagMRCHSaNUo353BtqcEhLK8B9gitOe5h2GdWf5UclQpNeg0vX1GipcLCpVky6YwOQilgz4hlgPLZI6QQ9zFhAJCMxQQmqGA0AwFhGYoIDRDwX8BjVGP6RBcTjQAAAAASUVORK5CYII=", + "image/png": "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", "text/plain": [ "" ] @@ -490,7 +490,7 @@ "outputs": [ { "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAKoAAAF3CAIAAAC6w0eQAAAAAXNSR0IArs4c6QAAIABJREFUeJztnXlYE0cfgCfZEAJJSCDcKLcXyg2KyiEiKoiAiEcVb2s9qrXWqm2ttVptrba1HrVVtLZe9Va8AW8uFRUFUeS+wxEgISHnJt8fa1M+5QgYWHT2fXx8wmR39rf77kxmd2dnSCqVChDAChnvAAjwhNAPNYR+qCH0Qw2hH2oI/VBDwTuAduBVSkV8VCRQSMVKmUSJdzgaQaWREYSkb4DoMxFzGxqJTMI7olYh9czr/rK8poJMUWGWyNyGJmlC6QYUAyMdvIPSFF09cn2NrEmAyqXK0lyx7QB9u0F0pyEGPfA86HH6K4vEKRd4bBMdYwtdu0F0A847Y701Cp+JCrNExc9FLn5szyBDvMP5P3qW/lunqmvLZMPGcywd9PCORfukXKjNShGMnWVu3V8f71he0VP0NzUqjv1YGhxjZt2vpxyarkAqRq8fqza3pXmM7BHVQI/QLxWjhzeXfLCqtz6zpzdFtUJyXC2DTXH1Z+MdSA/QL6iTn/q1bO63dviG0c3cPVujREFAtAm+YeB/3X/sx5KYL2zwjqK78ZtgokRVWal8fMPAWX/C0arIxVZUGv5nYfcTOMWUWyjhFolxjAHP4573RKiQKs2saTjGgC+DhrHunqvFMQA89adcqB023hjHAHDH3Jamz6QUZArxCgA3/Tnpgn6eTJbxO39X5y3xjTDOediI19bx0/9QaG7bTdU+iqIZGRl4rd42LGOdOq6sjivrovzbBh/9KKoqe9lkM4DePZvbuHHj5s2b8Vq9XewHMQqy8Kn/8dFf9Ew0cJhBt21OKpV2bkXspkinV9cQBzd6dUnXbqI18LnLVl8to+oiXZFzUlLSzp07y8rKLC0to6Ojp0yZsn79+oSEBACAl5cXACAuLs7S0jIjIyM2Nhar0gcOHLh8+fIBAwYAABITE9esWbNt27ZDhw49e/Zs1qxZVVVVb66u3ZgNjHTK8/C5/MNHf5MA7YpGX1NT0+rVq+3t7deuXZuXl1dTUwMAmDt3blVVVXl5+YYNGwAAxsbGAICKigqpVDp//nwymXzy5Mlly5ZduHCBRnvVFtmyZcuSJUsWLVpkbW0tkUjeXF270PQRuUyJKlQIpbufCOOkvxG1sNN+u6+urk4qlY4cOTIkJESdaG1tzWazeTyem5ubOjEkJCQ0NBT77OTktHDhwoyMDB8fHyxlypQpYWFh6oXfXF3r0FkUEV/R/U+38dFPJgMKVftnupWVlYuLy/79+/X09KKioqhUamtLkkikmzdvHj58uLCwUF9fHwDA4/HU3w4ePFjrsbWNHh1BURwevuDT9KPSyMIGVOvZkkikHTt2hIWFbd++PSoq6tGjR60tGRsb+/nnnzs5Of3888/Lly8HACiV//Ukw06I7qS+SkZn4VAU8dGvb0BpEii6ImcGg7FmzZrTp08zGIwVK1Y0NTVh6c0fbEql0j///DMyMvKzzz5zc3NzdnZuN9sufS4qkyoBAFRdHFzgo59lrKPsmuOJXaRZWVlNnTpVKBRWVFQAAPT09Hg8nrp8i8ViqVSKNfUBAA0NDa+V/td4bXWtI+LLrQfg08kFn99+6/76d8/UDA3laDdbuVw+ceLE4OBgBweHkydPMhiMXr16AQA8PDzi4uI2b97s5uZmYGDg7+/v6Oj4zz//cDgcoVC4d+9eMpmcl5fXWrZvrq7dsAueNuF18xtZv359929Vh0ouyBIZmukwDbW52yKRqKSk5ObNmzdu3DAxMVm/fj2m39HRkc/nX7169dGjR2w2e/DgwR4eHsnJySdOnCguLl66dKmNjc3p06enT59eXFycmJg4efJkNvu/rjhvrq7FmLHOP27+hgw2DkURt94+T5Ma5DKVZ8/o8oYjEhF67RA3YqEVLlvHrW+diy/799X5LsNZOq00eTIyMrA2+WswmczGxpYfkX3yyScTJkzQdqSvM3/+/BZ/KczMzKqqqt5Mnzlz5ty5c1vLLfUyz8GZoe0YNQXPvn5PkxrqufLW+rtJpdLm1+KawGKx6PQuf4xUU1Mjl8vfTJfL5To6LfyWMZlMJpPZYlaCOvnZXeWz1tl2QZgagXNXz4v7KgInm9BZkD71Tz5fa2FHs3fBrfTj3Mlu5FTTf7aV4RsDXjy6UQ9IAEf3+OvXZ1JGzzA7tQO6M+DFA0Hpy6bh4Tj3dcO/nz8AgMeV3jxeE/1JL7wD6Sae3xNUFIiDPjDDOxC8Sz8Gx1zXJ9Qodm1BY30LTar3jNSLtWV5PcJ9Tyn9GGIhev2fKn0mZdh4Dk2/SzqD4EtOemPKxVr3EYZuI/B/vQujB+nHeJbKT7nAcw1gW9jRevd9H173FNTJC7NE+U+FDDZlWJgxLnf3WqPH6cd4lsrPfSzkFkmcfVkqFaCzEKahDhnpccMjtAiCkBob5KIGhUSsrMgTyyRKu0F0Jx8DY0tdvEN7nR6qH0MuU5a8aBLw5CI+KpMqxUItdxHg8/nV1dV9+vTRbrZMNgVVqOhsCt0AMbOh9UDranq0/q4mNTX1yJEju3btwjsQ3OgRLX8CvCD0Qw3U+hEEsbCwwDsKPIFaP4qilZWVeEeBJ1DrJ5FI3d+pt0cBtX6VSqXuCgwnUOsnk8ksFgvvKPAEav1KpZLPx3lwJXyBWj+FQrGywqePZQ8Bav0KhaK8vBzvKPAEav0AgDZeA4UB2PXLZPgMqtNDgF0/5ECtH0EQrY/U8m4BtX4URbFXgKEFav0EUOsnkUjd8FJYTwZq/SqVSiQS4R0FnkCtH0EQc3NzvKPAE6j1oyjK5XLxjgJPoNZPALV+orsH1PqJ7h5Q6ycg9EMN1PqJ7h5Q6ye6e0Ctn4DQDzVQ6yeu+6HWT1z3Q62fAGr9ZDK5K6ZmeoeAWr9SqaytxXMGZdyBWj8B7PoR5D0cQFBzYNePotqfUOwdAmr9FAqF6OcPLwqFgujnDy9EV08Yh3WcOnUqdrNPIpEIhULs0l8sFmMzdUMFjKU/ICCgoqKioqKirq5OJpNhnxkMPGfVwAsY9U+ZMsXGxua1RPWE3lABo34jI6OgoCAS6b/xwa2srKZNm4ZrUPgAo37s5x+b4BNrAI4fPx7Ol/0g1W9kZDRmzBjsc+/evT/44AO8I8IHSPUDACZPnty7d28EQcLDw+Es+l07iatMqqwtl0qaumre87dGN3h4THp6upfTuIKsHvqeL5kMDM2oLE5XTXPZVdf9CUe5+U9E5rZ6ZPK7MQNLz4RhSCl9IWKZUL2DDa0c9bSev/b1q5Sqc3sq7JyZDq4G2s0ZWqQSNOHvisBJJua2NO3mrH3953+vcHA3sOkP412ULuXc7uJxcy2MzLU5EKGWm37F2SIaAyHcdwXDwk0fxNdpN08t66+tkOnSoO5A0XUYcKilOVrul6xl/WIRyjKBepjUroOmj9BZOjKJNq+ktKxfIVOhcugeIXYbAp6s+b3qtwfe2z4EhH7YIfRDDaEfagj9UEPohxpCP9QQ+qGG0A81hH6oIfRDzbuqv6AgLzwiMCn5FvanUCh8mfsC76DePd5V/RQKhcFgUpBXfRXnL5h65cp5vIN69+jCrp5dhEqlIpFI1ta2R4/EqRN7/myMWNh4R/E6OJf+1V8smz4jUv3n4SMHkpNvq/+cNSf6hx/X8/kNgUFex08c+m7z2pBxvp98+uHVaxcCg7wCg7zSH94DAEydFlZfX3fu/MnAIK+p08LUq5+POzV9RuSYkGGz5kT/fShWKpW2G8/5uFMzZ08cEzJs0ZJZJ04ejooejb0HHhjkdfTYQfViX3y1fPHHs7HPEolk1+6fJkwMHjfef+GiGTduxmPpt24nBgZ5JSXdWvrJvOAxPvtid40PH7Hn9+3qTMorygKDvG7ewvO9UpxL/4iAUT9u3VBYmG9n5wAAuHrtQu/eNsOHB2C/7iUlRYs+Wo4tefjw/oiIST9t+x1BEDbLcMGHS/fu24l9tf6bH1et/tjN1XNS9HSdf+fkPfjX3pOnDkdNmGpjY19aWnT8xN9l5SVfrtnQRjB//b3v4F9/DBky/IOpsxoa6g8fOUChtHN8lErlV2s/5XIrpk+bw2YbZWSkb/zuS4lEHBoSgS3w684t8+cumTtnUS8r66Ym0fUbVxd8uBQbUeb27URdXd0hg4dr4Th2Fpz1Dx8+gvLL5uSU23Z2Dk+ePCovL62sLK+q4pqZmd++k8igMzw9hzQ1iQAATk7O8+ctUa/o6uKh/ty/nxOFQuFwjJ2d3bCU2tqaI0cPrP1qU4B/EJbC4Zj8sv37j5esNGC23P+Yz284cvSAj4/v95teFdDqau7tO9fbjv/O3RtPMx8fO3LB2NgEADAqaKxY3HT6zDG1/gmRU8aMeVUhjRkz/nzcqQfpaT5DhmP6h/r44TusKM76DZgGHu7eycm3YqbPvXItzs3Vs66ed+Vq3OxZC27dThzuO0JH59UbDh4egzXP9uHDewqFYtPmtZs2r8VSsA7NtTXVrenPzMqQy+XhYRM7FH9aWpJCoZgWE65OQVGUTv+vp2vzsAf0H2hrax8ff9FnyPCKyvKXuS9mzJjfoc1pHfybfgEBo7Zu21hSUnT7duKqz7+p49WeOHXYzzewec0PAKDROvCSA6+uFgCwedN2UxOz5umWlr1aW0Ug4AMAjE1MOxR8fT2PwzH+edvvzRORZj8Z+nr/V7hDxobvP/Bbo7Dx9u1EBp2Bb82Pf9MPq/8RBPl+yzd6evp+voGjx4Tx+Q0/b9+M1fya59P8hQXmv0Xc2tq2+b82fss5HBMAAK+25s2v2mixM5kGDQ31ZmYWzbdi1fpJFjwqFEXRmzfjb99O9PcPUtdteIG/fpYBy8Pd+8WLZ6EhERQKhclgBo4YnZ2d2bzmbxc9mh6P99/4nO7u3iQS6ey54+oUsVjcdg4O9n0oFMqly+fe/ApBECbToJb36sxQqVTV1a9m//PwGIyiaNyFUxpuyNDQyMfH9/iJQzkvnwcFjdVw77oO/PVj9T+JRAobF4X9GR4eDQAY4T9K8xycnd3T7iUdPXbwwsUzBQV5vax6R02YmpJy58u1n16+cv7Q4f0xMyPbvi1obGwyLjQyKfnWF18tv3T53PETh+4m3VR/O9h7aEL8paTkW9nZmd9uWFNSUoSlB48K7d9/4O9//Lpj19ar1y7s2v3TnHmTJBJJGxsKGjm2oqKMwzF2c/XUfAe7CPx/+wEAvsNHpKUlmZtbYH8O6D/Qw927QzX/RwuW1dXVHjocy2YZLl68wt7eccniFaamZmfPHn/wIJXDMfbzDTQxbud3ffGiFRSKzvUbVx8/fmBn52hp2ausrAT7asniz6RS6Q9bvqHTGeHjoyVSCdZW0NHR2bpl977YnTduXLt48UyvXtbh46Pbvlx0GuAMAAgcMZpMxr/safkdv1snaxiG1H7eLC3miRe/7thy+871M6fitZttfn7u/AUf7Pnt7/79nDq67tHN+XM32Ovoau3uYY8o/d1GWlrSpu/XtvjVrh1/2tjYdenWq6q45+NOXr5y3t3NqxPuuwK49Lu5ee3942iLX7X70/D2lJQWxSdcCgoaO2/O4q7eloYQlf+7hNYrf/xbHwQ4QuiHGkI/1BD6oYbQDzWEfqgh9EMNoR9qCP1QQ+iHGi3r12MiZEqP683+3sCx0iVpddRELes3MKRUF7fTr4agczTUyKQilKLV0qVl/b366YkECu3mSYBRXSJ2dNPyaLla1s9k6zgNYd74p1K72RKU5ghzHwuGhHC0m22XjOefnylKu8TrP5jFsaTR9Ikhft8KXqWksV5elCWcsqIXSduTI3TVdA68SumTO/yGGrmAJ++K/LWCUqlUKBRUas8dhNjYkgaAyrq/nosfuyvyh3EWTzWpqalHjhzZtWsX3oHgBnHdDzWEfqiBWj+CIFZWVnhHgSdQ60dRtLy8HO8o8ARq/QiCmJp2ef/ungzU+lEUra6uxjsKPIFaP4IgFhYWeEeBJ1DrR1G0shLq+9NQ60cQxNzcHO8o8ARq/SiKcrlcvKPAE6j1E0Ctn0QiMRhQzzcLtX6VSiUUCvGOAk+g1k/c9oFaP3HbB2r9BFDrJyp/qPUTlT/U+kkkEpPJxDsKPIFav0qlamxsxDsKPIFaPwHU+ommH9T6iaYf1PoJCP1QA7V+BEEsLS3xjgJPoNaPomhFRQXeUeAJ1PoJCP1QA7V+4rofav3EdT/U+gmg1k+hUIg3fOFFoVAQb/gSwAvU+slkspGREd5R4AnU+pVKZV1dHd5R4AnU+hEEMTMz02DB9xao9aMoWlVVhXcUeAK1fqKrJ4zDOs6dO1ehUAAA+Hx+XV2dnZ0dAEAoFJ45cwbv0LobuObwxbCxsYmLiyORXo2Pm52djSXiHRcOwFj5x8TEvNbiI5FIAQEB+EWEGzDqd3Bw8PHxaf6rZ21tHR0djWtQ+ACjfqwCUD/qJZFI/v7+cPb6glS/vb29ugKwsbGZNGkS3hHhA6T6AQCzZ8/GBvXz8/ODs+hr2vJXyJViobLrg+lWjAys/IaOvn///rgx0Y3179v8QyQSYLDbl9vOdf/z+4Knd/l1XJkeg5iT5V3C2FK3olDc150ZMNG4jSlg2tJ/P76utkLuFmDENNLpsjgJugqpGOVVSBIOVS74wZ6q2/KvfKv6712tE/AUPmFQ94R8D5DLlCe2FS7c4tDity2fFPXVstpyKeH+PUCHSh423iT1Ym2L37asv7ZcqlIRc7G+JxhwqMUvWp5atWX9Qj5q0pvWxVERdBOG5jQdasuiW742kEuVckkXB0XQXaiUqqqSlnXCe9uHgNAPO4R+qCH0Qw2hH2oI/VBD6IcaQj/UEPqhhtAPNYR+qIFCP5dbWcmFevy+1nj/9ZdXlE2LCc/JycY7kJ7I+68fVSh6/nuMeEXYTfofZ6Qv/nj2mJBhU6eFbfnxWx6vFgBw42Z8YJDX3aSb2DLYn2lpSdif5+NOTZ8ROSZk2Kw50X8fipVKpVi6RCLZF7tr2vTw4DE+MTMn/H0oFkXR/Qd+Gz12qHpzL3KyA4O87t1PqeRWzJoTDQD4dsOawCCvH35cr85k1+6fJkwMHjfef+GiGTduxre7CxKJZPdvP0dPHjtuvP/X61Zu//WHDRu/AACkP7wXGOSVnZ2pXjJknO/efTuxz5Xciq/XrQwN84uMGrVq9ccv/q2Eft2xJSp6dErKnZiZEwKDvM6eO9F83wEAly6fCwzy4nK7doLx7njF8+Gj+2u+WBY8KnRC5JRGAf/0mWMrVi78Y8/hkYGjExIv7/7tJ2+voSKRcPuvP4SNm+Dj4wsAOPjX3pOnDkdNmGpjY19aWnT8xN9l5SVfrtmAouiXXy3PzMqImjDV0aFvUXFBaVkxgrTaC5ljZPzVl99t2rx2zuyF7m5ehoZG2KAeX639lMutmD5tDpttlJGRvvG7LyUScWhIRGv5YKs8zkiPCI92GuCc8/L52XPHA/yD2t5xHq926bK5Vla9P16ykkQixcdf+mT5/N9/O2Rn5wAAEImE+//8bfknayQS8fBhAefjTl6Lv4jtPgDgzp3rgwa5mptbvMWBb5/u0L9z19bxYVHLlq7C/vTy8pk1J/pBeqqfb+DyZWvmzJt06HBsQWGeAdNg8aIVAIDa2pojRw+s/WqT+vhyOCa/bP/+4yUr09PTHmekf77y6zZUNYdKpfbt0x8AYG1t6+zshiXeuXvjaebjY0cuGBubAABGBY0Vi5tOnznWRp5paUmPHj/4aMGyqVNmAgCCg0MfPrrX7tYPHY41ZBv9tHUPhUIBAASPCo2ZGXnx8tmlS1YCAGQy2coVawcMGIQtHDI2/MCfewSNAgOmgaBR8OjxgyWLP9NkH9+GLtfP5VYWFxeWl5devHS2eXp1dRUAwMzMfN7cJbt2byOTyTu2x+rp6QEAHj68p1AoNm1eu2nzWmxh7Kextqb6/oMUXV3dMaPD3iaktLQkhUIxLSZcnYKiKJ3e1lzODx/fBwCMD5vYoQ3du5dcXVMVGuanTpHL5TXVr8YTodFoavfYyRG7f/fNm/ER4dHJybdUKlXgiOAOba4TdLn++noeAGDWzAX+fiObpxsZGWMfxowO+2Pvr46O/QYOdMFSeHW1AIDNm7abmvzfa9iWlr3q63jGHJM2ansNQ+JwjH/e9nvzRITS1qFobBQwGAw6nd6hDdXV84YO9Vswf2nzRPV5pqen3zydwzH29h56Lf5iRHj0rduJnp5DWCx2hzbXCbpcP4PBBABIpRJra9sWF9i7bweFQnn+POvS5XPjQiMBAEymAfbVm6swGMy6et6bmajHatAEJtOgoaHezMxCV1dXw1WMOSZCoVAsFmP1k4abZjIN+PyG1nb8TUJDItZ983l2duajR/dXrVyn4VpvQ5e3/Hv1sjYzM79yNU4sftXXWKFQyOVy7POjxw8uXDyzZPFnEeHRu3ZvKykpAgC4u3uTSKSz546rM1Gv6+7uLRaLr9+4pv4KG6aFxTKUy+V8AR9L5Da7yaOrSwMA8Gpr1CkeHoNRFI27cOrN/Fujb98BAIDLl8+9+ZUh2wgAUMt7lT+PV6veOw+PwVlZT3JePtdwQ0N9/Fgs9qbvv6ZQKMOHj2g7JK2ArF+//s3U8nwxqgDmtq+f6Z2ARCKZmVlcvnw+JfWOSgWyszN37PxRrpA7OTmLxeI1a5ba2Tks+/hzdzfv6zeupqTcDhkbbsg2bGxsjI+/9DL3uVQqTbuXvPmHr93dvTkcYxsb+9S0u5cunW1sFNTX8RISL++L3Rk2LopBZ5yPO1VbW21mZvEw/d5ve36WSMSjRoX0supNp9MTEi5nPsvQ16c/fHivb58Bjg59H6SnXYu/yBc01NfXXb12ceeuH8PGRVFar/+trW3v3L2eeP0qX9DQUF+feP3K/fsp1ta2AQGjmEyD+ISLOTnZtrYORcUFW7dt4NXVDhrk6uk5xN6+T0Li5YSEyyiKlpYVHzly4Pbd6yMDx2DNguLiwimTZzTfCplM5nIr0tPT/HwDR40KefuDj6FSgsykeu/RLQxg2eX6AQA21nb9+zk9ffo4PuHS8xdZDvZ9goPHcTjGe37/5XFG+g+bf2WzDSkUyoABg44eOygSCQcPHubtPVRfn56aevfGzWtl5SXDhwUMG+qvp6dHoVACAoL5/IZbtxOSU27xBQ0jAoKdnJw5HGMLc6vr16+cOftPU5NoUvT0pORbmH4SieTk5HL/QcqNm9cquRW+wwNZLNaIgGChUHDrVsKduzdETcKQsRHOzm5kcqt1IYlEGurjV1lZfvfujfT0NH06XShsNDM1DwgYRSaTBw1yu/8g9cTJw7m5L2bP/Cgl9c6A/oM8PYcYMA2GDwsoLilMSLj0ID2VTmeMC420tbVvTT8AgM9vSEq+NX/uEs1/MtqlDf0tv+N3/1qdTAJcR0A94GnbzJk32c7WYd3X32s32zNn/jn41x+nT8Xr6GjttVpUoTr6fcHibS285gfjyF6tsS92V/MGgRoDJuvI4fNdvfXMzIxr8RevxV+MmT5Pi+7bhtD/H5MnzwgLi3oznUzqjlvjD9JTM7MyFn60PGrClG7YHAZR+b//tFH5v/9P/AjagNAPNYR+qCH0Qw2hH2oI/VBD6IcaQj/UaOeu35Vrpw3ZHK1kRaAJurpUd7dhb5+PdvRLpeIBA/ppJSsCTdDT17SjSttoR/+ooNC2+8oRaBelUqaVfLSjn0Enng50KwiZqpV8iKYf1BD6oYbQDzWEfqgh9EMNoR9qCP1QQ+iHGkI/1BD6oYbQDzWEfqgh9EMNoV875Lx8PnKUt0ym6XPYFznZHy2MCQsPeJn7ootDawt89H+64qOdu7e1sQCPV7t23WdVVdxuDApkZmZ8u2FN59YtKsy3MLekUjV6DiuRSNZ9s3J08LhTJ67Z2zl2botaAZ9XPL29h5qZtTVk2aPHD168eGZmZq5hhiiKvjbgz5sp7XIt/mKnRw0qKMzr1ctaw4UfPrwnFjdFRk7WcHOd2BcNwUF/zIzI8oqyzd/9AgD48+DvldwKhIzcTbpBoeh8vGTlqKCxidevbvlxPYlEChnnGxoaiQ2Ddu3axeMnD5WVlXCMjBcsWBY4IjgtLWnDd19MnTIrPuHSoEGua1at3/P79pyX2aam5g8f3ps/b4muLm3rtg2XLtzBxm2YOi0seuK06InT5n041c3NKyszo6S0yMGh7+effW1jY/fL9u8vXT5HpVJDxvmuWf1tu2P2vUZhYZ5MLps1J7qurtbfL2jZ0lXYwEFvhn3u/Mn9+3ejSnTOvMnz5i4O8A8qLMz/bc/PWc+e6OvTI8InzZwxHwDw2r6EjA3Pzs6M3b87+3mmri4tbNyED+d//PYucKj8f/h+BwDAzs4Rqwbv30/xHT7i+LHLHu7eR44ewAba69fPad7cxVcuJWHuT5w8vGPXj3NmLzx7OnH8+Il79+7ACpxEIrEwtzz899lPlq4GABQV5RcVFUyOjjl5/MrIwDGFhXl2do6Ye6FQWFXFdXDoi40bKOA3fLfx59h9/8hlsp27tgIAFi38FEGQ7b/su3IpqaPusWD09em//PTH5u+237174/iJQ62FHRkxydnFPXhU6N8HTwf4B5VXlH2yfP6QIcPPnk5c+9Wmg3/98fTp4zf3JSvryfIVC9zcvI7/c/m7DT8dPXZQKy5w0F9UXECn07HxKsvKS8aMDhs+PIBOp9vb98FUKRSKvLycAf1fjXnXKGz88+Dv06fN9fMNVKlU+fkvbe0csCM+fFhAcHAoAAAbcKugMG/G9HmOjn3JZLKurm5BYZ6DfR8sk8LCPACAvZ2jRCIRCPgzYuabmJhaWfYKChpbXFIIAMjJySaTyY4Ofd8M+Hzcqajo0c3/vbYAX8Dn8WpnTJ9nZMRxdnYbMSL44aN7rYUNACgoyLWxscc+Hzjwm6urZ/TEaTQazd3Ny9TULL8g98192fPHdnd375ngS4WrAAASfElEQVQz5tP16S9ynqkHP3tLcKj8CwrybG3/PRD5uf6+r8b7Kysvse5tCwDIzctRKBTYYFoAgBcvnkkkklOnjx47dlCukA/18Vv9+TdY+QgNiVRn2yhsrK2tcXf3VqcUFuR5T3410G9+Qa6JiSmLxX7+4hmVSrWy6o2lCwR8bPi85y+yHB37tTisRkR4dER4dBt7VFiQRyaT7f5txKlUKhRFWwsbq4fs/j0V7j9ImTd3iXpFPr/B0NDotX2RyWTZ2ZlstuG48f4KhaJPn/4/btnVqWP/Orjoz8WauyKRiFtVaWf/6qjl57308xsJAHj+PKt3b5vXRtA7fuySWCJm0BnqGqKkpKh5s7mwII9CoahHRBKLxZXcCrt/z7OsZ0+wmr+wMM/Wxh5rSSmVytS0uz5DfLGNYsO/vsn5uFN//b23ecqZU/83BHR+/ksbGzsajYbZTUm9ox7/87Ww1fUQFphSqWxqauJwXo1wee9+Coqi7m5er+0LxtdrN/ftM0BXV1eLQ7/gUPkXFOZhBaWgIJdMJtva2GM6i4oLMJ18fn1DQ31FZXl5RRkAwNGhL5VKPXL0gEqpLCoqKCsvBQCUl5fK5XJ1GQIAFBblW1vbqgdnk8ll6gFCExKv3LqVgP0QFBTkIRRKQ0N9aWnx91u+EYmEkyfPAADUN9RVVJTxeLU1NdWvBRwRHn3mVHzzf68tkP08UyaVVlVxi4sL165bwWAwJ0VPbzFsbPdZLDabbYiN5OZg3+fmzXiJRFJUVLBr97bp0+ayWOzX9oVKpfZx7Hfy1BGRSFhfX9d89PC3pLv1S6XS8vJSTDN2sYRdK5eUFCkUCqwmGBEQTKPRZs2eGBu7CwBgaGi0ZvW3CYlXJk0J+XbjGrlMhq3L4Rg3H/a0sDBPXdYBACwDVmTEpK3bNsTMiCwoyKVQKPb2fbDF5DLZzNkTFy2ZqZDLf/0llmXAAgCEj49+lv10+oyIu3dvdGiPlErls+yno0aFfrQoZumyuebmlr/+so9Op7cYNnbSN4/z88/XVVaWR0YFrV332YTIKbNmfvjmvgAAVq9az+c3zJozccnS2Vip0ArQje0TFT16zepvB3sP1WDZLmH+gg+8vYZ+tGBZt22xywd2mzNv8mspSqWSTCKDN4a7jd17rIvuYGhCQ0N9fX0d1sDEhR27tgoE/AmR3Td2V9toR/+f+09oJZ+upqAwT1dXV/ObiVqnf1+nubMXMRg95YU4uMb183D3vno5GccARo8eh+PW34R44gc1hH6oIfRDDaEfagj9UEPohxpCP9QQ+qGG0A81hH6oIfRDDaEfagj9UNPyEz8qjaR881k9wbsJiQTMbWktftVy6Wca6tQUtzOtLcG7Aq9SqpApW/yqZf2mvXU7MiU2QY+GXyuzGdjy1POtln4rR9qd0936hiVBV9BQI318nTdkTMvdNlvu6onxLJWfmyF0DeAYmlERCtFIfMdorJPzKiSpF2vmfWeHIC1X5m3pBwAUPhNl3G7gFkoQynv4Y6ACKpVS1cbE3e8uptY0AU/m6MYYFmbcxmLt6FcjFbfcdninefDgwYkTJ7Zu3Yp3INqHRAJUWvuntaZdPXX13sMigugolUD6Xu6ahsC75wSw60cQxNTUFO8o8ARq/SiKVle//kInVECtn0KhWFlZ4R0FnkCtX6FQlJeX4x0FnkCtH0EQovTDC4qiROmHFzKZbGCgnTGS3lGg1q9UKgUCAd5R4AnU+gmg1o8giLk5bkM99ASg1o+iKJcLdZ8GqPWTSCQ6veVuMJAAtX6VSiUSifCOAk+g1k8AtX4KhWJpaYl3FHgCtX6FQlFRUYF3FHgCtX4CqPWTSCQNZ995X4Fav0ql0nzurfcSqPUT3T2g1k9094BaPwGhH2qg1k8mk42M3sMpKzQHav1KpbKurg7vKPAEav0EhH6ogVo/cd0PtX7iuh9q/QRQ60cQhHjeDy8oihLP+wngBWr9ROUPtX6i8odaP9HPH2r9RD9/qPUTQK2f6Oqp6aie7xMrVqy4ffu2SqUik8lKpRL738zM7MqVK3iH1t3AWPpnzZrF4XCwoXzVA/p6enriHRcOwKjf1dXV2dm5ebVnaWk5ffp0XIPCBxj1AwBmzpxpbPzfUNeurq4DBgzANSJ8gFS/q6uri4sL9tnc3DwmJgbviPABUv0AgBkzZlhYWMBc9DswoPv7h4uLy8CBA2Uy2cyZM/GOBTe0duHHLZLkZ4qqS6XiRlQsQhEdkkSEaiXnrkOpVCqVSgrlHSgDbGNdqQTVYyAcC2ovB5rdILomszW0y9vql0mV967WP0/j6+jpME3pVD0KhYpQdCmIDgkQMwFqD5USKKQKhQxVKpSCGlFjdZO5rZ57IMvW6a2eWbyV/jtnec9SGsz7c5jG+hQq8jZxEHQUUb2EV9xA1VH5R3Es7fU6l0kn9VeXy+MPVVGZeqb27M5tmEAriOol9WV8SztaYLRRJ6Ze7Iz+wmeihKPVDj5WCIUo8T2C6rw6Xaoi4iOLjq7YYf3cYum1wzU2Hh3eEkGXUl/WSNOVhczs2Nw0HWs9covFV/6qItz3QAx7MSVy6oV9lR1aqwP6UYXq9I5yO2+oX4vpyRhaMiVSStqVDry02gH9F/ZV2noS5b5HY+JglJ8priqRaLi8pvpLXjQJGlR0w5angSfoObAsWXfO8jRcWFP9t8/UmjgYvkVUBN0Eg6MnFqlKXzZpsrBG+stym8g6FD2m7lvH1t2gKFpQnIF3FN2NUW/W41t8TZbUSH9uhojGfCer/ZPnN52O24J3FN0Nw1i/NEeEou1f0mukvyBTxDTt2L1llUpVW1fWoVU6Qbs3LeRyaVfHgAvt7jjbXL8wq/0+7O0/7KqvltHZOlS99pcsLs2Ku7K9kpvLZBqbm9qXV75cvfykDoUqk0muJO55/PSaXC41MbYZ4TvdzTkYAHAn5VhGZqL/sA+uJO5pbKy1suw/KeILUxNbLLe8goeXE36r4L5kMowc7bxCghcZMI0BAFt3fmBuam9uap+UdkIml6xbdamyKi/x1oHC4icAAOteTmFjlvW2GgAA+OfMhidZiQCAlV8PAQB8ueKskaElACDl/unbyUf5gmojQ0t3l9Ejhsfo6LT1u1ZQnNFi/m3En52TfDl+N6++zIhtOXRwlI/XhPVbxroODJoU+SWW5/5DK6ZGraPT2QAAQWPtxq1hkyPXenuEtXasnmRdP3T8y9kf/Hgr+Uhp+bOwMcv8hk5pI2Y6h15VLHF0ZbStDFm/fn3bS9RXyfIyRCwLZjuLNXB37J3LNjANG7NMqUIfP7020n+mo52nUqmMPbS8tOxZwPBpbi7BCoXsSuIeFsusl2W/4tKs+4/i6hu4keM+cxkY9Ojp1dz8+0O8IgAAufkPYg990sfB23/oVEvzvk+yEh89vertPh5BKCn3T5dX5iBkZGL4KmenQHNTu4Kix6Vl2UM8wx3tPF/m309/fGnY4GgEoZiZ2FXVFAIA5sZsG+wx3sTYBiEj8Tf2JdzcP9gzfIhnBINhdCf5aC2v1NlpRBu71lr+rcUvlTbt+GOOAdM4NHiRnh5TJhX36+NTVVP0PDfFf9g0EolU38A9e2krnc62tXYBADx4fDE3//6kiK8QRKe1Y1VVXfj02fWC4oxAv5jhQyY52Hno6uq3EbNMLG9qEPf3asda+2W6qRFFNHia9/DJFZlMHDNlkwGTM3CAf0HR4+cvU0b6z8rMvllYlPHlZ+dYBiYAAA+XMVJZU1Lq8SGe4diKc6ZvM2ByAAC+PpMvXP1V1MSn67POXfrJx2vChLCV2DJ9HYds3TElJy8N84SQKdMnf6dLffWYy8N1rKdbCPa5t5XT738uLix+0q/PEBNja7o+u1FYZ2fjhn3LF9Rcv3NwevRGl0EjsRQW0/j0hS0RoSv09Vud0K+1/FuLXyxplMulzk4jPFzHqjNxHRj0MONycWmmnY3rg8cXVSrVvfTzI3xjAABPs270sffW1zd4knW97WPl6zPJy31cuy4AABRdCr+q/f4W7euXiVEqvf13Ifj8apouHTsQJBKJY2RV38AFADzPSUaVis0/T1AvqVSierT/KiW1RUO2BQBAIKiRSpuqagpr60rT0s8130QDvwr7YN17oHotbHOZ2bduJx+trimkUvUBAI3Cli98c/Pvo6jiyKl1R06t+zdNBQDgN1a3ob/t/N+M39zMwba3S+LtP6lUPR/vCToUKnYG02iMZ8/v2Fq7pD++NMQz4v6jC3mFD005NoUlGZMj12pyrPo4eLcrAoNKQyi67Tfs2teP6JBlInm7ixlzekmkosqqPAszR4VCXlH50sHOEztSBkzjhXN2N1+YTG5huxREB9th7OAGB853cQpsvgCT+apvLlXn/x5vJ9zcf+3GXr+hU8eNXixo5B06/qVKpWwxSEFjLQBgXszPbNb/PRrhGPVqY9c0zF8dP4lEmjfzl8sJv128uuNOytGpUd842HlQKDoD+/k9e3GnX9+hDfyq4MD5oqaGe+nnbXs7k8nIwP7+mhwrXWpbFX5zFDKlRKiN0q/PRBTy9jPycht3O/nYgcOfebqG5hc9QlHF6MD5AAB9PQOhqN6QbdF286o5ejQm1mhXNwPbQC6X3rj71xDPiIjQT5vXEP/RrJGsp/eqiGuSs6b5t7wLjInjV40YPv3g0VV/Hv3865UXdHX1XQYFPXxy5UrCb079/dgs06HeUQeOrKyuKcJq/s4dq1bDlir0Ddr/yW6/ftBnIqis5cLUHDqdHRm6QodC41bn93UY/OniQybG1gAARwdvpRJNuX9avaRUJm47KxNjazbL/MGjC+olUVShULRcA0llYrlc2suyP/anSNQAAFD+WzqpVL1GIU+pfPVnH3svEomUdO+E5sG0nX9rYBecHCMrX5/JEomwrqHiVf2vSy8pezbUKwr7k80yK6/McR00ClurE8eqNRRSlMFuX3/7pZ9joStulCtRJRlp61wpKXt2/OzGCWErEUSHRCLX1ZczGRwEQTxdQ+6ln7t4bWd9Q6WVRb8Kbm5m9q1Vy45Tqa3eRyKRSBGhn/51bPXOP+YNHRylVKLpjy97uo31H/bBmwsz6GwLM8ektBNMJkciEcbfjCWRyNyqfOxbB1v3B48unI77wdbGVV/PYGB/P1+fKXdT/zlw+LOBAwIaG2uT752aN+Nntd2O5t8iCoX8xx2TXQeOMjezT7l/mkZjcAx7AQB0KFSn/n7FpVl9HQdju+njFXn1+u9YzQ8A6MSxag2pSOrQr/21NOrkajOA3ljTxDJv6yLSkG1hZGR1/OxG9R0JK4t+S+bvpVJpH87acTl+9+On8akPzppwrIcNjkKQdrbr7DRibszP167vjbv8C43GsLN1s7d1b23h6ZM3Hj+z8dDxr0w41uPHflLBzb2b+s+40R9TKDoeriGl5c8fZlzOzknydg8b2N8vPGQ5m2WalHYyJy/NgGk8yGkEy6CdLhKt5d/a8jKZ2NHO69HTqxKJ0NzMcV7MT2p/LgODLM37kP7tljXYY3xxaaa61Umh6HTiWLVIY3WTvTOn3cU06u3z8mFj+i2hpVM7hwlFUQRBsA9Zz28dOv7lR3N297H36kjYBFpALJDyCmpjvrBud0mNzqw+7ozkS3UqpYpEbrU3YVVN0Z79Cwf087U07yNXSDOf3aTq0Ew4vTsYOT48z0ludin4fyz9MNbM1K7bI3orBNUiZ99Wr2Obo2lfv0c36nOz5GZ9Wq1PBILam0mHsnOSGvhcPRrT1sY1yH82dnO05yOTSYSiljvJsAxMO1f94oVcoih+WDH/O41O2Q509dz3VaGtt6WO7rt0LCCkIrvaw5/e31uj0t+Bzl7BM0xrCzTtRkKAC8I6MdMAaOi+Y/pt+9P7uOhV5xFnQA9FLlVwn9eM/7AD/TE71tHba5RhbwcK9yVxBvQ4lKiyMrt6xlftt/ab0+G3RIeGGplZkqpe1nZ0RYKuo6lB8uJWydQVVrp6HXvvqpPv+GXcbnj5RMw0Y+kZvHsdAN8z6kr50gbRB6s6c43d+Td8KwrE1/+pIVN1TB0MdWg6ncuE4G3glQqqc+vcAg2HhnZyOrq3fb//5aPGJ0mNogaFPofOMtWn0nVInXjRlEBjUAUqrBU31jbJm2S9+ugFRBm/zTgP2hndo6pYkvdEVJ4vqS4R69DIVBqFRqco5O0/JyTQEF26jqBGLBOjhua6DBalnyfd1kmfSnvbN6y1P6pnU6OiSYBKJUoA3XChXQhCIekxEboBQtHR5mhcMA7qSqAG3oHdCAj9sEPohxpCP9QQ+qGG0A81/wN8KuwqllTSmwAAAABJRU5ErkJggg==", + "image/png": "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", "text/plain": [ "" ] @@ -519,7 +519,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "{'write_query': {'query': 'SELECT COUNT(EmployeeId) AS NumberOfEmployees FROM Employee;'}}\n", + "{'write_query': {'query': 'SELECT COUNT(EmployeeId) AS EmployeeCount FROM Employee;'}}\n", "{'__interrupt__': ()}\n" ] }, @@ -617,10 +617,10 @@ { "data": { "text/plain": [ - "[QuerySQLDataBaseTool(description=\"Input to this tool is a detailed and correct SQL query, output is a result from the database. If the query is not correct, an error message will be returned. If an error is returned, rewrite the query, check the query, and try again. If you encounter an issue with Unknown column 'xxxx' in 'field list', use sql_db_schema to query the correct table fields.\", db=),\n", - " InfoSQLDatabaseTool(description='Input to this tool is a comma-separated list of tables, output is the schema and sample rows for those tables. Be sure that the tables actually exist by calling sql_db_list_tables first! Example Input: table1, table2, table3', db=),\n", - " ListSQLDatabaseTool(db=),\n", - " QuerySQLCheckerTool(description='Use this tool to double check if your query is correct before executing it. Always use this tool before executing a query with sql_db_query!', db=, llm=ChatOpenAI(client=, async_client=, root_client=, root_async_client=, model_name='gpt-4o', temperature=0.0, model_kwargs={}, openai_api_key=SecretStr('**********')), llm_chain=LLMChain(verbose=False, prompt=PromptTemplate(input_variables=['dialect', 'query'], input_types={}, partial_variables={}, template='\\n{query}\\nDouble check the {dialect} query above for common mistakes, including:\\n- Using NOT IN with NULL values\\n- Using UNION when UNION ALL should have been used\\n- Using BETWEEN for exclusive ranges\\n- Data type mismatch in predicates\\n- Properly quoting identifiers\\n- Using the correct number of arguments for functions\\n- Casting to the correct data type\\n- Using the proper columns for joins\\n\\nIf there are any of the above mistakes, rewrite the query. If there are no mistakes, just reproduce the original query.\\n\\nOutput the final SQL query only.\\n\\nSQL Query: '), llm=ChatOpenAI(client=, async_client=, root_client=, root_async_client=, model_name='gpt-4o', temperature=0.0, model_kwargs={}, openai_api_key=SecretStr('**********')), output_parser=StrOutputParser(), llm_kwargs={}))]" + "[QuerySQLDatabaseTool(description=\"Input to this tool is a detailed and correct SQL query, output is a result from the database. If the query is not correct, an error message will be returned. If an error is returned, rewrite the query, check the query, and try again. If you encounter an issue with Unknown column 'xxxx' in 'field list', use sql_db_schema to query the correct table fields.\", db=),\n", + " InfoSQLDatabaseTool(description='Input to this tool is a comma-separated list of tables, output is the schema and sample rows for those tables. Be sure that the tables actually exist by calling sql_db_list_tables first! Example Input: table1, table2, table3', db=),\n", + " ListSQLDatabaseTool(db=),\n", + " QuerySQLCheckerTool(description='Use this tool to double check if your query is correct before executing it. Always use this tool before executing a query with sql_db_query!', db=, llm=ChatOpenAI(client=, async_client=, root_client=, root_async_client=, model_name='gpt-4o', temperature=0.0, model_kwargs={}, openai_api_key=SecretStr('**********')), llm_chain=LLMChain(verbose=False, prompt=PromptTemplate(input_variables=['dialect', 'query'], input_types={}, partial_variables={}, template='\\n{query}\\nDouble check the {dialect} query above for common mistakes, including:\\n- Using NOT IN with NULL values\\n- Using UNION when UNION ALL should have been used\\n- Using BETWEEN for exclusive ranges\\n- Data type mismatch in predicates\\n- Properly quoting identifiers\\n- Using the correct number of arguments for functions\\n- Casting to the correct data type\\n- Using the proper columns for joins\\n\\nIf there are any of the above mistakes, rewrite the query. If there are no mistakes, just reproduce the original query.\\n\\nOutput the final SQL query only.\\n\\nSQL Query: '), llm=ChatOpenAI(client=, async_client=, root_client=, root_async_client=, model_name='gpt-4o', temperature=0.0, model_kwargs={}, openai_api_key=SecretStr('**********')), output_parser=StrOutputParser(), llm_kwargs={}))]" ] }, "execution_count": 17, @@ -693,7 +693,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 19, "metadata": {}, "outputs": [], "source": [ @@ -711,7 +711,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 20, "metadata": {}, "outputs": [], "source": [ @@ -730,7 +730,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 21, "metadata": {}, "outputs": [ { @@ -742,8 +742,8 @@ "Which country's customers spent the most?\n", "==================================\u001b[1m Ai Message \u001b[0m==================================\n", "Tool Calls:\n", - " sql_db_list_tables (call_J7Cj6d9gqkaOwiB99fv8y6uh)\n", - " Call ID: call_J7Cj6d9gqkaOwiB99fv8y6uh\n", + " sql_db_list_tables (call_tFp7HYD6sAAmCShgeqkVZH6Q)\n", + " Call ID: call_tFp7HYD6sAAmCShgeqkVZH6Q\n", " Args:\n", "=================================\u001b[1m Tool Message \u001b[0m=================================\n", "Name: sql_db_list_tables\n", @@ -751,8 +751,8 @@ "Album, Artist, Customer, Employee, Genre, Invoice, InvoiceLine, MediaType, Playlist, PlaylistTrack, Track\n", "==================================\u001b[1m Ai Message \u001b[0m==================================\n", "Tool Calls:\n", - " sql_db_schema (call_zF4O1BfsAfEfQP85vvHPu6Kp)\n", - " Call ID: call_zF4O1BfsAfEfQP85vvHPu6Kp\n", + " sql_db_schema (call_KJZ1Jx6JazyDdJa0uH1UeiOz)\n", + " Call ID: call_KJZ1Jx6JazyDdJa0uH1UeiOz\n", " Args:\n", " table_names: Customer, Invoice\n", "=================================\u001b[1m Tool Message \u001b[0m=================================\n", @@ -809,8 +809,8 @@ "*/\n", "==================================\u001b[1m Ai Message \u001b[0m==================================\n", "Tool Calls:\n", - " sql_db_query_checker (call_YkdCcgJOzVUc4IsI3ytLPf0x)\n", - " Call ID: call_YkdCcgJOzVUc4IsI3ytLPf0x\n", + " sql_db_query_checker (call_AQuTGbgH63u4gPgyV723yrjX)\n", + " Call ID: call_AQuTGbgH63u4gPgyV723yrjX\n", " Args:\n", " query: SELECT c.Country, SUM(i.Total) as TotalSpent FROM Customer c JOIN Invoice i ON c.CustomerId = i.CustomerId GROUP BY c.Country ORDER BY TotalSpent DESC LIMIT 1;\n", "=================================\u001b[1m Tool Message \u001b[0m=================================\n", @@ -821,8 +821,8 @@ "```\n", "==================================\u001b[1m Ai Message \u001b[0m==================================\n", "Tool Calls:\n", - " sql_db_query (call_cojsHW4TTadu9fe6oooMr4Y9)\n", - " Call ID: call_cojsHW4TTadu9fe6oooMr4Y9\n", + " sql_db_query (call_B88EwU44nwwpQL5M9nlcemSU)\n", + " Call ID: call_B88EwU44nwwpQL5M9nlcemSU\n", " Args:\n", " query: SELECT c.Country, SUM(i.Total) as TotalSpent FROM Customer c JOIN Invoice i ON c.CustomerId = i.CustomerId GROUP BY c.Country ORDER BY TotalSpent DESC LIMIT 1;\n", "=================================\u001b[1m Tool Message \u001b[0m=================================\n", @@ -863,7 +863,7 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 22, "metadata": {}, "outputs": [ { @@ -875,8 +875,8 @@ "Describe the playlisttrack table\n", "==================================\u001b[1m Ai Message \u001b[0m==================================\n", "Tool Calls:\n", - " sql_db_list_tables (call_7o2lQBlRAoqGtbF17AW3b4RV)\n", - " Call ID: call_7o2lQBlRAoqGtbF17AW3b4RV\n", + " sql_db_list_tables (call_fMF8eTmX5TJDJjc3Mhdg52TI)\n", + " Call ID: call_fMF8eTmX5TJDJjc3Mhdg52TI\n", " Args:\n", "=================================\u001b[1m Tool Message \u001b[0m=================================\n", "Name: sql_db_list_tables\n", @@ -884,8 +884,8 @@ "Album, Artist, Customer, Employee, Genre, Invoice, InvoiceLine, MediaType, Playlist, PlaylistTrack, Track\n", "==================================\u001b[1m Ai Message \u001b[0m==================================\n", "Tool Calls:\n", - " sql_db_schema (call_xZx5tqzBAlPNiktWsuoafd4t)\n", - " Call ID: call_xZx5tqzBAlPNiktWsuoafd4t\n", + " sql_db_schema (call_W8Vkk4NEodkAAIg8nexAszUH)\n", + " Call ID: call_W8Vkk4NEodkAAIg8nexAszUH\n", " Args:\n", " table_names: PlaylistTrack\n", "=================================\u001b[1m Tool Message \u001b[0m=================================\n", @@ -909,12 +909,12 @@ "*/\n", "==================================\u001b[1m Ai Message \u001b[0m==================================\n", "\n", - "The `PlaylistTrack` table is a junction table that connects playlists and tracks. It has the following columns:\n", + "The `PlaylistTrack` table is designed to associate tracks with playlists. It has the following structure:\n", "\n", - "- `PlaylistId`: An integer that references the `PlaylistId` in the `Playlist` table. It is part of the composite primary key.\n", - "- `TrackId`: An integer that references the `TrackId` in the `Track` table. It is also part of the composite primary key.\n", + "- **PlaylistId**: An integer that serves as a foreign key referencing the `Playlist` table. It is part of the composite primary key.\n", + "- **TrackId**: An integer that serves as a foreign key referencing the `Track` table. It is also part of the composite primary key.\n", "\n", - "The primary key for this table is a combination of `PlaylistId` and `TrackId`, ensuring that each track can only appear once in a specific playlist. The table also enforces foreign key constraints to maintain referential integrity with the `Playlist` and `Track` tables.\n" + "The primary key for this table is a composite key consisting of both `PlaylistId` and `TrackId`, ensuring that each track can be uniquely associated with a playlist. The table enforces referential integrity by linking to the `Track` and `Playlist` tables through foreign keys.\n" ] } ], diff --git a/docs/package.json b/docs/package.json index b82fda1c4c0b3..b02d589e268c2 100644 --- a/docs/package.json +++ b/docs/package.json @@ -35,6 +35,7 @@ "json-loader": "^0.5.7", "prism-react-renderer": "^2.1.0", "process": "^0.11.10", + "raw-loader": "^4.0.2", "react": "^18", "react-dom": "^18", "typescript": "^5.2.2", diff --git a/docs/scripts/generate_api_reference_links.py b/docs/scripts/generate_api_reference_links.py index 662d1dd12f767..dd9ba388c31c2 100644 --- a/docs/scripts/generate_api_reference_links.py +++ b/docs/scripts/generate_api_reference_links.py @@ -5,6 +5,7 @@ import logging import os import re +import warnings from pathlib import Path from typing import List, Literal, Optional @@ -110,16 +111,36 @@ def find_files(path): def get_full_module_name(module_path, class_name) -> Optional[str]: """Get full module name using inspect""" - try: - module = importlib.import_module(module_path) - class_ = getattr(module, class_name) - return inspect.getmodule(class_).__name__ - except AttributeError as e: - logger.warning(f"Could not find module for {class_name}, {e}") - return None - except ImportError as e: - logger.warning(f"Failed to load for class {class_name}, {e}") - return None + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + try: + module = importlib.import_module(module_path) + except ImportError: + # check if it's a submodule + try: + module = importlib.import_module(module_path + "." + class_name) + except ImportError: + raise ValueError(f"Failed to import module {module_path}") + return module.__name__ + + try: + class_ = getattr(module, class_name) + except AttributeError: + # check if it's a submodule + try: + module = importlib.import_module(module_path + "." + class_name) + except ImportError: + raise ValueError( + f"Failed to import class {class_name} from module {module_path}" + ) + return module.__name__ + + inspectmodule = inspect.getmodule(class_) + if inspectmodule is None: + # it wasn't a class, it's a primitive (e.g. END="__end__") + # so no documentation link is necessary + return None + return inspectmodule.__name__ def get_args() -> argparse.Namespace: @@ -228,7 +249,17 @@ def _get_imports( if imp.strip() ] for class_name in imported_classes: - module_path = get_full_module_name(module, class_name) + try: + module_path = get_full_module_name(module, class_name) + except ValueError as e: + logger.warning(e) + continue + except Exception as e: + logger.error( + f"Failed to get full module name for {module}.{class_name}" + ) + logger.error(e) + continue if not module_path: continue if len(module_path.split(".")) < 2: diff --git a/docs/scripts/partner_deps_list.py b/docs/scripts/partner_deps_list.py new file mode 100644 index 0000000000000..2103619f5b09d --- /dev/null +++ b/docs/scripts/partner_deps_list.py @@ -0,0 +1,26 @@ +import yaml + +if __name__ == "__main__": + with open("../libs/packages.yml", "r") as f: + packages_yaml = yaml.safe_load(f) + + packages = packages_yaml["packages"] + + comaintain_packages = [ + p + for p in packages + if not p.get("disabled", False) + and p["repo"].startswith("langchain-ai/") + and p["repo"] != "langchain-ai/langchain" + ] + monorepo_packages = [ + p + for p in packages + if not p.get("disabled", False) and p["repo"] == "langchain-ai/langchain" + ] + + for p in monorepo_packages: + print("--editable ../" + p["path"]) + + for p in comaintain_packages: + print(p["name"]) diff --git a/docs/scripts/partner_pkg_table.py b/docs/scripts/partner_pkg_table.py index e5b4fd0eda37c..37a8a2f1fdd10 100644 --- a/docs/scripts/partner_pkg_table.py +++ b/docs/scripts/partner_pkg_table.py @@ -2,37 +2,31 @@ import sys from pathlib import Path -PARTNER_DIR = Path(__file__).parents[2] / "libs" / "partners" -DOCS_DIR = Path(__file__).parents[1] +import yaml -PLATFORMS = { - path.split("/")[-1][:-4] - for path in glob.glob( - str(DOCS_DIR) + "/docs/integrations/providers/*.mdx", recursive=True +DOCS_DIR = Path(__file__).parents[1] +PACKAGE_YML = Path(__file__).parents[2] / "libs" / "packages.yml" +IGNORE_PACKGAGES = {"langchain-experimental"} + +# for now, only include packages that are in the langchain-ai org +# because we don't have a policy for inclusion in this table yet, +# and including all packages will make the list too long +with open(PACKAGE_YML) as f: + data = yaml.safe_load(f) + EXTERNAL_PACKAGES = set( + p["name"][10:] + for p in data["packages"] + if p["repo"].startswith("langchain-ai/") + and p["repo"] != "langchain-ai/langchain" + and p["name"] not in IGNORE_PACKGAGES + ) + IN_REPO_PACKAGES = set( + p["name"][10:] + for p in data["packages"] + if p["repo"] == "langchain-ai/langchain" + and p["path"].startswith("libs/partners") + and p["name"] not in IGNORE_PACKGAGES ) -} -EXTERNAL_PACKAGES = { - "astradb", - "aws", - "cohere", - "databricks", - "elasticsearch", - "google-community", - "google-genai", - "google-vertexai", - "nvidia-ai-endpoints", - "postgres", - "redis", - "weaviate", - "upstage", - "mongodb", - "azure-dynamic-sessions", - "ibm", - "unstructured", - "milvus", - "together", - "ai21", -} JS_PACKAGES = { "google-gauth", @@ -66,11 +60,6 @@ "ibm", } - -IN_REPO_PACKAGES = { - path.split("/")[-2] - for path in glob.glob(str(PARTNER_DIR) + "/**/pyproject.toml", recursive=True) -} ALL_PACKAGES = IN_REPO_PACKAGES.union(EXTERNAL_PACKAGES) CUSTOM_NAME = { @@ -80,14 +69,17 @@ } CUSTOM_PROVIDER_PAGES = { "azure-dynamic-sessions": "/docs/integrations/providers/microsoft/", + "prompty": "/docs/integrations/providers/microsoft/", + "sqlserver": "/docs/integrations/providers/microsoft/", "google-community": "/docs/integrations/providers/google/", "google-genai": "/docs/integrations/providers/google/", "google-vertexai": "/docs/integrations/providers/google/", "nvidia-ai-endpoints": "/docs/integrations/providers/nvidia/", "exa": "/docs/integrations/providers/exa_search/", "mongodb": "/docs/integrations/providers/mongodb_atlas/", + "sema4": "/docs/integrations/providers/robocorp/", + "postgres": "/docs/integrations/providers/pgvector/", } -PLATFORM_PAGES = {name: f"/docs/integrations/providers/{name}/" for name in PLATFORMS} PROVIDER_PAGES = { name: f"/docs/integrations/providers/{name}/" for name in ALL_PACKAGES @@ -95,10 +87,8 @@ } PROVIDER_PAGES = { **PROVIDER_PAGES, - **PLATFORM_PAGES, **CUSTOM_PROVIDER_PAGES, } -print(PROVIDER_PAGES) def package_row(name: str) -> str: diff --git a/docs/scripts/prepare_notebooks_for_ci.py b/docs/scripts/prepare_notebooks_for_ci.py index 4fb96152a9a73..b96e262946293 100644 --- a/docs/scripts/prepare_notebooks_for_ci.py +++ b/docs/scripts/prepare_notebooks_for_ci.py @@ -25,8 +25,6 @@ "docs/docs/how_to/example_selectors_langsmith.ipynb", # TODO: add langchain-benchmarks; fix cassette issue "docs/docs/how_to/extraction_long_text.ipynb", # Non-determinism due to batch "docs/docs/how_to/graph_constructing.ipynb", # Requires local neo4j - "docs/docs/how_to/graph_mapping.ipynb", # Requires local neo4j - "docs/docs/how_to/graph_prompting.ipynb", # Requires local neo4j "docs/docs/how_to/graph_semantic.ipynb", # Requires local neo4j "docs/docs/how_to/hybrid.ipynb", # Requires AstraDB instance "docs/docs/how_to/indexing.ipynb", # Requires local Elasticsearch diff --git a/docs/src/theme/ChatModelTabs.js b/docs/src/theme/ChatModelTabs.js index f9efa57e97ceb..413af9ea9925a 100644 --- a/docs/src/theme/ChatModelTabs.js +++ b/docs/src/theme/ChatModelTabs.js @@ -1,9 +1,94 @@ /* eslint-disable react/jsx-props-no-spreading, react/destructuring-assignment */ -import React from "react"; -import Tabs from "@theme/Tabs"; -import TabItem from "@theme/TabItem"; +import React, { useState } from "react"; import CodeBlock from "@theme-original/CodeBlock"; +// Create a custom dropdown since Docusaurus's dropdown component isn't easily accessible +export const CustomDropdown = ({ selectedOption, options, onSelect, modelType }) => { + const [isOpen, setIsOpen] = React.useState(false); + + // Close dropdown when clicking outside + React.useEffect(() => { + const handleClickOutside = (event) => { + if (isOpen && !event.target.closest('.dropdown')) { + setIsOpen(false); + } + }; + + document.addEventListener('click', handleClickOutside); + return () => document.removeEventListener('click', handleClickOutside); + }, [isOpen]); + + // Determine the text and link based on the modelType + const getModelTextAndLink = () => { + switch (modelType) { + case 'chat': + return { text: 'chat model', link: '/docs/integrations/chat/' }; + case 'embeddings': + return { text: 'embeddings model', link: '/docs/integrations/text_embedding/' }; + case 'vectorstore': + return { text: 'vector store', link: '/docs/integrations/vectorstores/' }; + default: + return { text: 'chat model', link: '/docs/integrations/chat/' }; + } + }; + + const { text, link } = getModelTextAndLink(); + + return ( +
      + + Select {text}: + +
      + + +
      +
      + ); +}; + + /** * @typedef {Object} ChatModelTabsProps - Component props. * @property {string} [openaiParams] - Parameters for OpenAI chat model. Defaults to `model="gpt-3.5-turbo-0125"` @@ -36,6 +121,7 @@ import CodeBlock from "@theme-original/CodeBlock"; * @param {ChatModelTabsProps} props - Component props. */ export default function ChatModelTabs(props) { + const [selectedModel, setSelectedModel] = useState("OpenAI"); const { openaiParams, anthropicParams, @@ -153,7 +239,7 @@ export default function ChatModelTabs(props) { }, { value: "FireworksAI", - label: "FireworksAI", + label: "Fireworks AI", text: `from langchain_fireworks import ChatFireworks\n\n${llmVarName} = ChatFireworks(${fireworksParamsOrDefault})`, apiKeyName: "FIREWORKS_API_KEY", packageName: "langchain-fireworks", @@ -171,7 +257,7 @@ export default function ChatModelTabs(props) { }, { value: "MistralAI", - label: "MistralAI", + label: "Mistral AI", text: `from langchain_mistralai import ChatMistralAI\n\n${llmVarName} = ChatMistralAI(${mistralParamsOrDefault})`, apiKeyName: "MISTRAL_API_KEY", packageName: "langchain-mistralai", @@ -180,7 +266,7 @@ export default function ChatModelTabs(props) { }, { value: "TogetherAI", - label: "TogetherAI", + label: "Together AI", text: `from langchain_openai import ChatOpenAI\n\n${llmVarName} = ChatOpenAI(${togetherParamsOrDefault})`, apiKeyName: "TOGETHER_API_KEY", packageName: "langchain-openai", @@ -198,38 +284,47 @@ export default function ChatModelTabs(props) { }, ]; - return ( - - {tabItems - .filter((tabItem) => !tabItem.shouldHide) - .map((tabItem) => { - let apiKeyText = ""; - if (tabItem.apiKeyName) { - apiKeyText = `import getpass + const modelOptions = tabItems + .filter((item) => !item.shouldHide) + .map((item) => ({ + value: item.value, + label: item.label, + text: item.text, + apiKeyName: item.apiKeyName, + apiKeyText: item.apiKeyText, + packageName: item.packageName, + })); + +const selectedOption = modelOptions.find( + (option) => option.value === selectedModel +); + +let apiKeyText = ""; +if (selectedOption.apiKeyName) { + apiKeyText = `import getpass import os -os.environ["${tabItem.apiKeyName}"] = getpass.getpass()`; - } else if (tabItem.apiKeyText) { - apiKeyText = tabItem.apiKeyText; - } - - return ( - - - {`pip install -qU ${tabItem.packageName}`} - - - {apiKeyText ? apiKeyText + "\n\n" + tabItem.text : tabItem.text} - - - ); - }) - } - - ); -} +if not os.environ.get("${selectedOption.apiKeyName}"): + os.environ["${selectedOption.apiKeyName}"] = getpass.getpass("Enter API key for ${selectedOption.label}: ")`; + } else if (selectedOption.apiKeyText) { + apiKeyText = selectedOption.apiKeyText; + } + +return ( +
      + + + + {`pip install -qU ${selectedOption.packageName}`} + + + {apiKeyText ? apiKeyText + "\n\n" + selectedOption.text : selectedOption.text} + +
      +); +} \ No newline at end of file diff --git a/docs/src/theme/EmbeddingTabs.js b/docs/src/theme/EmbeddingTabs.js index f29da74f135fb..101adc7b9dc7f 100644 --- a/docs/src/theme/EmbeddingTabs.js +++ b/docs/src/theme/EmbeddingTabs.js @@ -1,9 +1,9 @@ -import React from "react"; -import Tabs from "@theme/Tabs"; -import TabItem from "@theme/TabItem"; +import React, { useState } from "react"; import CodeBlock from "@theme-original/CodeBlock"; +import { CustomDropdown } from './ChatModelTabs'; export default function EmbeddingTabs(props) { + const [selectedModel, setSelectedModel] = useState("OpenAI"); const { openaiParams, hideOpenai, @@ -148,24 +148,47 @@ export default function EmbeddingTabs(props) { }, ]; - return ( - - {tabItems - .filter((tabItem) => !tabItem.shouldHide) - .map((tabItem) => { - const apiKeyText = tabItem.apiKeyName ? `import getpass\n\nos.environ["${tabItem.apiKeyName}"] = getpass.getpass()` : ''; - return ( - - {`pip install -qU ${tabItem.packageName}`} - {apiKeyText + (apiKeyText ? "\n\n" : '') + tabItem.text} - - ); - }) - } - - ); - } \ No newline at end of file + const modelOptions = tabItems + .filter((item) => !item.shouldHide) + .map((item) => ({ + value: item.value, + label: item.label, + text: item.text, + apiKeyName: item.apiKeyName, + apiKeyText: item.apiKeyText, + packageName: item.packageName, + })); + +const selectedOption = modelOptions.find( + (option) => option.value === selectedModel +); + +let apiKeyText = ""; +if (selectedOption.apiKeyName) { + apiKeyText = `import getpass +import os + +if not os.environ.get("${selectedOption.apiKeyName}"): + os.environ["${selectedOption.apiKeyName}"] = getpass.getpass("Enter API key for ${selectedOption.label}: ")`; + } else if (selectedOption.apiKeyText) { + apiKeyText = selectedOption.apiKeyText; + } + +return ( +
      + + + + {`pip install -qU ${selectedOption.packageName}`} + + + {apiKeyText ? apiKeyText + "\n\n" + selectedOption.text : selectedOption.text} + +
      +); +} diff --git a/docs/src/theme/FeatureTables.js b/docs/src/theme/FeatureTables.js index afa27742aaee9..74b3a4525e10e 100644 --- a/docs/src/theme/FeatureTables.js +++ b/docs/src/theme/FeatureTables.js @@ -1138,7 +1138,20 @@ const FEATURE_TABLES = { multiTenancy: true, local: true, idsInAddDocuments: false, - } + }, + { + name: "SQLServer", + link: "sqlserver", + deleteById: true, + filtering: true, + searchByVector: true, + searchWithScore: true, + async: false, + passesStandardTests: false, + multiTenancy: false, + local: false, + idsInAddDocuments: false, + }, ], } }; diff --git a/docs/src/theme/VectorStoreTabs.js b/docs/src/theme/VectorStoreTabs.js index da246630d7574..d7a2042c96075 100644 --- a/docs/src/theme/VectorStoreTabs.js +++ b/docs/src/theme/VectorStoreTabs.js @@ -1,92 +1,109 @@ -import React from "react"; -import Tabs from "@theme/Tabs"; -import TabItem from "@theme/TabItem"; +import React, { useState } from "react"; import CodeBlock from "@theme-original/CodeBlock"; +import { CustomDropdown } from './ChatModelTabs'; export default function VectorStoreTabs(props) { - const { customVarName } = props; + const [selectedModel, setSelectedModel] = useState("In-memory"); + const { customVarName, useFakeEmbeddings = false } = props; const vectorStoreVarName = customVarName ?? "vector_store"; + const fakeEmbeddingsString = `from langchain_core.embeddings import DeterministicFakeEmbedding\n\nembeddings = DeterministicFakeEmbedding(size=100)`; + const tabItems = [ { value: "In-memory", label: "In-memory", - text: `from langchain_core.vectorstores import InMemoryVectorStore\n\n${vectorStoreVarName} = InMemoryVectorStore(embeddings)`, + text: `from langchain_core.vectorstores import InMemoryVectorStore\n${useFakeEmbeddings ? fakeEmbeddingsString : ""}\n${vectorStoreVarName} = InMemoryVectorStore(embeddings)`, packageName: "langchain-core", default: true, }, { value: "AstraDB", label: "AstraDB", - text: `from langchain_astradb import AstraDBVectorStore\n\n${vectorStoreVarName} = AstraDBVectorStore(\n embedding=embeddings,\n api_endpoint=ASTRA_DB_API_ENDPOINT,\n collection_name="astra_vector_langchain",\n token=ASTRA_DB_APPLICATION_TOKEN,\n namespace=ASTRA_DB_NAMESPACE,\n)`, + text: `from langchain_astradb import AstraDBVectorStore\n${useFakeEmbeddings ? fakeEmbeddingsString : ""}\n${vectorStoreVarName} = AstraDBVectorStore(\n embedding=embeddings,\n api_endpoint=ASTRA_DB_API_ENDPOINT,\n collection_name="astra_vector_langchain",\n token=ASTRA_DB_APPLICATION_TOKEN,\n namespace=ASTRA_DB_NAMESPACE,\n)`, packageName: "langchain-astradb", default: false, }, { value: "Chroma", label: "Chroma", - text: `from langchain_chroma import Chroma\n\n${vectorStoreVarName} = Chroma(embedding_function=embeddings)`, + text: `from langchain_chroma import Chroma\n${useFakeEmbeddings ? fakeEmbeddingsString : ""}\n${vectorStoreVarName} = Chroma(embedding_function=embeddings)`, packageName: "langchain-chroma", default: false, }, { value: "FAISS", label: "FAISS", - text: `from langchain_community.vectorstores import FAISS\n\n${vectorStoreVarName} = FAISS(embedding_function=embeddings)`, + text: `from langchain_community.vectorstores import FAISS\n${useFakeEmbeddings ? fakeEmbeddingsString : ""}\n${vectorStoreVarName} = FAISS(embedding_function=embeddings)`, packageName: "langchain-community", default: false, }, { value: "Milvus", label: "Milvus", - text: `from langchain_milvus import Milvus\n\n${vectorStoreVarName} = Milvus(embedding_function=embeddings)`, + text: `from langchain_milvus import Milvus\n${useFakeEmbeddings ? fakeEmbeddingsString : ""}\n${vectorStoreVarName} = Milvus(embedding_function=embeddings)`, packageName: "langchain-milvus", default: false, }, { value: "MongoDB", label: "MongoDB", - text: `from langchain_mongodb import MongoDBAtlasVectorSearch\n\n${vectorStoreVarName} = MongoDBAtlasVectorSearch(\n embedding=embeddings,\n collection=MONGODB_COLLECTION,\n index_name=ATLAS_VECTOR_SEARCH_INDEX_NAME,\n relevance_score_fn="cosine",\n)`, + text: `from langchain_mongodb import MongoDBAtlasVectorSearch\n${useFakeEmbeddings ? fakeEmbeddingsString : ""}\n${vectorStoreVarName} = MongoDBAtlasVectorSearch(\n embedding=embeddings,\n collection=MONGODB_COLLECTION,\n index_name=ATLAS_VECTOR_SEARCH_INDEX_NAME,\n relevance_score_fn="cosine",\n)`, packageName: "langchain-mongodb", default: false, }, { value: "PGVector", label: "PGVector", - text: `from langchain_postgres import PGVector\n\n${vectorStoreVarName} = PGVector(\n embedding=embeddings,\n collection_name="my_docs",\n connection="postgresql+psycopg://...",\n)`, + text: `from langchain_postgres import PGVector\n${useFakeEmbeddings ? fakeEmbeddingsString : ""}\n${vectorStoreVarName} = PGVector(\n embedding=embeddings,\n collection_name="my_docs",\n connection="postgresql+psycopg://...",\n)`, packageName: "langchain-postgres", default: false, }, { value: "Pinecone", label: "Pinecone", - text: `from langchain_pinecone import PineconeVectorStore\nfrom pinecone import Pinecone\n\npc = Pinecone(api_key=...)\nindex = pc.Index(index_name)\n\n${vectorStoreVarName} = PineconeVectorStore(embedding=embeddings, index=index)`, + text: `from langchain_pinecone import PineconeVectorStore\nfrom pinecone import Pinecone\n${useFakeEmbeddings ? fakeEmbeddingsString : ""}\npc = Pinecone(api_key=...)\nindex = pc.Index(index_name)\n\n${vectorStoreVarName} = PineconeVectorStore(embedding=embeddings, index=index)`, packageName: "langchain-pinecone", default: false, }, { value: "Qdrant", label: "Qdrant", - text: `from langchain_qdrant import QdrantVectorStore\nfrom qdrant_client import QdrantClient\n\nclient = QdrantClient(":memory:")\n${vectorStoreVarName} = QdrantVectorStore(\n client=client,\n collection_name="test",\n embedding=embeddings,\n)`, + text: `from langchain_qdrant import QdrantVectorStore\nfrom qdrant_client import QdrantClient\n${useFakeEmbeddings ? fakeEmbeddingsString : ""}\nclient = QdrantClient(":memory:")\n${vectorStoreVarName} = QdrantVectorStore(\n client=client,\n collection_name="test",\n embedding=embeddings,\n)`, packageName: "langchain-qdrant", default: false, }, ]; - return ( - - {tabItems.map((tabItem) => ( - - {`pip install -qU ${tabItem.packageName}`} - {tabItem.text} - - ))} - - ); -} + const modelOptions = tabItems + .filter((item) => !item.shouldHide) + .map((item) => ({ + value: item.value, + label: item.label, + text: item.text, + packageName: item.packageName, + })); + +const selectedOption = modelOptions.find( + (option) => option.value === selectedModel +); + +return ( +
      + + + + {`pip install -qU ${selectedOption.packageName}`} + + + {selectedOption.text} + +
      + ); + } diff --git a/docs/static/img/langgraph_text2cypher.webp b/docs/static/img/langgraph_text2cypher.webp new file mode 100644 index 0000000000000..a5afd292cebae Binary files /dev/null and b/docs/static/img/langgraph_text2cypher.webp differ diff --git a/docs/static/img/memgraph_kg.png b/docs/static/img/memgraph_kg.png new file mode 100644 index 0000000000000..3f518a18710ea Binary files /dev/null and b/docs/static/img/memgraph_kg.png differ diff --git a/docs/static/img/memgraph_kg_2.png b/docs/static/img/memgraph_kg_2.png new file mode 100644 index 0000000000000..3b69ad9a9048f Binary files /dev/null and b/docs/static/img/memgraph_kg_2.png differ diff --git a/docs/static/img/memgraph_kg_3.png b/docs/static/img/memgraph_kg_3.png new file mode 100644 index 0000000000000..ba6ac94489a11 Binary files /dev/null and b/docs/static/img/memgraph_kg_3.png differ diff --git a/docs/static/img/memgraph_kg_4.png b/docs/static/img/memgraph_kg_4.png new file mode 100644 index 0000000000000..e731941c38514 Binary files /dev/null and b/docs/static/img/memgraph_kg_4.png differ diff --git a/docs/vercel.json b/docs/vercel.json index 869e2e6b506ee..236755ba7dae4 100644 --- a/docs/vercel.json +++ b/docs/vercel.json @@ -62,6 +62,14 @@ "source": "/docs/tutorials/local_rag", "destination": "/docs/tutorials/rag" }, + { + "source": "/docs/how_to/graph_mapping(/?)", + "destination": "/docs/tutorials/graph#query-validation" + }, + { + "source": "/docs/how_to/graph_prompting(/?)", + "destination": "/docs/tutorials/graph#few-shot-prompting" + }, { "source": "/docs/tutorials/data_generation", "destination": "https://python.langchain.com/v0.2/docs/tutorials/data_generation/" @@ -113,6 +121,10 @@ { "source": "/docs/contributing/:path((?:faq|repo_structure|review_process)/?)", "destination": "/docs/contributing/reference/:path" + }, + { + "source": "/docs/integrations/retrievers/weaviate-hybrid(/?)", + "destination": "/docs/integrations/vectorstores/weaviate/#search-mechanism" } ] } diff --git a/docs/vercel_requirements.txt b/docs/vercel_requirements.txt index 365d23a43527e..b4a7d5d070b7a 100644 --- a/docs/vercel_requirements.txt +++ b/docs/vercel_requirements.txt @@ -1,9 +1,5 @@ --e ../libs/core --e ../libs/langchain --e ../libs/community --e ../libs/text-splitters langgraph -langchain-cohere +pyyaml urllib3==1.26.19 nbconvert==7.16.4 diff --git a/docs/yarn.lock b/docs/yarn.lock index 94436d8e05823..aad5a86bb52cd 100644 --- a/docs/yarn.lock +++ b/docs/yarn.lock @@ -9043,6 +9043,14 @@ raw-body@2.5.2: iconv-lite "0.4.24" unpipe "1.0.0" +raw-loader@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-4.0.2.tgz#1aac6b7d1ad1501e66efdac1522c73e59a584eb6" + integrity sha512-ZnScIV3ag9A4wPX/ZayxL/jZH+euYb6FcUinPcgiQW0+UBtEv0O6Q3lGd3cqJ+GHH+rksEv3Pj99oxJ3u3VIKA== + dependencies: + loader-utils "^2.0.0" + schema-utils "^3.0.0" + rc@1.2.8: version "1.2.8" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" diff --git a/libs/cli/.gitignore b/libs/cli/.gitignore index 68bc17f9ff210..413adb3d0f4fc 100644 --- a/libs/cli/.gitignore +++ b/libs/cli/.gitignore @@ -158,3 +158,5 @@ cython_debug/ # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. #.idea/ + +.integration_test diff --git a/libs/cli/Makefile b/libs/cli/Makefile index 445235c22c841..d93de61d8cc90 100644 --- a/libs/cli/Makefile +++ b/libs/cli/Makefile @@ -1,8 +1,48 @@ -lint lint_diff: - poetry run poe lint -test: - poetry run poe test +###################### +# LINTING AND FORMATTING +###################### -format: - poetry run poe format +# Define a variable for Python and notebook files. +PYTHON_FILES=. +MYPY_CACHE=.mypy_cache +lint format: PYTHON_FILES=. +lint_diff format_diff: PYTHON_FILES=$(shell git diff --relative=libs/cli --name-only --diff-filter=d master | grep -E '\.py$$|\.ipynb$$') +lint_package: PYTHON_FILES=langchain_cli +lint_tests: PYTHON_FILES=tests +lint_tests: MYPY_CACHE=.mypy_cache_test + +lint lint_diff lint_package lint_tests: + [ "$(PYTHON_FILES)" = "" ] || poetry run ruff check $(PYTHON_FILES) + [ "$(PYTHON_FILES)" = "" ] || poetry run ruff format $(PYTHON_FILES) --diff + [ "$(PYTHON_FILES)" = "" ] || mkdir -p $(MYPY_CACHE) && poetry run mypy $(PYTHON_FILES) --cache-dir $(MYPY_CACHE) + +format format_diff: + [ "$(PYTHON_FILES)" = "" ] || poetry run ruff format $(PYTHON_FILES) + [ "$(PYTHON_FILES)" = "" ] || poetry run ruff check --select I --fix $(PYTHON_FILES) + +test tests: _test _e2e_test + +PYTHON = .venv/bin/python + +_test: + poetry run pytest tests + +# custom integration testing for cli integration flow +# currently ignores vectorstores test because lacks implementation +_e2e_test: + rm -rf .integration_test + mkdir .integration_test + cd .integration_test && \ + python3 -m venv .venv && \ + $(PYTHON) -m pip install --upgrade poetry && \ + $(PYTHON) -m pip install -e .. && \ + $(PYTHON) -m langchain_cli.cli integration new --name parrot-link --name-class ParrotLink && \ + $(PYTHON) -m langchain_cli.cli integration new --name parrot-link --name-class ParrotLinkB --src=integration_template/chat_models.py --dst=langchain-parrot-link/langchain_parrot_link/chat_models_b.py && \ + $(PYTHON) -m langchain_cli.cli integration create-doc --name parrot-link --name-class ParrotLinkB --component-type ChatModel --destination-dir langchain-parrot-link/docs && \ + cd langchain-parrot-link && \ + poetry install --with lint,typing,test && \ + poetry run pip install -e ../../../standard-tests && \ + make format lint tests && \ + poetry install --with test_integration && \ + make integration_test diff --git a/libs/cli/langchain_cli/dev_scripts.py b/libs/cli/langchain_cli/dev_scripts.py index cccd9689e4482..ebb9331a9c61e 100644 --- a/libs/cli/langchain_cli/dev_scripts.py +++ b/libs/cli/langchain_cli/dev_scripts.py @@ -1,3 +1,4 @@ +# type: ignore """ Development Scripts for template packages """ diff --git a/libs/cli/langchain_cli/integration_template/Makefile b/libs/cli/langchain_cli/integration_template/Makefile index b30039ace1fe0..4c4bbf237d9dc 100644 --- a/libs/cli/langchain_cli/integration_template/Makefile +++ b/libs/cli/langchain_cli/integration_template/Makefile @@ -33,13 +33,13 @@ lint_tests: PYTHON_FILES=tests lint_tests: MYPY_CACHE=.mypy_cache_test lint lint_diff lint_package lint_tests: - [ "$(PYTHON_FILES)" = "" ] || poetry run ruff $(PYTHON_FILES) + [ "$(PYTHON_FILES)" = "" ] || poetry run ruff check $(PYTHON_FILES) [ "$(PYTHON_FILES)" = "" ] || poetry run ruff format $(PYTHON_FILES) --diff [ "$(PYTHON_FILES)" = "" ] || mkdir -p $(MYPY_CACHE) && poetry run mypy $(PYTHON_FILES) --cache-dir $(MYPY_CACHE) format format_diff: [ "$(PYTHON_FILES)" = "" ] || poetry run ruff format $(PYTHON_FILES) - [ "$(PYTHON_FILES)" = "" ] || poetry run ruff --select I --fix $(PYTHON_FILES) + [ "$(PYTHON_FILES)" = "" ] || poetry run ruff check --select I --fix $(PYTHON_FILES) spell_check: poetry run codespell --toml pyproject.toml diff --git a/libs/cli/langchain_cli/integration_template/integration_template/__init__.py b/libs/cli/langchain_cli/integration_template/integration_template/__init__.py index 60d8dac5d007b..44bec467eaa90 100644 --- a/libs/cli/langchain_cli/integration_template/integration_template/__init__.py +++ b/libs/cli/langchain_cli/integration_template/integration_template/__init__.py @@ -1,8 +1,11 @@ from importlib import metadata from __module_name__.chat_models import Chat__ModuleName__ +from __module_name__.document_loaders import __ModuleName__Loader from __module_name__.embeddings import __ModuleName__Embeddings -from __module_name__.llms import __ModuleName__LLM +from __module_name__.retrievers import __ModuleName__Retriever +from __module_name__.toolkits import __ModuleName__Toolkit +from __module_name__.tools import __ModuleName__Tool from __module_name__.vectorstores import __ModuleName__VectorStore try: @@ -14,8 +17,11 @@ __all__ = [ "Chat__ModuleName__", - "__ModuleName__LLM", "__ModuleName__VectorStore", "__ModuleName__Embeddings", + "__ModuleName__Loader", + "__ModuleName__Retriever", + "__ModuleName__Toolkit", + "__ModuleName__Tool", "__version__", ] diff --git a/libs/cli/langchain_cli/integration_template/integration_template/chat_models.py b/libs/cli/langchain_cli/integration_template/integration_template/chat_models.py index d299fb2ff26d1..917feb2df0ba2 100644 --- a/libs/cli/langchain_cli/integration_template/integration_template/chat_models.py +++ b/libs/cli/langchain_cli/integration_template/integration_template/chat_models.py @@ -1,13 +1,19 @@ """__ModuleName__ chat models.""" -from typing import Any, List, Optional +from typing import Any, Dict, Iterator, List, Optional from langchain_core.callbacks import ( CallbackManagerForLLMRun, ) -from langchain_core.language_models.chat_models import BaseChatModel -from langchain_core.messages import BaseMessage -from langchain_core.outputs import ChatResult +from langchain_core.language_models import BaseChatModel +from langchain_core.messages import ( + AIMessage, + AIMessageChunk, + BaseMessage, +) +from langchain_core.messages.ai import UsageMetadata +from langchain_core.outputs import ChatGeneration, ChatGenerationChunk, ChatResult +from pydantic import Field class Chat__ModuleName__(BaseChatModel): @@ -15,6 +21,8 @@ class Chat__ModuleName__(BaseChatModel): # https://github.com/langchain-ai/langchain/blob/7ff05357bac6eaedf5058a2af88f23a1817d40fe/libs/partners/openai/langchain_openai/chat_models/base.py#L1120 """__ModuleName__ chat model integration. + The default implementation echoes the first `parrot_buffer_length` characters of the input. + # TODO: Replace with relevant packages, env vars. Setup: Install ``__package_name__`` and set environment variable ``__MODULE_NAME___API_KEY``. @@ -258,7 +266,36 @@ class Joke(BaseModel): """ # noqa: E501 - # TODO: This method must be implemented to generate chat responses. + model_name: str = Field(alias="model") + """The name of the model""" + parrot_buffer_length: int + """The number of characters from the last message of the prompt to be echoed.""" + temperature: Optional[float] = None + max_tokens: Optional[int] = None + timeout: Optional[int] = None + stop: Optional[List[str]] = None + max_retries: int = 2 + + @property + def _llm_type(self) -> str: + """Return type of chat model.""" + return "chat-__package_name_short__" + + @property + def _identifying_params(self) -> Dict[str, Any]: + """Return a dictionary of identifying parameters. + + This information is used by the LangChain callback system, which + is used for tracing purposes make it possible to monitor LLMs. + """ + return { + # The model name allows users to specify custom token counting + # rules in LLM monitoring applications (e.g., in LangSmith users + # can provide per token pricing for their model and monitor + # costs for the given LLM.) + "model_name": self.model_name, + } + def _generate( self, messages: List[BaseMessage], @@ -266,16 +303,101 @@ def _generate( run_manager: Optional[CallbackManagerForLLMRun] = None, **kwargs: Any, ) -> ChatResult: - raise NotImplementedError() + """Override the _generate method to implement the chat model logic. + + This can be a call to an API, a call to a local model, or any other + implementation that generates a response to the input prompt. + + Args: + messages: the prompt composed of a list of messages. + stop: a list of strings on which the model should stop generating. + If generation stops due to a stop token, the stop token itself + SHOULD BE INCLUDED as part of the output. This is not enforced + across models right now, but it's a good practice to follow since + it makes it much easier to parse the output of the model + downstream and understand why generation stopped. + run_manager: A run manager with callbacks for the LLM. + """ + # Replace this with actual logic to generate a response from a list + # of messages. + last_message = messages[-1] + tokens = last_message.content[: self.parrot_buffer_length] + ct_input_tokens = sum(len(message.content) for message in messages) + ct_output_tokens = len(tokens) + message = AIMessage( + content=tokens, + additional_kwargs={}, # Used to add additional payload to the message + response_metadata={ # Use for response metadata + "time_in_seconds": 3, + }, + usage_metadata={ + "input_tokens": ct_input_tokens, + "output_tokens": ct_output_tokens, + "total_tokens": ct_input_tokens + ct_output_tokens, + }, + ) + ## + + generation = ChatGeneration(message=message) + return ChatResult(generations=[generation]) + + def _stream( + self, + messages: List[BaseMessage], + stop: Optional[List[str]] = None, + run_manager: Optional[CallbackManagerForLLMRun] = None, + **kwargs: Any, + ) -> Iterator[ChatGenerationChunk]: + """Stream the output of the model. + + This method should be implemented if the model can generate output + in a streaming fashion. If the model does not support streaming, + do not implement it. In that case streaming requests will be automatically + handled by the _generate method. + + Args: + messages: the prompt composed of a list of messages. + stop: a list of strings on which the model should stop generating. + If generation stops due to a stop token, the stop token itself + SHOULD BE INCLUDED as part of the output. This is not enforced + across models right now, but it's a good practice to follow since + it makes it much easier to parse the output of the model + downstream and understand why generation stopped. + run_manager: A run manager with callbacks for the LLM. + """ + last_message = messages[-1] + tokens = str(last_message.content[: self.parrot_buffer_length]) + ct_input_tokens = sum(len(message.content) for message in messages) + + for token in tokens: + usage_metadata = UsageMetadata( + { + "input_tokens": ct_input_tokens, + "output_tokens": 1, + "total_tokens": ct_input_tokens + 1, + } + ) + ct_input_tokens = 0 + chunk = ChatGenerationChunk( + message=AIMessageChunk(content=token, usage_metadata=usage_metadata) + ) - # TODO: Implement if Chat__ModuleName__ supports streaming. Otherwise delete method. - # def _stream( - # self, - # messages: List[BaseMessage], - # stop: Optional[List[str]] = None, - # run_manager: Optional[CallbackManagerForLLMRun] = None, - # **kwargs: Any, - # ) -> Iterator[ChatGenerationChunk]: + if run_manager: + # This is optional in newer versions of LangChain + # The on_llm_new_token will be called automatically + run_manager.on_llm_new_token(token, chunk=chunk) + + yield chunk + + # Let's add some other information (e.g., response metadata) + chunk = ChatGenerationChunk( + message=AIMessageChunk(content="", response_metadata={"time_in_sec": 3}) + ) + if run_manager: + # This is optional in newer versions of LangChain + # The on_llm_new_token will be called automatically + run_manager.on_llm_new_token(token, chunk=chunk) + yield chunk # TODO: Implement if Chat__ModuleName__ supports async streaming. Otherwise delete. # async def _astream( @@ -294,8 +416,3 @@ def _generate( # run_manager: Optional[AsyncCallbackManagerForLLMRun] = None, # **kwargs: Any, # ) -> ChatResult: - - @property - def _llm_type(self) -> str: - """Return type of chat model.""" - return "chat-__package_name_short__" diff --git a/libs/cli/langchain_cli/integration_template/integration_template/embeddings.py b/libs/cli/langchain_cli/integration_template/integration_template/embeddings.py index 174c567da8df1..5df2bbfbd402c 100644 --- a/libs/cli/langchain_cli/integration_template/integration_template/embeddings.py +++ b/libs/cli/langchain_cli/integration_template/integration_template/embeddings.py @@ -8,7 +8,8 @@ class __ModuleName__Embeddings(Embeddings): # TODO: Replace with relevant packages, env vars. Setup: - Install ``__package_name__`` and set environment variable ``__MODULE_NAME___API_KEY``. + Install ``__package_name__`` and set environment variable + ``__MODULE_NAME___API_KEY``. .. code-block:: bash @@ -70,21 +71,26 @@ class __ModuleName__Embeddings(Embeddings): """ + def __init__(self, model: str): + self.model = model + def embed_documents(self, texts: List[str]) -> List[List[float]]: """Embed search docs.""" - raise NotImplementedError + return [[0.5, 0.6, 0.7] for _ in texts] def embed_query(self, text: str) -> List[float]: """Embed query text.""" - raise NotImplementedError - - # only keep aembed_documents and aembed_query if they're implemented! - # delete them otherwise to use the base class' default - # implementation, which calls the sync version in an executor - async def aembed_documents(self, texts: List[str]) -> List[List[float]]: - """Asynchronous Embed search docs.""" - raise NotImplementedError - - async def aembed_query(self, text: str) -> List[float]: - """Asynchronous Embed query text.""" - raise NotImplementedError + return self.embed_documents([text])[0] + + # optional: add custom async implementations here + # you can also delete these, and the base class will + # use the default implementation, which calls the sync + # version in an async executor: + + # async def aembed_documents(self, texts: List[str]) -> List[List[float]]: + # """Asynchronous Embed search docs.""" + # ... + + # async def aembed_query(self, text: str) -> List[float]: + # """Asynchronous Embed query text.""" + # ... diff --git a/libs/cli/langchain_cli/integration_template/integration_template/llms.py b/libs/cli/langchain_cli/integration_template/integration_template/llms.py deleted file mode 100644 index 2dbe4ac918362..0000000000000 --- a/libs/cli/langchain_cli/integration_template/integration_template/llms.py +++ /dev/null @@ -1,155 +0,0 @@ -"""__ModuleName__ large language models.""" - -from typing import ( - Any, - List, - Optional, -) - -from langchain_core.callbacks import ( - CallbackManagerForLLMRun, -) -from langchain_core.language_models import BaseLLM -from langchain_core.outputs import LLMResult - - -class __ModuleName__LLM(BaseLLM): - """__ModuleName__ completion model integration. - - # TODO: Replace with relevant packages, env vars. - Setup: - Install ``__package_name__`` and set environment variable ``__MODULE_NAME___API_KEY``. - - .. code-block:: bash - - pip install -U __package_name__ - export __MODULE_NAME___API_KEY="your-api-key" - - # TODO: Populate with relevant params. - Key init args — completion params: - model: str - Name of __ModuleName__ model to use. - temperature: float - Sampling temperature. - max_tokens: Optional[int] - Max number of tokens to generate. - - # TODO: Populate with relevant params. - Key init args — client params: - timeout: Optional[float] - Timeout for requests. - max_retries: int - Max number of retries. - api_key: Optional[str] - __ModuleName__ API key. If not passed in will be read from env var __MODULE_NAME___API_KEY. - - See full list of supported init args and their descriptions in the params section. - - # TODO: Replace with relevant init params. - Instantiate: - .. code-block:: python - - from __module_name__ import __ModuleName__LLM - - llm = __ModuleName__LLM( - model="...", - temperature=0, - max_tokens=None, - timeout=None, - max_retries=2, - # api_key="...", - # other params... - ) - - Invoke: - .. code-block:: python - - input_text = "The meaning of life is " - llm.invoke(input_text) - - .. code-block:: python - - # TODO: Example output. - - # TODO: Delete if token-level streaming isn't supported. - Stream: - .. code-block:: python - - for chunk in llm.stream(input_text): - print(chunk) - - .. code-block:: python - - # TODO: Example output. - - .. code-block:: python - - ''.join(llm.stream(input_text)) - - .. code-block:: python - - # TODO: Example output. - - # TODO: Delete if native async isn't supported. - Async: - .. code-block:: python - - await llm.ainvoke(input_text) - - # stream: - # async for chunk in (await llm.astream(input_text)) - - # batch: - # await llm.abatch([input_text]) - - .. code-block:: python - - # TODO: Example output. - """ - - # TODO: This method must be implemented to generate text completions. - def _generate( - self, - prompts: List[str], - stop: Optional[List[str]] = None, - run_manager: Optional[CallbackManagerForLLMRun] = None, - **kwargs: Any, - ) -> LLMResult: - raise NotImplementedError - - # TODO: Implement if __ModuleName__LLM supports async generation. Otherwise - # delete method. - # async def _agenerate( - # self, - # prompts: List[str], - # stop: Optional[List[str]] = None, - # run_manager: Optional[AsyncCallbackManagerForLLMRun] = None, - # **kwargs: Any, - # ) -> LLMResult: - # raise NotImplementedError - - # TODO: Implement if __ModuleName__LLM supports streaming. Otherwise delete method. - # def _stream( - # self, - # prompt: str, - # stop: Optional[List[str]] = None, - # run_manager: Optional[CallbackManagerForLLMRun] = None, - # **kwargs: Any, - # ) -> Iterator[GenerationChunk]: - # raise NotImplementedError - - # TODO: Implement if __ModuleName__LLM supports async streaming. Otherwise delete - # method. - # async def _astream( - # self, - # prompt: str, - # stop: Optional[List[str]] = None, - # run_manager: Optional[AsyncCallbackManagerForLLMRun] = None, - # **kwargs: Any, - # ) -> AsyncIterator[GenerationChunk]: - # raise NotImplementedError - - @property - def _llm_type(self) -> str: - """Return type of LLM.""" - return "__package_name_short__-llm" diff --git a/libs/cli/langchain_cli/integration_template/integration_template/retrievers.py b/libs/cli/langchain_cli/integration_template/integration_template/retrievers.py index c48661873c8eb..242103f3a4056 100644 --- a/libs/cli/langchain_cli/integration_template/integration_template/retrievers.py +++ b/libs/cli/langchain_cli/integration_template/integration_template/retrievers.py @@ -1,7 +1,8 @@ """__ModuleName__ retrievers.""" -from typing import List +from typing import Any, List +from langchain_core.callbacks import CallbackManagerForRetrieverRun from langchain_core.documents import Document from langchain_core.retrievers import BaseRetriever @@ -13,7 +14,8 @@ class __ModuleName__Retriever(BaseRetriever): # TODO: Replace with relevant packages, env vars, etc. Setup: - Install ``__package_name__`` and set environment variable ``__MODULE_NAME___API_KEY``. + Install ``__package_name__`` and set environment variable + ``__MODULE_NAME___API_KEY``. .. code-block:: bash @@ -82,8 +84,24 @@ def format_docs(docs): # TODO: Example output. - """ # noqa: E501 + """ + + k: int = 3 # TODO: This method must be implemented to retrieve documents. - def _get_relevant_documents(self, query: str) -> List[Document]: - raise NotImplementedError() + def _get_relevant_documents( + self, query: str, *, run_manager: CallbackManagerForRetrieverRun, **kwargs: Any + ) -> List[Document]: + k = kwargs.get("k", self.k) + return [ + Document(page_content=f"Result {i} for query: {query}") for i in range(k) + ] + + # optional: add custom async implementations here + # async def _aget_relevant_documents( + # self, + # query: str, + # *, + # run_manager: AsyncCallbackManagerForRetrieverRun, + # **kwargs: Any, + # ) -> List[Document]: ... diff --git a/libs/cli/langchain_cli/integration_template/integration_template/toolkits.py b/libs/cli/langchain_cli/integration_template/integration_template/toolkits.py index 5a80398891167..b3eda23bd2343 100644 --- a/libs/cli/langchain_cli/integration_template/integration_template/toolkits.py +++ b/libs/cli/langchain_cli/integration_template/integration_template/toolkits.py @@ -2,10 +2,10 @@ from typing import List -from langchain_core.tools import BaseTool, BaseToolKit +from langchain_core.tools import BaseTool, BaseToolkit -class __ModuleName__Toolkit(BaseToolKit): +class __ModuleName__Toolkit(BaseToolkit): # TODO: Replace all TODOs in docstring. See example docstring: # https://github.com/langchain-ai/langchain/blob/c123cb2b304f52ab65db4714eeec46af69a861ec/libs/community/langchain_community/agent_toolkits/sql/toolkit.py#L19 """__ModuleName__ toolkit. diff --git a/libs/cli/langchain_cli/integration_template/integration_template/tools.py b/libs/cli/langchain_cli/integration_template/integration_template/tools.py index 57deb006f062a..1904c9865b76c 100644 --- a/libs/cli/langchain_cli/integration_template/integration_template/tools.py +++ b/libs/cli/langchain_cli/integration_template/integration_template/tools.py @@ -6,10 +6,10 @@ CallbackManagerForToolRun, ) from langchain_core.tools import BaseTool -from pydantic import BaseModel +from pydantic import BaseModel, Field -class __ModuleName__Input(BaseModel): +class __ModuleName__ToolInput(BaseModel): """Input schema for __ModuleName__ tool. This docstring is **not** part of what is sent to the model when performing tool @@ -18,12 +18,11 @@ class __ModuleName__Input(BaseModel): """ # TODO: Add input args and descriptions. - # a: int = Field(..., description="first number") - # b: int = Field(0, description="second number") - ... + a: int = Field(..., description="first number to add") + b: int = Field(..., description="second number to add") -class __ModuleName__Tool(BaseTool): +class __ModuleName__Tool(BaseTool): # type: ignore[override] """__ModuleName__ tool. Setup: @@ -69,24 +68,26 @@ class __ModuleName__Tool(BaseTool): """The name that is passed to the model when performing tool calling.""" description: str = "TODO: Tool description." """The description that is passed to the model when performing tool calling.""" - args_schema: Type[BaseModel] = __ModuleName__Input + args_schema: Type[BaseModel] = __ModuleName__ToolInput """The schema that is passed to the model when performing tool calling.""" # TODO: Add any other init params for the tool. # param1: Optional[str] # """param1 determines foobar""" - # TODO: Replaced *args with real tool arguments. + # TODO: Replaced (a, b) with real tool arguments. def _run( - self, *args, run_manager: Optional[CallbackManagerForToolRun] = None + self, a: int, b: int, *, run_manager: Optional[CallbackManagerForToolRun] = None ) -> str: - raise NotImplementedError + return str(a + b + 80) # TODO: Implement if tool has native async functionality, otherwise delete. # async def _arun( # self, - # *args, + # a: int, + # b: int, + # *, # run_manager: Optional[AsyncCallbackManagerForToolRun] = None, # ) -> str: # ... diff --git a/libs/cli/langchain_cli/integration_template/integration_template/vectorstores.py b/libs/cli/langchain_cli/integration_template/integration_template/vectorstores.py index 4236e8c037e73..cef35ecaf44c5 100644 --- a/libs/cli/langchain_cli/integration_template/integration_template/vectorstores.py +++ b/libs/cli/langchain_cli/integration_template/integration_template/vectorstores.py @@ -2,25 +2,23 @@ from __future__ import annotations -import asyncio -from functools import partial +import uuid from typing import ( - TYPE_CHECKING, Any, Callable, - Iterable, + Iterator, List, Optional, + Sequence, Tuple, Type, TypeVar, ) +from langchain_core.documents import Document from langchain_core.embeddings import Embeddings from langchain_core.vectorstores import VectorStore - -if TYPE_CHECKING: - from langchain_core.documents import Document +from langchain_core.vectorstores.utils import _cosine_similarity as cosine_similarity VST = TypeVar("VST", bound=VectorStore) @@ -160,146 +158,282 @@ class __ModuleName__VectorStore(VectorStore): """ # noqa: E501 - def add_texts( - self, - texts: Iterable[str], - metadatas: Optional[List[dict]] = None, - **kwargs: Any, - ) -> List[str]: - raise NotImplementedError + def __init__(self, embedding: Embeddings) -> None: + """Initialize with the given embedding function. - async def aadd_texts( - self, - texts: Iterable[str], + Args: + embedding: embedding function to use. + """ + self._database: dict[str, dict[str, Any]] = {} + self.embedding = embedding + + @classmethod + def from_texts( + cls: Type[__ModuleName__VectorStore], + texts: List[str], + embedding: Embeddings, metadatas: Optional[List[dict]] = None, **kwargs: Any, - ) -> List[str]: - return await asyncio.get_running_loop().run_in_executor( - None, partial(self.add_texts, **kwargs), texts, metadatas + ) -> __ModuleName__VectorStore: + store = cls( + embedding=embedding, ) - - def delete(self, ids: Optional[List[str]] = None, **kwargs: Any) -> Optional[bool]: - raise NotImplementedError - - async def adelete( - self, ids: Optional[List[str]] = None, **kwargs: Any - ) -> Optional[bool]: - raise NotImplementedError - - def similarity_search( - self, query: str, k: int = 4, **kwargs: Any - ) -> List[Document]: - raise NotImplementedError - - async def asimilarity_search( - self, query: str, k: int = 4, **kwargs: Any - ) -> List[Document]: - # This is a temporary workaround to make the similarity search - # asynchronous. The proper solution is to make the similarity search - # asynchronous in the vector store implementations. - func = partial(self.similarity_search, query, k=k, **kwargs) - return await asyncio.get_event_loop().run_in_executor(None, func) - - def similarity_search_with_score( - self, *args: Any, **kwargs: Any - ) -> List[Tuple[Document, float]]: - raise NotImplementedError - - async def asimilarity_search_with_score( - self, *args: Any, **kwargs: Any - ) -> List[Tuple[Document, float]]: - # This is a temporary workaround to make the similarity search - # asynchronous. The proper solution is to make the similarity search - # asynchronous in the vector store implementations. - func = partial(self.similarity_search_with_score, *args, **kwargs) - return await asyncio.get_event_loop().run_in_executor(None, func) - - def similarity_search_by_vector( - self, embedding: List[float], k: int = 4, **kwargs: Any - ) -> List[Document]: - raise NotImplementedError - - async def asimilarity_search_by_vector( - self, embedding: List[float], k: int = 4, **kwargs: Any - ) -> List[Document]: - # This is a temporary workaround to make the similarity search - # asynchronous. The proper solution is to make the similarity search - # asynchronous in the vector store implementations. - func = partial(self.similarity_search_by_vector, embedding, k=k, **kwargs) - return await asyncio.get_event_loop().run_in_executor(None, func) - - def max_marginal_relevance_search( + store.add_texts(texts=texts, metadatas=metadatas, **kwargs) + return store + + # optional: add custom async implementations + # @classmethod + # async def afrom_texts( + # cls: Type[VST], + # texts: List[str], + # embedding: Embeddings, + # metadatas: Optional[List[dict]] = None, + # **kwargs: Any, + # ) -> VST: + # return await asyncio.get_running_loop().run_in_executor( + # None, partial(cls.from_texts, **kwargs), texts, embedding, metadatas + # ) + + @property + def embeddings(self) -> Embeddings: + return self.embedding + + def add_documents( self, - query: str, - k: int = 4, - fetch_k: int = 20, - lambda_mult: float = 0.5, + documents: List[Document], + ids: Optional[List[str]] = None, **kwargs: Any, - ) -> List[Document]: - raise NotImplementedError + ) -> List[str]: + """Add documents to the store.""" + texts = [doc.page_content for doc in documents] + vectors = self.embedding.embed_documents(texts) + + if ids and len(ids) != len(texts): + msg = ( + f"ids must be the same length as texts. " + f"Got {len(ids)} ids and {len(texts)} texts." + ) + raise ValueError(msg) - async def amax_marginal_relevance_search( - self, - query: str, - k: int = 4, - fetch_k: int = 20, - lambda_mult: float = 0.5, - **kwargs: Any, - ) -> List[Document]: - # This is a temporary workaround to make the similarity search - # asynchronous. The proper solution is to make the similarity search - # asynchronous in the vector store implementations. - func = partial( - self.max_marginal_relevance_search, - query, - k=k, - fetch_k=fetch_k, - lambda_mult=lambda_mult, - **kwargs, + id_iterator: Iterator[Optional[str]] = ( + iter(ids) if ids else iter(doc.id for doc in documents) ) - return await asyncio.get_event_loop().run_in_executor(None, func) - def max_marginal_relevance_search_by_vector( + ids_ = [] + + for doc, vector in zip(documents, vectors): + doc_id = next(id_iterator) + doc_id_ = doc_id if doc_id else str(uuid.uuid4()) + ids_.append(doc_id_) + self._database[doc_id_] = { + "id": doc_id_, + "vector": vector, + "text": doc.page_content, + "metadata": doc.metadata, + } + + return ids_ + + # optional: add custom async implementations + # async def aadd_documents( + # self, + # documents: List[Document], + # ids: Optional[List[str]] = None, + # **kwargs: Any, + # ) -> List[str]: + # raise NotImplementedError + + def delete(self, ids: Optional[List[str]] = None, **kwargs: Any) -> None: + if ids: + for _id in ids: + self._database.pop(_id, None) + + # optional: add custom async implementations + # async def adelete( + # self, ids: Optional[List[str]] = None, **kwargs: Any + # ) -> None: + # raise NotImplementedError + + def get_by_ids(self, ids: Sequence[str], /) -> list[Document]: + """Get documents by their ids. + + Args: + ids: The ids of the documents to get. + + Returns: + A list of Document objects. + """ + documents = [] + + for doc_id in ids: + doc = self._database.get(doc_id) + if doc: + documents.append( + Document( + id=doc["id"], + page_content=doc["text"], + metadata=doc["metadata"], + ) + ) + return documents + + # optional: add custom async implementations + # async def aget_by_ids(self, ids: Sequence[str], /) -> list[Document]: + # raise NotImplementedError + + # NOTE: the below helper method implements similarity search for in-memory + # storage. It is optional and not a part of the vector store interface. + def _similarity_search_with_score_by_vector( self, embedding: List[float], k: int = 4, - fetch_k: int = 20, - lambda_mult: float = 0.5, + filter: Optional[Callable[[Document], bool]] = None, **kwargs: Any, - ) -> List[Document]: - raise NotImplementedError + ) -> List[tuple[Document, float, List[float]]]: + # get all docs with fixed order in list + docs = list(self._database.values()) + + if filter is not None: + docs = [ + doc + for doc in docs + if filter(Document(page_content=doc["text"], metadata=doc["metadata"])) + ] + + if not docs: + return [] + + similarity = cosine_similarity([embedding], [doc["vector"] for doc in docs])[0] + + # get the indices ordered by similarity score + top_k_idx = similarity.argsort()[::-1][:k] + + return [ + ( + # Document + Document( + id=doc_dict["id"], + page_content=doc_dict["text"], + metadata=doc_dict["metadata"], + ), + # Score + float(similarity[idx].item()), + # Embedding vector + doc_dict["vector"], + ) + for idx in top_k_idx + # Assign using walrus operator to avoid multiple lookups + if (doc_dict := docs[idx]) + ] - async def amax_marginal_relevance_search_by_vector( - self, - embedding: List[float], - k: int = 4, - fetch_k: int = 20, - lambda_mult: float = 0.5, - **kwargs: Any, + def similarity_search( + self, query: str, k: int = 4, **kwargs: Any ) -> List[Document]: - raise NotImplementedError - - @classmethod - def from_texts( - cls: Type[VST], - texts: List[str], - embedding: Embeddings, - metadatas: Optional[List[dict]] = None, - **kwargs: Any, - ) -> VST: - raise NotImplementedError - - @classmethod - async def afrom_texts( - cls: Type[VST], - texts: List[str], - embedding: Embeddings, - metadatas: Optional[List[dict]] = None, - **kwargs: Any, - ) -> VST: - return await asyncio.get_running_loop().run_in_executor( - None, partial(cls.from_texts, **kwargs), texts, embedding, metadatas - ) + embedding = self.embedding.embed_query(query) + return [ + doc + for doc, _, _ in self._similarity_search_with_score_by_vector( + embedding=embedding, k=k, **kwargs + ) + ] + + # optional: add custom async implementations + # async def asimilarity_search( + # self, query: str, k: int = 4, **kwargs: Any + # ) -> List[Document]: + # # This is a temporary workaround to make the similarity search + # # asynchronous. The proper solution is to make the similarity search + # # asynchronous in the vector store implementations. + # func = partial(self.similarity_search, query, k=k, **kwargs) + # return await asyncio.get_event_loop().run_in_executor(None, func) - def _select_relevance_score_fn(self) -> Callable[[float], float]: - raise NotImplementedError + def similarity_search_with_score( + self, query: str, k: int = 4, **kwargs: Any + ) -> List[Tuple[Document, float]]: + embedding = self.embedding.embed_query(query) + return [ + (doc, similarity) + for doc, similarity, _ in self._similarity_search_with_score_by_vector( + embedding=embedding, k=k, **kwargs + ) + ] + + # optional: add custom async implementations + # async def asimilarity_search_with_score( + # self, *args: Any, **kwargs: Any + # ) -> List[Tuple[Document, float]]: + # # This is a temporary workaround to make the similarity search + # # asynchronous. The proper solution is to make the similarity search + # # asynchronous in the vector store implementations. + # func = partial(self.similarity_search_with_score, *args, **kwargs) + # return await asyncio.get_event_loop().run_in_executor(None, func) + + ### ADDITIONAL OPTIONAL SEARCH METHODS BELOW ### + + # def similarity_search_by_vector( + # self, embedding: List[float], k: int = 4, **kwargs: Any + # ) -> List[Document]: + # raise NotImplementedError + + # optional: add custom async implementations + # async def asimilarity_search_by_vector( + # self, embedding: List[float], k: int = 4, **kwargs: Any + # ) -> List[Document]: + # # This is a temporary workaround to make the similarity search + # # asynchronous. The proper solution is to make the similarity search + # # asynchronous in the vector store implementations. + # func = partial(self.similarity_search_by_vector, embedding, k=k, **kwargs) + # return await asyncio.get_event_loop().run_in_executor(None, func) + + # def max_marginal_relevance_search( + # self, + # query: str, + # k: int = 4, + # fetch_k: int = 20, + # lambda_mult: float = 0.5, + # **kwargs: Any, + # ) -> List[Document]: + # raise NotImplementedError + + # optional: add custom async implementations + # async def amax_marginal_relevance_search( + # self, + # query: str, + # k: int = 4, + # fetch_k: int = 20, + # lambda_mult: float = 0.5, + # **kwargs: Any, + # ) -> List[Document]: + # # This is a temporary workaround to make the similarity search + # # asynchronous. The proper solution is to make the similarity search + # # asynchronous in the vector store implementations. + # func = partial( + # self.max_marginal_relevance_search, + # query, + # k=k, + # fetch_k=fetch_k, + # lambda_mult=lambda_mult, + # **kwargs, + # ) + # return await asyncio.get_event_loop().run_in_executor(None, func) + + # def max_marginal_relevance_search_by_vector( + # self, + # embedding: List[float], + # k: int = 4, + # fetch_k: int = 20, + # lambda_mult: float = 0.5, + # **kwargs: Any, + # ) -> List[Document]: + # raise NotImplementedError + + # optional: add custom async implementations + # async def amax_marginal_relevance_search_by_vector( + # self, + # embedding: List[float], + # k: int = 4, + # fetch_k: int = 20, + # lambda_mult: float = 0.5, + # **kwargs: Any, + # ) -> List[Document]: + # raise NotImplementedError diff --git a/libs/cli/langchain_cli/integration_template/pyproject.toml b/libs/cli/langchain_cli/integration_template/pyproject.toml index efdcb9efeadcd..fa3c596b069bb 100644 --- a/libs/cli/langchain_cli/integration_template/pyproject.toml +++ b/libs/cli/langchain_cli/integration_template/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = [ "poetry-core>=1.0.0",] +requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" [tool.poetry] @@ -23,14 +23,16 @@ python = ">=3.9,<4.0" langchain-core = "^0.3.15" [tool.ruff.lint] -select = [ "E", "F", "I", "T201",] +select = ["E", "F", "I", "T201"] [tool.coverage.run] -omit = [ "tests/*",] +omit = ["tests/*"] [tool.pytest.ini_options] addopts = "--strict-markers --strict-config --durations=5" -markers = [ "compile: mark placeholder test used to compile integration tests without running them",] +markers = [ + "compile: mark placeholder test used to compile integration tests without running them", +] asyncio_mode = "auto" [tool.poetry.group.test] @@ -48,11 +50,14 @@ optional = true [tool.poetry.group.dev] optional = true +[tool.poetry.group.dev.dependencies] + [tool.poetry.group.test.dependencies] pytest = "^7.4.3" pytest-asyncio = "^0.23.2" pytest-socket = "^0.7.0" pytest-watcher = "^0.3.4" +langchain-tests = "^0.3.5" [tool.poetry.group.codespell.dependencies] codespell = "^2.2.6" @@ -64,15 +69,3 @@ ruff = "^0.5" [tool.poetry.group.typing.dependencies] mypy = "^1.10" - -[tool.poetry.group.test.dependencies.langchain-core] -path = "../../core" -develop = true - -[tool.poetry.group.dev.dependencies.langchain-core] -path = "../../core" -develop = true - -[tool.poetry.group.typing.dependencies.langchain-core] -path = "../../core" -develop = true diff --git a/libs/cli/langchain_cli/integration_template/tests/integration_tests/test_chat_models.py b/libs/cli/langchain_cli/integration_template/tests/integration_tests/test_chat_models.py index 26b63bd706b5f..c84dafa973abf 100644 --- a/libs/cli/langchain_cli/integration_template/tests/integration_tests/test_chat_models.py +++ b/libs/cli/langchain_cli/integration_template/tests/integration_tests/test_chat_models.py @@ -1,64 +1,21 @@ """Test Chat__ModuleName__ chat model.""" -from __module_name__.chat_models import Chat__ModuleName__ - - -def test_stream() -> None: - """Test streaming tokens from OpenAI.""" - llm = Chat__ModuleName__() - - for token in llm.stream("I'm Pickle Rick"): - assert isinstance(token.content, str) - - -async def test_astream() -> None: - """Test streaming tokens from OpenAI.""" - llm = Chat__ModuleName__() - - async for token in llm.astream("I'm Pickle Rick"): - assert isinstance(token.content, str) - - -async def test_abatch() -> None: - """Test streaming tokens from Chat__ModuleName__.""" - llm = Chat__ModuleName__() - - result = await llm.abatch(["I'm Pickle Rick", "I'm not Pickle Rick"]) - for token in result: - assert isinstance(token.content, str) - +from typing import Type -async def test_abatch_tags() -> None: - """Test batch tokens from Chat__ModuleName__.""" - llm = Chat__ModuleName__() - - result = await llm.abatch( - ["I'm Pickle Rick", "I'm not Pickle Rick"], config={"tags": ["foo"]} - ) - for token in result: - assert isinstance(token.content, str) - - -def test_batch() -> None: - """Test batch tokens from Chat__ModuleName__.""" - llm = Chat__ModuleName__() - - result = llm.batch(["I'm Pickle Rick", "I'm not Pickle Rick"]) - for token in result: - assert isinstance(token.content, str) - - -async def test_ainvoke() -> None: - """Test invoke tokens from Chat__ModuleName__.""" - llm = Chat__ModuleName__() - - result = await llm.ainvoke("I'm Pickle Rick", config={"tags": ["foo"]}) - assert isinstance(result.content, str) - - -def test_invoke() -> None: - """Test invoke tokens from Chat__ModuleName__.""" - llm = Chat__ModuleName__() - - result = llm.invoke("I'm Pickle Rick", config=dict(tags=["foo"])) - assert isinstance(result.content, str) +from __module_name__.chat_models import Chat__ModuleName__ +from langchain_tests.integration_tests import ChatModelIntegrationTests + + +class TestChatParrotLinkIntegration(ChatModelIntegrationTests): + @property + def chat_model_class(self) -> Type[Chat__ModuleName__]: + return Chat__ModuleName__ + + @property + def chat_model_params(self) -> dict: + # These should be parameters used to initialize your integration for testing + return { + "model": "bird-brain-001", + "temperature": 0, + "parrot_buffer_length": 50, + } diff --git a/libs/cli/langchain_cli/integration_template/tests/integration_tests/test_embeddings.py b/libs/cli/langchain_cli/integration_template/tests/integration_tests/test_embeddings.py index 51937d157d255..f7bd526d04bd5 100644 --- a/libs/cli/langchain_cli/integration_template/tests/integration_tests/test_embeddings.py +++ b/libs/cli/langchain_cli/integration_template/tests/integration_tests/test_embeddings.py @@ -1,20 +1,16 @@ """Test __ModuleName__ embeddings.""" -from __module_name__.embeddings import __ModuleName__Embeddings +from typing import Type +from __module_name__.embeddings import __ModuleName__Embeddings +from langchain_tests.integration_tests import EmbeddingsIntegrationTests -def test___module_name___embedding_documents() -> None: - """Test cohere embeddings.""" - documents = ["foo bar"] - embedding = __ModuleName__Embeddings() - output = embedding.embed_documents(documents) - assert len(output) == 1 - assert len(output[0]) > 0 +class TestParrotLinkEmbeddingsIntegration(EmbeddingsIntegrationTests): + @property + def embeddings_class(self) -> Type[__ModuleName__Embeddings]: + return __ModuleName__Embeddings -def test___module_name___embedding_query() -> None: - """Test cohere embeddings.""" - document = "foo bar" - embedding = __ModuleName__Embeddings() - output = embedding.embed_query(document) - assert len(output) > 0 + @property + def embedding_model_params(self) -> dict: + return {"model": "nest-embed-001"} diff --git a/libs/cli/langchain_cli/integration_template/tests/integration_tests/test_llms.py b/libs/cli/langchain_cli/integration_template/tests/integration_tests/test_llms.py deleted file mode 100644 index 9fd6e0a628e94..0000000000000 --- a/libs/cli/langchain_cli/integration_template/tests/integration_tests/test_llms.py +++ /dev/null @@ -1,64 +0,0 @@ -"""Test __ModuleName__LLM llm.""" - -from __module_name__.llms import __ModuleName__LLM - - -def test_stream() -> None: - """Test streaming tokens from OpenAI.""" - llm = __ModuleName__LLM() - - for token in llm.stream("I'm Pickle Rick"): - assert isinstance(token, str) - - -async def test_astream() -> None: - """Test streaming tokens from OpenAI.""" - llm = __ModuleName__LLM() - - async for token in llm.astream("I'm Pickle Rick"): - assert isinstance(token, str) - - -async def test_abatch() -> None: - """Test streaming tokens from __ModuleName__LLM.""" - llm = __ModuleName__LLM() - - result = await llm.abatch(["I'm Pickle Rick", "I'm not Pickle Rick"]) - for token in result: - assert isinstance(token, str) - - -async def test_abatch_tags() -> None: - """Test batch tokens from __ModuleName__LLM.""" - llm = __ModuleName__LLM() - - result = await llm.abatch( - ["I'm Pickle Rick", "I'm not Pickle Rick"], config={"tags": ["foo"]} - ) - for token in result: - assert isinstance(token, str) - - -def test_batch() -> None: - """Test batch tokens from __ModuleName__LLM.""" - llm = __ModuleName__LLM() - - result = llm.batch(["I'm Pickle Rick", "I'm not Pickle Rick"]) - for token in result: - assert isinstance(token, str) - - -async def test_ainvoke() -> None: - """Test invoke tokens from __ModuleName__LLM.""" - llm = __ModuleName__LLM() - - result = await llm.ainvoke("I'm Pickle Rick", config={"tags": ["foo"]}) - assert isinstance(result, str) - - -def test_invoke() -> None: - """Test invoke tokens from __ModuleName__LLM.""" - llm = __ModuleName__LLM() - - result = llm.invoke("I'm Pickle Rick", config=dict(tags=["foo"])) - assert isinstance(result, str) diff --git a/libs/cli/langchain_cli/integration_template/tests/integration_tests/test_retrievers.py b/libs/cli/langchain_cli/integration_template/tests/integration_tests/test_retrievers.py new file mode 100644 index 0000000000000..f4164ebd1b0f4 --- /dev/null +++ b/libs/cli/langchain_cli/integration_template/tests/integration_tests/test_retrievers.py @@ -0,0 +1,24 @@ +from typing import Type + +from __module_name__.retrievers import __ModuleName__Retriever +from langchain_tests.integration_tests import ( + RetrieversIntegrationTests, +) + + +class Test__ModuleName__Retriever(RetrieversIntegrationTests): + @property + def retriever_constructor(self) -> Type[__ModuleName__Retriever]: + """Get an empty vectorstore for unit tests.""" + return __ModuleName__Retriever + + @property + def retriever_constructor_params(self) -> dict: + return {"k": 2} + + @property + def retriever_query_example(self) -> str: + """ + Returns a str representing the "query" of an example retriever call. + """ + return "example query" diff --git a/libs/cli/langchain_cli/integration_template/tests/integration_tests/test_tools.py b/libs/cli/langchain_cli/integration_template/tests/integration_tests/test_tools.py new file mode 100644 index 0000000000000..529bd7fe7ea16 --- /dev/null +++ b/libs/cli/langchain_cli/integration_template/tests/integration_tests/test_tools.py @@ -0,0 +1,27 @@ +from typing import Type + +from __module_name__.tools import __ModuleName__Tool +from langchain_tests.integration_tests import ToolsIntegrationTests + + +class TestParrotMultiplyToolIntegration(ToolsIntegrationTests): + @property + def tool_constructor(self) -> Type[__ModuleName__Tool]: + return __ModuleName__Tool + + @property + def tool_constructor_params(self) -> dict: + # if your tool constructor instead required initialization arguments like + # `def __init__(self, some_arg: int):`, you would return those here + # as a dictionary, e.g.: `return {'some_arg': 42}` + return {} + + @property + def tool_invoke_params_example(self) -> dict: + """ + Returns a dictionary representing the "args" of an example tool call. + + This should NOT be a ToolCall dict - i.e. it should not + have {"name", "id", "args"} keys. + """ + return {"a": 2, "b": 3} diff --git a/libs/cli/langchain_cli/integration_template/tests/integration_tests/test_vectorstores.py b/libs/cli/langchain_cli/integration_template/tests/integration_tests/test_vectorstores.py new file mode 100644 index 0000000000000..d7c71f662d951 --- /dev/null +++ b/libs/cli/langchain_cli/integration_template/tests/integration_tests/test_vectorstores.py @@ -0,0 +1,20 @@ +from typing import Generator + +import pytest +from __module_name__.vectorstores import __ModuleName__VectorStore +from langchain_core.vectorstores import VectorStore +from langchain_tests.integration_tests import VectorStoreIntegrationTests + + +class Test__ModuleName__VectorStore(VectorStoreIntegrationTests): + @pytest.fixture() + def vectorstore(self) -> Generator[VectorStore, None, None]: # type: ignore + """Get an empty vectorstore for unit tests.""" + store = __ModuleName__VectorStore(self.get_embeddings()) + # note: store should be EMPTY at this point + # if you need to delete data, you may do so here + try: + yield store + finally: + # cleanup operations, or deleting data + pass diff --git a/libs/cli/langchain_cli/integration_template/tests/unit_tests/test_chat_models.py b/libs/cli/langchain_cli/integration_template/tests/unit_tests/test_chat_models.py index 70cc82c75dcbf..c83081a44790b 100644 --- a/libs/cli/langchain_cli/integration_template/tests/unit_tests/test_chat_models.py +++ b/libs/cli/langchain_cli/integration_template/tests/unit_tests/test_chat_models.py @@ -1,8 +1,21 @@ """Test chat model integration.""" +from typing import Type + from __module_name__.chat_models import Chat__ModuleName__ +from langchain_tests.unit_tests import ChatModelUnitTests + +class TestChat__ModuleName__Unit(ChatModelUnitTests): + @property + def chat_model_class(self) -> Type[Chat__ModuleName__]: + return Chat__ModuleName__ -def test_initialization() -> None: - """Test chat model initialization.""" - Chat__ModuleName__() + @property + def chat_model_params(self) -> dict: + # These should be parameters used to initialize your integration for testing + return { + "model": "bird-brain-001", + "temperature": 0, + "parrot_buffer_length": 50, + } diff --git a/libs/cli/langchain_cli/integration_template/tests/unit_tests/test_embeddings.py b/libs/cli/langchain_cli/integration_template/tests/unit_tests/test_embeddings.py index ab4f45a9b7304..5fdf8feb0837d 100644 --- a/libs/cli/langchain_cli/integration_template/tests/unit_tests/test_embeddings.py +++ b/libs/cli/langchain_cli/integration_template/tests/unit_tests/test_embeddings.py @@ -1,8 +1,16 @@ """Test embedding model integration.""" +from typing import Type + from __module_name__.embeddings import __ModuleName__Embeddings +from langchain_tests.unit_tests import EmbeddingsUnitTests + +class TestParrotLinkEmbeddingsUnit(EmbeddingsUnitTests): + @property + def embeddings_class(self) -> Type[__ModuleName__Embeddings]: + return __ModuleName__Embeddings -def test_initialization() -> None: - """Test embedding model initialization.""" - __ModuleName__Embeddings() + @property + def embedding_model_params(self) -> dict: + return {"model": "nest-embed-001"} diff --git a/libs/cli/langchain_cli/integration_template/tests/unit_tests/test_imports.py b/libs/cli/langchain_cli/integration_template/tests/unit_tests/test_imports.py deleted file mode 100644 index 1dfa338914189..0000000000000 --- a/libs/cli/langchain_cli/integration_template/tests/unit_tests/test_imports.py +++ /dev/null @@ -1,12 +0,0 @@ -from __module_name__ import __all__ - -EXPECTED_ALL = [ - "__ModuleName__LLM", - "Chat__ModuleName__", - "__ModuleName__VectorStore", - "__ModuleName__Embeddings", -] - - -def test_all_imports() -> None: - assert sorted(EXPECTED_ALL) == sorted(__all__) diff --git a/libs/cli/langchain_cli/integration_template/tests/unit_tests/test_llms.py b/libs/cli/langchain_cli/integration_template/tests/unit_tests/test_llms.py deleted file mode 100644 index 7d53dad10acdd..0000000000000 --- a/libs/cli/langchain_cli/integration_template/tests/unit_tests/test_llms.py +++ /dev/null @@ -1,8 +0,0 @@ -"""Test __ModuleName__ Chat API wrapper.""" - -from __module_name__ import __ModuleName__LLM - - -def test_initialization() -> None: - """Test integration initialization.""" - __ModuleName__LLM() diff --git a/libs/cli/langchain_cli/integration_template/tests/unit_tests/test_tools.py b/libs/cli/langchain_cli/integration_template/tests/unit_tests/test_tools.py new file mode 100644 index 0000000000000..23231276d4125 --- /dev/null +++ b/libs/cli/langchain_cli/integration_template/tests/unit_tests/test_tools.py @@ -0,0 +1,27 @@ +from typing import Type + +from __module_name__.tools import __ModuleName__Tool +from langchain_tests.unit_tests import ToolsUnitTests + + +class TestParrotMultiplyToolUnit(ToolsUnitTests): + @property + def tool_constructor(self) -> Type[__ModuleName__Tool]: + return __ModuleName__Tool + + @property + def tool_constructor_params(self) -> dict: + # if your tool constructor instead required initialization arguments like + # `def __init__(self, some_arg: int):`, you would return those here + # as a dictionary, e.g.: `return {'some_arg': 42}` + return {} + + @property + def tool_invoke_params_example(self) -> dict: + """ + Returns a dictionary representing the "args" of an example tool call. + + This should NOT be a ToolCall dict - i.e. it should not + have {"name", "id", "args"} keys. + """ + return {"a": 2, "b": 3} diff --git a/libs/cli/langchain_cli/integration_template/tests/unit_tests/test_vectorstores.py b/libs/cli/langchain_cli/integration_template/tests/unit_tests/test_vectorstores.py deleted file mode 100644 index 6c044a9a66fb4..0000000000000 --- a/libs/cli/langchain_cli/integration_template/tests/unit_tests/test_vectorstores.py +++ /dev/null @@ -1,6 +0,0 @@ -from __module_name__.vectorstores import __ModuleName__VectorStore - - -def test_initialization() -> None: - """Test integration vectorstore initialization.""" - __ModuleName__VectorStore() diff --git a/libs/cli/langchain_cli/namespaces/app.py b/libs/cli/langchain_cli/namespaces/app.py index 9bf007652b448..d1fcb46b848fb 100644 --- a/libs/cli/langchain_cli/namespaces/app.py +++ b/libs/cli/langchain_cli/namespaces/app.py @@ -5,6 +5,7 @@ import shutil import subprocess import sys +import warnings from pathlib import Path from typing import Dict, List, Optional, Tuple @@ -163,6 +164,12 @@ def add( langchain app add git+ssh://git@github.com/efriis/simple-pirate.git """ + if not branch and not repo: + warnings.warn( + "Adding templates from the default branch and repo is deprecated." + " At a minimum, you will have to add `--branch v0.2` for this to work" + ) + parsed_deps = parse_dependencies(dependencies, repo, branch, api_path) project_root = get_package_root(project_dir) diff --git a/libs/cli/langchain_cli/namespaces/integration.py b/libs/cli/langchain_cli/namespaces/integration.py index e78af97c25d97..fbacf1274f177 100644 --- a/libs/cli/langchain_cli/namespaces/integration.py +++ b/libs/cli/langchain_cli/namespaces/integration.py @@ -6,7 +6,7 @@ import shutil import subprocess from pathlib import Path -from typing import Optional +from typing import Dict, Optional, cast import typer from typing_extensions import Annotated, TypedDict @@ -15,19 +15,17 @@ integration_cli = typer.Typer(no_args_is_help=True, add_completion=False) -Replacements = TypedDict( - "Replacements", - { - "__package_name__": str, - "__module_name__": str, - "__ModuleName__": str, - "__MODULE_NAME__": str, - "__package_name_short__": str, - }, -) +class Replacements(TypedDict): + __package_name__: str + __module_name__: str + __ModuleName__: str + __MODULE_NAME__: str + __package_name_short__: str + __package_name_short_snake__: str -def _process_name(name: str, *, community: bool = False): + +def _process_name(name: str, *, community: bool = False) -> Replacements: preprocessed = name.replace("_", "-").lower() if preprocessed.startswith("langchain-"): @@ -42,7 +40,7 @@ def _process_name(name: str, *, community: bool = False): raise ValueError("Name should not end with `-`.") if preprocessed.find("--") != -1: raise ValueError("Name should not contain consecutive hyphens.") - replacements = { + replacements: Replacements = { "__package_name__": f"langchain-{preprocessed}", "__module_name__": "langchain_" + preprocessed.replace("-", "_"), "__ModuleName__": preprocessed.title().replace("-", ""), @@ -52,7 +50,7 @@ def _process_name(name: str, *, community: bool = False): } if community: replacements["__module_name__"] = preprocessed.replace("-", "_") - return Replacements(replacements) + return replacements @integration_cli.command() @@ -71,19 +69,25 @@ def new( " This is used to name classes like `MyIntegrationVectorStore`" ), ] = None, + src: Annotated[ + Optional[list[str]], + typer.Option( + help="The name of the single template file to copy." + " e.g. `--src integration_template/chat_models.py " + "--dst my_integration/chat_models.py`. Can be used multiple times.", + ), + ] = None, + dst: Annotated[ + Optional[list[str]], + typer.Option( + help="The relative path to the integration package to place the new file in" + ". e.g. `my-integration/my_integration.py`", + ), + ] = None, ): """ Creates a new integration package. - - Should be run from libs/partners """ - # confirm that we are in the right directory - if not Path.cwd().name == "partners" or not Path.cwd().parent.name == "libs": - typer.echo( - "This command should be run from the `libs/partners` directory in the " - "langchain-ai/langchain monorepo. Continuing is NOT recommended." - ) - typer.confirm("Are you sure you want to continue?", abort=True) try: replacements = _process_name(name) @@ -104,27 +108,66 @@ def new( "Name of integration in PascalCase", default=replacements["__ModuleName__"] ) - destination_dir = Path.cwd() / replacements["__package_name_short__"] - if destination_dir.exists(): - typer.echo(f"Folder {destination_dir} exists.") - raise typer.Exit(code=1) - - # copy over template from ../integration_template project_template_dir = Path(__file__).parents[1] / "integration_template" - shutil.copytree(project_template_dir, destination_dir, dirs_exist_ok=False) + destination_dir = Path.cwd() / replacements["__package_name__"] + if not src and not dst: + if destination_dir.exists(): + typer.echo(f"Folder {destination_dir} exists.") + raise typer.Exit(code=1) - # folder movement - package_dir = destination_dir / replacements["__module_name__"] - shutil.move(destination_dir / "integration_template", package_dir) + # copy over template from ../integration_template + shutil.copytree(project_template_dir, destination_dir, dirs_exist_ok=False) - # replacements in files - replace_glob(destination_dir, "**/*", replacements) + # folder movement + package_dir = destination_dir / replacements["__module_name__"] + shutil.move(destination_dir / "integration_template", package_dir) - # poetry install - subprocess.run( - ["poetry", "install", "--with", "lint,test,typing,test_integration"], - cwd=destination_dir, - ) + # replacements in files + replace_glob(destination_dir, "**/*", cast(Dict[str, str], replacements)) + + # poetry install + subprocess.run( + ["poetry", "install", "--with", "lint,test,typing,test_integration"], + cwd=destination_dir, + ) + else: + # confirm src and dst are the same length + if not src: + typer.echo("Cannot provide --dst without --src.") + raise typer.Exit(code=1) + src_paths = [project_template_dir / p for p in src] + if dst and len(src) != len(dst): + typer.echo("Number of --src and --dst arguments must match.") + raise typer.Exit(code=1) + if not dst: + # assume we're in a package dir, copy to equivalent path + dst_paths = [destination_dir / p for p in src] + else: + dst_paths = [Path.cwd() / p for p in dst] + dst_paths = [ + p / f"{replacements['__package_name_short_snake__']}.ipynb" + if not p.suffix + else p + for p in dst_paths + ] + + # confirm no duplicate dst_paths + if len(dst_paths) != len(set(dst_paths)): + typer.echo( + "Duplicate destination paths provided or computed - please " + "specify them explicitly with --dst." + ) + raise typer.Exit(code=1) + + # confirm no files exist at dst_paths + for dst_path in dst_paths: + if dst_path.exists(): + typer.echo(f"File {dst_path} exists.") + raise typer.Exit(code=1) + + for src_path, dst_path in zip(src_paths, dst_paths): + shutil.copy(src_path, dst_path) + replace_file(dst_path, cast(Dict[str, str], replacements)) TEMPLATE_MAP: dict[str, str] = { @@ -187,43 +230,15 @@ def create_doc( """ Creates a new integration doc. """ - try: - replacements = _process_name(name, community=component_type == "Tool") - except ValueError as e: - typer.echo(e) - raise typer.Exit(code=1) - - if name_class: - if not re.match(r"^[A-Z][a-zA-Z0-9]*$", name_class): - typer.echo( - "Name should only contain letters (a-z, A-Z), numbers, and underscores" - ", and start with a capital letter." - ) - raise typer.Exit(code=1) - replacements["__ModuleName__"] = name_class - else: - replacements["__ModuleName__"] = typer.prompt( - ( - "The PascalCase name of the integration (e.g. `OpenAI`, `VertexAI`). " - "Do not include a 'Chat', 'VectorStore', etc. prefix/suffix." - ), - default=replacements["__ModuleName__"], - ) - destination_path = ( - Path.cwd() - / destination_dir - / (replacements["__package_name_short_snake__"] + ".ipynb") - ) - - # copy over template from ../integration_template - template_dir = Path(__file__).parents[1] / "integration_template" / "docs" - if component_type in TEMPLATE_MAP: - docs_template = template_dir / TEMPLATE_MAP[component_type] - else: - raise ValueError( + if component_type not in TEMPLATE_MAP: + typer.echo( f"Unrecognized {component_type=}. Expected one of {_component_types_str}." ) - shutil.copy(docs_template, destination_path) + raise typer.Exit(code=1) - # replacements in file - replace_file(destination_path, replacements) + new( + name=name, + name_class=name_class, + src=[f"docs/{TEMPLATE_MAP[component_type]}"], + dst=[destination_dir], + ) diff --git a/libs/cli/langchain_cli/namespaces/migrate/generate/utils.py b/libs/cli/langchain_cli/namespaces/migrate/generate/utils.py index 57fd457f19e4b..51b8b79bd3b6e 100644 --- a/libs/cli/langchain_cli/namespaces/migrate/generate/utils.py +++ b/libs/cli/langchain_cli/namespaces/migrate/generate/utils.py @@ -17,7 +17,7 @@ class ImportExtractor(ast.NodeVisitor): def __init__(self, *, from_package: Optional[str] = None) -> None: """Extract all imports from the given code, optionally filtering by package.""" - self.imports = [] + self.imports: list = [] self.package = from_package def visit_ImportFrom(self, node): @@ -68,7 +68,7 @@ def find_subclasses_in_module(module, classes_: List[Type]) -> List[str]: return subclasses -def _get_all_classnames_from_file(file: str, pkg: str) -> List[Tuple[str, str]]: +def _get_all_classnames_from_file(file: Path, pkg: str) -> List[Tuple[str, str]]: """Extract all class names from a file.""" with open(file, encoding="utf-8") as f: code = f.read() @@ -145,7 +145,7 @@ def find_imports_from_package( return extractor.imports -def _get_current_module(path: str, pkg_root: str) -> str: +def _get_current_module(path: Path, pkg_root: str) -> str: """Convert a path to a module name.""" path_as_pathlib = pathlib.Path(os.path.abspath(path)) relative_path = path_as_pathlib.relative_to(pkg_root).with_suffix("") diff --git a/libs/cli/langchain_cli/namespaces/migrate/main.py b/libs/cli/langchain_cli/namespaces/migrate/main.py index 483fa9f6ea6c7..0956f9d4f683c 100644 --- a/libs/cli/langchain_cli/namespaces/migrate/main.py +++ b/libs/cli/langchain_cli/namespaces/migrate/main.py @@ -4,7 +4,7 @@ import rich import typer -from gritql import run +from gritql import run # type: ignore from typer import Option diff --git a/libs/cli/langchain_cli/utils/find_replace.py b/libs/cli/langchain_cli/utils/find_replace.py index cfea68d3dfaa7..12cb9bb4a13fe 100644 --- a/libs/cli/langchain_cli/utils/find_replace.py +++ b/libs/cli/langchain_cli/utils/find_replace.py @@ -13,7 +13,7 @@ def find_and_replace(source: str, replacements: Dict[str, str]) -> str: return rtn -def replace_file(source: Path, replacements: Dict[str, str]) -> None: +def replace_file(source: Path, replacements: dict[str, str]) -> None: try: content = source.read_text() except UnicodeDecodeError: @@ -24,7 +24,7 @@ def replace_file(source: Path, replacements: Dict[str, str]) -> None: source.write_text(new_content) -def replace_glob(parent: Path, glob: str, replacements: Dict[str, str]) -> None: +def replace_glob(parent: Path, glob: str, replacements: dict[str, str]) -> None: for file in parent.glob(glob): if not file.is_file(): continue diff --git a/libs/cli/poetry.lock b/libs/cli/poetry.lock index d2026cb3a1119..72cd18d604530 100644 --- a/libs/cli/poetry.lock +++ b/libs/cli/poetry.lock @@ -1,5 +1,128 @@ # This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. +[[package]] +name = "aiohappyeyeballs" +version = "2.4.4" +description = "Happy Eyeballs for asyncio" +optional = false +python-versions = ">=3.8" +files = [ + {file = "aiohappyeyeballs-2.4.4-py3-none-any.whl", hash = "sha256:a980909d50efcd44795c4afeca523296716d50cd756ddca6af8c65b996e27de8"}, + {file = "aiohappyeyeballs-2.4.4.tar.gz", hash = "sha256:5fdd7d87889c63183afc18ce9271f9b0a7d32c2303e394468dd45d514a757745"}, +] + +[[package]] +name = "aiohttp" +version = "3.11.10" +description = "Async http client/server framework (asyncio)" +optional = false +python-versions = ">=3.9" +files = [ + {file = "aiohttp-3.11.10-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cbad88a61fa743c5d283ad501b01c153820734118b65aee2bd7dbb735475ce0d"}, + {file = "aiohttp-3.11.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:80886dac673ceaef499de2f393fc80bb4481a129e6cb29e624a12e3296cc088f"}, + {file = "aiohttp-3.11.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:61b9bae80ed1f338c42f57c16918853dc51775fb5cb61da70d590de14d8b5fb4"}, + {file = "aiohttp-3.11.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9e2e576caec5c6a6b93f41626c9c02fc87cd91538b81a3670b2e04452a63def6"}, + {file = "aiohttp-3.11.10-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:02c13415b5732fb6ee7ff64583a5e6ed1c57aa68f17d2bda79c04888dfdc2769"}, + {file = "aiohttp-3.11.10-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4cfce37f31f20800a6a6620ce2cdd6737b82e42e06e6e9bd1b36f546feb3c44f"}, + {file = "aiohttp-3.11.10-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3bbbfff4c679c64e6e23cb213f57cc2c9165c9a65d63717108a644eb5a7398df"}, + {file = "aiohttp-3.11.10-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49c7dbbc1a559ae14fc48387a115b7d4bbc84b4a2c3b9299c31696953c2a5219"}, + {file = "aiohttp-3.11.10-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:68386d78743e6570f054fe7949d6cb37ef2b672b4d3405ce91fafa996f7d9b4d"}, + {file = "aiohttp-3.11.10-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:9ef405356ba989fb57f84cac66f7b0260772836191ccefbb987f414bcd2979d9"}, + {file = "aiohttp-3.11.10-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:5d6958671b296febe7f5f859bea581a21c1d05430d1bbdcf2b393599b1cdce77"}, + {file = "aiohttp-3.11.10-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:99b7920e7165be5a9e9a3a7f1b680f06f68ff0d0328ff4079e5163990d046767"}, + {file = "aiohttp-3.11.10-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0dc49f42422163efb7e6f1df2636fe3db72713f6cd94688e339dbe33fe06d61d"}, + {file = "aiohttp-3.11.10-cp310-cp310-win32.whl", hash = "sha256:40d1c7a7f750b5648642586ba7206999650208dbe5afbcc5284bcec6579c9b91"}, + {file = "aiohttp-3.11.10-cp310-cp310-win_amd64.whl", hash = "sha256:68ff6f48b51bd78ea92b31079817aff539f6c8fc80b6b8d6ca347d7c02384e33"}, + {file = "aiohttp-3.11.10-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:77c4aa15a89847b9891abf97f3d4048f3c2d667e00f8a623c89ad2dccee6771b"}, + {file = "aiohttp-3.11.10-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:909af95a72cedbefe5596f0bdf3055740f96c1a4baa0dd11fd74ca4de0b4e3f1"}, + {file = "aiohttp-3.11.10-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:386fbe79863eb564e9f3615b959e28b222259da0c48fd1be5929ac838bc65683"}, + {file = "aiohttp-3.11.10-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3de34936eb1a647aa919655ff8d38b618e9f6b7f250cc19a57a4bf7fd2062b6d"}, + {file = "aiohttp-3.11.10-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0c9527819b29cd2b9f52033e7fb9ff08073df49b4799c89cb5754624ecd98299"}, + {file = "aiohttp-3.11.10-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65a96e3e03300b41f261bbfd40dfdbf1c301e87eab7cd61c054b1f2e7c89b9e8"}, + {file = "aiohttp-3.11.10-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98f5635f7b74bcd4f6f72fcd85bea2154b323a9f05226a80bc7398d0c90763b0"}, + {file = "aiohttp-3.11.10-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:03b6002e20938fc6ee0918c81d9e776bebccc84690e2b03ed132331cca065ee5"}, + {file = "aiohttp-3.11.10-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6362cc6c23c08d18ddbf0e8c4d5159b5df74fea1a5278ff4f2c79aed3f4e9f46"}, + {file = "aiohttp-3.11.10-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:3691ed7726fef54e928fe26344d930c0c8575bc968c3e239c2e1a04bd8cf7838"}, + {file = "aiohttp-3.11.10-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:31d5093d3acd02b31c649d3a69bb072d539d4c7659b87caa4f6d2bcf57c2fa2b"}, + {file = "aiohttp-3.11.10-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:8b3cf2dc0f0690a33f2d2b2cb15db87a65f1c609f53c37e226f84edb08d10f52"}, + {file = "aiohttp-3.11.10-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:fbbaea811a2bba171197b08eea288b9402faa2bab2ba0858eecdd0a4105753a3"}, + {file = "aiohttp-3.11.10-cp311-cp311-win32.whl", hash = "sha256:4b2c7ac59c5698a7a8207ba72d9e9c15b0fc484a560be0788b31312c2c5504e4"}, + {file = "aiohttp-3.11.10-cp311-cp311-win_amd64.whl", hash = "sha256:974d3a2cce5fcfa32f06b13ccc8f20c6ad9c51802bb7f829eae8a1845c4019ec"}, + {file = "aiohttp-3.11.10-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b78f053a7ecfc35f0451d961dacdc671f4bcbc2f58241a7c820e9d82559844cf"}, + {file = "aiohttp-3.11.10-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ab7485222db0959a87fbe8125e233b5a6f01f4400785b36e8a7878170d8c3138"}, + {file = "aiohttp-3.11.10-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cf14627232dfa8730453752e9cdc210966490992234d77ff90bc8dc0dce361d5"}, + {file = "aiohttp-3.11.10-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:076bc454a7e6fd646bc82ea7f98296be0b1219b5e3ef8a488afbdd8e81fbac50"}, + {file = "aiohttp-3.11.10-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:482cafb7dc886bebeb6c9ba7925e03591a62ab34298ee70d3dd47ba966370d2c"}, + {file = "aiohttp-3.11.10-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bf3d1a519a324af764a46da4115bdbd566b3c73fb793ffb97f9111dbc684fc4d"}, + {file = "aiohttp-3.11.10-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24213ba85a419103e641e55c27dc7ff03536c4873470c2478cce3311ba1eee7b"}, + {file = "aiohttp-3.11.10-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b99acd4730ad1b196bfb03ee0803e4adac371ae8efa7e1cbc820200fc5ded109"}, + {file = "aiohttp-3.11.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:14cdb5a9570be5a04eec2ace174a48ae85833c2aadc86de68f55541f66ce42ab"}, + {file = "aiohttp-3.11.10-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:7e97d622cb083e86f18317282084bc9fbf261801b0192c34fe4b1febd9f7ae69"}, + {file = "aiohttp-3.11.10-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:012f176945af138abc10c4a48743327a92b4ca9adc7a0e078077cdb5dbab7be0"}, + {file = "aiohttp-3.11.10-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:44224d815853962f48fe124748227773acd9686eba6dc102578defd6fc99e8d9"}, + {file = "aiohttp-3.11.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c87bf31b7fdab94ae3adbe4a48e711bfc5f89d21cf4c197e75561def39e223bc"}, + {file = "aiohttp-3.11.10-cp312-cp312-win32.whl", hash = "sha256:06a8e2ee1cbac16fe61e51e0b0c269400e781b13bcfc33f5425912391a542985"}, + {file = "aiohttp-3.11.10-cp312-cp312-win_amd64.whl", hash = "sha256:be2b516f56ea883a3e14dda17059716593526e10fb6303189aaf5503937db408"}, + {file = "aiohttp-3.11.10-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8cc5203b817b748adccb07f36390feb730b1bc5f56683445bfe924fc270b8816"}, + {file = "aiohttp-3.11.10-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5ef359ebc6949e3a34c65ce20230fae70920714367c63afd80ea0c2702902ccf"}, + {file = "aiohttp-3.11.10-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9bca390cb247dbfaec3c664326e034ef23882c3f3bfa5fbf0b56cad0320aaca5"}, + {file = "aiohttp-3.11.10-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:811f23b3351ca532af598405db1093f018edf81368e689d1b508c57dcc6b6a32"}, + {file = "aiohttp-3.11.10-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ddf5f7d877615f6a1e75971bfa5ac88609af3b74796ff3e06879e8422729fd01"}, + {file = "aiohttp-3.11.10-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6ab29b8a0beb6f8eaf1e5049252cfe74adbaafd39ba91e10f18caeb0e99ffb34"}, + {file = "aiohttp-3.11.10-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c49a76c1038c2dd116fa443eba26bbb8e6c37e924e2513574856de3b6516be99"}, + {file = "aiohttp-3.11.10-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7f3dc0e330575f5b134918976a645e79adf333c0a1439dcf6899a80776c9ab39"}, + {file = "aiohttp-3.11.10-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:efb15a17a12497685304b2d976cb4939e55137df7b09fa53f1b6a023f01fcb4e"}, + {file = "aiohttp-3.11.10-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:db1d0b28fcb7f1d35600150c3e4b490775251dea70f894bf15c678fdd84eda6a"}, + {file = "aiohttp-3.11.10-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:15fccaf62a4889527539ecb86834084ecf6e9ea70588efde86e8bc775e0e7542"}, + {file = "aiohttp-3.11.10-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:593c114a2221444f30749cc5e5f4012488f56bd14de2af44fe23e1e9894a9c60"}, + {file = "aiohttp-3.11.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7852bbcb4d0d2f0c4d583f40c3bc750ee033265d80598d0f9cb6f372baa6b836"}, + {file = "aiohttp-3.11.10-cp313-cp313-win32.whl", hash = "sha256:65e55ca7debae8faaffee0ebb4b47a51b4075f01e9b641c31e554fd376595c6c"}, + {file = "aiohttp-3.11.10-cp313-cp313-win_amd64.whl", hash = "sha256:beb39a6d60a709ae3fb3516a1581777e7e8b76933bb88c8f4420d875bb0267c6"}, + {file = "aiohttp-3.11.10-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0580f2e12de2138f34debcd5d88894786453a76e98febaf3e8fe5db62d01c9bf"}, + {file = "aiohttp-3.11.10-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a55d2ad345684e7c3dd2c20d2f9572e9e1d5446d57200ff630e6ede7612e307f"}, + {file = "aiohttp-3.11.10-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:04814571cb72d65a6899db6099e377ed00710bf2e3eafd2985166f2918beaf59"}, + {file = "aiohttp-3.11.10-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e44a9a3c053b90c6f09b1bb4edd880959f5328cf63052503f892c41ea786d99f"}, + {file = "aiohttp-3.11.10-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:502a1464ccbc800b4b1995b302efaf426e8763fadf185e933c2931df7db9a199"}, + {file = "aiohttp-3.11.10-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:613e5169f8ae77b1933e42e418a95931fb4867b2991fc311430b15901ed67079"}, + {file = "aiohttp-3.11.10-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cca22a61b7fe45da8fc73c3443150c3608750bbe27641fc7558ec5117b27fdf"}, + {file = "aiohttp-3.11.10-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:86a5dfcc39309470bd7b68c591d84056d195428d5d2e0b5ccadfbaf25b026ebc"}, + {file = "aiohttp-3.11.10-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:77ae58586930ee6b2b6f696c82cf8e78c8016ec4795c53e36718365f6959dc82"}, + {file = "aiohttp-3.11.10-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:78153314f26d5abef3239b4a9af20c229c6f3ecb97d4c1c01b22c4f87669820c"}, + {file = "aiohttp-3.11.10-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:98283b94cc0e11c73acaf1c9698dea80c830ca476492c0fe2622bd931f34b487"}, + {file = "aiohttp-3.11.10-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:53bf2097e05c2accc166c142a2090e4c6fd86581bde3fd9b2d3f9e93dda66ac1"}, + {file = "aiohttp-3.11.10-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c5532f0441fc09c119e1dca18fbc0687e64fbeb45aa4d6a87211ceaee50a74c4"}, + {file = "aiohttp-3.11.10-cp39-cp39-win32.whl", hash = "sha256:47ad15a65fb41c570cd0ad9a9ff8012489e68176e7207ec7b82a0940dddfd8be"}, + {file = "aiohttp-3.11.10-cp39-cp39-win_amd64.whl", hash = "sha256:c6b9e6d7e41656d78e37ce754813fa44b455c3d0d0dced2a047def7dc5570b74"}, + {file = "aiohttp-3.11.10.tar.gz", hash = "sha256:b1fc6b45010a8d0ff9e88f9f2418c6fd408c99c211257334aff41597ebece42e"}, +] + +[package.dependencies] +aiohappyeyeballs = ">=2.3.0" +aiosignal = ">=1.1.2" +async-timeout = {version = ">=4.0,<6.0", markers = "python_version < \"3.11\""} +attrs = ">=17.3.0" +frozenlist = ">=1.1.1" +multidict = ">=4.5,<7.0" +propcache = ">=0.2.0" +yarl = ">=1.17.0,<2.0" + +[package.extras] +speedups = ["Brotli", "aiodns (>=3.2.0)", "brotlicffi"] + +[[package]] +name = "aiosignal" +version = "1.3.1" +description = "aiosignal: a list of registered asynchronous callbacks" +optional = false +python-versions = ">=3.7" +files = [ + {file = "aiosignal-1.3.1-py3-none-any.whl", hash = "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17"}, + {file = "aiosignal-1.3.1.tar.gz", hash = "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc"}, +] + +[package.dependencies] +frozenlist = ">=1.1.0" + [[package]] name = "annotated-types" version = "0.7.0" @@ -13,26 +136,56 @@ files = [ [[package]] name = "anyio" -version = "4.6.2.post1" +version = "4.7.0" description = "High level compatibility layer for multiple asynchronous event loop implementations" optional = false python-versions = ">=3.9" files = [ - {file = "anyio-4.6.2.post1-py3-none-any.whl", hash = "sha256:6d170c36fba3bdd840c73d3868c1e777e33676a69c3a72cf0a0d5d6d8009b61d"}, - {file = "anyio-4.6.2.post1.tar.gz", hash = "sha256:4c8bc31ccdb51c7f7bd251f51c609e038d63e34219b44aa86e47576389880b4c"}, + {file = "anyio-4.7.0-py3-none-any.whl", hash = "sha256:ea60c3723ab42ba6fff7e8ccb0488c898ec538ff4df1f1d5e642c3601d07e352"}, + {file = "anyio-4.7.0.tar.gz", hash = "sha256:2f834749c602966b7d456a7567cafcb309f96482b5081d14ac93ccd457f9dd48"}, ] [package.dependencies] exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""} idna = ">=2.8" sniffio = ">=1.1" -typing-extensions = {version = ">=4.1", markers = "python_version < \"3.11\""} +typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""} [package.extras] -doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] -test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21.0b1)"] +doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx_rtd_theme"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21)"] trio = ["trio (>=0.26.1)"] +[[package]] +name = "async-timeout" +version = "4.0.3" +description = "Timeout context manager for asyncio programs" +optional = false +python-versions = ">=3.7" +files = [ + {file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"}, + {file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"}, +] + +[[package]] +name = "attrs" +version = "24.2.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.7" +files = [ + {file = "attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2"}, + {file = "attrs-24.2.0.tar.gz", hash = "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346"}, +] + +[package.extras] +benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"] +tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] + [[package]] name = "certifi" version = "2024.8.30" @@ -209,13 +362,13 @@ test = ["pytest (>=6)"] [[package]] name = "fastapi" -version = "0.115.5" +version = "0.115.6" description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" optional = false python-versions = ">=3.8" files = [ - {file = "fastapi-0.115.5-py3-none-any.whl", hash = "sha256:596b95adbe1474da47049e802f9a65ab2ffa9c2b07e7efee70eb8a66c9f2f796"}, - {file = "fastapi-0.115.5.tar.gz", hash = "sha256:0e7a4d0dc0d01c68df21887cce0945e72d3c48b9f4f79dfe7a7d53aa08fbb289"}, + {file = "fastapi-0.115.6-py3-none-any.whl", hash = "sha256:e9240b29e36fa8f4bb7290316988e90c381e5092e0cbe84e7818cc3713bcf305"}, + {file = "fastapi-0.115.6.tar.gz", hash = "sha256:9ec46f7addc14ea472958a96aae5b5de65f39721a46aaf5705c480d9a8b76654"}, ] [package.dependencies] @@ -227,6 +380,107 @@ typing-extensions = ">=4.8.0" 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 = "frozenlist" +version = "1.5.0" +description = "A list-like structure which implements collections.abc.MutableSequence" +optional = false +python-versions = ">=3.8" +files = [ + {file = "frozenlist-1.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5b6a66c18b5b9dd261ca98dffcb826a525334b2f29e7caa54e182255c5f6a65a"}, + {file = "frozenlist-1.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d1b3eb7b05ea246510b43a7e53ed1653e55c2121019a97e60cad7efb881a97bb"}, + {file = "frozenlist-1.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:15538c0cbf0e4fa11d1e3a71f823524b0c46299aed6e10ebb4c2089abd8c3bec"}, + {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e79225373c317ff1e35f210dd5f1344ff31066ba8067c307ab60254cd3a78ad5"}, + {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9272fa73ca71266702c4c3e2d4a28553ea03418e591e377a03b8e3659d94fa76"}, + {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:498524025a5b8ba81695761d78c8dd7382ac0b052f34e66939c42df860b8ff17"}, + {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:92b5278ed9d50fe610185ecd23c55d8b307d75ca18e94c0e7de328089ac5dcba"}, + {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f3c8c1dacd037df16e85227bac13cca58c30da836c6f936ba1df0c05d046d8d"}, + {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f2ac49a9bedb996086057b75bf93538240538c6d9b38e57c82d51f75a73409d2"}, + {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e66cc454f97053b79c2ab09c17fbe3c825ea6b4de20baf1be28919460dd7877f"}, + {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:5a3ba5f9a0dfed20337d3e966dc359784c9f96503674c2faf015f7fe8e96798c"}, + {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6321899477db90bdeb9299ac3627a6a53c7399c8cd58d25da094007402b039ab"}, + {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:76e4753701248476e6286f2ef492af900ea67d9706a0155335a40ea21bf3b2f5"}, + {file = "frozenlist-1.5.0-cp310-cp310-win32.whl", hash = "sha256:977701c081c0241d0955c9586ffdd9ce44f7a7795df39b9151cd9a6fd0ce4cfb"}, + {file = "frozenlist-1.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:189f03b53e64144f90990d29a27ec4f7997d91ed3d01b51fa39d2dbe77540fd4"}, + {file = "frozenlist-1.5.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:fd74520371c3c4175142d02a976aee0b4cb4a7cc912a60586ffd8d5929979b30"}, + {file = "frozenlist-1.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2f3f7a0fbc219fb4455264cae4d9f01ad41ae6ee8524500f381de64ffaa077d5"}, + {file = "frozenlist-1.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f47c9c9028f55a04ac254346e92977bf0f166c483c74b4232bee19a6697e4778"}, + {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0996c66760924da6e88922756d99b47512a71cfd45215f3570bf1e0b694c206a"}, + {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a2fe128eb4edeabe11896cb6af88fca5346059f6c8d807e3b910069f39157869"}, + {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1a8ea951bbb6cacd492e3948b8da8c502a3f814f5d20935aae74b5df2b19cf3d"}, + {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:de537c11e4aa01d37db0d403b57bd6f0546e71a82347a97c6a9f0dcc532b3a45"}, + {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c2623347b933fcb9095841f1cc5d4ff0b278addd743e0e966cb3d460278840d"}, + {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cee6798eaf8b1416ef6909b06f7dc04b60755206bddc599f52232606e18179d3"}, + {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f5f9da7f5dbc00a604fe74aa02ae7c98bcede8a3b8b9666f9f86fc13993bc71a"}, + {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:90646abbc7a5d5c7c19461d2e3eeb76eb0b204919e6ece342feb6032c9325ae9"}, + {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:bdac3c7d9b705d253b2ce370fde941836a5f8b3c5c2b8fd70940a3ea3af7f4f2"}, + {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:03d33c2ddbc1816237a67f66336616416e2bbb6beb306e5f890f2eb22b959cdf"}, + {file = "frozenlist-1.5.0-cp311-cp311-win32.whl", hash = "sha256:237f6b23ee0f44066219dae14c70ae38a63f0440ce6750f868ee08775073f942"}, + {file = "frozenlist-1.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:0cc974cc93d32c42e7b0f6cf242a6bd941c57c61b618e78b6c0a96cb72788c1d"}, + {file = "frozenlist-1.5.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:31115ba75889723431aa9a4e77d5f398f5cf976eea3bdf61749731f62d4a4a21"}, + {file = "frozenlist-1.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7437601c4d89d070eac8323f121fcf25f88674627505334654fd027b091db09d"}, + {file = "frozenlist-1.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7948140d9f8ece1745be806f2bfdf390127cf1a763b925c4a805c603df5e697e"}, + {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feeb64bc9bcc6b45c6311c9e9b99406660a9c05ca8a5b30d14a78555088b0b3a"}, + {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:683173d371daad49cffb8309779e886e59c2f369430ad28fe715f66d08d4ab1a"}, + {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7d57d8f702221405a9d9b40f9da8ac2e4a1a8b5285aac6100f3393675f0a85ee"}, + {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30c72000fbcc35b129cb09956836c7d7abf78ab5416595e4857d1cae8d6251a6"}, + {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:000a77d6034fbad9b6bb880f7ec073027908f1b40254b5d6f26210d2dab1240e"}, + {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5d7f5a50342475962eb18b740f3beecc685a15b52c91f7d975257e13e029eca9"}, + {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:87f724d055eb4785d9be84e9ebf0f24e392ddfad00b3fe036e43f489fafc9039"}, + {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:6e9080bb2fb195a046e5177f10d9d82b8a204c0736a97a153c2466127de87784"}, + {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9b93d7aaa36c966fa42efcaf716e6b3900438632a626fb09c049f6a2f09fc631"}, + {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:52ef692a4bc60a6dd57f507429636c2af8b6046db8b31b18dac02cbc8f507f7f"}, + {file = "frozenlist-1.5.0-cp312-cp312-win32.whl", hash = "sha256:29d94c256679247b33a3dc96cce0f93cbc69c23bf75ff715919332fdbb6a32b8"}, + {file = "frozenlist-1.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:8969190d709e7c48ea386db202d708eb94bdb29207a1f269bab1196ce0dcca1f"}, + {file = "frozenlist-1.5.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7a1a048f9215c90973402e26c01d1cff8a209e1f1b53f72b95c13db61b00f953"}, + {file = "frozenlist-1.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:dd47a5181ce5fcb463b5d9e17ecfdb02b678cca31280639255ce9d0e5aa67af0"}, + {file = "frozenlist-1.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1431d60b36d15cda188ea222033eec8e0eab488f39a272461f2e6d9e1a8e63c2"}, + {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6482a5851f5d72767fbd0e507e80737f9c8646ae7fd303def99bfe813f76cf7f"}, + {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:44c49271a937625619e862baacbd037a7ef86dd1ee215afc298a417ff3270608"}, + {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:12f78f98c2f1c2429d42e6a485f433722b0061d5c0b0139efa64f396efb5886b"}, + {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ce3aa154c452d2467487765e3adc730a8c153af77ad84096bc19ce19a2400840"}, + {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b7dc0c4338e6b8b091e8faf0db3168a37101943e687f373dce00959583f7439"}, + {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:45e0896250900b5aa25180f9aec243e84e92ac84bd4a74d9ad4138ef3f5c97de"}, + {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:561eb1c9579d495fddb6da8959fd2a1fca2c6d060d4113f5844b433fc02f2641"}, + {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:df6e2f325bfee1f49f81aaac97d2aa757c7646534a06f8f577ce184afe2f0a9e"}, + {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:140228863501b44b809fb39ec56b5d4071f4d0aa6d216c19cbb08b8c5a7eadb9"}, + {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7707a25d6a77f5d27ea7dc7d1fc608aa0a478193823f88511ef5e6b8a48f9d03"}, + {file = "frozenlist-1.5.0-cp313-cp313-win32.whl", hash = "sha256:31a9ac2b38ab9b5a8933b693db4939764ad3f299fcaa931a3e605bc3460e693c"}, + {file = "frozenlist-1.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:11aabdd62b8b9c4b84081a3c246506d1cddd2dd93ff0ad53ede5defec7886b28"}, + {file = "frozenlist-1.5.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:dd94994fc91a6177bfaafd7d9fd951bc8689b0a98168aa26b5f543868548d3ca"}, + {file = "frozenlist-1.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2d0da8bbec082bf6bf18345b180958775363588678f64998c2b7609e34719b10"}, + {file = "frozenlist-1.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:73f2e31ea8dd7df61a359b731716018c2be196e5bb3b74ddba107f694fbd7604"}, + {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:828afae9f17e6de596825cf4228ff28fbdf6065974e5ac1410cecc22f699d2b3"}, + {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f1577515d35ed5649d52ab4319db757bb881ce3b2b796d7283e6634d99ace307"}, + {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2150cc6305a2c2ab33299453e2968611dacb970d2283a14955923062c8d00b10"}, + {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a72b7a6e3cd2725eff67cd64c8f13335ee18fc3c7befc05aed043d24c7b9ccb9"}, + {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c16d2fa63e0800723139137d667e1056bee1a1cf7965153d2d104b62855e9b99"}, + {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:17dcc32fc7bda7ce5875435003220a457bcfa34ab7924a49a1c19f55b6ee185c"}, + {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:97160e245ea33d8609cd2b8fd997c850b56db147a304a262abc2b3be021a9171"}, + {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:f1e6540b7fa044eee0bb5111ada694cf3dc15f2b0347ca125ee9ca984d5e9e6e"}, + {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:91d6c171862df0a6c61479d9724f22efb6109111017c87567cfeb7b5d1449fdf"}, + {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:c1fac3e2ace2eb1052e9f7c7db480818371134410e1f5c55d65e8f3ac6d1407e"}, + {file = "frozenlist-1.5.0-cp38-cp38-win32.whl", hash = "sha256:b97f7b575ab4a8af9b7bc1d2ef7f29d3afee2226bd03ca3875c16451ad5a7723"}, + {file = "frozenlist-1.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:374ca2dabdccad8e2a76d40b1d037f5bd16824933bf7bcea3e59c891fd4a0923"}, + {file = "frozenlist-1.5.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9bbcdfaf4af7ce002694a4e10a0159d5a8d20056a12b05b45cea944a4953f972"}, + {file = "frozenlist-1.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1893f948bf6681733aaccf36c5232c231e3b5166d607c5fa77773611df6dc336"}, + {file = "frozenlist-1.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2b5e23253bb709ef57a8e95e6ae48daa9ac5f265637529e4ce6b003a37b2621f"}, + {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f253985bb515ecd89629db13cb58d702035ecd8cfbca7d7a7e29a0e6d39af5f"}, + {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:04a5c6babd5e8fb7d3c871dc8b321166b80e41b637c31a995ed844a6139942b6"}, + {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9fe0f1c29ba24ba6ff6abf688cb0b7cf1efab6b6aa6adc55441773c252f7411"}, + {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:226d72559fa19babe2ccd920273e767c96a49b9d3d38badd7c91a0fdeda8ea08"}, + {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15b731db116ab3aedec558573c1a5eec78822b32292fe4f2f0345b7f697745c2"}, + {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:366d8f93e3edfe5a918c874702f78faac300209a4d5bf38352b2c1bdc07a766d"}, + {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1b96af8c582b94d381a1c1f51ffaedeb77c821c690ea5f01da3d70a487dd0a9b"}, + {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:c03eff4a41bd4e38415cbed054bbaff4a075b093e2394b6915dca34a40d1e38b"}, + {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:50cf5e7ee9b98f22bdecbabf3800ae78ddcc26e4a435515fc72d97903e8488e0"}, + {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1e76bfbc72353269c44e0bc2cfe171900fbf7f722ad74c9a7b638052afe6a00c"}, + {file = "frozenlist-1.5.0-cp39-cp39-win32.whl", hash = "sha256:666534d15ba8f0fda3f53969117383d5dc021266b3c1a42c9ec4855e4b58b9d3"}, + {file = "frozenlist-1.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:5c28f4b5dbef8a0d8aad0d4de24d1e9e981728628afaf4ea0792f5d0939372f0"}, + {file = "frozenlist-1.5.0-py3-none-any.whl", hash = "sha256:d994863bba198a4a518b467bb971c56e1db3f180a25c6cf7bb1949c267f748c3"}, + {file = "frozenlist-1.5.0.tar.gz", hash = "sha256:81d5af29e61b9c8348e876d442253723928dce6433e0e76cd925cd83f1b4b817"}, +] + [[package]] name = "gitdb" version = "4.0.11" @@ -259,6 +513,92 @@ 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 = "greenlet" +version = "3.1.1" +description = "Lightweight in-process concurrent programming" +optional = false +python-versions = ">=3.7" +files = [ + {file = "greenlet-3.1.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:0bbae94a29c9e5c7e4a2b7f0aae5c17e8e90acbfd3bf6270eeba60c39fce3563"}, + {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fde093fb93f35ca72a556cf72c92ea3ebfda3d79fc35bb19fbe685853869a83"}, + {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:36b89d13c49216cadb828db8dfa6ce86bbbc476a82d3a6c397f0efae0525bdd0"}, + {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94b6150a85e1b33b40b1464a3f9988dcc5251d6ed06842abff82e42632fac120"}, + {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93147c513fac16385d1036b7e5b102c7fbbdb163d556b791f0f11eada7ba65dc"}, + {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:da7a9bff22ce038e19bf62c4dd1ec8391062878710ded0a845bcf47cc0200617"}, + {file = "greenlet-3.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b2795058c23988728eec1f36a4e5e4ebad22f8320c85f3587b539b9ac84128d7"}, + {file = "greenlet-3.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ed10eac5830befbdd0c32f83e8aa6288361597550ba669b04c48f0f9a2c843c6"}, + {file = "greenlet-3.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:77c386de38a60d1dfb8e55b8c1101d68c79dfdd25c7095d51fec2dd800892b80"}, + {file = "greenlet-3.1.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:e4d333e558953648ca09d64f13e6d8f0523fa705f51cae3f03b5983489958c70"}, + {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09fc016b73c94e98e29af67ab7b9a879c307c6731a2c9da0db5a7d9b7edd1159"}, + {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d5e975ca70269d66d17dd995dafc06f1b06e8cb1ec1e9ed54c1d1e4a7c4cf26e"}, + {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b2813dc3de8c1ee3f924e4d4227999285fd335d1bcc0d2be6dc3f1f6a318ec1"}, + {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e347b3bfcf985a05e8c0b7d462ba6f15b1ee1c909e2dcad795e49e91b152c383"}, + {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e8f8c9cb53cdac7ba9793c276acd90168f416b9ce36799b9b885790f8ad6c0a"}, + {file = "greenlet-3.1.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:62ee94988d6b4722ce0028644418d93a52429e977d742ca2ccbe1c4f4a792511"}, + {file = "greenlet-3.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1776fd7f989fc6b8d8c8cb8da1f6b82c5814957264d1f6cf818d475ec2bf6395"}, + {file = "greenlet-3.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:48ca08c771c268a768087b408658e216133aecd835c0ded47ce955381105ba39"}, + {file = "greenlet-3.1.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:4afe7ea89de619adc868e087b4d2359282058479d7cfb94970adf4b55284574d"}, + {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f406b22b7c9a9b4f8aa9d2ab13d6ae0ac3e85c9a809bd590ad53fed2bf70dc79"}, + {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c3a701fe5a9695b238503ce5bbe8218e03c3bcccf7e204e455e7462d770268aa"}, + {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2846930c65b47d70b9d178e89c7e1a69c95c1f68ea5aa0a58646b7a96df12441"}, + {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99cfaa2110534e2cf3ba31a7abcac9d328d1d9f1b95beede58294a60348fba36"}, + {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1443279c19fca463fc33e65ef2a935a5b09bb90f978beab37729e1c3c6c25fe9"}, + {file = "greenlet-3.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b7cede291382a78f7bb5f04a529cb18e068dd29e0fb27376074b6d0317bf4dd0"}, + {file = "greenlet-3.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:23f20bb60ae298d7d8656c6ec6db134bca379ecefadb0b19ce6f19d1f232a942"}, + {file = "greenlet-3.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:7124e16b4c55d417577c2077be379514321916d5790fa287c9ed6f23bd2ffd01"}, + {file = "greenlet-3.1.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:05175c27cb459dcfc05d026c4232f9de8913ed006d42713cb8a5137bd49375f1"}, + {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:935e943ec47c4afab8965954bf49bfa639c05d4ccf9ef6e924188f762145c0ff"}, + {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:667a9706c970cb552ede35aee17339a18e8f2a87a51fba2ed39ceeeb1004798a"}, + {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b8a678974d1f3aa55f6cc34dc480169d58f2e6d8958895d68845fa4ab566509e"}, + {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efc0f674aa41b92da8c49e0346318c6075d734994c3c4e4430b1c3f853e498e4"}, + {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0153404a4bb921f0ff1abeb5ce8a5131da56b953eda6e14b88dc6bbc04d2049e"}, + {file = "greenlet-3.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:275f72decf9932639c1c6dd1013a1bc266438eb32710016a1c742df5da6e60a1"}, + {file = "greenlet-3.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c4aab7f6381f38a4b42f269057aee279ab0fc7bf2e929e3d4abfae97b682a12c"}, + {file = "greenlet-3.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:b42703b1cf69f2aa1df7d1030b9d77d3e584a70755674d60e710f0af570f3761"}, + {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1695e76146579f8c06c1509c7ce4dfe0706f49c6831a817ac04eebb2fd02011"}, + {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7876452af029456b3f3549b696bb36a06db7c90747740c5302f74a9e9fa14b13"}, + {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ead44c85f8ab905852d3de8d86f6f8baf77109f9da589cb4fa142bd3b57b475"}, + {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8320f64b777d00dd7ccdade271eaf0cad6636343293a25074cc5566160e4de7b"}, + {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6510bf84a6b643dabba74d3049ead221257603a253d0a9873f55f6a59a65f822"}, + {file = "greenlet-3.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:04b013dc07c96f83134b1e99888e7a79979f1a247e2a9f59697fa14b5862ed01"}, + {file = "greenlet-3.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:411f015496fec93c1c8cd4e5238da364e1da7a124bcb293f085bf2860c32c6f6"}, + {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47da355d8687fd65240c364c90a31569a133b7b60de111c255ef5b606f2ae291"}, + {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:98884ecf2ffb7d7fe6bd517e8eb99d31ff7855a840fa6d0d63cd07c037f6a981"}, + {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1d4aeb8891338e60d1ab6127af1fe45def5259def8094b9c7e34690c8858803"}, + {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db32b5348615a04b82240cc67983cb315309e88d444a288934ee6ceaebcad6cc"}, + {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dcc62f31eae24de7f8dce72134c8651c58000d3b1868e01392baea7c32c247de"}, + {file = "greenlet-3.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1d3755bcb2e02de341c55b4fca7a745a24a9e7212ac953f6b3a48d117d7257aa"}, + {file = "greenlet-3.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:b8da394b34370874b4572676f36acabac172602abf054cbc4ac910219f3340af"}, + {file = "greenlet-3.1.1-cp37-cp37m-win32.whl", hash = "sha256:a0dfc6c143b519113354e780a50381508139b07d2177cb6ad6a08278ec655798"}, + {file = "greenlet-3.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:54558ea205654b50c438029505def3834e80f0869a70fb15b871c29b4575ddef"}, + {file = "greenlet-3.1.1-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:346bed03fe47414091be4ad44786d1bd8bef0c3fcad6ed3dee074a032ab408a9"}, + {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dfc59d69fc48664bc693842bd57acfdd490acafda1ab52c7836e3fc75c90a111"}, + {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d21e10da6ec19b457b82636209cbe2331ff4306b54d06fa04b7c138ba18c8a81"}, + {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:37b9de5a96111fc15418819ab4c4432e4f3c2ede61e660b1e33971eba26ef9ba"}, + {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ef9ea3f137e5711f0dbe5f9263e8c009b7069d8a1acea822bd5e9dae0ae49c8"}, + {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:85f3ff71e2e60bd4b4932a043fbbe0f499e263c628390b285cb599154a3b03b1"}, + {file = "greenlet-3.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:95ffcf719966dd7c453f908e208e14cde192e09fde6c7186c8f1896ef778d8cd"}, + {file = "greenlet-3.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:03a088b9de532cbfe2ba2034b2b85e82df37874681e8c470d6fb2f8c04d7e4b7"}, + {file = "greenlet-3.1.1-cp38-cp38-win32.whl", hash = "sha256:8b8b36671f10ba80e159378df9c4f15c14098c4fd73a36b9ad715f057272fbef"}, + {file = "greenlet-3.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:7017b2be767b9d43cc31416aba48aab0d2309ee31b4dbf10a1d38fb7972bdf9d"}, + {file = "greenlet-3.1.1-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:396979749bd95f018296af156201d6211240e7a23090f50a8d5d18c370084dc3"}, + {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca9d0ff5ad43e785350894d97e13633a66e2b50000e8a183a50a88d834752d42"}, + {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f6ff3b14f2df4c41660a7dec01045a045653998784bf8cfcb5a525bdffffbc8f"}, + {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94ebba31df2aa506d7b14866fed00ac141a867e63143fe5bca82a8e503b36437"}, + {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73aaad12ac0ff500f62cebed98d8789198ea0e6f233421059fa68a5aa7220145"}, + {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:63e4844797b975b9af3a3fb8f7866ff08775f5426925e1e0bbcfe7932059a12c"}, + {file = "greenlet-3.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7939aa3ca7d2a1593596e7ac6d59391ff30281ef280d8632fa03d81f7c5f955e"}, + {file = "greenlet-3.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d0028e725ee18175c6e422797c407874da24381ce0690d6b9396c204c7f7276e"}, + {file = "greenlet-3.1.1-cp39-cp39-win32.whl", hash = "sha256:5e06afd14cbaf9e00899fae69b24a32f2196c19de08fcb9f4779dd4f004e5e7c"}, + {file = "greenlet-3.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:3319aa75e0e0639bc15ff54ca327e8dc7a6fe404003496e3c6925cd3142e0e22"}, + {file = "greenlet-3.1.1.tar.gz", hash = "sha256:4ce3ac6cdb6adf7946475d7ef31777c26d94bccc377e070a7986bd2d5c515467"}, +] + +[package.extras] +docs = ["Sphinx", "furo"] +test = ["objgraph", "psutil"] + [[package]] name = "gritql" version = "0.1.5" @@ -304,13 +644,13 @@ trio = ["trio (>=0.22.0,<1.0)"] [[package]] name = "httpx" -version = "0.27.2" +version = "0.28.0" description = "The next generation HTTP client." optional = false python-versions = ">=3.8" files = [ - {file = "httpx-0.27.2-py3-none-any.whl", hash = "sha256:7bb2708e112d8fdd7829cd4243970f0c223274051cb35ee80c03301ee29a3df0"}, - {file = "httpx-0.27.2.tar.gz", hash = "sha256:f7c2be1d2f3c3c3160d441802406b206c2b76f5947b11115e6df10c6c65e66c2"}, + {file = "httpx-0.28.0-py3-none-any.whl", hash = "sha256:dc0b419a0cfeb6e8b34e85167c0da2671206f5095f1baa9663d23bcfd6b535fc"}, + {file = "httpx-0.28.0.tar.gz", hash = "sha256:0858d3bab51ba7e386637f22a61d8ccddaeec5f3fe4209da3a6168dbb91573e0"}, ] [package.dependencies] @@ -318,7 +658,6 @@ anyio = "*" certifi = "*" httpcore = "==1.*" idna = "*" -sniffio = "*" [package.extras] brotli = ["brotli", "brotlicffi"] @@ -377,15 +716,44 @@ files = [ {file = "jsonpointer-3.0.0.tar.gz", hash = "sha256:2b2d729f2091522d61c3b31f82e11870f60b68f43fbc705cb76bf4b832af59ef"}, ] +[[package]] +name = "langchain" +version = "0.3.9" +description = "Building applications with LLMs through composability" +optional = false +python-versions = ">=3.9,<4.0" +files = [] +develop = true + +[package.dependencies] +aiohttp = "^3.8.3" +async-timeout = {version = "^4.0.0", markers = "python_version < \"3.11\""} +langchain-core = "^0.3.21" +langchain-text-splitters = "^0.3.0" +langsmith = "^0.1.17" +numpy = [ + {version = ">=1.22.4,<2", markers = "python_version < \"3.12\""}, + {version = ">=1.26.2,<3", markers = "python_version >= \"3.12\""}, +] +pydantic = "^2.7.4" +PyYAML = ">=5.3" +requests = "^2" +SQLAlchemy = ">=1.4,<3" +tenacity = ">=8.1.0,!=8.4.0,<10" + +[package.source] +type = "directory" +url = "../langchain" + [[package]] name = "langchain-core" -version = "0.3.19" +version = "0.3.21" description = "Building applications with LLMs through composability" optional = false python-versions = "<4.0,>=3.9" files = [ - {file = "langchain_core-0.3.19-py3-none-any.whl", hash = "sha256:562b7cc3c15dfaa9270cb1496990c1f3b3e0b660c4d6a3236d7f693346f2a96c"}, - {file = "langchain_core-0.3.19.tar.gz", hash = "sha256:126d9e8cadb2a5b8d1793a228c0783a3b608e36064d5a2ef1a4d38d07a344523"}, + {file = "langchain_core-0.3.21-py3-none-any.whl", hash = "sha256:7e723dff80946a1198976c6876fea8326dc82566ef9bcb5f8d9188f738733665"}, + {file = "langchain_core-0.3.21.tar.gz", hash = "sha256:561b52b258ffa50a9fb11d7a1940ebfd915654d1ec95b35e81dfd5ee84143411"}, ] [package.dependencies] @@ -400,6 +768,20 @@ PyYAML = ">=5.3" tenacity = ">=8.1.0,<8.4.0 || >8.4.0,<10.0.0" typing-extensions = ">=4.7" +[[package]] +name = "langchain-text-splitters" +version = "0.3.2" +description = "LangChain text splitting utilities" +optional = false +python-versions = "<4.0,>=3.9" +files = [ + {file = "langchain_text_splitters-0.3.2-py3-none-any.whl", hash = "sha256:0db28c53f41d1bc024cdb3b1646741f6d46d5371e90f31e7e7c9fbe75d01c726"}, + {file = "langchain_text_splitters-0.3.2.tar.gz", hash = "sha256:81e6515d9901d6dd8e35fb31ccd4f30f76d44b771890c789dc835ef9f16204df"}, +] + +[package.dependencies] +langchain-core = ">=0.3.15,<0.4.0" + [[package]] name = "langserve" version = "0.3.0" @@ -426,13 +808,13 @@ server = ["fastapi (>=0.90.1,<1)", "sse-starlette (>=1.3.0,<2.0.0)"] [[package]] name = "langsmith" -version = "0.1.144" +version = "0.1.147" description = "Client library to connect to the LangSmith LLM Tracing and Evaluation Platform." optional = false python-versions = "<4.0,>=3.8.1" files = [ - {file = "langsmith-0.1.144-py3-none-any.whl", hash = "sha256:08ffb975bff2e82fc6f5428837c64c074ea25102d08a25e256361a80812c6100"}, - {file = "langsmith-0.1.144.tar.gz", hash = "sha256:b621f358d5a33441d7b5e7264c376bf4ea82bfc62d7e41aafc0f8094e3bd6369"}, + {file = "langsmith-0.1.147-py3-none-any.whl", hash = "sha256:7166fc23b965ccf839d64945a78e9f1157757add228b086141eb03a60d699a15"}, + {file = "langsmith-0.1.147.tar.gz", hash = "sha256:2e933220318a4e73034657103b3b1a3a6109cc5db3566a7e8e03be8d6d7def7a"}, ] [package.dependencies] @@ -445,6 +827,9 @@ pydantic = [ requests = ">=2,<3" requests-toolbelt = ">=1.0.0,<2.0.0" +[package.extras] +langsmith-pyo3 = ["langsmith-pyo3 (>=0.1.0rc2,<0.2.0)"] + [[package]] name = "markdown-it-py" version = "3.0.0" @@ -480,71 +865,365 @@ files = [ {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, ] +[[package]] +name = "multidict" +version = "6.1.0" +description = "multidict implementation" +optional = false +python-versions = ">=3.8" +files = [ + {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3380252550e372e8511d49481bd836264c009adb826b23fefcc5dd3c69692f60"}, + {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:99f826cbf970077383d7de805c0681799491cb939c25450b9b5b3ced03ca99f1"}, + {file = "multidict-6.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a114d03b938376557927ab23f1e950827c3b893ccb94b62fd95d430fd0e5cf53"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1c416351ee6271b2f49b56ad7f308072f6f44b37118d69c2cad94f3fa8a40d5"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6b5d83030255983181005e6cfbac1617ce9746b219bc2aad52201ad121226581"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3e97b5e938051226dc025ec80980c285b053ffb1e25a3db2a3aa3bc046bf7f56"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d618649d4e70ac6efcbba75be98b26ef5078faad23592f9b51ca492953012429"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10524ebd769727ac77ef2278390fb0068d83f3acb7773792a5080f2b0abf7748"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ff3827aef427c89a25cc96ded1759271a93603aba9fb977a6d264648ebf989db"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:06809f4f0f7ab7ea2cabf9caca7d79c22c0758b58a71f9d32943ae13c7ace056"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f179dee3b863ab1c59580ff60f9d99f632f34ccb38bf67a33ec6b3ecadd0fd76"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:aaed8b0562be4a0876ee3b6946f6869b7bcdb571a5d1496683505944e268b160"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3c8b88a2ccf5493b6c8da9076fb151ba106960a2df90c2633f342f120751a9e7"}, + {file = "multidict-6.1.0-cp310-cp310-win32.whl", hash = "sha256:4a9cb68166a34117d6646c0023c7b759bf197bee5ad4272f420a0141d7eb03a0"}, + {file = "multidict-6.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:20b9b5fbe0b88d0bdef2012ef7dee867f874b72528cf1d08f1d59b0e3850129d"}, + {file = "multidict-6.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3efe2c2cb5763f2f1b275ad2bf7a287d3f7ebbef35648a9726e3b69284a4f3d6"}, + {file = "multidict-6.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c7053d3b0353a8b9de430a4f4b4268ac9a4fb3481af37dfe49825bf45ca24156"}, + {file = "multidict-6.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:27e5fc84ccef8dfaabb09d82b7d179c7cf1a3fbc8a966f8274fcb4ab2eb4cadb"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e2b90b43e696f25c62656389d32236e049568b39320e2735d51f08fd362761b"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d83a047959d38a7ff552ff94be767b7fd79b831ad1cd9920662db05fec24fe72"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d1a9dd711d0877a1ece3d2e4fea11a8e75741ca21954c919406b44e7cf971304"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec2abea24d98246b94913b76a125e855eb5c434f7c46546046372fe60f666351"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4867cafcbc6585e4b678876c489b9273b13e9fff9f6d6d66add5e15d11d926cb"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5b48204e8d955c47c55b72779802b219a39acc3ee3d0116d5080c388970b76e3"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:d8fff389528cad1618fb4b26b95550327495462cd745d879a8c7c2115248e399"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a7a9541cd308eed5e30318430a9c74d2132e9a8cb46b901326272d780bf2d423"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:da1758c76f50c39a2efd5e9859ce7d776317eb1dd34317c8152ac9251fc574a3"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c943a53e9186688b45b323602298ab727d8865d8c9ee0b17f8d62d14b56f0753"}, + {file = "multidict-6.1.0-cp311-cp311-win32.whl", hash = "sha256:90f8717cb649eea3504091e640a1b8568faad18bd4b9fcd692853a04475a4b80"}, + {file = "multidict-6.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:82176036e65644a6cc5bd619f65f6f19781e8ec2e5330f51aa9ada7504cc1926"}, + {file = "multidict-6.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b04772ed465fa3cc947db808fa306d79b43e896beb677a56fb2347ca1a49c1fa"}, + {file = "multidict-6.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6180c0ae073bddeb5a97a38c03f30c233e0a4d39cd86166251617d1bbd0af436"}, + {file = "multidict-6.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:071120490b47aa997cca00666923a83f02c7fbb44f71cf7f136df753f7fa8761"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50b3a2710631848991d0bf7de077502e8994c804bb805aeb2925a981de58ec2e"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b58c621844d55e71c1b7f7c498ce5aa6985d743a1a59034c57a905b3f153c1ef"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55b6d90641869892caa9ca42ff913f7ff1c5ece06474fbd32fb2cf6834726c95"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b820514bfc0b98a30e3d85462084779900347e4d49267f747ff54060cc33925"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10a9b09aba0c5b48c53761b7c720aaaf7cf236d5fe394cd399c7ba662d5f9966"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e16bf3e5fc9f44632affb159d30a437bfe286ce9e02754759be5536b169b305"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:76f364861c3bfc98cbbcbd402d83454ed9e01a5224bb3a28bf70002a230f73e2"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:820c661588bd01a0aa62a1283f20d2be4281b086f80dad9e955e690c75fb54a2"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:0e5f362e895bc5b9e67fe6e4ded2492d8124bdf817827f33c5b46c2fe3ffaca6"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3ec660d19bbc671e3a6443325f07263be452c453ac9e512f5eb935e7d4ac28b3"}, + {file = "multidict-6.1.0-cp312-cp312-win32.whl", hash = "sha256:58130ecf8f7b8112cdb841486404f1282b9c86ccb30d3519faf301b2e5659133"}, + {file = "multidict-6.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:188215fc0aafb8e03341995e7c4797860181562380f81ed0a87ff455b70bf1f1"}, + {file = "multidict-6.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d569388c381b24671589335a3be6e1d45546c2988c2ebe30fdcada8457a31008"}, + {file = "multidict-6.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:052e10d2d37810b99cc170b785945421141bf7bb7d2f8799d431e7db229c385f"}, + {file = "multidict-6.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f90c822a402cb865e396a504f9fc8173ef34212a342d92e362ca498cad308e28"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b225d95519a5bf73860323e633a664b0d85ad3d5bede6d30d95b35d4dfe8805b"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:23bfd518810af7de1116313ebd9092cb9aa629beb12f6ed631ad53356ed6b86c"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c09fcfdccdd0b57867577b719c69e347a436b86cd83747f179dbf0cc0d4c1f3"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf6bea52ec97e95560af5ae576bdac3aa3aae0b6758c6efa115236d9e07dae44"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57feec87371dbb3520da6192213c7d6fc892d5589a93db548331954de8248fd2"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0c3f390dc53279cbc8ba976e5f8035eab997829066756d811616b652b00a23a3"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:59bfeae4b25ec05b34f1956eaa1cb38032282cd4dfabc5056d0a1ec4d696d3aa"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b2f59caeaf7632cc633b5cf6fc449372b83bbdf0da4ae04d5be36118e46cc0aa"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:37bb93b2178e02b7b618893990941900fd25b6b9ac0fa49931a40aecdf083fe4"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4e9f48f58c2c523d5a06faea47866cd35b32655c46b443f163d08c6d0ddb17d6"}, + {file = "multidict-6.1.0-cp313-cp313-win32.whl", hash = "sha256:3a37ffb35399029b45c6cc33640a92bef403c9fd388acce75cdc88f58bd19a81"}, + {file = "multidict-6.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:e9aa71e15d9d9beaad2c6b9319edcdc0a49a43ef5c0a4c8265ca9ee7d6c67774"}, + {file = "multidict-6.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:db7457bac39421addd0c8449933ac32d8042aae84a14911a757ae6ca3eef1392"}, + {file = "multidict-6.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d094ddec350a2fb899fec68d8353c78233debde9b7d8b4beeafa70825f1c281a"}, + {file = "multidict-6.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5845c1fd4866bb5dd3125d89b90e57ed3138241540897de748cdf19de8a2fca2"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9079dfc6a70abe341f521f78405b8949f96db48da98aeb43f9907f342f627cdc"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3914f5aaa0f36d5d60e8ece6a308ee1c9784cd75ec8151062614657a114c4478"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c08be4f460903e5a9d0f76818db3250f12e9c344e79314d1d570fc69d7f4eae4"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d093be959277cb7dee84b801eb1af388b6ad3ca6a6b6bf1ed7585895789d027d"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3702ea6872c5a2a4eeefa6ffd36b042e9773f05b1f37ae3ef7264b1163c2dcf6"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:2090f6a85cafc5b2db085124d752757c9d251548cedabe9bd31afe6363e0aff2"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:f67f217af4b1ff66c68a87318012de788dd95fcfeb24cc889011f4e1c7454dfd"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:189f652a87e876098bbc67b4da1049afb5f5dfbaa310dd67c594b01c10388db6"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:6bb5992037f7a9eff7991ebe4273ea7f51f1c1c511e6a2ce511d0e7bdb754492"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:ac10f4c2b9e770c4e393876e35a7046879d195cd123b4f116d299d442b335bcd"}, + {file = "multidict-6.1.0-cp38-cp38-win32.whl", hash = "sha256:e27bbb6d14416713a8bd7aaa1313c0fc8d44ee48d74497a0ff4c3a1b6ccb5167"}, + {file = "multidict-6.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:22f3105d4fb15c8f57ff3959a58fcab6ce36814486500cd7485651230ad4d4ef"}, + {file = "multidict-6.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:4e18b656c5e844539d506a0a06432274d7bd52a7487e6828c63a63d69185626c"}, + {file = "multidict-6.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a185f876e69897a6f3325c3f19f26a297fa058c5e456bfcff8015e9a27e83ae1"}, + {file = "multidict-6.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ab7c4ceb38d91570a650dba194e1ca87c2b543488fe9309b4212694174fd539c"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e617fb6b0b6953fffd762669610c1c4ffd05632c138d61ac7e14ad187870669c"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:16e5f4bf4e603eb1fdd5d8180f1a25f30056f22e55ce51fb3d6ad4ab29f7d96f"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4c035da3f544b1882bac24115f3e2e8760f10a0107614fc9839fd232200b875"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:957cf8e4b6e123a9eea554fa7ebc85674674b713551de587eb318a2df3e00255"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:483a6aea59cb89904e1ceabd2b47368b5600fb7de78a6e4a2c2987b2d256cf30"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:87701f25a2352e5bf7454caa64757642734da9f6b11384c1f9d1a8e699758057"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:682b987361e5fd7a139ed565e30d81fd81e9629acc7d925a205366877d8c8657"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ce2186a7df133a9c895dea3331ddc5ddad42cdd0d1ea2f0a51e5d161e4762f28"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:9f636b730f7e8cb19feb87094949ba54ee5357440b9658b2a32a5ce4bce53972"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:73eae06aa53af2ea5270cc066dcaf02cc60d2994bbb2c4ef5764949257d10f43"}, + {file = "multidict-6.1.0-cp39-cp39-win32.whl", hash = "sha256:1ca0083e80e791cffc6efce7660ad24af66c8d4079d2a750b29001b53ff59ada"}, + {file = "multidict-6.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:aa466da5b15ccea564bdab9c89175c762bc12825f4659c11227f515cee76fa4a"}, + {file = "multidict-6.1.0-py3-none-any.whl", hash = "sha256:48e171e52d1c4d33888e529b999e5900356b9ae588c2f09a52dcefb158b27506"}, + {file = "multidict-6.1.0.tar.gz", hash = "sha256:22ae2ebf9b0c69d206c003e2f6a914ea33f0a932d4aa16f236afc049d9958f4a"}, +] + +[package.dependencies] +typing-extensions = {version = ">=4.1.0", markers = "python_version < \"3.11\""} + +[[package]] +name = "mypy" +version = "1.13.0" +description = "Optional static typing for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "mypy-1.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6607e0f1dd1fb7f0aca14d936d13fd19eba5e17e1cd2a14f808fa5f8f6d8f60a"}, + {file = "mypy-1.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8a21be69bd26fa81b1f80a61ee7ab05b076c674d9b18fb56239d72e21d9f4c80"}, + {file = "mypy-1.13.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7b2353a44d2179846a096e25691d54d59904559f4232519d420d64da6828a3a7"}, + {file = "mypy-1.13.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0730d1c6a2739d4511dc4253f8274cdd140c55c32dfb0a4cf8b7a43f40abfa6f"}, + {file = "mypy-1.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:c5fc54dbb712ff5e5a0fca797e6e0aa25726c7e72c6a5850cfd2adbc1eb0a372"}, + {file = "mypy-1.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:581665e6f3a8a9078f28d5502f4c334c0c8d802ef55ea0e7276a6e409bc0d82d"}, + {file = "mypy-1.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3ddb5b9bf82e05cc9a627e84707b528e5c7caaa1c55c69e175abb15a761cec2d"}, + {file = "mypy-1.13.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:20c7ee0bc0d5a9595c46f38beb04201f2620065a93755704e141fcac9f59db2b"}, + {file = "mypy-1.13.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3790ded76f0b34bc9c8ba4def8f919dd6a46db0f5a6610fb994fe8efdd447f73"}, + {file = "mypy-1.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:51f869f4b6b538229c1d1bcc1dd7d119817206e2bc54e8e374b3dfa202defcca"}, + {file = "mypy-1.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5c7051a3461ae84dfb5dd15eff5094640c61c5f22257c8b766794e6dd85e72d5"}, + {file = "mypy-1.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:39bb21c69a5d6342f4ce526e4584bc5c197fd20a60d14a8624d8743fffb9472e"}, + {file = "mypy-1.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:164f28cb9d6367439031f4c81e84d3ccaa1e19232d9d05d37cb0bd880d3f93c2"}, + {file = "mypy-1.13.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a4c1bfcdbce96ff5d96fc9b08e3831acb30dc44ab02671eca5953eadad07d6d0"}, + {file = "mypy-1.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:a0affb3a79a256b4183ba09811e3577c5163ed06685e4d4b46429a271ba174d2"}, + {file = "mypy-1.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a7b44178c9760ce1a43f544e595d35ed61ac2c3de306599fa59b38a6048e1aa7"}, + {file = "mypy-1.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5d5092efb8516d08440e36626f0153b5006d4088c1d663d88bf79625af3d1d62"}, + {file = "mypy-1.13.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de2904956dac40ced10931ac967ae63c5089bd498542194b436eb097a9f77bc8"}, + {file = "mypy-1.13.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:7bfd8836970d33c2105562650656b6846149374dc8ed77d98424b40b09340ba7"}, + {file = "mypy-1.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:9f73dba9ec77acb86457a8fc04b5239822df0c14a082564737833d2963677dbc"}, + {file = "mypy-1.13.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:100fac22ce82925f676a734af0db922ecfea991e1d7ec0ceb1e115ebe501301a"}, + {file = "mypy-1.13.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7bcb0bb7f42a978bb323a7c88f1081d1b5dee77ca86f4100735a6f541299d8fb"}, + {file = "mypy-1.13.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bde31fc887c213e223bbfc34328070996061b0833b0a4cfec53745ed61f3519b"}, + {file = "mypy-1.13.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:07de989f89786f62b937851295ed62e51774722e5444a27cecca993fc3f9cd74"}, + {file = "mypy-1.13.0-cp38-cp38-win_amd64.whl", hash = "sha256:4bde84334fbe19bad704b3f5b78c4abd35ff1026f8ba72b29de70dda0916beb6"}, + {file = "mypy-1.13.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0246bcb1b5de7f08f2826451abd947bf656945209b140d16ed317f65a17dc7dc"}, + {file = "mypy-1.13.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7f5b7deae912cf8b77e990b9280f170381fdfbddf61b4ef80927edd813163732"}, + {file = "mypy-1.13.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7029881ec6ffb8bc233a4fa364736789582c738217b133f1b55967115288a2bc"}, + {file = "mypy-1.13.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3e38b980e5681f28f033f3be86b099a247b13c491f14bb8b1e1e134d23bb599d"}, + {file = "mypy-1.13.0-cp39-cp39-win_amd64.whl", hash = "sha256:a6789be98a2017c912ae6ccb77ea553bbaf13d27605d2ca20a76dfbced631b24"}, + {file = "mypy-1.13.0-py3-none-any.whl", hash = "sha256:9c250883f9fd81d212e0952c92dbfcc96fc237f4b7c92f56ac81fd48460b3e5a"}, + {file = "mypy-1.13.0.tar.gz", hash = "sha256:0291a61b6fbf3e6673e3405cfcc0e7650bebc7939659fdca2702958038bd835e"}, +] + +[package.dependencies] +mypy-extensions = ">=1.0.0" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = ">=4.6.0" + +[package.extras] +dmypy = ["psutil (>=4.0)"] +faster-cache = ["orjson"] +install-types = ["pip"] +mypyc = ["setuptools (>=50)"] +reports = ["lxml"] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = false +python-versions = ">=3.5" +files = [ + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +] + +[[package]] +name = "numpy" +version = "1.26.4" +description = "Fundamental package for array computing in Python" +optional = false +python-versions = ">=3.9" +files = [ + {file = "numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0"}, + {file = "numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a"}, + {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4"}, + {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f"}, + {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a"}, + {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2"}, + {file = "numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07"}, + {file = "numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5"}, + {file = "numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71"}, + {file = "numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef"}, + {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e"}, + {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5"}, + {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a"}, + {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a"}, + {file = "numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20"}, + {file = "numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2"}, + {file = "numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218"}, + {file = "numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b"}, + {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b"}, + {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed"}, + {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a"}, + {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0"}, + {file = "numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110"}, + {file = "numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818"}, + {file = "numpy-1.26.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7349ab0fa0c429c82442a27a9673fc802ffdb7c7775fad780226cb234965e53c"}, + {file = "numpy-1.26.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:52b8b60467cd7dd1e9ed082188b4e6bb35aa5cdd01777621a1658910745b90be"}, + {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5241e0a80d808d70546c697135da2c613f30e28251ff8307eb72ba696945764"}, + {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f870204a840a60da0b12273ef34f7051e98c3b5961b61b0c2c1be6dfd64fbcd3"}, + {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:679b0076f67ecc0138fd2ede3a8fd196dddc2ad3254069bcb9faf9a79b1cebcd"}, + {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:47711010ad8555514b434df65f7d7b076bb8261df1ca9bb78f53d3b2db02e95c"}, + {file = "numpy-1.26.4-cp39-cp39-win32.whl", hash = "sha256:a354325ee03388678242a4d7ebcd08b5c727033fcff3b2f536aea978e15ee9e6"}, + {file = "numpy-1.26.4-cp39-cp39-win_amd64.whl", hash = "sha256:3373d5d70a5fe74a2c1bb6d2cfd9609ecf686d47a2d7b1d37a8f3b6bf6003aea"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:afedb719a9dcfc7eaf2287b839d8198e06dcd4cb5d276a3df279231138e83d30"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95a7476c59002f2f6c590b9b7b998306fba6a5aa646b1e22ddfeaf8f78c3a29c"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7e50d0a0cc3189f9cb0aeb3a6a6af18c16f59f004b866cd2be1c14b36134a4a0"}, + {file = "numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010"}, +] + +[[package]] +name = "numpy" +version = "2.1.3" +description = "Fundamental package for array computing in Python" +optional = false +python-versions = ">=3.10" +files = [ + {file = "numpy-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c894b4305373b9c5576d7a12b473702afdf48ce5369c074ba304cc5ad8730dff"}, + {file = "numpy-2.1.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b47fbb433d3260adcd51eb54f92a2ffbc90a4595f8970ee00e064c644ac788f5"}, + {file = "numpy-2.1.3-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:825656d0743699c529c5943554d223c021ff0494ff1442152ce887ef4f7561a1"}, + {file = "numpy-2.1.3-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:6a4825252fcc430a182ac4dee5a505053d262c807f8a924603d411f6718b88fd"}, + {file = "numpy-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e711e02f49e176a01d0349d82cb5f05ba4db7d5e7e0defd026328e5cfb3226d3"}, + {file = "numpy-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78574ac2d1a4a02421f25da9559850d59457bac82f2b8d7a44fe83a64f770098"}, + {file = "numpy-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c7662f0e3673fe4e832fe07b65c50342ea27d989f92c80355658c7f888fcc83c"}, + {file = "numpy-2.1.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:fa2d1337dc61c8dc417fbccf20f6d1e139896a30721b7f1e832b2bb6ef4eb6c4"}, + {file = "numpy-2.1.3-cp310-cp310-win32.whl", hash = "sha256:72dcc4a35a8515d83e76b58fdf8113a5c969ccd505c8a946759b24e3182d1f23"}, + {file = "numpy-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:ecc76a9ba2911d8d37ac01de72834d8849e55473457558e12995f4cd53e778e0"}, + {file = "numpy-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4d1167c53b93f1f5d8a139a742b3c6f4d429b54e74e6b57d0eff40045187b15d"}, + {file = "numpy-2.1.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c80e4a09b3d95b4e1cac08643f1152fa71a0a821a2d4277334c88d54b2219a41"}, + {file = "numpy-2.1.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:576a1c1d25e9e02ed7fa5477f30a127fe56debd53b8d2c89d5578f9857d03ca9"}, + {file = "numpy-2.1.3-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:973faafebaae4c0aaa1a1ca1ce02434554d67e628b8d805e61f874b84e136b09"}, + {file = "numpy-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:762479be47a4863e261a840e8e01608d124ee1361e48b96916f38b119cfda04a"}, + {file = "numpy-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc6f24b3d1ecc1eebfbf5d6051faa49af40b03be1aaa781ebdadcbc090b4539b"}, + {file = "numpy-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:17ee83a1f4fef3c94d16dc1802b998668b5419362c8a4f4e8a491de1b41cc3ee"}, + {file = "numpy-2.1.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:15cb89f39fa6d0bdfb600ea24b250e5f1a3df23f901f51c8debaa6a5d122b2f0"}, + {file = "numpy-2.1.3-cp311-cp311-win32.whl", hash = "sha256:d9beb777a78c331580705326d2367488d5bc473b49a9bc3036c154832520aca9"}, + {file = "numpy-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:d89dd2b6da69c4fff5e39c28a382199ddedc3a5be5390115608345dec660b9e2"}, + {file = "numpy-2.1.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f55ba01150f52b1027829b50d70ef1dafd9821ea82905b63936668403c3b471e"}, + {file = "numpy-2.1.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:13138eadd4f4da03074851a698ffa7e405f41a0845a6b1ad135b81596e4e9958"}, + {file = "numpy-2.1.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:a6b46587b14b888e95e4a24d7b13ae91fa22386c199ee7b418f449032b2fa3b8"}, + {file = "numpy-2.1.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:0fa14563cc46422e99daef53d725d0c326e99e468a9320a240affffe87852564"}, + {file = "numpy-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8637dcd2caa676e475503d1f8fdb327bc495554e10838019651b76d17b98e512"}, + {file = "numpy-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2312b2aa89e1f43ecea6da6ea9a810d06aae08321609d8dc0d0eda6d946a541b"}, + {file = "numpy-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a38c19106902bb19351b83802531fea19dee18e5b37b36454f27f11ff956f7fc"}, + {file = "numpy-2.1.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:02135ade8b8a84011cbb67dc44e07c58f28575cf9ecf8ab304e51c05528c19f0"}, + {file = "numpy-2.1.3-cp312-cp312-win32.whl", hash = "sha256:e6988e90fcf617da2b5c78902fe8e668361b43b4fe26dbf2d7b0f8034d4cafb9"}, + {file = "numpy-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:0d30c543f02e84e92c4b1f415b7c6b5326cbe45ee7882b6b77db7195fb971e3a"}, + {file = "numpy-2.1.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96fe52fcdb9345b7cd82ecd34547fca4321f7656d500eca497eb7ea5a926692f"}, + {file = "numpy-2.1.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f653490b33e9c3a4c1c01d41bc2aef08f9475af51146e4a7710c450cf9761598"}, + {file = "numpy-2.1.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:dc258a761a16daa791081d026f0ed4399b582712e6fc887a95af09df10c5ca57"}, + {file = "numpy-2.1.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:016d0f6f5e77b0f0d45d77387ffa4bb89816b57c835580c3ce8e099ef830befe"}, + {file = "numpy-2.1.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c181ba05ce8299c7aa3125c27b9c2167bca4a4445b7ce73d5febc411ca692e43"}, + {file = "numpy-2.1.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5641516794ca9e5f8a4d17bb45446998c6554704d888f86df9b200e66bdcce56"}, + {file = "numpy-2.1.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ea4dedd6e394a9c180b33c2c872b92f7ce0f8e7ad93e9585312b0c5a04777a4a"}, + {file = "numpy-2.1.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b0df3635b9c8ef48bd3be5f862cf71b0a4716fa0e702155c45067c6b711ddcef"}, + {file = "numpy-2.1.3-cp313-cp313-win32.whl", hash = "sha256:50ca6aba6e163363f132b5c101ba078b8cbd3fa92c7865fd7d4d62d9779ac29f"}, + {file = "numpy-2.1.3-cp313-cp313-win_amd64.whl", hash = "sha256:747641635d3d44bcb380d950679462fae44f54b131be347d5ec2bce47d3df9ed"}, + {file = "numpy-2.1.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:996bb9399059c5b82f76b53ff8bb686069c05acc94656bb259b1d63d04a9506f"}, + {file = "numpy-2.1.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:45966d859916ad02b779706bb43b954281db43e185015df6eb3323120188f9e4"}, + {file = "numpy-2.1.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:baed7e8d7481bfe0874b566850cb0b85243e982388b7b23348c6db2ee2b2ae8e"}, + {file = "numpy-2.1.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:a9f7f672a3388133335589cfca93ed468509cb7b93ba3105fce780d04a6576a0"}, + {file = "numpy-2.1.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7aac50327da5d208db2eec22eb11e491e3fe13d22653dce51b0f4109101b408"}, + {file = "numpy-2.1.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4394bc0dbd074b7f9b52024832d16e019decebf86caf909d94f6b3f77a8ee3b6"}, + {file = "numpy-2.1.3-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:50d18c4358a0a8a53f12a8ba9d772ab2d460321e6a93d6064fc22443d189853f"}, + {file = "numpy-2.1.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:14e253bd43fc6b37af4921b10f6add6925878a42a0c5fe83daee390bca80bc17"}, + {file = "numpy-2.1.3-cp313-cp313t-win32.whl", hash = "sha256:08788d27a5fd867a663f6fc753fd7c3ad7e92747efc73c53bca2f19f8bc06f48"}, + {file = "numpy-2.1.3-cp313-cp313t-win_amd64.whl", hash = "sha256:2564fbdf2b99b3f815f2107c1bbc93e2de8ee655a69c261363a1172a79a257d4"}, + {file = "numpy-2.1.3-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:4f2015dfe437dfebbfce7c85c7b53d81ba49e71ba7eadbf1df40c915af75979f"}, + {file = "numpy-2.1.3-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:3522b0dfe983a575e6a9ab3a4a4dfe156c3e428468ff08ce582b9bb6bd1d71d4"}, + {file = "numpy-2.1.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c006b607a865b07cd981ccb218a04fc86b600411d83d6fc261357f1c0966755d"}, + {file = "numpy-2.1.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e14e26956e6f1696070788252dcdff11b4aca4c3e8bd166e0df1bb8f315a67cb"}, + {file = "numpy-2.1.3.tar.gz", hash = "sha256:aa08e04e08aaf974d4458def539dece0d28146d866a39da5639596f4921fd761"}, +] + [[package]] name = "orjson" -version = "3.10.11" +version = "3.10.12" description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" optional = false python-versions = ">=3.8" files = [ - {file = "orjson-3.10.11-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:6dade64687f2bd7c090281652fe18f1151292d567a9302b34c2dbb92a3872f1f"}, - {file = "orjson-3.10.11-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82f07c550a6ccd2b9290849b22316a609023ed851a87ea888c0456485a7d196a"}, - {file = "orjson-3.10.11-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bd9a187742d3ead9df2e49240234d728c67c356516cf4db018833a86f20ec18c"}, - {file = "orjson-3.10.11-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:77b0fed6f209d76c1c39f032a70df2d7acf24b1812ca3e6078fd04e8972685a3"}, - {file = "orjson-3.10.11-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:63fc9d5fe1d4e8868f6aae547a7b8ba0a2e592929245fff61d633f4caccdcdd6"}, - {file = "orjson-3.10.11-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65cd3e3bb4fbb4eddc3c1e8dce10dc0b73e808fcb875f9fab40c81903dd9323e"}, - {file = "orjson-3.10.11-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6f67c570602300c4befbda12d153113b8974a3340fdcf3d6de095ede86c06d92"}, - {file = "orjson-3.10.11-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1f39728c7f7d766f1f5a769ce4d54b5aaa4c3f92d5b84817053cc9995b977acc"}, - {file = "orjson-3.10.11-cp310-none-win32.whl", hash = "sha256:1789d9db7968d805f3d94aae2c25d04014aae3a2fa65b1443117cd462c6da647"}, - {file = "orjson-3.10.11-cp310-none-win_amd64.whl", hash = "sha256:5576b1e5a53a5ba8f8df81872bb0878a112b3ebb1d392155f00f54dd86c83ff6"}, - {file = "orjson-3.10.11-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:1444f9cb7c14055d595de1036f74ecd6ce15f04a715e73f33bb6326c9cef01b6"}, - {file = "orjson-3.10.11-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdec57fe3b4bdebcc08a946db3365630332dbe575125ff3d80a3272ebd0ddafe"}, - {file = "orjson-3.10.11-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4eed32f33a0ea6ef36ccc1d37f8d17f28a1d6e8eefae5928f76aff8f1df85e67"}, - {file = "orjson-3.10.11-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80df27dd8697242b904f4ea54820e2d98d3f51f91e97e358fc13359721233e4b"}, - {file = "orjson-3.10.11-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:705f03cee0cb797256d54de6695ef219e5bc8c8120b6654dd460848d57a9af3d"}, - {file = "orjson-3.10.11-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03246774131701de8e7059b2e382597da43144a9a7400f178b2a32feafc54bd5"}, - {file = "orjson-3.10.11-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8b5759063a6c940a69c728ea70d7c33583991c6982915a839c8da5f957e0103a"}, - {file = "orjson-3.10.11-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:677f23e32491520eebb19c99bb34675daf5410c449c13416f7f0d93e2cf5f981"}, - {file = "orjson-3.10.11-cp311-none-win32.whl", hash = "sha256:a11225d7b30468dcb099498296ffac36b4673a8398ca30fdaec1e6c20df6aa55"}, - {file = "orjson-3.10.11-cp311-none-win_amd64.whl", hash = "sha256:df8c677df2f9f385fcc85ab859704045fa88d4668bc9991a527c86e710392bec"}, - {file = "orjson-3.10.11-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:360a4e2c0943da7c21505e47cf6bd725588962ff1d739b99b14e2f7f3545ba51"}, - {file = "orjson-3.10.11-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:496e2cb45de21c369079ef2d662670a4892c81573bcc143c4205cae98282ba97"}, - {file = "orjson-3.10.11-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7dfa8db55c9792d53c5952900c6a919cfa377b4f4534c7a786484a6a4a350c19"}, - {file = "orjson-3.10.11-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:51f3382415747e0dbda9dade6f1e1a01a9d37f630d8c9049a8ed0e385b7a90c0"}, - {file = "orjson-3.10.11-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f35a1b9f50a219f470e0e497ca30b285c9f34948d3c8160d5ad3a755d9299433"}, - {file = "orjson-3.10.11-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2f3b7c5803138e67028dde33450e054c87e0703afbe730c105f1fcd873496d5"}, - {file = "orjson-3.10.11-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f91d9eb554310472bd09f5347950b24442600594c2edc1421403d7610a0998fd"}, - {file = "orjson-3.10.11-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dfbb2d460a855c9744bbc8e36f9c3a997c4b27d842f3d5559ed54326e6911f9b"}, - {file = "orjson-3.10.11-cp312-none-win32.whl", hash = "sha256:d4a62c49c506d4d73f59514986cadebb7e8d186ad510c518f439176cf8d5359d"}, - {file = "orjson-3.10.11-cp312-none-win_amd64.whl", hash = "sha256:f1eec3421a558ff7a9b010a6c7effcfa0ade65327a71bb9b02a1c3b77a247284"}, - {file = "orjson-3.10.11-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:c46294faa4e4d0eb73ab68f1a794d2cbf7bab33b1dda2ac2959ffb7c61591899"}, - {file = "orjson-3.10.11-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52e5834d7d6e58a36846e059d00559cb9ed20410664f3ad156cd2cc239a11230"}, - {file = "orjson-3.10.11-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2fc947e5350fdce548bfc94f434e8760d5cafa97fb9c495d2fef6757aa02ec0"}, - {file = "orjson-3.10.11-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0efabbf839388a1dab5b72b5d3baedbd6039ac83f3b55736eb9934ea5494d258"}, - {file = "orjson-3.10.11-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a3f29634260708c200c4fe148e42b4aae97d7b9fee417fbdd74f8cfc265f15b0"}, - {file = "orjson-3.10.11-cp313-none-win32.whl", hash = "sha256:1a1222ffcee8a09476bbdd5d4f6f33d06d0d6642df2a3d78b7a195ca880d669b"}, - {file = "orjson-3.10.11-cp313-none-win_amd64.whl", hash = "sha256:bc274ac261cc69260913b2d1610760e55d3c0801bb3457ba7b9004420b6b4270"}, - {file = "orjson-3.10.11-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:19b3763e8bbf8ad797df6b6b5e0fc7c843ec2e2fc0621398534e0c6400098f87"}, - {file = "orjson-3.10.11-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1be83a13312e5e58d633580c5eb8d0495ae61f180da2722f20562974188af205"}, - {file = "orjson-3.10.11-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:afacfd1ab81f46dedd7f6001b6d4e8de23396e4884cd3c3436bd05defb1a6446"}, - {file = "orjson-3.10.11-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cb4d0bea56bba596723d73f074c420aec3b2e5d7d30698bc56e6048066bd560c"}, - {file = "orjson-3.10.11-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96ed1de70fcb15d5fed529a656df29f768187628727ee2788344e8a51e1c1350"}, - {file = "orjson-3.10.11-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4bfb30c891b530f3f80e801e3ad82ef150b964e5c38e1fb8482441c69c35c61c"}, - {file = "orjson-3.10.11-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d496c74fc2b61341e3cefda7eec21b7854c5f672ee350bc55d9a4997a8a95204"}, - {file = "orjson-3.10.11-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:655a493bac606655db9a47fe94d3d84fc7f3ad766d894197c94ccf0c5408e7d3"}, - {file = "orjson-3.10.11-cp38-none-win32.whl", hash = "sha256:b9546b278c9fb5d45380f4809e11b4dd9844ca7aaf1134024503e134ed226161"}, - {file = "orjson-3.10.11-cp38-none-win_amd64.whl", hash = "sha256:b592597fe551d518f42c5a2eb07422eb475aa8cfdc8c51e6da7054b836b26782"}, - {file = "orjson-3.10.11-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:c95f2ecafe709b4e5c733b5e2768ac569bed308623c85806c395d9cca00e08af"}, - {file = "orjson-3.10.11-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:80c00d4acded0c51c98754fe8218cb49cb854f0f7eb39ea4641b7f71732d2cb7"}, - {file = "orjson-3.10.11-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:461311b693d3d0a060439aa669c74f3603264d4e7a08faa68c47ae5a863f352d"}, - {file = "orjson-3.10.11-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:52ca832f17d86a78cbab86cdc25f8c13756ebe182b6fc1a97d534051c18a08de"}, - {file = "orjson-3.10.11-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4c57ea78a753812f528178aa2f1c57da633754c91d2124cb28991dab4c79a54"}, - {file = "orjson-3.10.11-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7fcfc6f7ca046383fb954ba528587e0f9336828b568282b27579c49f8e16aad"}, - {file = "orjson-3.10.11-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:86b9dd983857970c29e4c71bb3e95ff085c07d3e83e7c46ebe959bac07ebd80b"}, - {file = "orjson-3.10.11-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:4d83f87582d223e54efb2242a79547611ba4ebae3af8bae1e80fa9a0af83bb7f"}, - {file = "orjson-3.10.11-cp39-none-win32.whl", hash = "sha256:9fd0ad1c129bc9beb1154c2655f177620b5beaf9a11e0d10bac63ef3fce96950"}, - {file = "orjson-3.10.11-cp39-none-win_amd64.whl", hash = "sha256:10f416b2a017c8bd17f325fb9dee1fb5cdd7a54e814284896b7c3f2763faa017"}, - {file = "orjson-3.10.11.tar.gz", hash = "sha256:e35b6d730de6384d5b2dab5fd23f0d76fae8bbc8c353c2f78210aa5fa4beb3ef"}, + {file = "orjson-3.10.12-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:ece01a7ec71d9940cc654c482907a6b65df27251255097629d0dea781f255c6d"}, + {file = "orjson-3.10.12-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c34ec9aebc04f11f4b978dd6caf697a2df2dd9b47d35aa4cc606cabcb9df69d7"}, + {file = "orjson-3.10.12-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fd6ec8658da3480939c79b9e9e27e0db31dffcd4ba69c334e98c9976ac29140e"}, + {file = "orjson-3.10.12-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f17e6baf4cf01534c9de8a16c0c611f3d94925d1701bf5f4aff17003677d8ced"}, + {file = "orjson-3.10.12-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6402ebb74a14ef96f94a868569f5dccf70d791de49feb73180eb3c6fda2ade56"}, + {file = "orjson-3.10.12-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0000758ae7c7853e0a4a6063f534c61656ebff644391e1f81698c1b2d2fc8cd2"}, + {file = "orjson-3.10.12-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:888442dcee99fd1e5bd37a4abb94930915ca6af4db50e23e746cdf4d1e63db13"}, + {file = "orjson-3.10.12-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c1f7a3ce79246aa0e92f5458d86c54f257fb5dfdc14a192651ba7ec2c00f8a05"}, + {file = "orjson-3.10.12-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:802a3935f45605c66fb4a586488a38af63cb37aaad1c1d94c982c40dcc452e85"}, + {file = "orjson-3.10.12-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:1da1ef0113a2be19bb6c557fb0ec2d79c92ebd2fed4cfb1b26bab93f021fb885"}, + {file = "orjson-3.10.12-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7a3273e99f367f137d5b3fecb5e9f45bcdbfac2a8b2f32fbc72129bbd48789c2"}, + {file = "orjson-3.10.12-cp310-none-win32.whl", hash = "sha256:475661bf249fd7907d9b0a2a2421b4e684355a77ceef85b8352439a9163418c3"}, + {file = "orjson-3.10.12-cp310-none-win_amd64.whl", hash = "sha256:87251dc1fb2b9e5ab91ce65d8f4caf21910d99ba8fb24b49fd0c118b2362d509"}, + {file = "orjson-3.10.12-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a734c62efa42e7df94926d70fe7d37621c783dea9f707a98cdea796964d4cf74"}, + {file = "orjson-3.10.12-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:750f8b27259d3409eda8350c2919a58b0cfcd2054ddc1bd317a643afc646ef23"}, + {file = "orjson-3.10.12-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bb52c22bfffe2857e7aa13b4622afd0dd9d16ea7cc65fd2bf318d3223b1b6252"}, + {file = "orjson-3.10.12-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:440d9a337ac8c199ff8251e100c62e9488924c92852362cd27af0e67308c16ef"}, + {file = "orjson-3.10.12-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9e15c06491c69997dfa067369baab3bf094ecb74be9912bdc4339972323f252"}, + {file = "orjson-3.10.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:362d204ad4b0b8724cf370d0cd917bb2dc913c394030da748a3bb632445ce7c4"}, + {file = "orjson-3.10.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2b57cbb4031153db37b41622eac67329c7810e5f480fda4cfd30542186f006ae"}, + {file = "orjson-3.10.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:165c89b53ef03ce0d7c59ca5c82fa65fe13ddf52eeb22e859e58c237d4e33b9b"}, + {file = "orjson-3.10.12-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:5dee91b8dfd54557c1a1596eb90bcd47dbcd26b0baaed919e6861f076583e9da"}, + {file = "orjson-3.10.12-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:77a4e1cfb72de6f905bdff061172adfb3caf7a4578ebf481d8f0530879476c07"}, + {file = "orjson-3.10.12-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:038d42c7bc0606443459b8fe2d1f121db474c49067d8d14c6a075bbea8bf14dd"}, + {file = "orjson-3.10.12-cp311-none-win32.whl", hash = "sha256:03b553c02ab39bed249bedd4abe37b2118324d1674e639b33fab3d1dafdf4d79"}, + {file = "orjson-3.10.12-cp311-none-win_amd64.whl", hash = "sha256:8b8713b9e46a45b2af6b96f559bfb13b1e02006f4242c156cbadef27800a55a8"}, + {file = "orjson-3.10.12-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:53206d72eb656ca5ac7d3a7141e83c5bbd3ac30d5eccfe019409177a57634b0d"}, + {file = "orjson-3.10.12-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac8010afc2150d417ebda810e8df08dd3f544e0dd2acab5370cfa6bcc0662f8f"}, + {file = "orjson-3.10.12-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed459b46012ae950dd2e17150e838ab08215421487371fa79d0eced8d1461d70"}, + {file = "orjson-3.10.12-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8dcb9673f108a93c1b52bfc51b0af422c2d08d4fc710ce9c839faad25020bb69"}, + {file = "orjson-3.10.12-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:22a51ae77680c5c4652ebc63a83d5255ac7d65582891d9424b566fb3b5375ee9"}, + {file = "orjson-3.10.12-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:910fdf2ac0637b9a77d1aad65f803bac414f0b06f720073438a7bd8906298192"}, + {file = "orjson-3.10.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:24ce85f7100160936bc2116c09d1a8492639418633119a2224114f67f63a4559"}, + {file = "orjson-3.10.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8a76ba5fc8dd9c913640292df27bff80a685bed3a3c990d59aa6ce24c352f8fc"}, + {file = "orjson-3.10.12-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ff70ef093895fd53f4055ca75f93f047e088d1430888ca1229393a7c0521100f"}, + {file = "orjson-3.10.12-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:f4244b7018b5753ecd10a6d324ec1f347da130c953a9c88432c7fbc8875d13be"}, + {file = "orjson-3.10.12-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:16135ccca03445f37921fa4b585cff9a58aa8d81ebcb27622e69bfadd220b32c"}, + {file = "orjson-3.10.12-cp312-none-win32.whl", hash = "sha256:2d879c81172d583e34153d524fcba5d4adafbab8349a7b9f16ae511c2cee8708"}, + {file = "orjson-3.10.12-cp312-none-win_amd64.whl", hash = "sha256:fc23f691fa0f5c140576b8c365bc942d577d861a9ee1142e4db468e4e17094fb"}, + {file = "orjson-3.10.12-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:47962841b2a8aa9a258b377f5188db31ba49af47d4003a32f55d6f8b19006543"}, + {file = "orjson-3.10.12-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6334730e2532e77b6054e87ca84f3072bee308a45a452ea0bffbbbc40a67e296"}, + {file = "orjson-3.10.12-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:accfe93f42713c899fdac2747e8d0d5c659592df2792888c6c5f829472e4f85e"}, + {file = "orjson-3.10.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a7974c490c014c48810d1dede6c754c3cc46598da758c25ca3b4001ac45b703f"}, + {file = "orjson-3.10.12-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:3f250ce7727b0b2682f834a3facff88e310f52f07a5dcfd852d99637d386e79e"}, + {file = "orjson-3.10.12-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f31422ff9486ae484f10ffc51b5ab2a60359e92d0716fcce1b3593d7bb8a9af6"}, + {file = "orjson-3.10.12-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5f29c5d282bb2d577c2a6bbde88d8fdcc4919c593f806aac50133f01b733846e"}, + {file = "orjson-3.10.12-cp313-none-win32.whl", hash = "sha256:f45653775f38f63dc0e6cd4f14323984c3149c05d6007b58cb154dd080ddc0dc"}, + {file = "orjson-3.10.12-cp313-none-win_amd64.whl", hash = "sha256:229994d0c376d5bdc91d92b3c9e6be2f1fbabd4cc1b59daae1443a46ee5e9825"}, + {file = "orjson-3.10.12-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:7d69af5b54617a5fac5c8e5ed0859eb798e2ce8913262eb522590239db6c6763"}, + {file = "orjson-3.10.12-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ed119ea7d2953365724a7059231a44830eb6bbb0cfead33fcbc562f5fd8f935"}, + {file = "orjson-3.10.12-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9c5fc1238ef197e7cad5c91415f524aaa51e004be5a9b35a1b8a84ade196f73f"}, + {file = "orjson-3.10.12-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:43509843990439b05f848539d6f6198d4ac86ff01dd024b2f9a795c0daeeab60"}, + {file = "orjson-3.10.12-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f72e27a62041cfb37a3de512247ece9f240a561e6c8662276beaf4d53d406db4"}, + {file = "orjson-3.10.12-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a904f9572092bb6742ab7c16c623f0cdccbad9eeb2d14d4aa06284867bddd31"}, + {file = "orjson-3.10.12-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:855c0833999ed5dc62f64552db26f9be767434917d8348d77bacaab84f787d7b"}, + {file = "orjson-3.10.12-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:897830244e2320f6184699f598df7fb9db9f5087d6f3f03666ae89d607e4f8ed"}, + {file = "orjson-3.10.12-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:0b32652eaa4a7539f6f04abc6243619c56f8530c53bf9b023e1269df5f7816dd"}, + {file = "orjson-3.10.12-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:36b4aa31e0f6a1aeeb6f8377769ca5d125db000f05c20e54163aef1d3fe8e833"}, + {file = "orjson-3.10.12-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:5535163054d6cbf2796f93e4f0dbc800f61914c0e3c4ed8499cf6ece22b4a3da"}, + {file = "orjson-3.10.12-cp38-none-win32.whl", hash = "sha256:90a5551f6f5a5fa07010bf3d0b4ca2de21adafbbc0af6cb700b63cd767266cb9"}, + {file = "orjson-3.10.12-cp38-none-win_amd64.whl", hash = "sha256:703a2fb35a06cdd45adf5d733cf613cbc0cb3ae57643472b16bc22d325b5fb6c"}, + {file = "orjson-3.10.12-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:f29de3ef71a42a5822765def1febfb36e0859d33abf5c2ad240acad5c6a1b78d"}, + {file = "orjson-3.10.12-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:de365a42acc65d74953f05e4772c974dad6c51cfc13c3240899f534d611be967"}, + {file = "orjson-3.10.12-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:91a5a0158648a67ff0004cb0df5df7dcc55bfc9ca154d9c01597a23ad54c8d0c"}, + {file = "orjson-3.10.12-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c47ce6b8d90fe9646a25b6fb52284a14ff215c9595914af63a5933a49972ce36"}, + {file = "orjson-3.10.12-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0eee4c2c5bfb5c1b47a5db80d2ac7aaa7e938956ae88089f098aff2c0f35d5d8"}, + {file = "orjson-3.10.12-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:35d3081bbe8b86587eb5c98a73b97f13d8f9fea685cf91a579beddacc0d10566"}, + {file = "orjson-3.10.12-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:73c23a6e90383884068bc2dba83d5222c9fcc3b99a0ed2411d38150734236755"}, + {file = "orjson-3.10.12-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5472be7dc3269b4b52acba1433dac239215366f89dc1d8d0e64029abac4e714e"}, + {file = "orjson-3.10.12-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:7319cda750fca96ae5973efb31b17d97a5c5225ae0bc79bf5bf84df9e1ec2ab6"}, + {file = "orjson-3.10.12-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:74d5ca5a255bf20b8def6a2b96b1e18ad37b4a122d59b154c458ee9494377f80"}, + {file = "orjson-3.10.12-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:ff31d22ecc5fb85ef62c7d4afe8301d10c558d00dd24274d4bbe464380d3cd69"}, + {file = "orjson-3.10.12-cp39-none-win32.whl", hash = "sha256:c22c3ea6fba91d84fcb4cda30e64aff548fcf0c44c876e681f47d61d24b12e6b"}, + {file = "orjson-3.10.12-cp39-none-win_amd64.whl", hash = "sha256:be604f60d45ace6b0b33dd990a66b4526f1a7a186ac411c942674625456ca548"}, + {file = "orjson-3.10.12.tar.gz", hash = "sha256:0a78bbda3aea0f9f079057ee1ee8a1ecf790d4f1af88dd67493c6b8ee52506ff"}, ] [[package]] @@ -558,17 +1237,6 @@ files = [ {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, ] -[[package]] -name = "pastel" -version = "0.2.1" -description = "Bring colors to your terminal." -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -files = [ - {file = "pastel-0.2.1-py2.py3-none-any.whl", hash = "sha256:4349225fcdf6c2bb34d483e523475de5bb04a5c10ef711263452cb37d7dd4364"}, - {file = "pastel-0.2.1.tar.gz", hash = "sha256:e6581ac04e973cac858828c6202c1e1e81fee1dc7de7683f3e1ffe0bfd8a573d"}, -] - [[package]] name = "pluggy" version = "1.5.0" @@ -585,32 +1253,105 @@ dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] [[package]] -name = "poethepoet" -version = "0.24.4" -description = "A task runner that works well with poetry." +name = "propcache" +version = "0.2.1" +description = "Accelerated property cache" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "poethepoet-0.24.4-py3-none-any.whl", hash = "sha256:fb4ea35d7f40fe2081ea917d2e4102e2310fda2cde78974050ca83896e229075"}, - {file = "poethepoet-0.24.4.tar.gz", hash = "sha256:ff4220843a87c888cbcb5312c8905214701d0af60ac7271795baa8369b428fef"}, + {file = "propcache-0.2.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6b3f39a85d671436ee3d12c017f8fdea38509e4f25b28eb25877293c98c243f6"}, + {file = "propcache-0.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d51fbe4285d5db5d92a929e3e21536ea3dd43732c5b177c7ef03f918dff9f2"}, + {file = "propcache-0.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6445804cf4ec763dc70de65a3b0d9954e868609e83850a47ca4f0cb64bd79fea"}, + {file = "propcache-0.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9479aa06a793c5aeba49ce5c5692ffb51fcd9a7016e017d555d5e2b0045d212"}, + {file = "propcache-0.2.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9631c5e8b5b3a0fda99cb0d29c18133bca1e18aea9effe55adb3da1adef80d3"}, + {file = "propcache-0.2.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3156628250f46a0895f1f36e1d4fbe062a1af8718ec3ebeb746f1d23f0c5dc4d"}, + {file = "propcache-0.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b6fb63ae352e13748289f04f37868099e69dba4c2b3e271c46061e82c745634"}, + {file = "propcache-0.2.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:887d9b0a65404929641a9fabb6452b07fe4572b269d901d622d8a34a4e9043b2"}, + {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a96dc1fa45bd8c407a0af03b2d5218392729e1822b0c32e62c5bf7eeb5fb3958"}, + {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:a7e65eb5c003a303b94aa2c3852ef130230ec79e349632d030e9571b87c4698c"}, + {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:999779addc413181912e984b942fbcc951be1f5b3663cd80b2687758f434c583"}, + {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:19a0f89a7bb9d8048d9c4370c9c543c396e894c76be5525f5e1ad287f1750ddf"}, + {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:1ac2f5fe02fa75f56e1ad473f1175e11f475606ec9bd0be2e78e4734ad575034"}, + {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:574faa3b79e8ebac7cb1d7930f51184ba1ccf69adfdec53a12f319a06030a68b"}, + {file = "propcache-0.2.1-cp310-cp310-win32.whl", hash = "sha256:03ff9d3f665769b2a85e6157ac8b439644f2d7fd17615a82fa55739bc97863f4"}, + {file = "propcache-0.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:2d3af2e79991102678f53e0dbf4c35de99b6b8b58f29a27ca0325816364caaba"}, + {file = "propcache-0.2.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1ffc3cca89bb438fb9c95c13fc874012f7b9466b89328c3c8b1aa93cdcfadd16"}, + {file = "propcache-0.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f174bbd484294ed9fdf09437f889f95807e5f229d5d93588d34e92106fbf6717"}, + {file = "propcache-0.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:70693319e0b8fd35dd863e3e29513875eb15c51945bf32519ef52927ca883bc3"}, + {file = "propcache-0.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b480c6a4e1138e1aa137c0079b9b6305ec6dcc1098a8ca5196283e8a49df95a9"}, + {file = "propcache-0.2.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d27b84d5880f6d8aa9ae3edb253c59d9f6642ffbb2c889b78b60361eed449787"}, + {file = "propcache-0.2.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:857112b22acd417c40fa4595db2fe28ab900c8c5fe4670c7989b1c0230955465"}, + {file = "propcache-0.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf6c4150f8c0e32d241436526f3c3f9cbd34429492abddbada2ffcff506c51af"}, + {file = "propcache-0.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:66d4cfda1d8ed687daa4bc0274fcfd5267873db9a5bc0418c2da19273040eeb7"}, + {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c2f992c07c0fca81655066705beae35fc95a2fa7366467366db627d9f2ee097f"}, + {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:4a571d97dbe66ef38e472703067021b1467025ec85707d57e78711c085984e54"}, + {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:bb6178c241278d5fe853b3de743087be7f5f4c6f7d6d22a3b524d323eecec505"}, + {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:ad1af54a62ffe39cf34db1aa6ed1a1873bd548f6401db39d8e7cd060b9211f82"}, + {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e7048abd75fe40712005bcfc06bb44b9dfcd8e101dda2ecf2f5aa46115ad07ca"}, + {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:160291c60081f23ee43d44b08a7e5fb76681221a8e10b3139618c5a9a291b84e"}, + {file = "propcache-0.2.1-cp311-cp311-win32.whl", hash = "sha256:819ce3b883b7576ca28da3861c7e1a88afd08cc8c96908e08a3f4dd64a228034"}, + {file = "propcache-0.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:edc9fc7051e3350643ad929df55c451899bb9ae6d24998a949d2e4c87fb596d3"}, + {file = "propcache-0.2.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:081a430aa8d5e8876c6909b67bd2d937bfd531b0382d3fdedb82612c618bc41a"}, + {file = "propcache-0.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d2ccec9ac47cf4e04897619c0e0c1a48c54a71bdf045117d3a26f80d38ab1fb0"}, + {file = "propcache-0.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:14d86fe14b7e04fa306e0c43cdbeebe6b2c2156a0c9ce56b815faacc193e320d"}, + {file = "propcache-0.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:049324ee97bb67285b49632132db351b41e77833678432be52bdd0289c0e05e4"}, + {file = "propcache-0.2.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1cd9a1d071158de1cc1c71a26014dcdfa7dd3d5f4f88c298c7f90ad6f27bb46d"}, + {file = "propcache-0.2.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98110aa363f1bb4c073e8dcfaefd3a5cea0f0834c2aab23dda657e4dab2f53b5"}, + {file = "propcache-0.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:647894f5ae99c4cf6bb82a1bb3a796f6e06af3caa3d32e26d2350d0e3e3faf24"}, + {file = "propcache-0.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bfd3223c15bebe26518d58ccf9a39b93948d3dcb3e57a20480dfdd315356baff"}, + {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d71264a80f3fcf512eb4f18f59423fe82d6e346ee97b90625f283df56aee103f"}, + {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:e73091191e4280403bde6c9a52a6999d69cdfde498f1fdf629105247599b57ec"}, + {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3935bfa5fede35fb202c4b569bb9c042f337ca4ff7bd540a0aa5e37131659348"}, + {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f508b0491767bb1f2b87fdfacaba5f7eddc2f867740ec69ece6d1946d29029a6"}, + {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:1672137af7c46662a1c2be1e8dc78cb6d224319aaa40271c9257d886be4363a6"}, + {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b74c261802d3d2b85c9df2dfb2fa81b6f90deeef63c2db9f0e029a3cac50b518"}, + {file = "propcache-0.2.1-cp312-cp312-win32.whl", hash = "sha256:d09c333d36c1409d56a9d29b3a1b800a42c76a57a5a8907eacdbce3f18768246"}, + {file = "propcache-0.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:c214999039d4f2a5b2073ac506bba279945233da8c786e490d411dfc30f855c1"}, + {file = "propcache-0.2.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aca405706e0b0a44cc6bfd41fbe89919a6a56999157f6de7e182a990c36e37bc"}, + {file = "propcache-0.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:12d1083f001ace206fe34b6bdc2cb94be66d57a850866f0b908972f90996b3e9"}, + {file = "propcache-0.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d93f3307ad32a27bda2e88ec81134b823c240aa3abb55821a8da553eed8d9439"}, + {file = "propcache-0.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba278acf14471d36316159c94a802933d10b6a1e117b8554fe0d0d9b75c9d536"}, + {file = "propcache-0.2.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4e6281aedfca15301c41f74d7005e6e3f4ca143584ba696ac69df4f02f40d629"}, + {file = "propcache-0.2.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5b750a8e5a1262434fb1517ddf64b5de58327f1adc3524a5e44c2ca43305eb0b"}, + {file = "propcache-0.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf72af5e0fb40e9babf594308911436c8efde3cb5e75b6f206c34ad18be5c052"}, + {file = "propcache-0.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b2d0a12018b04f4cb820781ec0dffb5f7c7c1d2a5cd22bff7fb055a2cb19ebce"}, + {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e800776a79a5aabdb17dcc2346a7d66d0777e942e4cd251defeb084762ecd17d"}, + {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:4160d9283bd382fa6c0c2b5e017acc95bc183570cd70968b9202ad6d8fc48dce"}, + {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:30b43e74f1359353341a7adb783c8f1b1c676367b011709f466f42fda2045e95"}, + {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:58791550b27d5488b1bb52bc96328456095d96206a250d28d874fafe11b3dfaf"}, + {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:0f022d381747f0dfe27e99d928e31bc51a18b65bb9e481ae0af1380a6725dd1f"}, + {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:297878dc9d0a334358f9b608b56d02e72899f3b8499fc6044133f0d319e2ec30"}, + {file = "propcache-0.2.1-cp313-cp313-win32.whl", hash = "sha256:ddfab44e4489bd79bda09d84c430677fc7f0a4939a73d2bba3073036f487a0a6"}, + {file = "propcache-0.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:556fc6c10989f19a179e4321e5d678db8eb2924131e64652a51fe83e4c3db0e1"}, + {file = "propcache-0.2.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:6a9a8c34fb7bb609419a211e59da8887eeca40d300b5ea8e56af98f6fbbb1541"}, + {file = "propcache-0.2.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ae1aa1cd222c6d205853b3013c69cd04515f9d6ab6de4b0603e2e1c33221303e"}, + {file = "propcache-0.2.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:accb6150ce61c9c4b7738d45550806aa2b71c7668c6942f17b0ac182b6142fd4"}, + {file = "propcache-0.2.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5eee736daafa7af6d0a2dc15cc75e05c64f37fc37bafef2e00d77c14171c2097"}, + {file = "propcache-0.2.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7a31fc1e1bd362874863fdeed71aed92d348f5336fd84f2197ba40c59f061bd"}, + {file = "propcache-0.2.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba4cfa1052819d16699e1d55d18c92b6e094d4517c41dd231a8b9f87b6fa681"}, + {file = "propcache-0.2.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f089118d584e859c62b3da0892b88a83d611c2033ac410e929cb6754eec0ed16"}, + {file = "propcache-0.2.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:781e65134efaf88feb447e8c97a51772aa75e48b794352f94cb7ea717dedda0d"}, + {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:31f5af773530fd3c658b32b6bdc2d0838543de70eb9a2156c03e410f7b0d3aae"}, + {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:a7a078f5d37bee6690959c813977da5291b24286e7b962e62a94cec31aa5188b"}, + {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:cea7daf9fc7ae6687cf1e2c049752f19f146fdc37c2cc376e7d0032cf4f25347"}, + {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:8b3489ff1ed1e8315674d0775dc7d2195fb13ca17b3808721b54dbe9fd020faf"}, + {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:9403db39be1393618dd80c746cb22ccda168efce239c73af13c3763ef56ffc04"}, + {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5d97151bc92d2b2578ff7ce779cdb9174337390a535953cbb9452fb65164c587"}, + {file = "propcache-0.2.1-cp39-cp39-win32.whl", hash = "sha256:9caac6b54914bdf41bcc91e7eb9147d331d29235a7c967c150ef5df6464fd1bb"}, + {file = "propcache-0.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:92fc4500fcb33899b05ba73276dfb684a20d31caa567b7cb5252d48f896a91b1"}, + {file = "propcache-0.2.1-py3-none-any.whl", hash = "sha256:52277518d6aae65536e9cea52d4e7fd2f7a66f4aa2d30ed3f2fcea620ace3c54"}, + {file = "propcache-0.2.1.tar.gz", hash = "sha256:3f77ce728b19cb537714499928fe800c3dda29e8d9428778fc7c186da4c09a64"}, ] -[package.dependencies] -pastel = ">=0.2.1,<0.3.0" -tomli = ">=1.2.2" - -[package.extras] -poetry-plugin = ["poetry (>=1.0,<2.0)"] - [[package]] name = "pydantic" -version = "2.10.1" +version = "2.10.3" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic-2.10.1-py3-none-any.whl", hash = "sha256:a8d20db84de64cf4a7d59e899c2caf0fe9d660c7cfc482528e7020d7dd189a7e"}, - {file = "pydantic-2.10.1.tar.gz", hash = "sha256:a4daca2dc0aa429555e0656d6bf94873a7dc5f54ee42b1f5873d666fb3f35560"}, + {file = "pydantic-2.10.3-py3-none-any.whl", hash = "sha256:be04d85bbc7b65651c5f8e6b9976ed9c6f41782a55524cef079a34a0bb82144d"}, + {file = "pydantic-2.10.3.tar.gz", hash = "sha256:cb5ac360ce894ceacd69c403187900a02c4b20b693a9dd1d643e1effab9eadf9"}, ] [package.dependencies] @@ -962,6 +1703,54 @@ files = [ {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, ] +[[package]] +name = "sqlalchemy" +version = "2.0.36" +description = "Database Abstraction Library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "SQLAlchemy-2.0.36-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c245b1fbade9c35e5bd3b64270ab49ce990369018289ecfde3f9c318411aaa07"}, + {file = "SQLAlchemy-2.0.36-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f9511d8dd4a6e9271d07d150fb2f81874a3c8c95e11ff9af3a2dfc35fe42ee44"}, + {file = "SQLAlchemy-2.0.36-cp310-cp310-win32.whl", hash = "sha256:c3f3631693003d8e585d4200730616b78fafd5a01ef8b698f6967da5c605b3fa"}, + {file = "SQLAlchemy-2.0.36-cp310-cp310-win_amd64.whl", hash = "sha256:a86bfab2ef46d63300c0f06936bd6e6c0105faa11d509083ba8f2f9d237fb5b5"}, + {file = "SQLAlchemy-2.0.36-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2519f3a5d0517fc159afab1015e54bb81b4406c278749779be57a569d8d1bb0d"}, + {file = "SQLAlchemy-2.0.36-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:39769a115f730d683b0eb7b694db9789267bcd027326cccc3125e862eb03bfd8"}, + {file = "SQLAlchemy-2.0.36-cp311-cp311-win32.whl", hash = "sha256:66bffbad8d6271bb1cc2f9a4ea4f86f80fe5e2e3e501a5ae2a3dc6a76e604e6f"}, + {file = "SQLAlchemy-2.0.36-cp311-cp311-win_amd64.whl", hash = "sha256:23623166bfefe1487d81b698c423f8678e80df8b54614c2bf4b4cfcd7c711959"}, + {file = "SQLAlchemy-2.0.36-py3-none-any.whl", hash = "sha256:fddbe92b4760c6f5d48162aef14824add991aeda8ddadb3c31d56eb15ca69f8e"}, + {file = "sqlalchemy-2.0.36.tar.gz", hash = "sha256:7f2767680b6d2398aea7082e45a774b2b0767b5c8d8ffb9c8b683088ea9b29c5"}, +] + +[package.dependencies] +greenlet = {version = "!=0.4.17", markers = "python_version < \"3.13\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\")"} +typing-extensions = ">=4.6.0" + +[package.extras] +aiomysql = ["aiomysql (>=0.2.0)", "greenlet (!=0.4.17)"] +aioodbc = ["aioodbc", "greenlet (!=0.4.17)"] +aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing_extensions (!=3.10.0.1)"] +asyncio = ["greenlet (!=0.4.17)"] +asyncmy = ["asyncmy (>=0.2.3,!=0.2.4,!=0.2.6)", "greenlet (!=0.4.17)"] +mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2,!=1.1.5,!=1.1.10)"] +mssql = ["pyodbc"] +mssql-pymssql = ["pymssql"] +mssql-pyodbc = ["pyodbc"] +mypy = ["mypy (>=0.910)"] +mysql = ["mysqlclient (>=1.4.0)"] +mysql-connector = ["mysql-connector-python"] +oracle = ["cx_oracle (>=8)"] +oracle-oracledb = ["oracledb (>=1.0.1)"] +postgresql = ["psycopg2 (>=2.7)"] +postgresql-asyncpg = ["asyncpg", "greenlet (!=0.4.17)"] +postgresql-pg8000 = ["pg8000 (>=1.29.1)"] +postgresql-psycopg = ["psycopg (>=3.0.7)"] +postgresql-psycopg2binary = ["psycopg2-binary"] +postgresql-psycopg2cffi = ["psycopg2cffi"] +postgresql-psycopgbinary = ["psycopg[binary] (>=3.0.7)"] +pymysql = ["pymysql"] +sqlcipher = ["sqlcipher3_binary"] + [[package]] name = "sse-starlette" version = "1.8.2" @@ -1014,13 +1803,43 @@ test = ["pytest", "tornado (>=4.5)", "typeguard"] [[package]] name = "tomli" -version = "2.1.0" +version = "2.2.1" description = "A lil' TOML parser" optional = false python-versions = ">=3.8" files = [ - {file = "tomli-2.1.0-py3-none-any.whl", hash = "sha256:a5c57c3d1c56f5ccdf89f6523458f60ef716e210fc47c4cfb188c5ba473e0391"}, - {file = "tomli-2.1.0.tar.gz", hash = "sha256:3f646cae2aec94e17d04973e4249548320197cfabdf130015d023de4b74d8ab8"}, + {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, + {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"}, + {file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"}, + {file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"}, + {file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"}, + {file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"}, + {file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"}, + {file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"}, + {file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"}, + {file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"}, + {file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"}, + {file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"}, + {file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"}, + {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"}, ] [[package]] @@ -1147,10 +1966,106 @@ files = [ [package.extras] watchmedo = ["PyYAML (>=3.10)"] +[[package]] +name = "yarl" +version = "1.18.3" +description = "Yet another URL library" +optional = false +python-versions = ">=3.9" +files = [ + {file = "yarl-1.18.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7df647e8edd71f000a5208fe6ff8c382a1de8edfbccdbbfe649d263de07d8c34"}, + {file = "yarl-1.18.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c69697d3adff5aa4f874b19c0e4ed65180ceed6318ec856ebc423aa5850d84f7"}, + {file = "yarl-1.18.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:602d98f2c2d929f8e697ed274fbadc09902c4025c5a9963bf4e9edfc3ab6f7ed"}, + {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c654d5207c78e0bd6d749f6dae1dcbbfde3403ad3a4b11f3c5544d9906969dde"}, + {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5094d9206c64181d0f6e76ebd8fb2f8fe274950a63890ee9e0ebfd58bf9d787b"}, + {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35098b24e0327fc4ebdc8ffe336cee0a87a700c24ffed13161af80124b7dc8e5"}, + {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3236da9272872443f81fedc389bace88408f64f89f75d1bdb2256069a8730ccc"}, + {file = "yarl-1.18.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2c08cc9b16f4f4bc522771d96734c7901e7ebef70c6c5c35dd0f10845270bcd"}, + {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:80316a8bd5109320d38eef8833ccf5f89608c9107d02d2a7f985f98ed6876990"}, + {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:c1e1cc06da1491e6734f0ea1e6294ce00792193c463350626571c287c9a704db"}, + {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fea09ca13323376a2fdfb353a5fa2e59f90cd18d7ca4eaa1fd31f0a8b4f91e62"}, + {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e3b9fd71836999aad54084906f8663dffcd2a7fb5cdafd6c37713b2e72be1760"}, + {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:757e81cae69244257d125ff31663249b3013b5dc0a8520d73694aed497fb195b"}, + {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b1771de9944d875f1b98a745bc547e684b863abf8f8287da8466cf470ef52690"}, + {file = "yarl-1.18.3-cp310-cp310-win32.whl", hash = "sha256:8874027a53e3aea659a6d62751800cf6e63314c160fd607489ba5c2edd753cf6"}, + {file = "yarl-1.18.3-cp310-cp310-win_amd64.whl", hash = "sha256:93b2e109287f93db79210f86deb6b9bbb81ac32fc97236b16f7433db7fc437d8"}, + {file = "yarl-1.18.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8503ad47387b8ebd39cbbbdf0bf113e17330ffd339ba1144074da24c545f0069"}, + {file = "yarl-1.18.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:02ddb6756f8f4517a2d5e99d8b2f272488e18dd0bfbc802f31c16c6c20f22193"}, + {file = "yarl-1.18.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:67a283dd2882ac98cc6318384f565bffc751ab564605959df4752d42483ad889"}, + {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d980e0325b6eddc81331d3f4551e2a333999fb176fd153e075c6d1c2530aa8a8"}, + {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b643562c12680b01e17239be267bc306bbc6aac1f34f6444d1bded0c5ce438ca"}, + {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c017a3b6df3a1bd45b9fa49a0f54005e53fbcad16633870104b66fa1a30a29d8"}, + {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75674776d96d7b851b6498f17824ba17849d790a44d282929c42dbb77d4f17ae"}, + {file = "yarl-1.18.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ccaa3a4b521b780a7e771cc336a2dba389a0861592bbce09a476190bb0c8b4b3"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2d06d3005e668744e11ed80812e61efd77d70bb7f03e33c1598c301eea20efbb"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:9d41beda9dc97ca9ab0b9888cb71f7539124bc05df02c0cff6e5acc5a19dcc6e"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ba23302c0c61a9999784e73809427c9dbedd79f66a13d84ad1b1943802eaaf59"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:6748dbf9bfa5ba1afcc7556b71cda0d7ce5f24768043a02a58846e4a443d808d"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0b0cad37311123211dc91eadcb322ef4d4a66008d3e1bdc404808992260e1a0e"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0fb2171a4486bb075316ee754c6d8382ea6eb8b399d4ec62fde2b591f879778a"}, + {file = "yarl-1.18.3-cp311-cp311-win32.whl", hash = "sha256:61b1a825a13bef4a5f10b1885245377d3cd0bf87cba068e1d9a88c2ae36880e1"}, + {file = "yarl-1.18.3-cp311-cp311-win_amd64.whl", hash = "sha256:b9d60031cf568c627d028239693fd718025719c02c9f55df0a53e587aab951b5"}, + {file = "yarl-1.18.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1dd4bdd05407ced96fed3d7f25dbbf88d2ffb045a0db60dbc247f5b3c5c25d50"}, + {file = "yarl-1.18.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7c33dd1931a95e5d9a772d0ac5e44cac8957eaf58e3c8da8c1414de7dd27c576"}, + {file = "yarl-1.18.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:25b411eddcfd56a2f0cd6a384e9f4f7aa3efee14b188de13048c25b5e91f1640"}, + {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:436c4fc0a4d66b2badc6c5fc5ef4e47bb10e4fd9bf0c79524ac719a01f3607c2"}, + {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e35ef8683211db69ffe129a25d5634319a677570ab6b2eba4afa860f54eeaf75"}, + {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:84b2deecba4a3f1a398df819151eb72d29bfeb3b69abb145a00ddc8d30094512"}, + {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00e5a1fea0fd4f5bfa7440a47eff01d9822a65b4488f7cff83155a0f31a2ecba"}, + {file = "yarl-1.18.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d0e883008013c0e4aef84dcfe2a0b172c4d23c2669412cf5b3371003941f72bb"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5a3f356548e34a70b0172d8890006c37be92995f62d95a07b4a42e90fba54272"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ccd17349166b1bee6e529b4add61727d3f55edb7babbe4069b5764c9587a8cc6"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b958ddd075ddba5b09bb0be8a6d9906d2ce933aee81100db289badbeb966f54e"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c7d79f7d9aabd6011004e33b22bc13056a3e3fb54794d138af57f5ee9d9032cb"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:4891ed92157e5430874dad17b15eb1fda57627710756c27422200c52d8a4e393"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ce1af883b94304f493698b00d0f006d56aea98aeb49d75ec7d98cd4a777e9285"}, + {file = "yarl-1.18.3-cp312-cp312-win32.whl", hash = "sha256:f91c4803173928a25e1a55b943c81f55b8872f0018be83e3ad4938adffb77dd2"}, + {file = "yarl-1.18.3-cp312-cp312-win_amd64.whl", hash = "sha256:7e2ee16578af3b52ac2f334c3b1f92262f47e02cc6193c598502bd46f5cd1477"}, + {file = "yarl-1.18.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:90adb47ad432332d4f0bc28f83a5963f426ce9a1a8809f5e584e704b82685dcb"}, + {file = "yarl-1.18.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:913829534200eb0f789d45349e55203a091f45c37a2674678744ae52fae23efa"}, + {file = "yarl-1.18.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ef9f7768395923c3039055c14334ba4d926f3baf7b776c923c93d80195624782"}, + {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88a19f62ff30117e706ebc9090b8ecc79aeb77d0b1f5ec10d2d27a12bc9f66d0"}, + {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e17c9361d46a4d5addf777c6dd5eab0715a7684c2f11b88c67ac37edfba6c482"}, + {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1a74a13a4c857a84a845505fd2d68e54826a2cd01935a96efb1e9d86c728e186"}, + {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41f7ce59d6ee7741af71d82020346af364949314ed3d87553763a2df1829cc58"}, + {file = "yarl-1.18.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f52a265001d830bc425f82ca9eabda94a64a4d753b07d623a9f2863fde532b53"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:82123d0c954dc58db301f5021a01854a85bf1f3bb7d12ae0c01afc414a882ca2"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:2ec9bbba33b2d00999af4631a3397d1fd78290c48e2a3e52d8dd72db3a067ac8"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:fbd6748e8ab9b41171bb95c6142faf068f5ef1511935a0aa07025438dd9a9bc1"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:877d209b6aebeb5b16c42cbb377f5f94d9e556626b1bfff66d7b0d115be88d0a"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b464c4ab4bfcb41e3bfd3f1c26600d038376c2de3297760dfe064d2cb7ea8e10"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8d39d351e7faf01483cc7ff7c0213c412e38e5a340238826be7e0e4da450fdc8"}, + {file = "yarl-1.18.3-cp313-cp313-win32.whl", hash = "sha256:61ee62ead9b68b9123ec24bc866cbef297dd266175d53296e2db5e7f797f902d"}, + {file = "yarl-1.18.3-cp313-cp313-win_amd64.whl", hash = "sha256:578e281c393af575879990861823ef19d66e2b1d0098414855dd367e234f5b3c"}, + {file = "yarl-1.18.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:61e5e68cb65ac8f547f6b5ef933f510134a6bf31bb178be428994b0cb46c2a04"}, + {file = "yarl-1.18.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fe57328fbc1bfd0bd0514470ac692630f3901c0ee39052ae47acd1d90a436719"}, + {file = "yarl-1.18.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a440a2a624683108a1b454705ecd7afc1c3438a08e890a1513d468671d90a04e"}, + {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09c7907c8548bcd6ab860e5f513e727c53b4a714f459b084f6580b49fa1b9cee"}, + {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b4f6450109834af88cb4cc5ecddfc5380ebb9c228695afc11915a0bf82116789"}, + {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9ca04806f3be0ac6d558fffc2fdf8fcef767e0489d2684a21912cc4ed0cd1b8"}, + {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77a6e85b90a7641d2e07184df5557132a337f136250caafc9ccaa4a2a998ca2c"}, + {file = "yarl-1.18.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6333c5a377c8e2f5fae35e7b8f145c617b02c939d04110c76f29ee3676b5f9a5"}, + {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0b3c92fa08759dbf12b3a59579a4096ba9af8dd344d9a813fc7f5070d86bbab1"}, + {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:4ac515b860c36becb81bb84b667466885096b5fc85596948548b667da3bf9f24"}, + {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:045b8482ce9483ada4f3f23b3774f4e1bf4f23a2d5c912ed5170f68efb053318"}, + {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:a4bb030cf46a434ec0225bddbebd4b89e6471814ca851abb8696170adb163985"}, + {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:54d6921f07555713b9300bee9c50fb46e57e2e639027089b1d795ecd9f7fa910"}, + {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1d407181cfa6e70077df3377938c08012d18893f9f20e92f7d2f314a437c30b1"}, + {file = "yarl-1.18.3-cp39-cp39-win32.whl", hash = "sha256:ac36703a585e0929b032fbaab0707b75dc12703766d0b53486eabd5139ebadd5"}, + {file = "yarl-1.18.3-cp39-cp39-win_amd64.whl", hash = "sha256:ba87babd629f8af77f557b61e49e7c7cac36f22f871156b91e10a6e9d4f829e9"}, + {file = "yarl-1.18.3-py3-none-any.whl", hash = "sha256:b57f4f58099328dfb26c6a771d09fb20dbbae81d20cfb66141251ea063bd101b"}, + {file = "yarl-1.18.3.tar.gz", hash = "sha256:ac1801c45cbf77b6c99242eeff4fffb5e4e73a800b5c4ad4fc0be5def634d2e1"}, +] + +[package.dependencies] +idna = ">=2.0" +multidict = ">=4.0" +propcache = ">=0.2.0" + [extras] serve = [] [metadata] lock-version = "2.0" python-versions = ">=3.9,<4.0" -content-hash = "ed6ba14db90499ff27fefaab277bb8b879a48e024f4dabf944d76d8f7375002b" +content-hash = "c666eaa9945394483db2cf56ec2c147869b2fcefb767184c83c4f0e2f211ea2b" diff --git a/libs/cli/pyproject.toml b/libs/cli/pyproject.toml index 004d48b3bb3fb..2e3ee70fea7ab 100644 --- a/libs/cli/pyproject.toml +++ b/libs/cli/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "langchain-cli" -version = "0.0.33" +version = "0.0.35" description = "CLI for interacting with LangChain" authors = ["Erick Friis "] readme = "README.md" @@ -25,16 +25,18 @@ langchain = "langchain_cli.cli:app" langchain-cli = "langchain_cli.cli:app" [tool.poetry.group.dev.dependencies] -poethepoet = "^0.24.1" pytest = "^7.4.2" pytest-watch = "^4.2.0" [tool.poetry.group.lint.dependencies] ruff = "^0.5" +mypy = "^1.13.0" [tool.poetry.group.test.dependencies] +langchain = {path = "../langchain", develop = true} [tool.poetry.group.typing.dependencies] +langchain = {path = "../langchain", develop = true} [tool.poetry.group.test_integration.dependencies] @@ -50,22 +52,11 @@ select = [ "T201", # print ] -[tool.poe.tasks] -test = "poetry run pytest tests" -watch = "poetry run ptw" -version = "poetry version --short" -bump = ["_bump_1", "_bump_2"] -lint = ["_lint", "_check_formatting"] -format = ["_format", "_lint_fix"] - -_bump_2.shell = """sed -i "" "/^__version__ =/c\\ \n__version__ = \\"$version\\"\n" langchain_cli/cli.py""" -_bump_2.uses = { version = "version" } - -_bump_1 = "poetry version patch" -_check_formatting = "poetry run ruff format . --diff" -_lint = "poetry run ruff check ." -_format = "poetry run ruff format ." -_lint_fix = "poetry run ruff check . --fix" +[tool.mypy] +exclude = [ + "langchain_cli/integration_template", + "langchain_cli/package_template", +] [build-system] requires = ["poetry-core"] diff --git a/libs/cli/scripts/generate_migrations.py b/libs/cli/scripts/generate_migrations.py index 38c830443c775..11b5a8a6b0ed3 100644 --- a/libs/cli/scripts/generate_migrations.py +++ b/libs/cli/scripts/generate_migrations.py @@ -1,3 +1,4 @@ +# type: ignore """Script to generate migrations for the migration script.""" import json diff --git a/libs/cli/tests/integration_tests/test_compile.py b/libs/cli/tests/integration_tests/test_compile.py new file mode 100644 index 0000000000000..33ecccdfa0fbd --- /dev/null +++ b/libs/cli/tests/integration_tests/test_compile.py @@ -0,0 +1,7 @@ +import pytest + + +@pytest.mark.compile +def test_placeholder() -> None: + """Used for compiling integration tests without running any real tests.""" + pass diff --git a/libs/cli/tests/unit_tests/migrate/cli_runner/test_cli.py b/libs/cli/tests/unit_tests/migrate/cli_runner/test_cli.py index 4fbeae295bacf..3cc57112ead53 100644 --- a/libs/cli/tests/unit_tests/migrate/cli_runner/test_cli.py +++ b/libs/cli/tests/unit_tests/migrate/cli_runner/test_cli.py @@ -41,6 +41,7 @@ def find_issue(current: Folder, expected: Folder) -> str: return "Unknown" +@pytest.mark.xfail(reason="grit may not be installed in env") def test_command_line(tmp_path: Path) -> None: runner = CliRunner() diff --git a/libs/community/README.md b/libs/community/README.md index 02b1e754764d5..e3f1a94aff4d6 100644 --- a/libs/community/README.md +++ b/libs/community/README.md @@ -15,7 +15,7 @@ LangChain Community contains third-party integrations that implement the base in For full documentation see the [API reference](https://python.langchain.com/api_reference/community/index.html). -![Diagram outlining the hierarchical organization of the LangChain framework, displaying the interconnected parts across multiple layers.](https://raw.githubusercontent.com/langchain-ai/langchain/e1d113ea84a2edcf4a7709fc5be0e972ea74a5d9/docs/static/svg/langchain_stack_112024.svg "LangChain Framework Overview") +![Diagram outlining the hierarchical organization of the LangChain framework, displaying the interconnected parts across multiple layers.](https://raw.githubusercontent.com/langchain-ai/langchain/master/docs/static/svg/langchain_stack_112024.svg "LangChain Framework Overview") ## 📕 Releases & Versioning diff --git a/libs/community/extended_testing_deps.txt b/libs/community/extended_testing_deps.txt index fc208a71ef21c..d9c8177cd6a07 100644 --- a/libs/community/extended_testing_deps.txt +++ b/libs/community/extended_testing_deps.txt @@ -29,6 +29,7 @@ gliner>=0.2.7 google-cloud-documentai>=2.20.1,<3 gql>=3.4.1,<4 gradientai>=1.4.0,<2 +graphviz>=0.20.3,<0.21 hdbcli>=2.19.21,<3 hologres-vector==0.0.6 html2text>=2020.1.16 diff --git a/libs/community/langchain_community/agent_toolkits/gitlab/toolkit.py b/libs/community/langchain_community/agent_toolkits/gitlab/toolkit.py index 2e58e5b31f518..8e50610770aab 100644 --- a/libs/community/langchain_community/agent_toolkits/gitlab/toolkit.py +++ b/libs/community/langchain_community/agent_toolkits/gitlab/toolkit.py @@ -1,6 +1,6 @@ """GitLab Toolkit.""" -from typing import Dict, List +from typing import Dict, List, Optional from langchain_core.tools import BaseTool from langchain_core.tools.base import BaseToolkit @@ -9,15 +9,35 @@ COMMENT_ON_ISSUE_PROMPT, CREATE_FILE_PROMPT, CREATE_PULL_REQUEST_PROMPT, + CREATE_REPO_BRANCH, DELETE_FILE_PROMPT, GET_ISSUE_PROMPT, GET_ISSUES_PROMPT, + GET_REPO_FILES_FROM_DIRECTORY, + GET_REPO_FILES_IN_BOT_BRANCH, + GET_REPO_FILES_IN_MAIN, + LIST_REPO_BRANCES, READ_FILE_PROMPT, + SET_ACTIVE_BRANCH, UPDATE_FILE_PROMPT, ) from langchain_community.tools.gitlab.tool import GitLabAction from langchain_community.utilities.gitlab import GitLabAPIWrapper +# only include a subset of tools by default to avoid a breaking change, where +# new tools are added to the toolkit and the user's code breaks because of +# the new tools +DEFAULT_INCLUDED_TOOLS = [ + "get_issues", + "get_issue", + "comment_on_issue", + "create_pull_request", + "create_file", + "read_file", + "update_file", + "delete_file", +] + class GitLabToolkit(BaseToolkit): """GitLab Toolkit. @@ -39,7 +59,10 @@ class GitLabToolkit(BaseToolkit): @classmethod def from_gitlab_api_wrapper( - cls, gitlab_api_wrapper: GitLabAPIWrapper + cls, + gitlab_api_wrapper: GitLabAPIWrapper, + *, + included_tools: Optional[List[str]] = None, ) -> "GitLabToolkit": """Create a GitLabToolkit from a GitLabAPIWrapper. @@ -50,6 +73,10 @@ def from_gitlab_api_wrapper( GitLabToolkit. The GitLab toolkit. """ + tools_to_include = ( + included_tools if included_tools is not None else DEFAULT_INCLUDED_TOOLS + ) + operations: List[Dict] = [ { "mode": "get_issues", @@ -91,6 +118,41 @@ def from_gitlab_api_wrapper( "name": "Delete File", "description": DELETE_FILE_PROMPT, }, + { + "mode": "create_branch", + "name": "Create a new branch", + "description": CREATE_REPO_BRANCH, + }, + { + "mode": "list_branches_in_repo", + "name": "Get the list of branches", + "description": LIST_REPO_BRANCES, + }, + { + "mode": "set_active_branch", + "name": "Change the active branch", + "description": SET_ACTIVE_BRANCH, + }, + { + "mode": "list_files_in_main_branch", + "name": "Overview of existing files in Main branch", + "description": GET_REPO_FILES_IN_MAIN, + }, + { + "mode": "list_files_in_bot_branch", + "name": "Overview of files in current working branch", + "description": GET_REPO_FILES_IN_BOT_BRANCH, + }, + { + "mode": "list_files_from_directory", + "name": "Overview of files in current working branch from a specific path", # noqa: E501 + "description": GET_REPO_FILES_FROM_DIRECTORY, + }, + ] + operations_filtered = [ + operation + for operation in operations + if operation["mode"] in tools_to_include ] tools = [ GitLabAction( @@ -99,7 +161,7 @@ def from_gitlab_api_wrapper( mode=action["mode"], api_wrapper=gitlab_api_wrapper, ) - for action in operations + for action in operations_filtered ] return cls(tools=tools) # type: ignore[arg-type] diff --git a/libs/community/langchain_community/agent_toolkits/sql/toolkit.py b/libs/community/langchain_community/agent_toolkits/sql/toolkit.py index 75566c96b9307..aa4df2663f2f4 100644 --- a/libs/community/langchain_community/agent_toolkits/sql/toolkit.py +++ b/libs/community/langchain_community/agent_toolkits/sql/toolkit.py @@ -13,7 +13,10 @@ InfoSQLDatabaseTool, ListSQLDatabaseTool, QuerySQLCheckerTool, - QuerySQLDataBaseTool, + QuerySQLDatabaseTool, +) +from langchain_community.tools.sql_database.tool import ( + QuerySQLDataBaseTool as QuerySQLDataBaseTool, # keep import for backwards compat. ) from langchain_community.utilities.sql_database import SQLDatabase @@ -110,7 +113,7 @@ def get_tools(self) -> List[BaseTool]: f"'xxxx' in 'field list', use {info_sql_database_tool.name} " "to query the correct table fields." ) - query_sql_database_tool = QuerySQLDataBaseTool( + query_sql_database_tool = QuerySQLDatabaseTool( db=self.db, description=query_sql_database_tool_description ) query_sql_checker_tool_description = ( diff --git a/libs/community/langchain_community/callbacks/bedrock_anthropic_callback.py b/libs/community/langchain_community/callbacks/bedrock_anthropic_callback.py index 32c36f40768be..6dd88639cbb7e 100644 --- a/libs/community/langchain_community/callbacks/bedrock_anthropic_callback.py +++ b/libs/community/langchain_community/callbacks/bedrock_anthropic_callback.py @@ -28,15 +28,25 @@ def _get_anthropic_claude_token_cost( prompt_tokens: int, completion_tokens: int, model_id: Union[str, None] ) -> float: + if model_id: + # The model ID can be a cross-region (system-defined) inference profile ID, + # which has a prefix indicating the region (e.g., 'us', 'eu') but + # shares the same token costs as the "base model". + # By extracting the "base model ID", by taking the last two segments + # of the model ID, we can map cross-region inference profile IDs to + # their corresponding cost entries. + base_model_id = model_id.split(".")[-2] + "." + model_id.split(".")[-1] + else: + base_model_id = None """Get the cost of tokens for the Claude model.""" - if model_id not in MODEL_COST_PER_1K_INPUT_TOKENS: + if base_model_id not in MODEL_COST_PER_1K_INPUT_TOKENS: raise ValueError( f"Unknown model: {model_id}. Please provide a valid Anthropic model name." "Known models are: " + ", ".join(MODEL_COST_PER_1K_INPUT_TOKENS.keys()) ) - return (prompt_tokens / 1000) * MODEL_COST_PER_1K_INPUT_TOKENS[model_id] + ( + return (prompt_tokens / 1000) * MODEL_COST_PER_1K_INPUT_TOKENS[base_model_id] + ( completion_tokens / 1000 - ) * MODEL_COST_PER_1K_OUTPUT_TOKENS[model_id] + ) * MODEL_COST_PER_1K_OUTPUT_TOKENS[base_model_id] class BedrockAnthropicTokenUsageCallbackHandler(BaseCallbackHandler): diff --git a/libs/community/langchain_community/callbacks/openai_info.py b/libs/community/langchain_community/callbacks/openai_info.py index 9e2c070ccd2bf..4976f6331f11d 100644 --- a/libs/community/langchain_community/callbacks/openai_info.py +++ b/libs/community/langchain_community/callbacks/openai_info.py @@ -30,10 +30,12 @@ "gpt-4o": 0.0025, "gpt-4o-2024-05-13": 0.005, "gpt-4o-2024-08-06": 0.0025, + "gpt-4o-2024-11-20": 0.0025, # GPT-4o output "gpt-4o-completion": 0.01, "gpt-4o-2024-05-13-completion": 0.015, "gpt-4o-2024-08-06-completion": 0.01, + "gpt-4o-2024-11-20-completion": 0.01, # GPT-4 input "gpt-4": 0.03, "gpt-4-0314": 0.03, diff --git a/libs/community/langchain_community/chains/graph_qa/memgraph.py b/libs/community/langchain_community/chains/graph_qa/memgraph.py new file mode 100644 index 0000000000000..02fa66992a2fd --- /dev/null +++ b/libs/community/langchain_community/chains/graph_qa/memgraph.py @@ -0,0 +1,316 @@ +"""Question answering over a graph.""" + +from __future__ import annotations + +import re +from typing import Any, Dict, List, Optional, Union + +from langchain.chains.base import Chain +from langchain_core.callbacks import CallbackManagerForChainRun +from langchain_core.language_models import BaseLanguageModel +from langchain_core.messages import ( + AIMessage, + BaseMessage, + SystemMessage, + ToolMessage, +) +from langchain_core.output_parsers import StrOutputParser +from langchain_core.prompts import ( + BasePromptTemplate, + ChatPromptTemplate, + HumanMessagePromptTemplate, + MessagesPlaceholder, +) +from langchain_core.runnables import Runnable +from pydantic import Field + +from langchain_community.chains.graph_qa.prompts import ( + MEMGRAPH_GENERATION_PROMPT, + MEMGRAPH_QA_PROMPT, +) +from langchain_community.graphs.memgraph_graph import MemgraphGraph + +INTERMEDIATE_STEPS_KEY = "intermediate_steps" + +FUNCTION_RESPONSE_SYSTEM = """You are an assistant that helps to form nice and human +understandable answers based on the provided information from tools. +Do not add any other information that wasn't present in the tools, and use +very concise style in interpreting results! +""" + + +def extract_cypher(text: str) -> str: + """Extract Cypher code from a text. + + Args: + text: Text to extract Cypher code from. + + Returns: + Cypher code extracted from the text. + """ + # The pattern to find Cypher code enclosed in triple backticks + pattern = r"```(.*?)```" + + # Find all matches in the input text + matches = re.findall(pattern, text, re.DOTALL) + + return matches[0] if matches else text + + +def get_function_response( + question: str, context: List[Dict[str, Any]] +) -> List[BaseMessage]: + TOOL_ID = "call_H7fABDuzEau48T10Qn0Lsh0D" + messages = [ + AIMessage( + content="", + additional_kwargs={ + "tool_calls": [ + { + "id": TOOL_ID, + "function": { + "arguments": '{"question":"' + question + '"}', + "name": "GetInformation", + }, + "type": "function", + } + ] + }, + ), + ToolMessage(content=str(context), tool_call_id=TOOL_ID), + ] + return messages + + +class MemgraphQAChain(Chain): + """Chain for question-answering against a graph by generating Cypher statements. + + *Security note*: Make sure that the database connection uses credentials + that are narrowly-scoped to only include necessary permissions. + Failure to do so may result in data corruption or loss, since the calling + code may attempt commands that would result in deletion, mutation + of data if appropriately prompted or reading sensitive data if such + data is present in the database. + The best way to guard against such negative outcomes is to (as appropriate) + limit the permissions granted to the credentials used with this tool. + + See https://python.langchain.com/docs/security for more information. + """ + + graph: MemgraphGraph = Field(exclude=True) + cypher_generation_chain: Runnable + qa_chain: Runnable + graph_schema: str + input_key: str = "query" #: :meta private: + output_key: str = "result" #: :meta private: + top_k: int = 10 + """Number of results to return from the query""" + return_intermediate_steps: bool = False + """Whether or not to return the intermediate steps along with the final answer.""" + return_direct: bool = False + """Optional cypher validation tool""" + use_function_response: bool = False + """Whether to wrap the database context as tool/function response""" + allow_dangerous_requests: bool = False + """Forced user opt-in to acknowledge that the chain can make dangerous requests. + + *Security note*: Make sure that the database connection uses credentials + that are narrowly-scoped to only include necessary permissions. + Failure to do so may result in data corruption or loss, since the calling + code may attempt commands that would result in deletion, mutation + of data if appropriately prompted or reading sensitive data if such + data is present in the database. + The best way to guard against such negative outcomes is to (as appropriate) + limit the permissions granted to the credentials used with this tool. + + See https://python.langchain.com/docs/security for more information. + """ + + def __init__(self, **kwargs: Any) -> None: + """Initialize the chain.""" + super().__init__(**kwargs) + if self.allow_dangerous_requests is not True: + raise ValueError( + "In order to use this chain, you must acknowledge that it can make " + "dangerous requests by setting `allow_dangerous_requests` to `True`." + "You must narrowly scope the permissions of the database connection " + "to only include necessary permissions. Failure to do so may result " + "in data corruption or loss or reading sensitive data if such data is " + "present in the database." + "Only use this chain if you understand the risks and have taken the " + "necessary precautions. " + "See https://python.langchain.com/docs/security for more information." + ) + + @property + def input_keys(self) -> List[str]: + """Return the input keys. + + :meta private: + """ + return [self.input_key] + + @property + def output_keys(self) -> List[str]: + """Return the output keys. + + :meta private: + """ + _output_keys = [self.output_key] + return _output_keys + + @property + def _chain_type(self) -> str: + return "graph_cypher_chain" + + @classmethod + def from_llm( + cls, + llm: Optional[BaseLanguageModel] = None, + *, + qa_prompt: Optional[BasePromptTemplate] = None, + cypher_prompt: Optional[BasePromptTemplate] = None, + cypher_llm: Optional[BaseLanguageModel] = None, + qa_llm: Optional[Union[BaseLanguageModel, Any]] = None, + qa_llm_kwargs: Optional[Dict[str, Any]] = None, + cypher_llm_kwargs: Optional[Dict[str, Any]] = None, + use_function_response: bool = False, + function_response_system: str = FUNCTION_RESPONSE_SYSTEM, + **kwargs: Any, + ) -> MemgraphQAChain: + """Initialize from LLM.""" + + if not cypher_llm and not llm: + raise ValueError("Either `llm` or `cypher_llm` parameters must be provided") + if not qa_llm and not llm: + raise ValueError("Either `llm` or `qa_llm` parameters must be provided") + if cypher_llm and qa_llm and llm: + raise ValueError( + "You can specify up to two of 'cypher_llm', 'qa_llm'" + ", and 'llm', but not all three simultaneously." + ) + if cypher_prompt and cypher_llm_kwargs: + raise ValueError( + "Specifying cypher_prompt and cypher_llm_kwargs together is" + " not allowed. Please pass prompt via cypher_llm_kwargs." + ) + if qa_prompt and qa_llm_kwargs: + raise ValueError( + "Specifying qa_prompt and qa_llm_kwargs together is" + " not allowed. Please pass prompt via qa_llm_kwargs." + ) + use_qa_llm_kwargs = qa_llm_kwargs if qa_llm_kwargs is not None else {} + use_cypher_llm_kwargs = ( + cypher_llm_kwargs if cypher_llm_kwargs is not None else {} + ) + if "prompt" not in use_qa_llm_kwargs: + use_qa_llm_kwargs["prompt"] = ( + qa_prompt if qa_prompt is not None else MEMGRAPH_QA_PROMPT + ) + if "prompt" not in use_cypher_llm_kwargs: + use_cypher_llm_kwargs["prompt"] = ( + cypher_prompt + if cypher_prompt is not None + else MEMGRAPH_GENERATION_PROMPT + ) + + qa_llm = qa_llm or llm + if use_function_response: + try: + qa_llm.bind_tools({}) # type: ignore[union-attr] + response_prompt = ChatPromptTemplate.from_messages( + [ + SystemMessage(content=function_response_system), + HumanMessagePromptTemplate.from_template("{question}"), + MessagesPlaceholder(variable_name="function_response"), + ] + ) + qa_chain = response_prompt | qa_llm | StrOutputParser() # type: ignore + except (NotImplementedError, AttributeError): + raise ValueError("Provided LLM does not support native tools/functions") + else: + qa_chain = use_qa_llm_kwargs["prompt"] | qa_llm | StrOutputParser() # type: ignore + + prompt = use_cypher_llm_kwargs["prompt"] + llm_to_use = cypher_llm if cypher_llm is not None else llm + + if prompt is not None and llm_to_use is not None: + cypher_generation_chain = prompt | llm_to_use | StrOutputParser() # type: ignore[arg-type] + else: + raise ValueError( + "Missing required components for the cypher generation chain: " + "'prompt' or 'llm'" + ) + + graph_schema = kwargs["graph"].get_schema + + return cls( + graph_schema=graph_schema, + qa_chain=qa_chain, + cypher_generation_chain=cypher_generation_chain, + use_function_response=use_function_response, + **kwargs, + ) + + def _call( + self, + inputs: Dict[str, Any], + run_manager: Optional[CallbackManagerForChainRun] = None, + ) -> Dict[str, Any]: + """Generate Cypher statement, use it to look up in db and answer question.""" + _run_manager = run_manager or CallbackManagerForChainRun.get_noop_manager() + callbacks = _run_manager.get_child() + question = inputs[self.input_key] + args = { + "question": question, + "schema": self.graph_schema, + } + args.update(inputs) + + intermediate_steps: List = [] + + generated_cypher = self.cypher_generation_chain.invoke( + args, callbacks=callbacks + ) + # Extract Cypher code if it is wrapped in backticks + generated_cypher = extract_cypher(generated_cypher) + + _run_manager.on_text("Generated Cypher:", end="\n", verbose=self.verbose) + _run_manager.on_text( + generated_cypher, color="green", end="\n", verbose=self.verbose + ) + + intermediate_steps.append({"query": generated_cypher}) + + # Retrieve and limit the number of results + # Generated Cypher be null if query corrector identifies invalid schema + if generated_cypher: + context = self.graph.query(generated_cypher)[: self.top_k] + else: + context = [] + + if self.return_direct: + result = context + else: + _run_manager.on_text("Full Context:", end="\n", verbose=self.verbose) + _run_manager.on_text( + str(context), color="green", end="\n", verbose=self.verbose + ) + + intermediate_steps.append({"context": context}) + if self.use_function_response: + function_response = get_function_response(question, context) + result = self.qa_chain.invoke( # type: ignore + {"question": question, "function_response": function_response}, + ) + else: + result = self.qa_chain.invoke( # type: ignore + {"question": question, "context": context}, + callbacks=callbacks, + ) + + chain_result: Dict[str, Any] = {"result": result} + if self.return_intermediate_steps: + chain_result[INTERMEDIATE_STEPS_KEY] = intermediate_steps + + return chain_result diff --git a/libs/community/langchain_community/chains/graph_qa/prompts.py b/libs/community/langchain_community/chains/graph_qa/prompts.py index ec4d9a4c750a2..9077da3e00e3d 100644 --- a/libs/community/langchain_community/chains/graph_qa/prompts.py +++ b/libs/community/langchain_community/chains/graph_qa/prompts.py @@ -411,3 +411,58 @@ input_variables=["schema", "question", "extra_instructions"], template=NEPTUNE_OPENCYPHER_GENERATION_SIMPLE_TEMPLATE, ) + +MEMGRAPH_GENERATION_TEMPLATE = """Your task is to directly translate natural language inquiry into precise and executable Cypher query for Memgraph database. +You will utilize a provided database schema to understand the structure, nodes and relationships within the Memgraph database. +Instructions: +- Use provided node and relationship labels and property names from the +schema which describes the database's structure. Upon receiving a user +question, synthesize the schema to craft a precise Cypher query that +directly corresponds to the user's intent. +- Generate valid executable Cypher queries on top of Memgraph database. +Any explanation, context, or additional information that is not a part +of the Cypher query syntax should be omitted entirely. +- Use Memgraph MAGE procedures instead of Neo4j APOC procedures. +- Do not include any explanations or apologies in your responses. +- Do not include any text except the generated Cypher statement. +- For queries that ask for information or functionalities outside the direct +generation of Cypher queries, use the Cypher query format to communicate +limitations or capabilities. For example: RETURN "I am designed to generate +Cypher queries based on the provided schema only." +Schema: +{schema} + +With all the above information and instructions, generate Cypher query for the +user question. + +The question is: +{question}""" + +MEMGRAPH_GENERATION_PROMPT = PromptTemplate( + input_variables=["schema", "question"], template=MEMGRAPH_GENERATION_TEMPLATE +) + + +MEMGRAPH_QA_TEMPLATE = """Your task is to form nice and human +understandable answers. The information part contains the provided +information that you must use to construct an answer. +The provided information is authoritative, you must never doubt it or try to +use your internal knowledge to correct it. Make the answer sound as a +response to the question. Do not mention that you based the result on the +given information. Here is an example: + +Question: Which managers own Neo4j stocks? +Context:[manager:CTL LLC, manager:JANE STREET GROUP LLC] +Helpful Answer: CTL LLC, JANE STREET GROUP LLC owns Neo4j stocks. + +Follow this example when generating answers. If the provided information is +empty, say that you don't know the answer. + +Information: +{context} + +Question: {question} +Helpful Answer:""" +MEMGRAPH_QA_PROMPT = PromptTemplate( + input_variables=["context", "question"], template=MEMGRAPH_QA_TEMPLATE +) diff --git a/libs/community/langchain_community/chains/pebblo_retrieval/enforcement_filters.py b/libs/community/langchain_community/chains/pebblo_retrieval/enforcement_filters.py index 570cbdfa783f8..579b86acb0ebc 100644 --- a/libs/community/langchain_community/chains/pebblo_retrieval/enforcement_filters.py +++ b/libs/community/langchain_community/chains/pebblo_retrieval/enforcement_filters.py @@ -27,8 +27,9 @@ PINECONE = "Pinecone" QDRANT = "Qdrant" PGVECTOR = "PGVector" +PINECONE_VECTOR_STORE = "PineconeVectorStore" -SUPPORTED_VECTORSTORES = {PINECONE, QDRANT, PGVECTOR} +SUPPORTED_VECTORSTORES = {PINECONE, QDRANT, PGVECTOR, PINECONE_VECTOR_STORE} def clear_enforcement_filters(retriever: VectorStoreRetriever) -> None: @@ -505,7 +506,7 @@ def _set_identity_enforcement_filter( of the retriever based on the type of the vectorstore. """ search_kwargs = retriever.search_kwargs - if retriever.vectorstore.__class__.__name__ == PINECONE: + if retriever.vectorstore.__class__.__name__ in [PINECONE, PINECONE_VECTOR_STORE]: _apply_pinecone_authorization_filter(search_kwargs, auth_context) elif retriever.vectorstore.__class__.__name__ == QDRANT: _apply_qdrant_authorization_filter(search_kwargs, auth_context) diff --git a/libs/community/langchain_community/chat_models/deepinfra.py b/libs/community/langchain_community/chat_models/deepinfra.py index bb5309a6c4319..a0875aa8fe86b 100644 --- a/libs/community/langchain_community/chat_models/deepinfra.py +++ b/libs/community/langchain_community/chat_models/deepinfra.py @@ -207,6 +207,10 @@ class ChatDeepInfra(BaseChatModel): # client: Any #: :meta private: model_name: str = Field(default="meta-llama/Llama-2-70b-chat-hf", alias="model") """Model name to use.""" + + url: str = "https://api.deepinfra.com/v1/openai/chat/completions" + """URL to use for the API call.""" + deepinfra_api_token: Optional[str] = None request_timeout: Optional[float] = Field(default=None, alias="timeout") temperature: Optional[float] = 1 @@ -469,7 +473,7 @@ def _handle_status(self, code: int, text: Any) -> None: ) def _url(self) -> str: - return "https://stage.api.deepinfra.com/v1/openai/chat/completions" + return self.url def _headers(self) -> Dict: return { diff --git a/libs/community/langchain_community/chat_models/litellm.py b/libs/community/langchain_community/chat_models/litellm.py index d6c9557339857..83c3020910155 100644 --- a/libs/community/langchain_community/chat_models/litellm.py +++ b/libs/community/langchain_community/chat_models/litellm.py @@ -11,6 +11,7 @@ Dict, Iterator, List, + Literal, Mapping, Optional, Sequence, @@ -212,6 +213,33 @@ def _convert_message_to_dict(message: BaseMessage) -> dict: return message_dict +_OPENAI_MODELS = [ + "o1-mini", + "o1-preview", + "gpt-4o-mini", + "gpt-4o-mini-2024-07-18", + "gpt-4o", + "gpt-4o-2024-08-06", + "gpt-4o-2024-05-13", + "gpt-4-turbo", + "gpt-4-turbo-preview", + "gpt-4-0125-preview", + "gpt-4-1106-preview", + "gpt-3.5-turbo-1106", + "gpt-3.5-turbo", + "gpt-3.5-turbo-0301", + "gpt-3.5-turbo-0613", + "gpt-3.5-turbo-16k", + "gpt-3.5-turbo-16k-0613", + "gpt-4", + "gpt-4-0314", + "gpt-4-0613", + "gpt-4-32k", + "gpt-4-32k-0314", + "gpt-4-32k-0613", +] + + class ChatLiteLLM(BaseChatModel): """Chat model that uses the LiteLLM API.""" @@ -465,6 +493,9 @@ async def _agenerate( def bind_tools( self, tools: Sequence[Union[Dict[str, Any], Type[BaseModel], Callable, BaseTool]], + tool_choice: Optional[ + Union[dict, str, Literal["auto", "none", "required", "any"], bool] + ] = None, **kwargs: Any, ) -> Runnable[LanguageModelInput, BaseMessage]: """Bind tool-like objects to this chat model. @@ -476,17 +507,47 @@ def bind_tools( Can be a dictionary, pydantic model, callable, or BaseTool. Pydantic models, callables, and BaseTools will be automatically converted to their schema dictionary representation. - tool_choice: Which tool to require the model to call. - Must be the name of the single provided function or - "auto" to automatically determine which function to call - (if any), or a dict of the form: - {"type": "function", "function": {"name": <>}}. + tool_choice: Which tool to require the model to call. Options are: + - str of the form ``"<>"``: calls <> tool. + - ``"auto"``: + automatically selects a tool (including no tool). + - ``"none"``: + does not call a tool. + - ``"any"`` or ``"required"`` or ``True``: + forces least one tool to be called. + - dict of the form: + ``{"type": "function", "function": {"name": <>}}`` + - ``False`` or ``None``: no effect **kwargs: Any additional parameters to pass to the :class:`~langchain.runnable.Runnable` constructor. """ formatted_tools = [convert_to_openai_tool(tool) for tool in tools] - return super().bind(tools=formatted_tools, **kwargs) + + # In case of openai if tool_choice is `any` or if bool has been provided we + # change it to `required` as that is suppored by openai. + if ( + (self.model is not None and "azure" in self.model) + or (self.model_name is not None and "azure" in self.model_name) + or (self.model is not None and self.model in _OPENAI_MODELS) + or (self.model_name is not None and self.model_name in _OPENAI_MODELS) + ) and (tool_choice == "any" or isinstance(tool_choice, bool)): + tool_choice = "required" + # If tool_choice is bool apart from openai we make it `any` + elif isinstance(tool_choice, bool): + tool_choice = "any" + elif isinstance(tool_choice, dict): + tool_names = [ + formatted_tool["function"]["name"] for formatted_tool in formatted_tools + ] + if not any( + tool_name == tool_choice["function"]["name"] for tool_name in tool_names + ): + raise ValueError( + f"Tool choice {tool_choice} was specified, but the only " + f"provided tools were {tool_names}." + ) + return super().bind(tools=formatted_tools, tool_choice=tool_choice, **kwargs) @property def _identifying_params(self) -> Dict[str, Any]: diff --git a/libs/community/langchain_community/chat_models/moonshot.py b/libs/community/langchain_community/chat_models/moonshot.py index 7290c52b76e8b..68f8fdc5a5a28 100644 --- a/libs/community/langchain_community/chat_models/moonshot.py +++ b/libs/community/langchain_community/chat_models/moonshot.py @@ -13,21 +13,142 @@ class MoonshotChat(MoonshotCommon, ChatOpenAI): # type: ignore[misc, override, override] - """Moonshot large language models. + """Moonshot chat model integration. - To use, you should have the ``openai`` python package installed, and the - environment variable ``MOONSHOT_API_KEY`` set with your API key. - (Moonshot's chat API is compatible with OpenAI's SDK.) + Setup: + Install ``openai`` and set environment variables ``MOONSHOT_API_KEY``. - Referenced from https://platform.moonshot.cn/docs + .. code-block:: bash - Example: + pip install openai + export MOONSHOT_API_KEY="your-api-key" + + Key init args — completion params: + model: str + Name of Moonshot model to use. + temperature: float + Sampling temperature. + max_tokens: Optional[int] + Max number of tokens to generate. + + Key init args — client params: + api_key: Optional[str] + Moonshot API KEY. If not passed in will be read from env var MOONSHOT_API_KEY. + api_base: Optional[str] + Base URL for API requests. + + See full list of supported init args and their descriptions in the params section. + + Instantiate: + .. code-block:: python + + from langchain_community.chat_models import MoonshotChat + + chat = MoonshotChat( + temperature=0.5, + api_key="your-api-key", + model="moonshot-v1-8k", + # api_base="...", + # other params... + ) + + Invoke: + .. code-block:: python + + messages = [ + ("system", "你是一名专业的翻译家,可以将用户的中文翻译为英文。"), + ("human", "我喜欢编程。"), + ] + chat.invoke(messages) + + .. code-block:: python + + AIMessage( + content='I like programming.', + additional_kwargs={}, + response_metadata={ + 'token_usage': { + 'completion_tokens': 5, + 'prompt_tokens': 27, + 'total_tokens': 32 + }, + 'model_name': 'moonshot-v1-8k', + 'system_fingerprint': None, + 'finish_reason': 'stop', + 'logprobs': None + }, + id='run-71c03f4e-6628-41d5-beb6-d2559ae68266-0' + ) + + Stream: .. code-block:: python - from langchain_community.chat_models.moonshot import MoonshotChat + for chunk in chat.stream(messages): + print(chunk) + + .. code-block:: python + + content='' additional_kwargs={} response_metadata={} id='run-80d77096-8b83-4c39-a84d-71d9c746da92' + content='I' additional_kwargs={} response_metadata={} id='run-80d77096-8b83-4c39-a84d-71d9c746da92' + content=' like' additional_kwargs={} response_metadata={} id='run-80d77096-8b83-4c39-a84d-71d9c746da92' + content=' programming' additional_kwargs={} response_metadata={} id='run-80d77096-8b83-4c39-a84d-71d9c746da92' + content='.' additional_kwargs={} response_metadata={} id='run-80d77096-8b83-4c39-a84d-71d9c746da92' + content='' additional_kwargs={} response_metadata={'finish_reason': 'stop'} id='run-80d77096-8b83-4c39-a84d-71d9c746da92' + + .. code-block:: python + + stream = chat.stream(messages) + full = next(stream) + for chunk in stream: + full += chunk + full + + .. code-block:: python + + AIMessageChunk( + content='I like programming.', + additional_kwargs={}, + response_metadata={'finish_reason': 'stop'}, + id='run-10c80976-7aa5-4ff7-ba3e-1251665557ef' + ) + + Async: + .. code-block:: python + + await chat.ainvoke(messages) + + # stream: + # async for chunk in chat.astream(messages): + # print(chunk) + + # batch: + # await chat.abatch([messages]) + + .. code-block:: python + + [AIMessage(content='I like programming.', additional_kwargs={}, response_metadata={'token_usage': {'completion_tokens': 5, 'prompt_tokens': 27, 'total_tokens': 32}, 'model_name': 'moonshot-v1-8k', 'system_fingerprint': None, 'finish_reason': 'stop', 'logprobs': None}, id='run-2938b005-9204-4b9f-b273-1c3272fce9e5-0')] + + Response metadata + .. code-block:: python + + ai_msg = chat.invoke(messages) + ai_msg.response_metadata + + .. code-block:: python - moonshot = MoonshotChat(model="moonshot-v1-8k") - """ + { + 'token_usage': { + 'completion_tokens': 5, + 'prompt_tokens': 27, + 'total_tokens': 32 + }, + 'model_name': 'moonshot-v1-8k', + 'system_fingerprint': None, + 'finish_reason': 'stop', + 'logprobs': None + } + + """ # noqa: E501 @pre_init def validate_environment(cls, values: Dict) -> Dict: diff --git a/libs/community/langchain_community/chat_models/outlines.py b/libs/community/langchain_community/chat_models/outlines.py index 00aa05adfe1e6..64894ff23732a 100644 --- a/libs/community/langchain_community/chat_models/outlines.py +++ b/libs/community/langchain_community/chat_models/outlines.py @@ -122,13 +122,13 @@ class ChatOutlines(BaseChatModel): This can be useful for generating structured outputs like IP addresses, dates, etc. Example: (valid IP address) - regex = r"((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)" + regex = r"((25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)" Note: Computing the regex index can take some time, so it's recommended to reuse the same regex for multiple generations if possible. For more details, see: https://dottxt-ai.github.io/outlines/reference/generation/regex/ - """ + """ # noqa: E501 type_constraints: Optional[Union[type, str]] = None """Type constraints for structured generation. diff --git a/libs/community/langchain_community/chat_models/perplexity.py b/libs/community/langchain_community/chat_models/perplexity.py index ce415dd59cbd5..d168b1363e56f 100644 --- a/libs/community/langchain_community/chat_models/perplexity.py +++ b/libs/community/langchain_community/chat_models/perplexity.py @@ -148,7 +148,6 @@ def validate_environment(self) -> Self: def _default_params(self) -> Dict[str, Any]: """Get the default parameters for calling PerplexityChat API.""" return { - "request_timeout": self.request_timeout, "max_tokens": self.max_tokens, "stream": self.streaming, "temperature": self.temperature, @@ -222,7 +221,7 @@ def _stream( if stop: params["stop_sequences"] = stop stream_resp = self.client.chat.completions.create( - model=params["model"], messages=message_dicts, stream=True + messages=message_dicts, stream=True, **params ) for chunk in stream_resp: if not isinstance(chunk, dict): @@ -258,9 +257,7 @@ def _generate( return generate_from_stream(stream_iter) message_dicts, params = self._create_message_dicts(messages, stop) params = {**params, **kwargs} - response = self.client.chat.completions.create( - model=params["model"], messages=message_dicts - ) + response = self.client.chat.completions.create(messages=message_dicts, **params) message = AIMessage( content=response.choices[0].message.content, additional_kwargs={"citations": response.citations}, @@ -271,8 +268,6 @@ def _generate( def _invocation_params(self) -> Mapping[str, Any]: """Get the parameters used to invoke the model.""" pplx_creds: Dict[str, Any] = { - "api_key": self.pplx_api_key, - "api_base": "https://api.perplexity.ai", "model": self.model, } return {**pplx_creds, **self._default_params} diff --git a/libs/community/langchain_community/chat_models/snowflake.py b/libs/community/langchain_community/chat_models/snowflake.py index 84883b2cd87f6..c53f02aeee097 100644 --- a/libs/community/langchain_community/chat_models/snowflake.py +++ b/libs/community/langchain_community/chat_models/snowflake.py @@ -1,22 +1,35 @@ import json -from typing import Any, Dict, List, Optional +from typing import ( + Any, + Callable, + Dict, + Iterator, + List, + Literal, + Optional, + Sequence, + Type, + Union, +) from langchain_core.callbacks.manager import CallbackManagerForLLMRun from langchain_core.language_models import BaseChatModel from langchain_core.messages import ( AIMessage, + AIMessageChunk, BaseMessage, ChatMessage, HumanMessage, SystemMessage, ) -from langchain_core.outputs import ChatGeneration, ChatResult +from langchain_core.outputs import ChatGeneration, ChatGenerationChunk, ChatResult +from langchain_core.tools import BaseTool from langchain_core.utils import ( convert_to_secret_str, get_from_dict_or_env, get_pydantic_field_names, - pre_init, ) +from langchain_core.utils.function_calling import convert_to_openai_tool from langchain_core.utils.utils import _build_model_kwargs from pydantic import Field, SecretStr, model_validator @@ -44,7 +57,7 @@ def _convert_message_to_dict(message: BaseMessage) -> dict: "content": message.content, } - # populate role and additional message data + # Populate role and additional message data if isinstance(message, ChatMessage) and message.role in SUPPORTED_ROLES: message_dict["role"] = message.role elif isinstance(message, SystemMessage): @@ -76,8 +89,8 @@ def _truncate_at_stop_tokens( class ChatSnowflakeCortex(BaseChatModel): """Snowflake Cortex based Chat model - To use you must have the ``snowflake-snowpark-python`` Python package installed and - either: + To use the chat model, you must have the ``snowflake-snowpark-python`` Python + package installed and either: 1. environment variables set with your snowflake credentials or 2. directly passed in as kwargs to the ChatSnowflakeCortex constructor. @@ -89,24 +102,30 @@ class ChatSnowflakeCortex(BaseChatModel): chat = ChatSnowflakeCortex() """ - _sp_session: Any = None + # test_tools: Dict[str, Any] = Field(default_factory=dict) + test_tools: Dict[str, Union[Dict[str, Any], Type, Callable, BaseTool]] = Field( + default_factory=dict + ) + + session: Any = None """Snowpark session object.""" - model: str = "snowflake-arctic" - """Snowflake cortex hosted LLM model name, defaulted to `snowflake-arctic`. - Refer to docs for more options.""" + model: str = "mistral-large" + """Snowflake cortex hosted LLM model name, defaulted to `mistral-large`. + Refer to docs for more options. Also note, not all models support + agentic workflows.""" cortex_function: str = "complete" """Cortex function to use, defaulted to `complete`. Refer to docs for more options.""" - temperature: float = 0.7 + temperature: float = 0 """Model temperature. Value should be >= 0 and <= 1.0""" max_tokens: Optional[int] = None """The maximum number of output tokens in the response.""" - top_p: Optional[float] = None + top_p: Optional[float] = 0 """top_p adjusts the number of choices for each predicted tokens based on cumulative probabilities. Value should be ranging between 0.0 and 1.0. """ @@ -126,6 +145,27 @@ class ChatSnowflakeCortex(BaseChatModel): snowflake_role: Optional[str] = Field(default=None, alias="role") """Automatically inferred from env var `SNOWFLAKE_ROLE` if not provided.""" + def bind_tools( + self, + tools: Sequence[Union[Dict[str, Any], Type, Callable, BaseTool]], + *, + tool_choice: Optional[ + Union[dict, str, Literal["auto", "any", "none"], bool] + ] = "auto", + **kwargs: Any, + ) -> "ChatSnowflakeCortex": + """Bind tool-like objects to this chat model, ensuring they conform to + expected formats.""" + + formatted_tools = [convert_to_openai_tool(tool) for tool in tools] + # self.test_tools.update(formatted_tools) + formatted_tools_dict = { + tool["name"]: tool for tool in formatted_tools if "name" in tool + } + self.test_tools.update(formatted_tools_dict) + + return self + @model_validator(mode="before") @classmethod def build_extra(cls, values: Dict[str, Any]) -> Any: @@ -134,14 +174,15 @@ def build_extra(cls, values: Dict[str, Any]) -> Any: values = _build_model_kwargs(values, all_required_field_names) return values - @pre_init + @model_validator(mode="before") def validate_environment(cls, values: Dict) -> Dict: try: from snowflake.snowpark import Session except ImportError: raise ImportError( - "`snowflake-snowpark-python` package not found, please install it with " - "`pip install snowflake-snowpark-python`" + """`snowflake-snowpark-python` package not found, please install: + `pip install snowflake-snowpark-python` + """ ) values["snowflake_username"] = get_from_dict_or_env( @@ -174,18 +215,19 @@ def validate_environment(cls, values: Dict) -> Dict: "schema": values["snowflake_schema"], "warehouse": values["snowflake_warehouse"], "role": values["snowflake_role"], + "client_session_keep_alive": "True", } try: - values["_sp_session"] = Session.builder.configs(connection_params).create() + values["session"] = Session.builder.configs(connection_params).create() except Exception as e: raise ChatSnowflakeCortexError(f"Failed to create session: {e}") return values def __del__(self) -> None: - if getattr(self, "_sp_session", None) is not None: - self._sp_session.close() + if getattr(self, "session", None) is not None: + self.session.close() @property def _llm_type(self) -> str: @@ -200,23 +242,55 @@ def _generate( **kwargs: Any, ) -> ChatResult: message_dicts = [_convert_message_to_dict(m) for m in messages] - message_str = str(message_dicts) - options = {"temperature": self.temperature} - if self.top_p is not None: - options["top_p"] = self.top_p - if self.max_tokens is not None: - options["max_tokens"] = self.max_tokens - options_str = str(options) + + # Check for tool invocation in the messages and prepare for tool use + tool_output = None + for message in messages: + if ( + isinstance(message.content, dict) + and isinstance(message, SystemMessage) + and "invoke_tool" in message.content + ): + tool_info = json.loads(message.content.get("invoke_tool")) + tool_name = tool_info.get("tool_name") + if tool_name in self.test_tools: + tool_args = tool_info.get("args", {}) + tool_output = self.test_tools[tool_name](**tool_args) + break + + # Prepare messages for SQL query + if tool_output: + message_dicts.append( + {"tool_output": str(tool_output)} + ) # Ensure tool_output is a string + + # JSON dump the message_dicts and options without additional escaping + message_json = json.dumps(message_dicts) + options = { + "temperature": self.temperature, + "top_p": self.top_p if self.top_p is not None else 1.0, + "max_tokens": self.max_tokens if self.max_tokens is not None else 2048, + } + options_json = json.dumps(options) # JSON string of options + + # Form the SQL statement using JSON literals sql_stmt = f""" select snowflake.cortex.{self.cortex_function}( - '{self.model}' - ,{message_str},{options_str}) as llm_response;""" + '{self.model}', + parse_json('{message_json}'), + parse_json('{options_json}') + ) as llm_response; + """ try: - l_rows = self._sp_session.sql(sql_stmt).collect() + # Use the Snowflake Cortex Complete function + self.session.sql( + f"USE WAREHOUSE {self.session.get_current_warehouse()};" + ).collect() + l_rows = self.session.sql(sql_stmt).collect() except Exception as e: raise ChatSnowflakeCortexError( - f"Error while making request to Snowflake Cortex via Snowpark: {e}" + f"Error while making request to Snowflake Cortex: {e}" ) response = json.loads(l_rows[0]["LLM_RESPONSE"]) @@ -229,3 +303,89 @@ def _generate( ) generation = ChatGeneration(message=message) return ChatResult(generations=[generation]) + + def _stream_content( + self, content: str, stop: Optional[List[str]] + ) -> Iterator[ChatGenerationChunk]: + """ + Stream the output of the model in chunks to return ChatGenerationChunk. + """ + chunk_size = 50 # Define a reasonable chunk size for streaming + truncated_content = _truncate_at_stop_tokens(content, stop) + + for i in range(0, len(truncated_content), chunk_size): + chunk_content = truncated_content[i : i + chunk_size] + + # Create and yield a ChatGenerationChunk with partial content + yield ChatGenerationChunk(message=AIMessageChunk(content=chunk_content)) + + def _stream( + self, + messages: List[BaseMessage], + stop: Optional[List[str]] = None, + run_manager: Optional[CallbackManagerForLLMRun] = None, + **kwargs: Any, + ) -> Iterator[ChatGenerationChunk]: + """Stream the output of the model in chunks to return ChatGenerationChunk.""" + message_dicts = [_convert_message_to_dict(m) for m in messages] + + # Check for and potentially use a tool before streaming + for message in messages: + if ( + isinstance(message, str) + and isinstance(message, SystemMessage) + and "invoke_tool" in message.content + ): + tool_info = json.loads(message.content) + tool_list = tool_info.get("invoke_tools", []) + for tool in tool_list: + tool_name = tool.get("tool_name") + tool_args = tool.get("args", {}) + + if tool_name in self.test_tools: + tool_args = tool_info.get("args", {}) + tool_result = self.test_tools[tool_name](**tool_args) + additional_context = {"tool_output": tool_result} + message_dicts.append( + additional_context + ) # Append tool result to message dicts + + # JSON dump the message_dicts and options without additional escaping + message_json = json.dumps(message_dicts) + options = { + "temperature": self.temperature, + "top_p": self.top_p if self.top_p is not None else 1.0, + "max_tokens": self.max_tokens if self.max_tokens is not None else 2048, + # "stream": True, + } + options_json = json.dumps(options) # JSON string of options + + # Form the SQL statement using JSON literals + sql_stmt = f""" + select snowflake.cortex.{self.cortex_function}( + '{self.model}', + parse_json('{message_json}'), + parse_json('{options_json}') + ) as llm_stream_response; + """ + + try: + # Use the Snowflake Cortex Complete function + self.session.sql( + f"USE WAREHOUSE {self.session.get_current_warehouse()};" + ).collect() + result = self.session.sql(sql_stmt).collect() + + # Iterate over the generator to yield streaming responses + for row in result: + response = json.loads(row["LLM_STREAM_RESPONSE"]) + ai_message_content = response["choices"][0]["messages"] + + # Stream response content in chunks + for chunk in self._stream_content(ai_message_content, stop): + yield chunk + + except Exception as e: + raise ChatSnowflakeCortexError( + f"Error while making request to Snowflake Cortex stream: {e}" + ) diff --git a/libs/community/langchain_community/chat_models/tongyi.py b/libs/community/langchain_community/chat_models/tongyi.py index a39e744531a84..acb099b18b878 100644 --- a/libs/community/langchain_community/chat_models/tongyi.py +++ b/libs/community/langchain_community/chat_models/tongyi.py @@ -547,6 +547,19 @@ def _stream_completion_with_retry(**_kwargs: Any) -> Any: if _kwargs.get("stream") and not _kwargs.get( "incremental_output", False ): + # inline fix response text logic + resp_copy = json.loads(json.dumps(resp)) + if resp_copy.get("output") and resp_copy["output"].get("choices"): + choice = resp_copy["output"]["choices"][0] + message = choice["message"] + if isinstance(message.get("content"), list): + content_text = "".join( + item.get("text", "") + for item in message["content"] + if isinstance(item, dict) + ) + message["content"] = content_text + resp = resp_copy if prev_resp is None: delta_resp = resp else: diff --git a/libs/community/langchain_community/document_loaders/base_o365.py b/libs/community/langchain_community/document_loaders/base_o365.py index 5f89d0794fccd..4cd341fadde19 100644 --- a/libs/community/langchain_community/document_loaders/base_o365.py +++ b/libs/community/langchain_community/document_loaders/base_o365.py @@ -5,8 +5,11 @@ import logging import mimetypes import os +import re import tempfile +import urllib from abc import abstractmethod +from datetime import datetime from pathlib import Path, PurePath from typing import TYPE_CHECKING, Any, Dict, Iterable, List, Optional, Sequence, Union @@ -84,6 +87,9 @@ class O365BaseLoader(BaseLoader, BaseModel): """Number of bytes to retrieve from each api call to the server. int or 'auto'.""" recursive: bool = False """Should the loader recursively load subfolders?""" + modified_since: Optional[datetime] = None + """Only fetch documents modified since given datetime. The datetime object + must be timezone aware.""" handlers: Optional[Dict[str, Any]] = {} """ Provide custom handlers for MimeTypeBasedParser. @@ -186,17 +192,29 @@ def _load_from_folder(self, folder: Folder) -> Iterable[Blob]: for file in items: if file.is_file: if file.mime_type in list(file_mime_types.values()): - file.download(to_path=temp_dir, chunk_size=self.chunk_size) - metadata_dict[file.name] = { - "source": file.web_url, - "mime_type": file.mime_type, - "created": str(file.created), - "modified": str(file.modified), - "created_by": str(file.created_by), - "modified_by": str(file.modified_by), - "description": file.description, - "id": str(file.object_id), - } + if (not self.modified_since) or ( + file.modified > self.modified_since + ): + source = file.web_url + if re.search( + r"Doc.aspx\?sourcedoc=.*file=([^&]+)", file.web_url + ): + source = ( + file._parent.web_url + + "/" + + urllib.parse.quote(file.name) + ) + file.download(to_path=temp_dir, chunk_size=self.chunk_size) + metadata_dict[file.name] = { + "source": source, + "mime_type": file.mime_type, + "created": str(file.created), + "modified": str(file.modified), + "created_by": str(file.created_by), + "modified_by": str(file.modified_by), + "description": file.description, + "id": str(file.object_id), + } loader = FileSystemBlobLoader(path=temp_dir) for blob in loader.yield_blobs(): @@ -241,9 +259,18 @@ def _load_from_object_ids( continue if file.is_file: if file.mime_type in list(file_mime_types.values()): + source = file.web_url + if re.search( + r"Doc.aspx\?sourcedoc=.*file=([^&]+)", file.web_url + ): + source = ( + file._parent.web_url + + "/" + + urllib.parse.quote(file.name) + ) file.download(to_path=temp_dir, chunk_size=self.chunk_size) metadata_dict[file.name] = { - "source": file.web_url, + "source": source, "mime_type": file.mime_type, "created": file.created, "modified": file.modified, diff --git a/libs/community/langchain_community/document_loaders/confluence.py b/libs/community/langchain_community/document_loaders/confluence.py index 70c86e7dce962..954f4139cae5c 100644 --- a/libs/community/langchain_community/document_loaders/confluence.py +++ b/libs/community/langchain_community/document_loaders/confluence.py @@ -166,6 +166,7 @@ def __init__( include_archived_content: bool = False, include_attachments: bool = False, include_comments: bool = False, + include_labels: bool = False, content_format: ContentFormat = ContentFormat.STORAGE, limit: Optional[int] = 50, max_pages: Optional[int] = 1000, @@ -181,6 +182,7 @@ def __init__( self.include_archived_content = include_archived_content self.include_attachments = include_attachments self.include_comments = include_comments + self.include_labels = include_labels self.content_format = content_format self.limit = limit self.max_pages = max_pages @@ -327,12 +329,20 @@ def _lazy_load(self, **kwargs: Any) -> Iterator[Document]: ) include_attachments = self._resolve_param("include_attachments", kwargs) include_comments = self._resolve_param("include_comments", kwargs) + include_labels = self._resolve_param("include_labels", kwargs) content_format = self._resolve_param("content_format", kwargs) limit = self._resolve_param("limit", kwargs) max_pages = self._resolve_param("max_pages", kwargs) ocr_languages = self._resolve_param("ocr_languages", kwargs) keep_markdown_format = self._resolve_param("keep_markdown_format", kwargs) keep_newlines = self._resolve_param("keep_newlines", kwargs) + expand = ",".join( + [ + content_format.value, + "version", + *(["metadata.labels"] if include_labels else []), + ] + ) if not space_key and not page_ids and not label and not cql: raise ValueError( @@ -347,13 +357,14 @@ def _lazy_load(self, **kwargs: Any) -> Iterator[Document]: limit=limit, max_pages=max_pages, status="any" if include_archived_content else "current", - expand=f"{content_format.value},version", + expand=expand, ) yield from self.process_pages( pages, include_restricted_content, include_attachments, include_comments, + include_labels, content_format, ocr_languages=ocr_languages, keep_markdown_format=keep_markdown_format, @@ -380,13 +391,14 @@ def _lazy_load(self, **kwargs: Any) -> Iterator[Document]: limit=limit, max_pages=max_pages, include_archived_spaces=include_archived_content, - expand=f"{content_format.value},version", + expand=expand, ) yield from self.process_pages( pages, include_restricted_content, include_attachments, include_comments, + False, # labels are not included in the search results content_format, ocr_languages, keep_markdown_format, @@ -408,7 +420,8 @@ def _lazy_load(self, **kwargs: Any) -> Iterator[Document]: before_sleep=before_sleep_log(logger, logging.WARNING), )(self.confluence.get_page_by_id) page = get_page( - page_id=page_id, expand=f"{content_format.value},version" + page_id=page_id, + expand=expand, ) if not include_restricted_content and not self.is_public_page(page): continue @@ -416,6 +429,7 @@ def _lazy_load(self, **kwargs: Any) -> Iterator[Document]: page, include_attachments, include_comments, + include_labels, content_format, ocr_languages, keep_markdown_format, @@ -428,17 +442,25 @@ def lazy_load(self) -> Iterator[Document]: yield from self._lazy_load() def _search_content_by_cql( - self, cql: str, include_archived_spaces: Optional[bool] = None, **kwargs: Any - ) -> List[dict]: - url = "rest/api/content/search" + self, + cql: str, + include_archived_spaces: Optional[bool] = None, + next_url: str = "", + **kwargs: Any, + ) -> tuple[List[dict], str]: + if next_url: + response = self.confluence.get(next_url) + else: + url = "rest/api/content/search" + + params: Dict[str, Any] = {"cql": cql} + params.update(kwargs) + if include_archived_spaces is not None: + params["includeArchivedSpaces"] = include_archived_spaces - params: Dict[str, Any] = {"cql": cql} - params.update(kwargs) - if include_archived_spaces is not None: - params["includeArchivedSpaces"] = include_archived_spaces + response = self.confluence.get(url, params=params) - response = self.confluence.get(url, params=params) - return response.get("results", []) + return response.get("results", []), response.get("_links", {}).get("next", "") def paginate_request(self, retrieval_method: Callable, **kwargs: Any) -> List: """Paginate the various methods to retrieve groups of pages. @@ -463,6 +485,7 @@ def paginate_request(self, retrieval_method: Callable, **kwargs: Any) -> List: max_pages = kwargs.pop("max_pages") docs: List[dict] = [] + next_url: str = "" while len(docs) < max_pages: get_pages = retry( reraise=True, @@ -476,9 +499,15 @@ def paginate_request(self, retrieval_method: Callable, **kwargs: Any) -> List: ), before_sleep=before_sleep_log(logger, logging.WARNING), )(retrieval_method) - batch = get_pages(**kwargs, start=len(docs)) - if not batch: - break + if self.cql: # cursor pagination for CQL + batch, next_url = get_pages(**kwargs, next_url=next_url) + if not next_url: + docs.extend(batch) + break + else: + batch = get_pages(**kwargs, start=len(docs)) + if not batch: + break docs.extend(batch) return docs[:max_pages] @@ -498,6 +527,7 @@ def process_pages( include_restricted_content: bool, include_attachments: bool, include_comments: bool, + include_labels: bool, content_format: ContentFormat, ocr_languages: Optional[str] = None, keep_markdown_format: Optional[bool] = False, @@ -511,6 +541,7 @@ def process_pages( page, include_attachments, include_comments, + include_labels, content_format, ocr_languages=ocr_languages, keep_markdown_format=keep_markdown_format, @@ -522,6 +553,7 @@ def process_page( page: dict, include_attachments: bool, include_comments: bool, + include_labels: bool, content_format: ContentFormat, ocr_languages: Optional[str] = None, keep_markdown_format: Optional[bool] = False, @@ -575,10 +607,19 @@ def process_page( ] text = text + "".join(comment_texts) + if include_labels: + labels = [ + label["name"] + for label in page.get("metadata", {}) + .get("labels", {}) + .get("results", []) + ] + metadata = { "title": page["title"], "id": page["id"], "source": self.base_url.strip("/") + page["_links"]["webui"], + **({"labels": labels} if include_labels else {}), } if "version" in page and "when" in page["version"]: @@ -668,8 +709,11 @@ def process_pdf( return text for i, image in enumerate(images): - image_text = pytesseract.image_to_string(image, lang=ocr_languages) - text += f"Page {i + 1}:\n{image_text}\n\n" + try: + image_text = pytesseract.image_to_string(image, lang=ocr_languages) + text += f"Page {i + 1}:\n{image_text}\n\n" + except pytesseract.TesseractError as ex: + logger.warning(f"TesseractError: {ex}") return text diff --git a/libs/community/langchain_community/document_loaders/json_loader.py b/libs/community/langchain_community/document_loaders/json_loader.py index d255523e7f2b5..08d5823e2a2d9 100644 --- a/libs/community/langchain_community/document_loaders/json_loader.py +++ b/libs/community/langchain_community/document_loaders/json_loader.py @@ -157,8 +157,6 @@ def _parse(self, content: str, index: int) -> Iterator[Document]: # and prevent the user from getting a cryptic error later on. if self._content_key is not None: self._validate_content_key(data) - if self._metadata_func is not None: - self._validate_metadata_func(data) for i, sample in enumerate(data, index + 1): text = self._get_text(sample=sample) @@ -178,7 +176,7 @@ def _get_text(self, sample: Any) -> str: else: content = sample - if self._text_content and not isinstance(content, str): + if self._text_content and not isinstance(content, str) and content is not None: raise ValueError( f"Expected page_content is string, got {type(content)} instead. \ Set `text_content=False` if the desired input for \ @@ -188,7 +186,7 @@ def _get_text(self, sample: Any) -> str: # In case the text is None, set it to an empty string elif isinstance(content, str): return content - elif isinstance(content, dict): + elif isinstance(content, (dict, list)): return json.dumps(content) if content else "" else: return str(content) if content is not None else "" @@ -203,7 +201,13 @@ def _get_metadata( :return: """ if self._metadata_func is not None: - return self._metadata_func(sample, additional_fields) + result = self._metadata_func(sample, additional_fields) + if not isinstance(result, dict): + raise ValueError( + f"Expected the metadata_func to return a dict but got \ + `{type(result)}`" + ) + return result else: return additional_fields @@ -233,15 +237,3 @@ def _validate_content_key(self, data: Any) -> None: f"Expected the jq schema to result in a list of objects (dict) \ with the key `{self._content_key}` which should be parsable by jq" ) - - def _validate_metadata_func(self, data: Any) -> None: - """Check if the metadata_func output is valid""" - - sample = data.first() - if self._metadata_func is not None: - sample_metadata = self._metadata_func(sample, {}) - if not isinstance(sample_metadata, dict): - raise ValueError( - f"Expected the metadata_func to return a dict but got \ - `{type(sample_metadata)}`" - ) diff --git a/libs/community/langchain_community/document_loaders/parsers/doc_intelligence.py b/libs/community/langchain_community/document_loaders/parsers/doc_intelligence.py index 2d77fcd1f87a7..107e569339fb5 100644 --- a/libs/community/langchain_community/document_loaders/parsers/doc_intelligence.py +++ b/libs/community/langchain_community/document_loaders/parsers/doc_intelligence.py @@ -71,7 +71,7 @@ def _generate_docs_page(self, result: Any) -> Iterator[Document]: yield d def _generate_docs_single(self, result: Any) -> Iterator[Document]: - yield Document(page_content=result.content, metadata={}) + yield Document(page_content=result.content, metadata=result.as_dict()) def lazy_parse(self, blob: Blob) -> Iterator[Document]: """Lazily parse the blob.""" diff --git a/libs/community/langchain_community/document_loaders/parsers/pdf.py b/libs/community/langchain_community/document_loaders/parsers/pdf.py index ef9ca7561abd6..c603dde71eb32 100644 --- a/libs/community/langchain_community/document_loaders/parsers/pdf.py +++ b/libs/community/langchain_community/document_loaders/parsers/pdf.py @@ -236,17 +236,39 @@ def get_image(layout_object: Any) -> Any: images = [] - for img in list(filter(bool, map(get_image, page))): - if img.stream["Filter"].name in _PDF_FILTER_WITHOUT_LOSS: + for img in filter(bool, map(get_image, page)): + img_filter = img.stream["Filter"] + if isinstance(img_filter, list): + filter_names = [f.name for f in img_filter] + else: + filter_names = [img_filter.name] + + without_loss = any( + name in _PDF_FILTER_WITHOUT_LOSS for name in filter_names + ) + with_loss = any(name in _PDF_FILTER_WITH_LOSS for name in filter_names) + non_matching = {name for name in filter_names} - { + *_PDF_FILTER_WITHOUT_LOSS, + *_PDF_FILTER_WITH_LOSS, + } + + if without_loss and with_loss: + warnings.warn( + "Image has both lossy and lossless filters. Defaulting to lossless" + ) + + if non_matching: + warnings.warn(f"Unknown PDF Filter(s): {non_matching}") + + if without_loss: images.append( np.frombuffer(img.stream.get_data(), dtype=np.uint8).reshape( img.stream["Height"], img.stream["Width"], -1 ) ) - elif img.stream["Filter"].name in _PDF_FILTER_WITH_LOSS: + elif with_loss: images.append(img.stream.get_data()) - else: - warnings.warn("Unknown PDF Filter!") + return extract_from_images_with_rapidocr(images) diff --git a/libs/community/langchain_community/document_loaders/web_base.py b/libs/community/langchain_community/document_loaders/web_base.py index 3eab7f351a113..26bc5504d5879 100644 --- a/libs/community/langchain_community/document_loaders/web_base.py +++ b/libs/community/langchain_community/document_loaders/web_base.py @@ -208,7 +208,10 @@ async def _fetch( ) if not self.session.verify: kwargs["ssl"] = False - async with session.get(url, **kwargs) as response: + + async with session.get( + url, **(self.requests_kwargs | kwargs) + ) as response: if self.raise_for_status: response.raise_for_status() return await response.text() diff --git a/libs/community/langchain_community/document_transformers/markdownify.py b/libs/community/langchain_community/document_transformers/markdownify.py index cb8108deeaaaa..91c580e591de5 100644 --- a/libs/community/langchain_community/document_transformers/markdownify.py +++ b/libs/community/langchain_community/document_transformers/markdownify.py @@ -74,10 +74,3 @@ def transform_documents( ) return converted_documents - - async def atransform_documents( - self, - documents: Sequence[Document], - **kwargs: Any, - ) -> Sequence[Document]: - raise NotImplementedError diff --git a/libs/community/langchain_community/embeddings/__init__.py b/libs/community/langchain_community/embeddings/__init__.py index 38c7d5a76bc1d..05a503ca299ac 100644 --- a/libs/community/langchain_community/embeddings/__init__.py +++ b/libs/community/langchain_community/embeddings/__init__.py @@ -145,6 +145,9 @@ from langchain_community.embeddings.mlflow_gateway import ( MlflowAIGatewayEmbeddings, ) + from langchain_community.embeddings.model2vec import ( + Model2vecEmbeddings, + ) from langchain_community.embeddings.modelscope_hub import ( ModelScopeEmbeddings, ) @@ -289,6 +292,7 @@ "MlflowAIGatewayEmbeddings", "MlflowCohereEmbeddings", "MlflowEmbeddings", + "Model2vecEmbeddings", "ModelScopeEmbeddings", "MosaicMLInstructorEmbeddings", "NLPCloudEmbeddings", @@ -372,6 +376,7 @@ "MlflowAIGatewayEmbeddings": "langchain_community.embeddings.mlflow_gateway", "MlflowCohereEmbeddings": "langchain_community.embeddings.mlflow", "MlflowEmbeddings": "langchain_community.embeddings.mlflow", + "Model2vecEmbeddings": "langchain_community.embeddings.model2vec", "ModelScopeEmbeddings": "langchain_community.embeddings.modelscope_hub", "MosaicMLInstructorEmbeddings": "langchain_community.embeddings.mosaicml", "NLPCloudEmbeddings": "langchain_community.embeddings.nlpcloud", diff --git a/libs/community/langchain_community/embeddings/fastembed.py b/libs/community/langchain_community/embeddings/fastembed.py index 28105dd72088b..e40a9d473ddc0 100644 --- a/libs/community/langchain_community/embeddings/fastembed.py +++ b/libs/community/langchain_community/embeddings/fastembed.py @@ -1,6 +1,6 @@ import importlib import importlib.metadata -from typing import Any, Dict, List, Literal, Optional +from typing import Any, Dict, List, Literal, Optional, cast import numpy as np from langchain_core.embeddings import Embeddings @@ -117,7 +117,7 @@ def embed_documents(self, texts: List[str]) -> List[List[float]]: embeddings = self.model.embed( texts, batch_size=self.batch_size, parallel=self.parallel ) - return [e.tolist() for e in embeddings] + return [cast(List[float], e.tolist()) for e in embeddings] def embed_query(self, text: str) -> List[float]: """Generate query embeddings using FastEmbed. @@ -133,4 +133,4 @@ def embed_query(self, text: str) -> List[float]: text, batch_size=self.batch_size, parallel=self.parallel ) ) - return query_embeddings.tolist() + return cast(List[float], query_embeddings.tolist()) diff --git a/libs/community/langchain_community/embeddings/laser.py b/libs/community/langchain_community/embeddings/laser.py index 1299e55e518c0..088ffbb4d1bd4 100644 --- a/libs/community/langchain_community/embeddings/laser.py +++ b/libs/community/langchain_community/embeddings/laser.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, List, Optional +from typing import Any, Dict, List, Optional, cast import numpy as np from langchain_core.embeddings import Embeddings @@ -73,7 +73,7 @@ def embed_documents(self, texts: List[str]) -> List[List[float]]: embeddings: np.ndarray embeddings = self._encoder_pipeline.encode_sentences(texts) - return embeddings.tolist() + return cast(List[List[float]], embeddings.tolist()) def embed_query(self, text: str) -> List[float]: """Generate single query text embeddings using LASER. @@ -86,4 +86,4 @@ def embed_query(self, text: str) -> List[float]: """ query_embeddings: np.ndarray query_embeddings = self._encoder_pipeline.encode_sentences([text]) - return query_embeddings.tolist()[0] + return cast(List[List[float]], query_embeddings.tolist())[0] diff --git a/libs/community/langchain_community/embeddings/model2vec.py b/libs/community/langchain_community/embeddings/model2vec.py new file mode 100644 index 0000000000000..223f611b0ed01 --- /dev/null +++ b/libs/community/langchain_community/embeddings/model2vec.py @@ -0,0 +1,66 @@ +"""Wrapper around model2vec embedding models.""" + +from typing import List + +from langchain_core.embeddings import Embeddings + + +class Model2vecEmbeddings(Embeddings): + """Model2Vec embedding models. + + Install model2vec first, run 'pip install -U model2vec'. + The github repository for model2vec is : https://github.com/MinishLab/model2vec + + Example: + .. code-block:: python + + from langchain_community.embeddings import Model2vecEmbeddings + + embedding = Model2vecEmbeddings("minishlab/potion-base-8M") + embedding.embed_documents([ + "It's dangerous to go alone!", + "It's a secret to everybody.", + ]) + embedding.embed_query( + "Take this with you." + ) + """ + + def __init__(self, model: str): + """Initialize embeddings. + + Args: + model: Model name. + """ + try: + from model2vec import StaticModel + except ImportError as e: + raise ImportError( + "Unable to import model2vec, please install with " + "`pip install -U model2vec`." + ) from e + self._model = StaticModel.from_pretrained(model) + + def embed_documents(self, texts: List[str]) -> List[List[float]]: + """Embed documents using the model2vec embeddings model. + + Args: + texts: The list of texts to embed. + + Returns: + List of embeddings, one for each text. + """ + + return self._model.encode(texts).tolist() + + def embed_query(self, text: str) -> List[float]: + """Embed a query using the model2vec embeddings model. + + Args: + text: The text to embed. + + Returns: + Embeddings for the text. + """ + + return self._model.encode(text).tolist() diff --git a/libs/community/langchain_community/graph_vectorstores/visualize.py b/libs/community/langchain_community/graph_vectorstores/visualize.py new file mode 100644 index 0000000000000..7dd0f1dcfcf20 --- /dev/null +++ b/libs/community/langchain_community/graph_vectorstores/visualize.py @@ -0,0 +1,122 @@ +import re +from typing import TYPE_CHECKING, Dict, Iterable, Optional, Tuple + +from langchain_core._api import beta +from langchain_core.documents import Document + +from langchain_community.graph_vectorstores.links import get_links + +if TYPE_CHECKING: + import graphviz + + +def _escape_id(id: str) -> str: + return id.replace(":", "_") + + +_EDGE_DIRECTION = { + "in": "back", + "out": "forward", + "bidir": "both", +} + +_WORD_RE = re.compile("\s*\S+") + + +def _split_prefix(s: str, max_chars: int = 50) -> str: + words = _WORD_RE.finditer(s) + + split = min(len(s), max_chars) + for word in words: + if word.end(0) > max_chars: + break + split = word.end(0) + + if split == len(s): + return s + else: + return f"{s[0:split]}..." + + +@beta() +def render_graphviz( + documents: Iterable[Document], + engine: Optional[str] = None, + node_color: Optional[str] = None, + node_colors: Optional[Dict[str, Optional[str]]] = None, + skip_tags: Iterable[Tuple[str, str]] = (), +) -> "graphviz.Digraph": + """Render a collection of GraphVectorStore documents to GraphViz format. + + Args: + documents: The documents to render. + engine: GraphViz layout engine to use. `None` uses the default. + node_color: Default node color. + node_colors: Dictionary specifying colors of specific nodes. Useful for + emphasizing nodes that were selected by MMR, or differ from other + results. + skip_tags: Set of tags to skip when rendering the graph. Specified as + tuples containing the kind and tag. + + Returns: + The "graphviz.Digraph" representing the nodes. May be printed to source, + or rendered using `dot`. + + Note: + To render the generated DOT source code, you also need to install Graphviz_ + (`download page `_, + `archived versions `_, + `installation procedure for Windows `_). + """ + if node_colors is None: + node_colors = {} + + try: + import graphviz + except (ImportError, ModuleNotFoundError): + raise ImportError( + "Could not import graphviz python package. " + "Please install it with `pip install graphviz`." + ) + + graph = graphviz.Digraph(engine=engine) + graph.attr(rankdir="LR") + graph.attr("node", style="filled") + + skip_tags = set(skip_tags) + tags: dict[Tuple[str, str], str] = {} + + for document in documents: + id = document.id + if id is None: + raise ValueError(f"Illegal graph document without ID: {document}") + escaped_id = _escape_id(id) + color = node_colors[id] if id in node_colors else node_color + + node_label = "\n".join( + [ + graphviz.escape(id), + graphviz.escape(_split_prefix(document.page_content)), + ] + ) + graph.node( + escaped_id, + label=node_label, + shape="note", + fillcolor=color, + tooltip=graphviz.escape(document.page_content), + ) + + for link in get_links(document): + tag_key = (link.kind, link.tag) + if tag_key in skip_tags: + continue + + tag_id = tags.get(tag_key) + if tag_id is None: + tag_id = f"tag_{len(tags)}" + tags[tag_key] = tag_id + graph.node(tag_id, label=graphviz.escape(f"{link.kind}:{link.tag}")) + + graph.edge(escaped_id, tag_id, dir=_EDGE_DIRECTION[link.direction]) + return graph diff --git a/libs/community/langchain_community/graphs/kuzu_graph.py b/libs/community/langchain_community/graphs/kuzu_graph.py index 1f99f49fc9435..3fe3d60c283c2 100644 --- a/libs/community/langchain_community/graphs/kuzu_graph.py +++ b/libs/community/langchain_community/graphs/kuzu_graph.py @@ -1,4 +1,7 @@ -from typing import Any, Dict, List +from hashlib import md5 +from typing import Any, Dict, List, Tuple + +from langchain_community.graphs.graph_document import GraphDocument, Relationship class KuzuGraph: @@ -16,7 +19,19 @@ class KuzuGraph: See https://python.langchain.com/docs/security for more information. """ - def __init__(self, db: Any, database: str = "kuzu") -> None: + def __init__( + self, db: Any, database: str = "kuzu", allow_dangerous_requests: bool = False + ) -> None: + """Initializes the Kùzu graph database connection.""" + + if allow_dangerous_requests is not True: + raise ValueError( + "The KuzuGraph class is a powerful tool that can be used to execute " + "arbitrary queries on the database. To enable this functionality, " + "set the `allow_dangerous_requests` parameter to `True` when " + "constructing the KuzuGraph object." + ) + try: import kuzu except ImportError: @@ -57,7 +72,7 @@ def refresh_schema(self) -> None: if properties[property_name]["dimension"] > 0: if "shape" in properties[property_name]: for s in properties[property_name]["shape"]: - list_type_flag += "[%s]" % s + list_type_flag += f"[{s}]" else: for i in range(properties[property_name]["dimension"]): list_type_flag += "[]" @@ -71,7 +86,7 @@ def refresh_schema(self) -> None: rel_tables = self.conn._get_rel_table_names() for table in rel_tables: relationships.append( - "(:%s)-[:%s]->(:%s)" % (table["src"], table["name"], table["dst"]) + f"(:{table['src']})-[:{table['name']}]->(:{table['dst']})" ) rel_properties = [] @@ -93,3 +108,154 @@ def refresh_schema(self) -> None: f"Relationships properties: {rel_properties}\n" f"Relationships: {relationships}\n" ) + + def _create_chunk_node_table(self) -> None: + self.conn.execute( + """ + CREATE NODE TABLE IF NOT EXISTS Chunk ( + id STRING, + text STRING, + type STRING, + PRIMARY KEY(id) + ); + """ + ) + + def _create_entity_node_table(self, node_label: str) -> None: + self.conn.execute( + f""" + CREATE NODE TABLE IF NOT EXISTS {node_label} ( + id STRING, + type STRING, + PRIMARY KEY(id) + ); + """ + ) + + def _create_entity_relationship_table(self, rel: Relationship) -> None: + self.conn.execute( + f""" + CREATE REL TABLE IF NOT EXISTS {rel.type} ( + FROM {rel.source.type} TO {rel.target.type} + ); + """ + ) + + def add_graph_documents( + self, + graph_documents: List[GraphDocument], + allowed_relationships: List[Tuple[str, str, str]], + include_source: bool = False, + ) -> None: + """ + Adds a list of `GraphDocument` objects that represent nodes and relationships + in a graph to a Kùzu backend. + + Parameters: + - graph_documents (List[GraphDocument]): A list of `GraphDocument` objects + that contain the nodes and relationships to be added to the graph. Each + `GraphDocument` should encapsulate the structure of part of the graph, + including nodes, relationships, and the source document information. + + - allowed_relationships (List[Tuple[str, str, str]]): A list of allowed + relationships that exist in the graph. Each tuple contains three elements: + the source node type, the relationship type, and the target node type. + Required for Kùzu, as the names of the relationship tables that need to + pre-exist are derived from these tuples. + + - include_source (bool): If True, stores the source document + and links it to nodes in the graph using the `MENTIONS` relationship. + This is useful for tracing back the origin of data. Merges source + documents based on the `id` property from the source document metadata + if available; otherwise it calculates the MD5 hash of `page_content` + for merging process. Defaults to False. + """ + # Get unique node labels in the graph documents + node_labels = list( + {node.type for document in graph_documents for node in document.nodes} + ) + + for document in graph_documents: + # Add chunk nodes and create source document relationships if include_source + # is True + if include_source: + self._create_chunk_node_table() + if not document.source.metadata.get("id"): + # Add a unique id to each document chunk via an md5 hash + document.source.metadata["id"] = md5( + document.source.page_content.encode("utf-8") + ).hexdigest() + + self.conn.execute( + f""" + MERGE (c:Chunk {{id: $id}}) + SET c.text = $text, + c.type = "text_chunk" + """, # noqa: F541 + parameters={ + "id": document.source.metadata["id"], + "text": document.source.page_content, + }, + ) + + for node_label in node_labels: + self._create_entity_node_table(node_label) + + # Add entity nodes from data + for node in document.nodes: + self.conn.execute( + f""" + MERGE (e:{node.type} {{id: $id}}) + SET e.type = "entity" + """, + parameters={"id": node.id}, + ) + if include_source: + # If include_source is True, we need to create a relationship table + # between the chunk nodes and the entity nodes + self._create_chunk_node_table() + ddl = "CREATE REL TABLE GROUP IF NOT EXISTS MENTIONS (" + table_names = [] + for node_label in node_labels: + table_names.append(f"FROM Chunk TO {node_label}") + table_names = list(set(table_names)) + ddl += ", ".join(table_names) + # Add common properties for all the tables here + ddl += ", label STRING, triplet_source_id STRING)" + if ddl: + self.conn.execute(ddl) + + # Only allow relationships that exist in the schema + if node.type in node_labels: + self.conn.execute( + f""" + MATCH (c:Chunk {{id: $id}}), + (e:{node.type} {{id: $node_id}}) + MERGE (c)-[m:MENTIONS]->(e) + SET m.triplet_source_id = $id + """, + parameters={ + "id": document.source.metadata["id"], + "node_id": node.id, + }, + ) + + # Add entity relationships + for rel in document.relationships: + self._create_entity_relationship_table(rel) + # Create relationship + source_label = rel.source.type + source_id = rel.source.id + target_label = rel.target.type + target_id = rel.target.id + self.conn.execute( + f""" + MATCH (e1:{source_label} {{id: $source_id}}), + (e2:{target_label} {{id: $target_id}}) + MERGE (e1)-[:{rel.type}]->(e2) + """, + parameters={ + "source_id": source_id, + "target_id": target_id, + }, + ) diff --git a/libs/community/langchain_community/graphs/memgraph_graph.py b/libs/community/langchain_community/graphs/memgraph_graph.py index 34e9f7145bb22..fa829a0e5db81 100644 --- a/libs/community/langchain_community/graphs/memgraph_graph.py +++ b/libs/community/langchain_community/graphs/memgraph_graph.py @@ -1,15 +1,272 @@ -from langchain_community.graphs.neo4j_graph import Neo4jGraph +import logging +from hashlib import md5 +from typing import Any, Dict, List, Optional + +from langchain_core.utils import get_from_dict_or_env + +from langchain_community.graphs.graph_document import GraphDocument, Node, Relationship +from langchain_community.graphs.graph_store import GraphStore + +logger = logging.getLogger(__name__) + + +BASE_ENTITY_LABEL = "__Entity__" SCHEMA_QUERY = """ -CALL llm_util.schema("raw") -YIELD * -RETURN * +SHOW SCHEMA INFO +""" + +NODE_PROPERTIES_QUERY = """ +CALL schema.node_type_properties() +YIELD nodeType AS label, propertyName AS property, propertyTypes AS type +WITH label AS nodeLabels, collect({key: property, types: type}) AS properties +RETURN {labels: nodeLabels, properties: properties} AS output +""" + +REL_QUERY = """ +MATCH (n)-[e]->(m) +WITH DISTINCT + labels(n) AS start_node_labels, + type(e) AS rel_type, + labels(m) AS end_node_labels, + e, + keys(e) AS properties +UNWIND CASE WHEN size(properties) > 0 THEN properties ELSE [null] END AS prop +WITH + start_node_labels, + rel_type, + end_node_labels, + CASE WHEN prop IS NULL THEN [] ELSE [prop, valueType(e[prop])] END AS property_info +RETURN + start_node_labels, + rel_type, + end_node_labels, + COLLECT(DISTINCT CASE + WHEN property_info <> [] + THEN property_info + ELSE null END) AS properties_info +""" + +NODE_IMPORT_QUERY = """ +UNWIND $data AS row +CALL merge.node(row.label, row.properties, {}, {}) +YIELD node +RETURN distinct 'done' AS result +""" + +REL_NODES_IMPORT_QUERY = """ +UNWIND $data AS row +MERGE (source {id: row.source_id}) +MERGE (target {id: row.target_id}) +RETURN distinct 'done' AS result +""" + +REL_IMPORT_QUERY = """ +UNWIND $data AS row +MATCH (source {id: row.source_id}) +MATCH (target {id: row.target_id}) +WITH source, target, row +CALL merge.relationship(source, row.type, {}, {}, target, {}) +YIELD rel +RETURN distinct 'done' AS result +""" + +INCLUDE_DOCS_QUERY = """ +MERGE (d:Document {id:$document.metadata.id}) +SET d.content = $document.page_content +SET d += $document.metadata +RETURN distinct 'done' AS result +""" + +INCLUDE_DOCS_SOURCE_QUERY = """ +UNWIND $data AS row +MATCH (source {id: row.source_id}), (d:Document {id: $document.metadata.id}) +MERGE (d)-[:MENTIONS]->(source) +RETURN distinct 'done' AS result +""" + +NODE_PROPS_TEXT = """ +Node labels and properties (name and type) are: """ +REL_PROPS_TEXT = """ +Relationship labels and properties are: +""" + +REL_TEXT = """ +Nodes are connected with the following relationships: +""" + + +def get_schema_subset(data: Dict[str, Any]) -> Dict[str, Any]: + return { + "edges": [ + { + "end_node_labels": edge["end_node_labels"], + "properties": [ + { + "key": prop["key"], + "types": [ + {"type": type_item["type"].lower()} + for type_item in prop["types"] + ], + } + for prop in edge["properties"] + ], + "start_node_labels": edge["start_node_labels"], + "type": edge["type"], + } + for edge in data["edges"] + ], + "nodes": [ + { + "labels": node["labels"], + "properties": [ + { + "key": prop["key"], + "types": [ + {"type": type_item["type"].lower()} + for type_item in prop["types"] + ], + } + for prop in node["properties"] + ], + } + for node in data["nodes"] + ], + } + + +def get_reformated_schema( + nodes: List[Dict[str, Any]], rels: List[Dict[str, Any]] +) -> Dict[str, Any]: + return { + "edges": [ + { + "end_node_labels": rel["end_node_labels"], + "properties": [ + {"key": prop[0], "types": [{"type": prop[1].lower()}]} + for prop in rel["properties_info"] + ], + "start_node_labels": rel["start_node_labels"], + "type": rel["rel_type"], + } + for rel in rels + ], + "nodes": [ + { + "labels": [_remove_backticks(node["labels"])[1:]], + "properties": [ + { + "key": prop["key"], + "types": [ + {"type": type_item.lower()} for type_item in prop["types"] + ], + } + for prop in node["properties"] + if node["properties"][0]["key"] != "" + ], + } + for node in nodes + ], + } + + +def transform_schema_to_text(schema: Dict[str, Any]) -> str: + node_props_data = "" + rel_props_data = "" + rel_data = "" + + for node in schema["nodes"]: + node_props_data += f"- labels: (:{':'.join(node['labels'])})\n" + if node["properties"] == []: + continue + node_props_data += " properties:\n" + for prop in node["properties"]: + prop_types_str = " or ".join( + {prop_types["type"] for prop_types in prop["types"]} + ) + node_props_data += f" - {prop['key']}: {prop_types_str}\n" + + for rel in schema["edges"]: + rel_type = rel["type"] + start_labels = ":".join(rel["start_node_labels"]) + end_labels = ":".join(rel["end_node_labels"]) + rel_data += f"(:{start_labels})-[:{rel_type}]->(:{end_labels})\n" + + if rel["properties"] == []: + continue + + rel_props_data += f"- labels: {rel_type}\n properties:\n" + for prop in rel["properties"]: + prop_types_str = " or ".join( + {prop_types["type"].lower() for prop_types in prop["types"]} + ) + rel_props_data += f" - {prop['key']}: {prop_types_str}\n" + + return "".join( + [ + NODE_PROPS_TEXT + node_props_data if node_props_data else "", + REL_PROPS_TEXT + rel_props_data if rel_props_data else "", + REL_TEXT + rel_data if rel_data else "", + ] + ) + + +def _remove_backticks(text: str) -> str: + return text.replace("`", "") + + +def _transform_nodes(nodes: list[Node], baseEntityLabel: bool) -> List[dict]: + transformed_nodes = [] + for node in nodes: + properties_dict = node.properties | {"id": node.id} + label = ( + [_remove_backticks(node.type), BASE_ENTITY_LABEL] + if baseEntityLabel + else [_remove_backticks(node.type)] + ) + node_dict = {"label": label, "properties": properties_dict} + transformed_nodes.append(node_dict) + return transformed_nodes + + +def _transform_relationships( + relationships: list[Relationship], baseEntityLabel: bool +) -> List[dict]: + transformed_relationships = [] + for rel in relationships: + rel_dict = { + "type": _remove_backticks(rel.type), + "source_label": ( + [BASE_ENTITY_LABEL] + if baseEntityLabel + else [_remove_backticks(rel.source.type)] + ), + "source_id": rel.source.id, + "target_label": ( + [BASE_ENTITY_LABEL] + if baseEntityLabel + else [_remove_backticks(rel.target.type)] + ), + "target_id": rel.target.id, + } + transformed_relationships.append(rel_dict) + return transformed_relationships -class MemgraphGraph(Neo4jGraph): + +class MemgraphGraph(GraphStore): """Memgraph wrapper for graph operations. + Parameters: + url (Optional[str]): The URL of the Memgraph database server. + username (Optional[str]): The username for database authentication. + password (Optional[str]): The password for database authentication. + database (str): The name of the database to connect to. Default is 'memgraph'. + refresh_schema (bool): A flag whether to refresh schema information + at initialization. Default is True. + driver_config (Dict): Configuration passed to Neo4j Driver. + *Security note*: Make sure that the database connection uses credentials that are narrowly-scoped to only include necessary permissions. Failure to do so may result in data corruption or loss, since the calling @@ -23,49 +280,247 @@ class MemgraphGraph(Neo4jGraph): """ def __init__( - self, url: str, username: str, password: str, *, database: str = "memgraph" + self, + url: Optional[str] = None, + username: Optional[str] = None, + password: Optional[str] = None, + database: Optional[str] = None, + refresh_schema: bool = True, + *, + driver_config: Optional[Dict] = None, ) -> None: """Create a new Memgraph graph wrapper instance.""" - super().__init__(url, username, password, database=database) + try: + import neo4j + except ImportError: + raise ImportError( + "Could not import neo4j python package. " + "Please install it with `pip install neo4j`." + ) + + url = get_from_dict_or_env({"url": url}, "url", "MEMGRAPH_URI") + + # if username and password are "", assume auth is disabled + if username == "" and password == "": + auth = None + else: + username = get_from_dict_or_env( + {"username": username}, + "username", + "MEMGRAPH_USERNAME", + ) + password = get_from_dict_or_env( + {"password": password}, + "password", + "MEMGRAPH_PASSWORD", + ) + auth = (username, password) + database = get_from_dict_or_env( + {"database": database}, "database", "MEMGRAPH_DATABASE", "memgraph" + ) + + self._driver = neo4j.GraphDatabase.driver( + url, auth=auth, **(driver_config or {}) + ) + + self._database = database + self.schema: str = "" + self.structured_schema: Dict[str, Any] = {} + + # Verify connection + try: + self._driver.verify_connectivity() + except neo4j.exceptions.ServiceUnavailable: + raise ValueError( + "Could not connect to Memgraph database. " + "Please ensure that the url is correct" + ) + except neo4j.exceptions.AuthError: + raise ValueError( + "Could not connect to Memgraph database. " + "Please ensure that the username and password are correct" + ) + + # Set schema + if refresh_schema: + try: + self.refresh_schema() + except neo4j.exceptions.ClientError as e: + raise e + + def close(self) -> None: + if self._driver: + logger.info("Closing the driver connection.") + self._driver.close() + self._driver = None + + @property + def get_schema(self) -> str: + """Returns the schema of the Graph database""" + return self.schema + + @property + def get_structured_schema(self) -> Dict[str, Any]: + """Returns the structured schema of the Graph database""" + return self.structured_schema + + def query(self, query: str, params: dict = {}) -> List[Dict[str, Any]]: + """Query the graph. + + Args: + query (str): The Cypher query to execute. + params (dict): The parameters to pass to the query. + + Returns: + List[Dict[str, Any]]: The list of dictionaries containing the query results. + """ + from neo4j.exceptions import Neo4jError + + try: + data, _, _ = self._driver.execute_query( + query, + database_=self._database, + parameters_=params, + ) + json_data = [r.data() for r in data] + return json_data + except Neo4jError as e: + if not ( + ( + ( # isCallInTransactionError + e.code == "Neo.DatabaseError.Statement.ExecutionFailed" + or e.code + == "Neo.DatabaseError.Transaction.TransactionStartFailed" + ) + and "in an implicit transaction" in e.message + ) + or ( # isPeriodicCommitError + e.code == "Neo.ClientError.Statement.SemanticError" + and ( + "in an open transaction is not possible" in e.message + or "tried to execute in an explicit transaction" in e.message + ) + ) + or ( + e.code == "Memgraph.ClientError.MemgraphError.MemgraphError" + and ("in multicommand transactions" in e.message) + ) + or ( + e.code == "Memgraph.ClientError.MemgraphError.MemgraphError" + and "SchemaInfo disabled" in e.message + ) + ): + raise + + # fallback to allow implicit transactions + with self._driver.session(database=self._database) as session: + data = session.run(query, params) + json_data = [r.data() for r in data] + return json_data def refresh_schema(self) -> None: """ Refreshes the Memgraph graph schema information. """ + import ast + + from neo4j.exceptions import Neo4jError + + # leave schema empty if db is empty + if self.query("MATCH (n) RETURN n LIMIT 1") == []: + return - db_structured_schema = self.query(SCHEMA_QUERY)[0].get("schema") - assert db_structured_schema is not None - self.structured_schema = db_structured_schema + # first try with SHOW SCHEMA INFO + try: + result = self.query(SCHEMA_QUERY)[0].get("schema") + if result is not None and isinstance(result, (str, ast.AST)): + schema_result = ast.literal_eval(result) + else: + schema_result = result + assert schema_result is not None + structured_schema = get_schema_subset(schema_result) + self.structured_schema = structured_schema + self.schema = transform_schema_to_text(structured_schema) + return + except Neo4jError as e: + if ( + e.code == "Memgraph.ClientError.MemgraphError.MemgraphError" + and "SchemaInfo disabled" in e.message + ): + logger.info( + "Schema generation with SHOW SCHEMA INFO query failed. " + "Set --schema-info-enabled=true to use SHOW SCHEMA INFO query. " + "Falling back to alternative queries." + ) - # Format node properties - formatted_node_props = [] + # fallback on Cypher without SHOW SCHEMA INFO + nodes = [query["output"] for query in self.query(NODE_PROPERTIES_QUERY)] + rels = self.query(REL_QUERY) - for node_name, properties in db_structured_schema["node_props"].items(): - formatted_node_props.append( - f"Node name: '{node_name}', Node properties: {properties}" + structured_schema = get_reformated_schema(nodes, rels) + self.structured_schema = structured_schema + self.schema = transform_schema_to_text(structured_schema) + + def add_graph_documents( + self, + graph_documents: List[GraphDocument], + include_source: bool = False, + baseEntityLabel: bool = False, + ) -> None: + """ + Take GraphDocument as input as uses it to construct a graph in Memgraph. + + Parameters: + - graph_documents (List[GraphDocument]): A list of GraphDocument objects + that contain the nodes and relationships to be added to the graph. Each + GraphDocument should encapsulate the structure of part of the graph, + including nodes, relationships, and the source document information. + - include_source (bool, optional): If True, stores the source document + and links it to nodes in the graph using the MENTIONS relationship. + This is useful for tracing back the origin of data. Merges source + documents based on the `id` property from the source document metadata + if available; otherwise it calculates the MD5 hash of `page_content` + for merging process. Defaults to False. + - baseEntityLabel (bool, optional): If True, each newly created node + gets a secondary __Entity__ label, which is indexed and improves import + speed and performance. Defaults to False. + """ + + if baseEntityLabel: + self.query( + f"CREATE CONSTRAINT ON (b:{BASE_ENTITY_LABEL}) " + "ASSERT b.id IS UNIQUE;" ) + self.query(f"CREATE INDEX ON :{BASE_ENTITY_LABEL}(id);") + self.query(f"CREATE INDEX ON :{BASE_ENTITY_LABEL};") + + for document in graph_documents: + if include_source: + if not document.source.metadata.get("id"): + document.source.metadata["id"] = md5( + document.source.page_content.encode("utf-8") + ).hexdigest() - # Format relationship properties - formatted_rel_props = [] - for rel_name, properties in db_structured_schema["rel_props"].items(): - formatted_rel_props.append( - f"Relationship name: '{rel_name}', " - f"Relationship properties: {properties}" + self.query(INCLUDE_DOCS_QUERY, {"document": document.source.__dict__}) + + self.query( + NODE_IMPORT_QUERY, + {"data": _transform_nodes(document.nodes, baseEntityLabel)}, ) - # Format relationships - formatted_rels = [ - f"(:{rel['start']})-[:{rel['type']}]->(:{rel['end']})" - for rel in db_structured_schema["relationships"] - ] + rel_data = _transform_relationships(document.relationships, baseEntityLabel) + self.query( + REL_NODES_IMPORT_QUERY, + {"data": rel_data}, + ) + self.query( + REL_IMPORT_QUERY, + {"data": rel_data}, + ) - self.schema = "\n".join( - [ - "Node properties are the following:", - *formatted_node_props, - "Relationship properties are the following:", - *formatted_rel_props, - "The relationships are the following:", - *formatted_rels, - ] - ) + if include_source: + self.query( + INCLUDE_DOCS_SOURCE_QUERY, + {"data": rel_data, "document": document.source.__dict__}, + ) + self.refresh_schema() diff --git a/libs/community/langchain_community/tools/__init__.py b/libs/community/langchain_community/tools/__init__.py index 5b4677f53dc2e..de486cfbb3f87 100644 --- a/libs/community/langchain_community/tools/__init__.py +++ b/libs/community/langchain_community/tools/__init__.py @@ -302,6 +302,7 @@ ListSQLDatabaseTool, QuerySQLCheckerTool, QuerySQLDataBaseTool, + QuerySQLDatabaseTool, ) from langchain_community.tools.stackexchange.tool import ( StackExchangeTool, @@ -453,7 +454,8 @@ "QueryCheckerTool", "QueryPowerBITool", "QuerySQLCheckerTool", - "QuerySQLDataBaseTool", + "QuerySQLDatabaseTool", + "QuerySQLDataBaseTool", # Legacy, kept for backwards compatibility. "QuerySparkSQLTool", "ReadFileTool", "RedditSearchRun", @@ -606,6 +608,8 @@ "QueryCheckerTool": "langchain_community.tools.spark_sql.tool", "QueryPowerBITool": "langchain_community.tools.powerbi.tool", "QuerySQLCheckerTool": "langchain_community.tools.sql_database.tool", + "QuerySQLDatabaseTool": "langchain_community.tools.sql_database.tool", + # Legacy, kept for backwards compatibility. "QuerySQLDataBaseTool": "langchain_community.tools.sql_database.tool", "QuerySparkSQLTool": "langchain_community.tools.spark_sql.tool", "ReadFileTool": "langchain_community.tools.file_management", diff --git a/libs/community/langchain_community/tools/gitlab/prompt.py b/libs/community/langchain_community/tools/gitlab/prompt.py index 3f303155cd427..e8a33ccb57edc 100644 --- a/libs/community/langchain_community/tools/gitlab/prompt.py +++ b/libs/community/langchain_community/tools/gitlab/prompt.py @@ -68,3 +68,27 @@ DELETE_FILE_PROMPT = """ This tool is a wrapper for the GitLab API, useful when you need to delete a file in a GitLab repository. Simply pass in the full file path of the file you would like to delete. **IMPORTANT**: the path must not start with a slash """ + +GET_REPO_FILES_IN_MAIN = """ +This tool will provide an overview of all existing files in the main branch of the GitLab repository repository. It will list the file names. No input parameters are required. +""" + +GET_REPO_FILES_IN_BOT_BRANCH = """ +This tool will provide an overview of all files in your current working branch where you should implement changes. No input parameters are required. +""" + +GET_REPO_FILES_FROM_DIRECTORY = """ +This tool will provide an overview of all files in your current working branch from a specific directory. **VERY IMPORTANT**: You must specify the path of the directory as a string input parameter. +""" + +LIST_REPO_BRANCES = """ +This tool is a wrapper for the GitLab API, useful when you need to read the branches names in a GitLab repository. No input parameters are required. +""" + +CREATE_REPO_BRANCH = """ +This tool will create a new branch in the repository. **VERY IMPORTANT**: You must specify the name of the new branch as a string input parameter. +""" + +SET_ACTIVE_BRANCH = """ +This tool will set the active branch in the repository, similar to `git checkout ` and `git switch -c `. **VERY IMPORTANT**: You must specify the name of the branch as a string input parameter. +""" diff --git a/libs/community/langchain_community/tools/gmail/utils.py b/libs/community/langchain_community/tools/gmail/utils.py index e8bb4d468b3f3..01a6b7744868a 100644 --- a/libs/community/langchain_community/tools/gmail/utils.py +++ b/libs/community/langchain_community/tools/gmail/utils.py @@ -89,7 +89,7 @@ def get_gmail_credentials( flow = InstalledAppFlow.from_client_secrets_file( client_secrets_file, scopes ) - creds = flow.run_local_server(port=0) + creds = flow.run_local_server(port=0, open_browser=False) # Save the credentials for the next run with open(token_file, "w") as token: token.write(creds.to_json()) diff --git a/libs/community/langchain_community/tools/sql_database/tool.py b/libs/community/langchain_community/tools/sql_database/tool.py index 42cee9e7ed8af..8bbc595581f9b 100644 --- a/libs/community/langchain_community/tools/sql_database/tool.py +++ b/libs/community/langchain_community/tools/sql_database/tool.py @@ -7,6 +7,7 @@ from pydantic import BaseModel, Field, root_validator, model_validator, ConfigDict +from langchain_core._api.deprecation import deprecated from langchain_core.language_models import BaseLanguageModel from langchain_core.callbacks import ( AsyncCallbackManagerForToolRun, @@ -28,12 +29,18 @@ class BaseSQLDatabaseTool(BaseModel): ) -class _QuerySQLDataBaseToolInput(BaseModel): +class _QuerySQLDatabaseToolInput(BaseModel): query: str = Field(..., description="A detailed and correct SQL query.") -class QuerySQLDataBaseTool(BaseSQLDatabaseTool, BaseTool): # type: ignore[override, override] - """Tool for querying a SQL database.""" +class QuerySQLDatabaseTool(BaseSQLDatabaseTool, BaseTool): # type: ignore[override, override] + """Tool for querying a SQL database. + + .. versionchanged:: 0.3.12 + + Renamed from QuerySQLDataBaseTool to QuerySQLDatabaseTool. + Legacy name still works for backwards compatibility. + """ name: str = "sql_db_query" description: str = """ @@ -41,7 +48,7 @@ class QuerySQLDataBaseTool(BaseSQLDatabaseTool, BaseTool): # type: ignore[overr If the query is not correct, an error message will be returned. If an error is returned, rewrite the query, check the query, and try again. """ - args_schema: Type[BaseModel] = _QuerySQLDataBaseToolInput + args_schema: Type[BaseModel] = _QuerySQLDatabaseToolInput def _run( self, @@ -52,6 +59,19 @@ def _run( return self.db.run_no_throw(query) +@deprecated( + since="0.3.12", + removal="1.0", + alternative_import="langchain_community.tools.QuerySQLDatabaseTool", +) +class QuerySQLDataBaseTool(QuerySQLDatabaseTool): # type: ignore[override] + """ + Equivalent stub to QuerySQLDatabaseTool for backwards compatibility. + :private:""" + + ... + + class _InfoSQLDatabaseToolInput(BaseModel): table_names: str = Field( ..., @@ -80,7 +100,7 @@ def _run( ) -class _ListSQLDataBaseToolInput(BaseModel): +class _ListSQLDatabaseToolInput(BaseModel): tool_input: str = Field("", description="An empty string") @@ -89,7 +109,7 @@ class ListSQLDatabaseTool(BaseSQLDatabaseTool, BaseTool): # type: ignore[overri name: str = "sql_db_list_tables" description: str = "Input is an empty string, output is a comma-separated list of tables in the database." - args_schema: Type[BaseModel] = _ListSQLDataBaseToolInput + args_schema: Type[BaseModel] = _ListSQLDatabaseToolInput def _run( self, diff --git a/libs/community/langchain_community/utilities/gitlab.py b/libs/community/langchain_community/utilities/gitlab.py index 2a1d759afa668..b33ddc1180306 100644 --- a/libs/community/langchain_community/utilities/gitlab.py +++ b/libs/community/langchain_community/utilities/gitlab.py @@ -325,6 +325,161 @@ def delete_file(self, file_path: str) -> str: except Exception as e: return "Unable to delete file due to error:\n" + str(e) + def list_files_in_main_branch(self) -> str: + """ + Get the list of files in the main branch of the repository + + Returns: + str: A plaintext report containing the list of files + in the repository in the main branch + """ + if self.gitlab_base_branch is None: + return "No base branch set. Please set a base branch." + return self._list_files(self.gitlab_base_branch) + + def list_files_in_bot_branch(self) -> str: + """ + Get the list of files in the active branch of the repository + + Returns: + str: A plaintext report containing the list of files + in the repository in the active branch + """ + if self.gitlab_branch is None: + return "No active branch set. Please set a branch." + return self._list_files(self.gitlab_branch) + + def list_files_from_directory(self, path: str) -> str: + """ + Get the list of files in the active branch of the repository + from a specific directory + + Returns: + str: A plaintext report containing the list of files + in the repository in the active branch from the specified directory + """ + if self.gitlab_branch is None: + return "No active branch set. Please set a branch." + return self._list_files( + branch=self.gitlab_branch, + path=path, + ) + + def _list_files(self, branch: str, path: str = "") -> str: + try: + files = self._get_repository_files( + branch=branch, + path=path, + ) + if files: + files_str = "\n".join(files) + return f"Found {len(files)} files in branch `{branch}`:\n{files_str}" + else: + return f"No files found in branch: `{branch}`" + except Exception as e: + return f"Error: {e}" + + def _get_repository_files(self, branch: str, path: str = "") -> List[str]: + repo_contents = self.gitlab_repo_instance.repository_tree(ref=branch, path=path) + + files: List[str] = [] + for content in repo_contents: + if content["type"] == "tree": + files.extend(self._get_repository_files(branch, content["path"])) + else: + files.append(content["path"]) + + return files + + def create_branch(self, proposed_branch_name: str) -> str: + """ + Create a new branch in the repository and set it as the active branch + + Parameters: + proposed_branch_name (str): The name of the new branch to be created + Returns: + str: A success or failure message + """ + from gitlab import GitlabCreateError + + max_attempts = 100 + new_branch_name = proposed_branch_name + for i in range(max_attempts): + try: + response = self.gitlab_repo_instance.branches.create( + { + "branch": new_branch_name, + "ref": self.gitlab_branch, + } + ) + + self.gitlab_branch = response.name + return ( + f"Branch '{response.name}' " + "created successfully, and set as current active branch." + ) + + except GitlabCreateError as e: + if ( + e.response_code == 400 + and "Branch already exists" in e.error_message + ): + i += 1 + new_branch_name = f"{proposed_branch_name}_v{i}" + else: + # Handle any other exceptions + print(f"Failed to create branch. Error: {e}") # noqa: T201 + raise Exception( + "Unable to create branch name from proposed_branch_name: " + f"{proposed_branch_name}" + ) + + return ( + f"Unable to create branch. At least {max_attempts} branches exist " + f"with named derived from " + f"proposed_branch_name: `{proposed_branch_name}`" + ) + + def list_branches_in_repo(self) -> str: + """ + Get the list of branches in the repository + + Returns: + str: A plaintext report containing the number of branches + and each branch name + """ + branches = [ + branch.name for branch in self.gitlab_repo_instance.branches.list(all=True) + ] + if branches: + branches_str = "\n".join(branches) + return ( + f"Found {str(len(branches))} branches in the repository:" + f"\n{branches_str}" + ) + return "No branches found in the repository" + + def set_active_branch(self, branch_name: str) -> str: + """Equivalent to `git checkout branch_name` for this Agent. + Clones formatting from Gitlab. + + Returns an Error (as a string) if branch doesn't exist. + """ + curr_branches = [ + branch.name + for branch in self.gitlab_repo_instance.branches.list( + all=True, + ) + ] + if branch_name in curr_branches: + self.gitlab_branch = branch_name + return f"Switched to branch `{branch_name}`" + else: + return ( + f"Error {branch_name} does not exist," + f"in repo with current branches: {str(curr_branches)}" + ) + def run(self, mode: str, query: str) -> str: if mode == "get_issues": return self.get_issues() @@ -342,5 +497,17 @@ def run(self, mode: str, query: str) -> str: return self.update_file(query) elif mode == "delete_file": return self.delete_file(query) + elif mode == "create_branch": + return self.create_branch(query) + elif mode == "list_branches_in_repo": + return self.list_branches_in_repo() + elif mode == "set_active_branch": + return self.set_active_branch(query) + elif mode == "list_files_in_main_branch": + return self.list_files_in_main_branch() + elif mode == "list_files_in_bot_branch": + return self.list_files_in_bot_branch() + elif mode == "list_files_from_directory": + return self.list_files_from_directory(query) else: raise ValueError("Invalid mode" + mode) diff --git a/libs/community/langchain_community/utilities/redis.py b/libs/community/langchain_community/utilities/redis.py index 09aa0d274461d..7877e6ae6cb6d 100644 --- a/libs/community/langchain_community/utilities/redis.py +++ b/libs/community/langchain_community/utilities/redis.py @@ -2,7 +2,7 @@ import logging import re -from typing import TYPE_CHECKING, Any, List, Optional, Pattern +from typing import TYPE_CHECKING, Any, List, Optional, Pattern, cast from urllib.parse import urlparse import numpy as np @@ -18,7 +18,7 @@ def _array_to_buffer(array: List[float], dtype: Any = np.float32) -> bytes: def _buffer_to_array(buffer: bytes, dtype: Any = np.float32) -> List[float]: - return np.frombuffer(buffer, dtype=dtype).tolist() + return cast(List[float], np.frombuffer(buffer, dtype=dtype).tolist()) class TokenEscaper: diff --git a/libs/community/langchain_community/vectorstores/__init__.py b/libs/community/langchain_community/vectorstores/__init__.py index c38beea0ed6d2..b3f1ac6a27cdf 100644 --- a/libs/community/langchain_community/vectorstores/__init__.py +++ b/libs/community/langchain_community/vectorstores/__init__.py @@ -245,6 +245,9 @@ from langchain_community.vectorstores.surrealdb import ( SurrealDBStore, ) + from langchain_community.vectorstores.tablestore import ( + TablestoreVectorStore, + ) from langchain_community.vectorstores.tair import ( Tair, ) @@ -391,6 +394,7 @@ "StarRocks", "SupabaseVectorStore", "SurrealDBStore", + "TablestoreVectorStore", "Tair", "TencentVectorDB", "TiDBVectorStore", @@ -495,6 +499,7 @@ "StarRocks": "langchain_community.vectorstores.starrocks", "SupabaseVectorStore": "langchain_community.vectorstores.supabase", "SurrealDBStore": "langchain_community.vectorstores.surrealdb", + "TablestoreVectorStore": "langchain_community.vectorstores.tablestore", "Tair": "langchain_community.vectorstores.tair", "TencentVectorDB": "langchain_community.vectorstores.tencentvectordb", "TiDBVectorStore": "langchain_community.vectorstores.tidb_vector", diff --git a/libs/community/langchain_community/vectorstores/aerospike.py b/libs/community/langchain_community/vectorstores/aerospike.py index 10265cb5e9846..997646a00f98a 100644 --- a/libs/community/langchain_community/vectorstores/aerospike.py +++ b/libs/community/langchain_community/vectorstores/aerospike.py @@ -168,7 +168,7 @@ def add_texts( Args: texts: Iterable of strings to add to the vectorstore. - metadatas: Optional list of metadatas associated with the texts. + metadatas: Optional list of metadata associated with the texts. ids: Optional list of ids to associate with the texts. set_name: Optional aerospike set name to add the texts to. batch_size: Batch size to use when adding the texts to the vectorstore. diff --git a/libs/community/langchain_community/vectorstores/azure_cosmos_db.py b/libs/community/langchain_community/vectorstores/azure_cosmos_db.py index 0db46922a6dcc..446e628c68f78 100644 --- a/libs/community/langchain_community/vectorstores/azure_cosmos_db.py +++ b/libs/community/langchain_community/vectorstores/azure_cosmos_db.py @@ -44,6 +44,8 @@ class CosmosDBVectorSearchType(str, Enum): """IVF vector index""" VECTOR_HNSW = "vector-hnsw" """HNSW vector index""" + VECTOR_DISKANN = "vector-diskann" + """DISKANN vector index""" logger = logging.getLogger(__name__) @@ -181,6 +183,8 @@ def create_index( kind: str = "vector-ivf", m: int = 16, ef_construction: int = 64, + max_degree: int = 32, + l_build: int = 50, ) -> dict[str, Any]: """Creates an index using the index name specified at instance construction @@ -215,6 +219,7 @@ def create_index( - vector-ivf - vector-hnsw: available as a preview feature only, to enable visit https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/preview-features + - vector-diskann: available as a preview feature only num_lists: This integer is the number of clusters that the inverted file (IVF) index uses to group the vector data. We recommend that numLists is set to documentCount/1000 @@ -239,6 +244,12 @@ def create_index( better index quality and higher accuracy, but it will also increase the time required to build the index. ef_construction has to be at least 2 * m + max_degree: Max number of neighbors. + Default value is 32, range from 20 to 2048. + Only vector-diskann search supports this for now. + l_build: l value for index building. + Default value is 50, range from 10 to 500. + Only vector-diskann search supports this for now. Returns: An object describing the created index @@ -254,6 +265,10 @@ def create_index( create_index_commands = self._get_vector_index_hnsw( kind, m, ef_construction, similarity, dimensions ) + elif kind == CosmosDBVectorSearchType.VECTOR_DISKANN: + create_index_commands = self._get_vector_index_diskann( + kind, max_degree, l_build, similarity, dimensions + ) # retrieve the database object current_database = self._collection.database @@ -306,6 +321,27 @@ def _get_vector_index_hnsw( } return command + def _get_vector_index_diskann( + self, kind: str, max_degree: int, l_build: int, similarity: str, dimensions: int + ) -> Dict[str, Any]: + command = { + "createIndexes": self._collection.name, + "indexes": [ + { + "name": self._index_name, + "key": {self._embedding_key: "cosmosSearch"}, + "cosmosSearchOptions": { + "kind": kind, + "maxDegree": max_degree, + "lBuild": l_build, + "similarity": similarity, + "dimensions": dimensions, + }, + } + ], + } + return command + def create_filter_index( self, property_to_filter: str, @@ -421,6 +457,7 @@ def _similarity_search_with_score( pre_filter: Optional[Dict] = None, ef_search: int = 40, score_threshold: float = 0.0, + l_search: int = 40, with_embedding: bool = False, ) -> List[Tuple[Document, float]]: """Returns a list of documents with their scores @@ -433,12 +470,16 @@ def _similarity_search_with_score( - vector-ivf - vector-hnsw: available as a preview feature only, to enable visit https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/preview-features + - vector-diskann: available as a preview feature only ef_search: The size of the dynamic candidate list for search (40 by default). A higher value provides better recall at the cost of speed. score_threshold: (Optional[float], optional): Maximum vector distance between selected documents and the query vector. Defaults to None. Only vector-ivf search supports this for now. + l_search: l value for index searching. + Default value is 40, range from 10 to 10000. + Only vector-diskann search supports this. Returns: A list of documents closest to the query vector @@ -450,6 +491,10 @@ def _similarity_search_with_score( pipeline = self._get_pipeline_vector_hnsw( embeddings, k, ef_search, pre_filter ) + elif kind == CosmosDBVectorSearchType.VECTOR_DISKANN: + pipeline = self._get_pipeline_vector_diskann( + embeddings, k, l_search, pre_filter + ) cursor = self._collection.aggregate(pipeline) @@ -461,6 +506,9 @@ def _similarity_search_with_score( document_object_field = res.pop("document") text = document_object_field.pop(self._text_key) metadata = document_object_field.pop("metadata", {}) + metadata["_id"] = document_object_field.pop( + "_id" + ) # '_id' is in new position if with_embedding: metadata[self._embedding_key] = document_object_field.pop( self._embedding_key @@ -527,6 +575,37 @@ def _get_pipeline_vector_hnsw( ] return pipeline + def _get_pipeline_vector_diskann( + self, + embeddings: List[float], + k: int = 4, + l_search: int = 40, + pre_filter: Optional[Dict] = None, + ) -> List[dict[str, Any]]: + params = { + "vector": embeddings, + "path": self._embedding_key, + "k": k, + "lSearch": l_search, + } + if pre_filter: + params["filter"] = pre_filter + + pipeline: List[dict[str, Any]] = [ + { + "$search": { + "cosmosSearch": params, + } + }, + { + "$project": { + "similarityScore": {"$meta": "searchScore"}, + "document": "$$ROOT", + } + }, + ] + return pipeline + def similarity_search_with_score( self, query: str, @@ -535,6 +614,7 @@ def similarity_search_with_score( pre_filter: Optional[Dict] = None, ef_search: int = 40, score_threshold: float = 0.0, + l_search: int = 40, with_embedding: bool = False, ) -> List[Tuple[Document, float]]: embeddings = self._embedding.embed_query(query) @@ -545,6 +625,7 @@ def similarity_search_with_score( pre_filter=pre_filter, ef_search=ef_search, score_threshold=score_threshold, + l_search=l_search, with_embedding=with_embedding, ) return docs @@ -557,6 +638,7 @@ def similarity_search( pre_filter: Optional[Dict] = None, ef_search: int = 40, score_threshold: float = 0.0, + l_search: int = 40, with_embedding: bool = False, **kwargs: Any, ) -> List[Document]: @@ -567,6 +649,7 @@ def similarity_search( pre_filter=pre_filter, ef_search=ef_search, score_threshold=score_threshold, + l_search=l_search, with_embedding=with_embedding, ) return [doc for doc, _ in docs_and_scores] @@ -581,6 +664,7 @@ def max_marginal_relevance_search_by_vector( pre_filter: Optional[Dict] = None, ef_search: int = 40, score_threshold: float = 0.0, + l_search: int = 40, with_embedding: bool = False, **kwargs: Any, ) -> List[Document]: @@ -593,6 +677,7 @@ def max_marginal_relevance_search_by_vector( pre_filter=pre_filter, ef_search=ef_search, score_threshold=score_threshold, + l_search=l_search, with_embedding=with_embedding, ) @@ -616,6 +701,7 @@ def max_marginal_relevance_search( pre_filter: Optional[Dict] = None, ef_search: int = 40, score_threshold: float = 0.0, + l_search: int = 40, with_embedding: bool = False, **kwargs: Any, ) -> List[Document]: @@ -631,6 +717,7 @@ def max_marginal_relevance_search( pre_filter=pre_filter, ef_search=ef_search, score_threshold=score_threshold, + l_search=l_search, with_embedding=with_embedding, ) return docs diff --git a/libs/community/langchain_community/vectorstores/azuresearch.py b/libs/community/langchain_community/vectorstores/azuresearch.py index 2a715b846f816..6d19574e8ce30 100644 --- a/libs/community/langchain_community/vectorstores/azuresearch.py +++ b/libs/community/langchain_community/vectorstores/azuresearch.py @@ -1545,10 +1545,9 @@ def as_retriever(self, **kwargs: Any) -> AzureSearchVectorStoreRetriever: # typ """Return AzureSearchVectorStoreRetriever initialized from this VectorStore. Args: - search_type (Optional[str]): Defines the type of search that - the Retriever should perform. - Can be "similarity" (default), "hybrid", or - "semantic_hybrid". + search_type (Optional[str]): Overrides the type of search that + the Retriever should perform. Defaults to `self.search_type`. + Can be "similarity", "hybrid", or "semantic_hybrid". search_kwargs (Optional[Dict]): Keyword arguments to pass to the search function. Can include things like: score_threshold: Minimum relevance threshold @@ -1561,6 +1560,9 @@ def as_retriever(self, **kwargs: Any) -> AzureSearchVectorStoreRetriever: # typ Returns: AzureSearchVectorStoreRetriever: Retriever class for VectorStore. """ + search_type = kwargs.get("search_type", self.search_type) + kwargs["search_type"] = search_type + tags = kwargs.pop("tags", None) or [] tags.extend(self._get_retriever_tags()) return AzureSearchVectorStoreRetriever(vectorstore=self, **kwargs, tags=tags) diff --git a/libs/community/langchain_community/vectorstores/faiss.py b/libs/community/langchain_community/vectorstores/faiss.py index 48225888e5940..67380614cb449 100644 --- a/libs/community/langchain_community/vectorstores/faiss.py +++ b/libs/community/langchain_community/vectorstores/faiss.py @@ -1346,8 +1346,11 @@ def _create_filter_func( conditions for documents. Returns: - Callable[[Dict[str, Any]], bool]: A function that takes Document's metadata - and returns True if it satisfies the filter conditions, otherwise False. + A function that takes Document's metadata and returns True if it + satisfies the filter conditions, otherwise False. + + Raises: + ValueError: If the filter is invalid or contains unsuported operators. """ if callable(filter): return filter @@ -1357,12 +1360,118 @@ def _create_filter_func( f"filter must be a dict of metadata or a callable, not {type(filter)}" ) - def filter_func(metadata: Dict[str, Any]) -> bool: - return all( - metadata.get(key) in value - if isinstance(value, list) - else metadata.get(key) == value - for key, value in filter.items() # type: ignore - ) + from operator import eq, ge, gt, le, lt, ne + + COMPARISON_OPERATORS = { + "$eq": eq, + "$neq": ne, + "$gt": gt, + "$lt": lt, + "$gte": ge, + "$lte": le, + } + SEQUENCE_OPERATORS = { + "$in": lambda a, b: a in b, + "$nin": lambda a, b: a not in b, + } + OPERATIONS = COMPARISON_OPERATORS | SEQUENCE_OPERATORS + VALID_OPERATORS = frozenset(list(OPERATIONS) + ["$and", "$or", "$not"]) + SET_CONVERT_THRESHOLD = 10 + + # Validate top-level filter operators. + for op in filter: + if op and op.startswith("$") and op not in VALID_OPERATORS: + raise ValueError(f"filter contains unsupported operator: {op}") + + def filter_func_cond( + field: str, condition: Union[Dict[str, Any], List[Any], Any] + ) -> Callable[[Dict[str, Any]], bool]: + """ + Creates a filter function based on field and condition. + + Args: + field: The document field to filter on + condition: Filter condition (dict for operators, list for in, + or direct value for equality) + + Returns: + A filter function that takes a document and returns boolean + """ + if isinstance(condition, dict): + operators = [] + for op, value in condition.items(): + if op not in OPERATIONS: + raise ValueError(f"filter contains unsupported operator: {op}") + operators.append((OPERATIONS[op], value)) + + def filter_fn(doc: Dict[str, Any]) -> bool: + """ + Evaluates a document against a set of predefined operators + and their values. This function applies multiple + comparison/sequence operators to a specific field value + from the document. All conditions must be satisfied for the + function to return True. + + Args: + doc (Dict[str, Any]): The document to evaluate, containing + key-value pairs where keys are field names and values + are the field values. The document must contain the field + being filtered. + + Returns: + bool: True if the document's field value satisfies all + operator conditions, False otherwise. + """ + doc_value = doc.get(field) + return all(op(doc_value, value) for op, value in operators) + + return filter_fn + + if isinstance(condition, list): + if len(condition) > SET_CONVERT_THRESHOLD: + condition_set = frozenset(condition) + return lambda doc: doc.get(field) in condition_set + return lambda doc: doc.get(field) in condition + + return lambda doc: doc.get(field) == condition + + def filter_func(filter: Dict[str, Any]) -> Callable[[Dict[str, Any]], bool]: + """ + Creates a filter function that evaluates documents against specified + filter conditions. + + This function processes a dictionary of filter conditions and returns + a callable that can evaluate documents against these conditions. It + supports logical operators ($and, $or, $not) and field-level filtering. + + Args: + filter (Dict[str, Any]): A dictionary containing filter conditions. + Can include: + - Logical operators ($and, $or, $not) with lists of sub-filters + - Field-level conditions with comparison or sequence operators + - Direct field-value mappings for equality comparison + + Returns: + Callable[[Dict[str, Any]], bool]: A function that takes a document + (as a dictionary) and returns True if the document matches all + filter conditions, False otherwise. + """ + if "$and" in filter: + filters = [filter_func(sub_filter) for sub_filter in filter["$and"]] + return lambda doc: all(f(doc) for f in filters) + + if "$or" in filter: + filters = [filter_func(sub_filter) for sub_filter in filter["$or"]] + return lambda doc: any(f(doc) for f in filters) + + if "$not" in filter: + cond = filter_func(filter["$not"]) + return lambda doc: not cond(doc) + + conditions = [ + filter_func_cond(field, condition) + for field, condition in filter.items() + ] + return lambda doc: all(condition(doc) for condition in conditions) - return filter_func + return filter_func(filter) diff --git a/libs/community/langchain_community/vectorstores/hanavector.py b/libs/community/langchain_community/vectorstores/hanavector.py index 07ced73afe971..6c0a8040b255c 100644 --- a/libs/community/langchain_community/vectorstores/hanavector.py +++ b/libs/community/langchain_community/vectorstores/hanavector.py @@ -256,6 +256,89 @@ def _split_off_special_metadata(self, metadata: dict) -> Tuple[dict, list]: return metadata, special_metadata + def create_hnsw_index( + self, + m: Optional[int] = None, # Optional M parameter + ef_construction: Optional[int] = None, # Optional efConstruction parameter + ef_search: Optional[int] = None, # Optional efSearch parameter + index_name: Optional[str] = None, # Optional custom index name + ) -> None: + """ + Creates an HNSW vector index on a specified table and vector column with + optional build and search configurations. If no configurations are provided, + default parameters from the database are used. If provided values exceed the + valid ranges, an error will be raised. + The index is always created in ONLINE mode. + + Args: + m: (Optional) Maximum number of neighbors per graph node + (Valid Range: [4, 1000]) + ef_construction: (Optional) Maximal candidates to consider when building + the graph (Valid Range: [1, 100000]) + ef_search: (Optional) Minimum candidates for top-k-nearest neighbor + queries (Valid Range: [1, 100000]) + index_name: (Optional) Custom index name. Defaults to + __idx + """ + # Set default index name if not provided + distance_func_name = HANA_DISTANCE_FUNCTION[self.distance_strategy][0] + default_index_name = f"{self.table_name}_{distance_func_name}_idx" + # Use provided index_name or default + index_name = ( + HanaDB._sanitize_name(index_name) if index_name else default_index_name + ) + # Initialize build_config and search_config as empty dictionaries + build_config = {} + search_config = {} + + # Validate and add m parameter to build_config if provided + if m is not None: + m = HanaDB._sanitize_int(m) + if not (4 <= m <= 1000): + raise ValueError("M must be in the range [4, 1000]") + build_config["M"] = m + + # Validate and add ef_construction to build_config if provided + if ef_construction is not None: + ef_construction = HanaDB._sanitize_int(ef_construction) + if not (1 <= ef_construction <= 100000): + raise ValueError("efConstruction must be in the range [1, 100000]") + build_config["efConstruction"] = ef_construction + + # Validate and add ef_search to search_config if provided + if ef_search is not None: + ef_search = HanaDB._sanitize_int(ef_search) + if not (1 <= ef_search <= 100000): + raise ValueError("efSearch must be in the range [1, 100000]") + search_config["efSearch"] = ef_search + + # Convert build_config and search_config to JSON strings if they contain values + build_config_str = json.dumps(build_config) if build_config else "" + search_config_str = json.dumps(search_config) if search_config else "" + + # Create the index SQL string with the ONLINE keyword + sql_str = ( + f'CREATE HNSW VECTOR INDEX {index_name} ON "{self.table_name}" ' + f'("{self.vector_column}") ' + f"SIMILARITY FUNCTION {distance_func_name} " + ) + + # Append build_config to the SQL string if provided + if build_config_str: + sql_str += f"BUILD CONFIGURATION '{build_config_str}' " + + # Append search_config to the SQL string if provided + if search_config_str: + sql_str += f"SEARCH CONFIGURATION '{search_config_str}' " + + # Always add the ONLINE option + sql_str += "ONLINE " + cur = self.connection.cursor() + try: + cur.execute(sql_str) + finally: + cur.close() + def add_texts( # type: ignore[override] self, texts: Iterable[str], @@ -418,18 +501,18 @@ def similarity_search_with_score_and_vector_by_vector( k = HanaDB._sanitize_int(k) embedding = HanaDB._sanitize_list_float(embedding) distance_func_name = HANA_DISTANCE_FUNCTION[self.distance_strategy][0] - embedding_as_str = ",".join(map(str, embedding)) + embedding_as_str = "[" + ",".join(map(str, embedding)) + "]" sql_str = ( f"SELECT TOP {k}" f' "{self.content_column}", ' # row[0] f' "{self.metadata_column}", ' # row[1] f' TO_NVARCHAR("{self.vector_column}"), ' # row[2] - f' {distance_func_name}("{self.vector_column}", TO_REAL_VECTOR ' - f" (ARRAY({embedding_as_str}))) AS CS " # row[3] + f' {distance_func_name}("{self.vector_column}", TO_REAL_VECTOR (?)) AS CS ' f'FROM "{self.table_name}"' ) order_str = f" order by CS {HANA_DISTANCE_FUNCTION[self.distance_strategy][1]}" where_str, query_tuple = self._create_where_by_filter(filter) + query_tuple = (embedding_as_str,) + tuple(query_tuple) sql_str = sql_str + where_str sql_str = sql_str + order_str try: @@ -512,7 +595,7 @@ def _process_filter_object(self, filter): # type: ignore[no-untyped-def] where_str_logical, query_tuple_logical, ) = self._process_filter_object(logical_operand) - where_str += where_str_logical + where_str += "(" + where_str_logical + ")" query_tuple += query_tuple_logical continue diff --git a/libs/community/langchain_community/vectorstores/lancedb.py b/libs/community/langchain_community/vectorstores/lancedb.py index bcde354e8888e..7253d885b11e6 100644 --- a/libs/community/langchain_community/vectorstores/lancedb.py +++ b/libs/community/langchain_community/vectorstores/lancedb.py @@ -676,7 +676,7 @@ def delete( if filter: tbl.delete(filter) elif ids: - tbl.delete("id in ('{}')".format(",".join(ids))) + tbl.delete(f"{self._id_key} in ('{{}}')".format(",".join(ids))) elif drop_columns: if self.api_key is not None: raise NotImplementedError( diff --git a/libs/community/langchain_community/vectorstores/opensearch_vector_search.py b/libs/community/langchain_community/vectorstores/opensearch_vector_search.py index f08620eef6383..9cb60913e2139 100644 --- a/libs/community/langchain_community/vectorstores/opensearch_vector_search.py +++ b/libs/community/langchain_community/vectorstores/opensearch_vector_search.py @@ -22,6 +22,7 @@ SCRIPT_SCORING_SEARCH = "script_scoring" PAINLESS_SCRIPTING_SEARCH = "painless_scripting" MATCH_ALL_QUERY = {"match_all": {}} # type: Dict +HYBRID_SEARCH = "hybrid_search" if TYPE_CHECKING: from opensearchpy import AsyncOpenSearch, OpenSearch @@ -372,6 +373,65 @@ def _default_painless_scripting_query( } +def _default_hybrid_search_query( + query_text: str, query_vector: List[float], k: int = 4 +) -> Dict: + """Returns payload for performing hybrid search for given options. + + Args: + query_text: The query text to search for. + query_vector: The embedding vector (query) to search for. + k: Number of Documents to return. Defaults to 4. + + Returns: + dict: The payload for hybrid search. + """ + payload = { + "_source": {"exclude": ["vector_field"]}, + "query": { + "hybrid": { + "queries": [ + { + "match": { + "text": { + "query": query_text, + } + } + }, + {"knn": {"vector_field": {"vector": query_vector, "k": k}}}, + ] + } + }, + "size": k, + } + + return payload + + +def _hybrid_search_query_with_post_filter( + query_text: str, + query_vector: List[float], + k: int, + post_filter: Dict, +) -> Dict: + """Returns payload for performing hybrid search with post filter. + + Args: + query_text: The query text to search for. + query_vector: The embedding vector to search for. + k: Number of Documents to return. + post_filter: The post filter to apply. + + Returns: + dict: The payload for hybrid search with post filter. + """ + search_query = _default_hybrid_search_query(query_text, query_vector, k) + + search_query["post_filter"] = post_filter + + return search_query + + class OpenSearchVectorSearch(VectorStore): """`Amazon OpenSearch Vector Engine` vector store. @@ -402,6 +462,7 @@ def __init__( self.client = _get_opensearch_client(opensearch_url, **kwargs) self.async_client = _get_async_opensearch_client(opensearch_url, **kwargs) self.engine = kwargs.get("engine", "nmslib") + self.bulk_size = kwargs.get("bulk_size", 500) @property def embeddings(self) -> Embeddings: @@ -413,9 +474,10 @@ def __add( embeddings: List[List[float]], metadatas: Optional[List[dict]] = None, ids: Optional[List[str]] = None, - bulk_size: int = 500, + bulk_size: Optional[int] = None, **kwargs: Any, ) -> List[str]: + bulk_size = bulk_size if bulk_size is not None else self.bulk_size _validate_embeddings_and_bulk_size(len(embeddings), bulk_size) index_name = kwargs.get("index_name", self.index_name) text_field = kwargs.get("text_field", "text") @@ -454,9 +516,10 @@ async def __aadd( embeddings: List[List[float]], metadatas: Optional[List[dict]] = None, ids: Optional[List[str]] = None, - bulk_size: int = 500, + bulk_size: Optional[int] = None, **kwargs: Any, ) -> List[str]: + bulk_size = bulk_size if bulk_size is not None else self.bulk_size _validate_embeddings_and_bulk_size(len(embeddings), bulk_size) index_name = kwargs.get("index_name", self.index_name) text_field = kwargs.get("text_field", "text") @@ -560,7 +623,7 @@ def add_texts( texts: Iterable[str], metadatas: Optional[List[dict]] = None, ids: Optional[List[str]] = None, - bulk_size: int = 500, + bulk_size: Optional[int] = None, **kwargs: Any, ) -> List[str]: """Run more texts through the embeddings and add to the vectorstore. @@ -582,6 +645,7 @@ def add_texts( to "text". """ embeddings = self.embedding_function.embed_documents(list(texts)) + bulk_size = bulk_size if bulk_size is not None else self.bulk_size return self.__add( texts, embeddings, @@ -596,7 +660,7 @@ async def aadd_texts( texts: Iterable[str], metadatas: Optional[List[dict]] = None, ids: Optional[List[str]] = None, - bulk_size: int = 500, + bulk_size: Optional[int] = None, **kwargs: Any, ) -> List[str]: """ @@ -604,6 +668,7 @@ async def aadd_texts( and add to the vectorstore. """ embeddings = await self.embedding_function.aembed_documents(list(texts)) + bulk_size = bulk_size if bulk_size is not None else self.bulk_size return await self.__aadd( texts, embeddings, @@ -618,7 +683,7 @@ def add_embeddings( text_embeddings: Iterable[Tuple[str, List[float]]], metadatas: Optional[List[dict]] = None, ids: Optional[List[str]] = None, - bulk_size: int = 500, + bulk_size: Optional[int] = None, **kwargs: Any, ) -> List[str]: """Add the given texts and embeddings to the vectorstore. @@ -641,6 +706,7 @@ def add_embeddings( to "text". """ texts, embeddings = zip(*text_embeddings) + bulk_size = bulk_size if bulk_size is not None else self.bulk_size return self.__add( list(texts), list(embeddings), @@ -707,6 +773,122 @@ async def adelete( item.get("delete", {}).get("error") for item in response["items"] ) + def configure_search_pipelines( + self, + pipeline_name: str, + keyword_weight: float = 0.7, + vector_weight: float = 0.3, + ) -> dict: + """ + Configures a search pipeline for hybrid search. + Args: + pipeline_name: Name of the pipeline + keyword_weight: Weight for keyword search + vector_weight: Weight for vector search + Returns: + response: Acknowledgement of the pipeline creation. + (if there is any error while configuring the pipeline, it will return None) + Raises: + Exception: If an error occurs + """ + if not pipeline_name.isidentifier(): + raise ValueError(f"Invalid pipeline name: {pipeline_name}") + + path = f"/_search/pipeline/{pipeline_name}" + + payload = { + "description": "Post processor for hybrid search", + "phase_results_processors": [ + { + "normalization-processor": { + "normalization": {"technique": "min_max"}, + "combination": { + "technique": "arithmetic_mean", + "parameters": {"weights": [keyword_weight, vector_weight]}, + }, + } + } + ], + } + + response = self.client.transport.perform_request( + method="PUT", url=path, body=payload + ) + return response + + def search_pipeline_exists(self, pipeline_name: str) -> bool: + """ + Checks if a search pipeline exists. + + Args: + pipeline_name: Name of the pipeline + + Returns: + bool: True if the pipeline exists, False otherwise + + Raises: + Exception: If an error occurs + + Example: + >>> search_pipeline_exists("my_pipeline_1") + True + >>> search_pipeline_exists("my_pipeline_2") + False + """ + if not pipeline_name.isidentifier(): + raise ValueError(f"Invalid pipeline name: {pipeline_name}") + + existed_pipelines = self.client.transport.perform_request( + method="GET", url="/_search/pipeline/" + ) + + return pipeline_name in existed_pipelines + + def get_search_pipeline_info(self, pipeline_name: str) -> Optional[Dict]: + """ + Get information about a search pipeline. + + Args: + pipeline_name: Name of the pipeline + + Returns: + dict: Information about the pipeline + None: If pipeline does not exist + + Raises: + Exception: If an error occurs + + Example: + >>> get_search_pipeline_info("my_pipeline_1") + {'search_pipeline_1': { + "description": "Post processor for hybrid search", + "phase_results_processors": [ + { + "normalization-processor": { + "normalization": {"technique": "min_max"}, + "combination": { + "technique": "arithmetic_mean", + "parameters": {"weights": [0.7, 0.3]} + } + } + } + ] + } + } + >>> get_search_pipeline_info("my_pipeline_2") + None + """ + response = None + + if not pipeline_name.isidentifier(): + raise ValueError(f"Invalid pipeline name: {pipeline_name}") + + response = self.client.transport.perform_request( + method="GET", url=f"/_search/pipeline/{pipeline_name}" + ) + + return response + @staticmethod def _identity_fn(score: float) -> float: return score @@ -831,6 +1013,8 @@ def similarity_search_with_score( Optional Args: same as `similarity_search` """ + # added query_text to kwargs for Hybrid Search + kwargs["query_text"] = query embedding = self.embedding_function.embed_query(query) return self.similarity_search_with_score_by_vector( embedding, k, score_threshold, **kwargs @@ -1018,6 +1202,38 @@ def _raw_similarity_search_with_score_by_vector( vector_field, score_threshold=score_threshold, ) + + elif search_type == HYBRID_SEARCH: + search_pipeline = kwargs.get("search_pipeline") + post_filter = kwargs.get("post_filter", {}) + query_text = kwargs.get("query_text") + path = f"/{index_name}/_search?search_pipeline={search_pipeline}" + + if query_text is None: + raise ValueError("query_text must be provided for hybrid search") + + if search_pipeline is None: + raise ValueError("search_pipeline must be provided for hybrid search") + + # embedding the query_text + embeded_query = self.embedding_function.embed_query(query_text) + + # if post filter is provided + if post_filter != {}: + # hybrid search with post filter + payload = _hybrid_search_query_with_post_filter( + query_text, embeded_query, k, post_filter + ) + else: + # hybrid search without post filter + payload = _default_hybrid_search_query(query_text, embeded_query, k) + + response = self.client.transport.perform_request( + method="GET", url=path, body=payload + ) + + return [hit for hit in response["hits"]["hits"]] + else: raise ValueError("Invalid `search_type` provided as an argument") @@ -1085,7 +1301,7 @@ def from_texts( texts: List[str], embedding: Embeddings, metadatas: Optional[List[dict]] = None, - bulk_size: int = 500, + bulk_size: Optional[int] = None, ids: Optional[List[str]] = None, **kwargs: Any, ) -> OpenSearchVectorSearch: @@ -1134,6 +1350,7 @@ def from_texts( """ embeddings = embedding.embed_documents(texts) + bulk_size = bulk_size if bulk_size is not None else cls.bulk_size return cls.from_embeddings( embeddings, texts, @@ -1150,7 +1367,7 @@ async def afrom_texts( texts: List[str], embedding: Embeddings, metadatas: Optional[List[dict]] = None, - bulk_size: int = 500, + bulk_size: Optional[int] = None, ids: Optional[List[str]] = None, **kwargs: Any, ) -> OpenSearchVectorSearch: @@ -1199,6 +1416,7 @@ async def afrom_texts( """ embeddings = await embedding.aembed_documents(texts) + bulk_size = bulk_size if bulk_size is not None else cls.bulk_size return await cls.afrom_embeddings( embeddings, texts, @@ -1216,7 +1434,7 @@ def from_embeddings( texts: List[str], embedding: Embeddings, metadatas: Optional[List[dict]] = None, - bulk_size: int = 500, + bulk_size: Optional[int] = None, ids: Optional[List[str]] = None, **kwargs: Any, ) -> OpenSearchVectorSearch: @@ -1285,6 +1503,7 @@ def from_embeddings( "max_chunk_bytes", "is_aoss", ] + bulk_size = bulk_size if bulk_size is not None else cls.bulk_size _validate_embeddings_and_bulk_size(len(embeddings), bulk_size) dim = len(embeddings[0]) # Get the index name from either from kwargs or ENV Variable @@ -1346,7 +1565,7 @@ async def afrom_embeddings( texts: List[str], embedding: Embeddings, metadatas: Optional[List[dict]] = None, - bulk_size: int = 500, + bulk_size: Optional[int] = None, ids: Optional[List[str]] = None, **kwargs: Any, ) -> OpenSearchVectorSearch: @@ -1417,6 +1636,7 @@ async def afrom_embeddings( "max_chunk_bytes", "is_aoss", ] + bulk_size = bulk_size if bulk_size is not None else cls.bulk_size _validate_embeddings_and_bulk_size(len(embeddings), bulk_size) dim = len(embeddings[0]) # Get the index name from either from kwargs or ENV Variable diff --git a/libs/community/langchain_community/vectorstores/semadb.py b/libs/community/langchain_community/vectorstores/semadb.py index b54f832cee6cf..b39d46eb6bd27 100644 --- a/libs/community/langchain_community/vectorstores/semadb.py +++ b/libs/community/langchain_community/vectorstores/semadb.py @@ -1,4 +1,4 @@ -from typing import Any, Iterable, List, Optional, Tuple +from typing import Any, Iterable, List, Optional, Tuple, cast from uuid import uuid4 import numpy as np @@ -111,7 +111,7 @@ def add_texts( embed_matrix = embed_matrix / np.linalg.norm( embed_matrix, axis=1, keepdims=True ) - embeddings = embed_matrix.tolist() + embeddings = cast(List[List[float]], embed_matrix.tolist()) # Create points ids: List[str] = [] points = [] @@ -186,7 +186,7 @@ def _search_points(self, embedding: List[float], k: int = 4) -> List[dict]: if self.distance_strategy == DistanceStrategy.COSINE: vec = np.array(embedding) vec = vec / np.linalg.norm(vec) - embedding = vec.tolist() + embedding = cast(List[float], vec.tolist()) # Perform search request payload = { "vector": embedding, diff --git a/libs/community/langchain_community/vectorstores/tablestore.py b/libs/community/langchain_community/vectorstores/tablestore.py new file mode 100644 index 0000000000000..a00e102839c0b --- /dev/null +++ b/libs/community/langchain_community/vectorstores/tablestore.py @@ -0,0 +1,564 @@ +import json +import logging +import uuid +from typing import ( + Any, + Iterable, + List, + Optional, + Sequence, + Tuple, +) + +from langchain_core.documents import Document +from langchain_core.embeddings import Embeddings +from langchain_core.vectorstores import VectorStore + +logger = logging.getLogger(__name__) + + +class TablestoreVectorStore(VectorStore): + """`Tablestore` vector store. + + To use, you should have the ``tablestore`` python package installed. + + Example: + .. code-block:: python + + import os + + from langchain_openai import OpenAIEmbeddings + from langchain_community.vectorstores import TablestoreVectorStore + import tablestore + + embeddings = OpenAIEmbeddings() + store = TablestoreVectorStore( + embeddings, + endpoint=os.getenv("end_point"), + instance_name=os.getenv("instance_name"), + access_key_id=os.getenv("access_key_id"), + access_key_secret=os.getenv("access_key_secret"), + vector_dimension=512, + # metadata mapping is used to filter non-vector fields. + metadata_mappings=[ + tablestore.FieldSchema( + "type", + tablestore.FieldType.KEYWORD, + index=True, + enable_sort_and_agg=True + ), + tablestore.FieldSchema( + "time", + tablestore.FieldType.LONG, + index=True, + enable_sort_and_agg=True + ), + ] + ) + """ + + def __init__( + self, + embedding: Embeddings, + *, + endpoint: Optional[str] = None, + instance_name: Optional[str] = None, + access_key_id: Optional[str] = None, + access_key_secret: Optional[str] = None, + table_name: Optional[str] = "langchain_vector_store_ots_v1", + index_name: Optional[str] = "langchain_vector_store_ots_index_v1", + text_field: Optional[str] = "content", + vector_field: Optional[str] = "embedding", + vector_dimension: int = 512, + vector_metric_type: Optional[str] = "cosine", + metadata_mappings: Optional[List[Any]] = None, + ): + try: + import tablestore + except ImportError: + raise ImportError( + "Could not import tablestore python package. " + "Please install it with `pip install tablestore`." + ) + self.__embedding = embedding + self.__tablestore_client = tablestore.OTSClient( + endpoint, + access_key_id, + access_key_secret, + instance_name, + retry_policy=tablestore.WriteRetryPolicy(), + ) + self.__table_name = table_name + self.__index_name = index_name + self.__vector_dimension = vector_dimension + self.__vector_field = vector_field + self.__text_field = text_field + if vector_metric_type == "cosine": + self.__vector_metric_type = tablestore.VectorMetricType.VM_COSINE + elif vector_metric_type == "euclidean": + self.__vector_metric_type = tablestore.VectorMetricType.VM_EUCLIDEAN + elif vector_metric_type == "dot_product": + self.__vector_metric_type = tablestore.VectorMetricType.VM_DOT_PRODUCT + else: + raise ValueError( + f"Unsupported vector_metric_type operator: {vector_metric_type}" + ) + + self.__metadata_mappings = [ + tablestore.FieldSchema( + self.__text_field, + tablestore.FieldType.TEXT, + index=True, + enable_sort_and_agg=False, + store=False, + analyzer=tablestore.AnalyzerType.MAXWORD, + ), + tablestore.FieldSchema( + self.__vector_field, + tablestore.FieldType.VECTOR, + vector_options=tablestore.VectorOptions( + data_type=tablestore.VectorDataType.VD_FLOAT_32, + dimension=self.__vector_dimension, + metric_type=self.__vector_metric_type, + ), + ), + ] + + if metadata_mappings: + for mapping in metadata_mappings: + if not isinstance(mapping, tablestore.FieldSchema): + raise ValueError( + f"meta_data mapping should be an " + f"instance of tablestore.FieldSchema, " + f"bug got {type(mapping)}" + ) + if ( + mapping.field_name == text_field + or mapping.field_name == vector_field + ): + continue + self.__metadata_mappings.append(mapping) + + def create_table_if_not_exist(self) -> None: + """Create table if not exist.""" + + try: + import tablestore + except ImportError: + raise ImportError( + "Could not import tablestore python package. " + "Please install it with `pip install tablestore`." + ) + table_list = self.__tablestore_client.list_table() + if self.__table_name in table_list: + logger.info("Tablestore system table[%s] already exists", self.__table_name) + return None + logger.info( + "Tablestore system table[%s] does not exist, try to create the table.", + self.__table_name, + ) + + schema_of_primary_key = [("id", "STRING")] + table_meta = tablestore.TableMeta(self.__table_name, schema_of_primary_key) + table_options = tablestore.TableOptions() + reserved_throughput = tablestore.ReservedThroughput( + tablestore.CapacityUnit(0, 0) + ) + try: + self.__tablestore_client.create_table( + table_meta, table_options, reserved_throughput + ) + logger.info("Tablestore create table[%s] successfully.", self.__table_name) + except tablestore.OTSClientError as e: + logger.exception( + "Tablestore create system table[%s] failed with client error, " + "http_status:%d, error_message:%s", + self.__table_name, + e.get_http_status(), + e.get_error_message(), + ) + except tablestore.OTSServiceError as e: + logger.exception( + "Tablestore create system table[%s] failed with client error, " + "http_status:%d, error_code:%s, error_message:%s, request_id:%s", + self.__table_name, + e.get_http_status(), + e.get_error_code(), + e.get_error_message(), + e.get_request_id(), + ) + + def create_search_index_if_not_exist(self) -> None: + """Create search index if not exist.""" + + try: + import tablestore + except ImportError: + raise ImportError( + "Could not import tablestore python package. " + "Please install it with `pip install tablestore`." + ) + search_index_list = self.__tablestore_client.list_search_index( + table_name=self.__table_name + ) + if self.__index_name in [t[1] for t in search_index_list]: + logger.info("Tablestore system index[%s] already exists", self.__index_name) + return None + index_meta = tablestore.SearchIndexMeta(self.__metadata_mappings) + self.__tablestore_client.create_search_index( + self.__table_name, self.__index_name, index_meta + ) + logger.info( + "Tablestore create system index[%s] successfully.", self.__index_name + ) + + def delete_table_if_exists(self) -> None: + """Delete table if exists.""" + + search_index_list = self.__tablestore_client.list_search_index( + table_name=self.__table_name + ) + for resp_tuple in search_index_list: + self.__tablestore_client.delete_search_index(resp_tuple[0], resp_tuple[1]) + self.__tablestore_client.delete_table(self.__table_name) + + def delete_search_index(self, table_name: str, index_name: str) -> None: + """Delete search index.""" + + self.__tablestore_client.delete_search_index(table_name, index_name) + + def __write_row( + self, row_id: str, content: str, embedding_vector: List[float], meta_data: dict + ) -> None: + try: + import tablestore + except ImportError: + raise ImportError( + "Could not import tablestore python package. " + "Please install it with `pip install tablestore`." + ) + primary_key = [("id", row_id)] + attribute_columns = [ + (self.__text_field, content), + (self.__vector_field, json.dumps(embedding_vector)), + ] + for k, v in meta_data.items(): + item = (k, v) + attribute_columns.append(item) + row = tablestore.Row(primary_key, attribute_columns) + + try: + self.__tablestore_client.put_row(self.__table_name, row) + logger.debug( + "Tablestore put row successfully. id:%s, content:%s, meta_data:%s", + row_id, + content, + meta_data, + ) + except tablestore.OTSClientError as e: + logger.exception( + "Tablestore put row failed with client error:%s, " + "id:%s, content:%s, meta_data:%s", + e, + row_id, + content, + meta_data, + ) + except tablestore.OTSServiceError as e: + logger.exception( + "Tablestore put row failed with client error:%s, id:%s, content:%s, " + "meta_data:%s, http_status:%d, " + "error_code:%s, error_message:%s, request_id:%s", + e, + row_id, + content, + meta_data, + e.get_http_status(), + e.get_error_code(), + e.get_error_message(), + e.get_request_id(), + ) + + def __delete_row(self, row_id: str) -> None: + try: + import tablestore + except ImportError: + raise ImportError( + "Could not import tablestore python package. " + "Please install it with `pip install tablestore`." + ) + primary_key = [("id", row_id)] + try: + self.__tablestore_client.delete_row(self.__table_name, primary_key, None) + logger.info("Tablestore delete row successfully. id:%s", row_id) + except tablestore.OTSClientError as e: + logger.exception( + "Tablestore delete row failed with client error:%s, id:%s", e, row_id + ) + except tablestore.OTSServiceError as e: + logger.exception( + "Tablestore delete row failed with client error:%s, " + "id:%s, http_status:%d, error_code:%s, error_message:%s, request_id:%s", + e, + row_id, + e.get_http_status(), + e.get_error_code(), + e.get_error_message(), + e.get_request_id(), + ) + + def __get_row(self, row_id: str) -> Document: + try: + import tablestore + except ImportError: + raise ImportError( + "Could not import tablestore python package. " + "Please install it with `pip install tablestore`." + ) + primary_key = [("id", row_id)] + try: + _, row, _ = self.__tablestore_client.get_row( + self.__table_name, primary_key, None, None, 1 + ) + logger.debug("Tablestore get row successfully. id:%s", row_id) + if row is None: + raise ValueError("Can't not find row_id:%s in tablestore." % row_id) + document_id = row.primary_key[0][1] + meta_data = {} + text = "" + for col in row.attribute_columns: + key = col[0] + val = col[1] + if key == self.__text_field: + text = val + continue + meta_data[key] = val + return Document( + id=document_id, + page_content=text, + metadata=meta_data, + ) + except tablestore.OTSClientError as e: + logger.exception( + "Tablestore get row failed with client error:%s, id:%s", e, row_id + ) + raise e + except tablestore.OTSServiceError as e: + logger.exception( + "Tablestore get row failed with client error:%s, " + "id:%s, http_status:%d, error_code:%s, error_message:%s, request_id:%s", + e, + row_id, + e.get_http_status(), + e.get_error_code(), + e.get_error_message(), + e.get_request_id(), + ) + raise e + + def _tablestore_search( + self, + query_embedding: List[float], + k: int = 5, + tablestore_filter_query: Optional[Any] = None, + **kwargs: Any, + ) -> List[Tuple[Document, float]]: + try: + import tablestore + except ImportError: + raise ImportError( + "Could not import tablestore python package. " + "Please install it with `pip install tablestore`." + ) + if tablestore_filter_query: + if not isinstance(tablestore_filter_query, tablestore.Query): + raise ValueError( + f"table_store_filter_query should be " + f"an instance of tablestore.Query, " + f"bug got {type(tablestore_filter_query)}" + ) + if "knn_top_k" in kwargs: + knn_top_k = kwargs["knn_top_k"] + else: + knn_top_k = k + ots_query = tablestore.KnnVectorQuery( + field_name=self.__vector_field, + top_k=knn_top_k, + float32_query_vector=query_embedding, + filter=tablestore_filter_query, + ) + sort = tablestore.Sort( + sorters=[tablestore.ScoreSort(sort_order=tablestore.SortOrder.DESC)] + ) + search_query = tablestore.SearchQuery( + ots_query, limit=k, get_total_count=False, sort=sort + ) + try: + search_response = self.__tablestore_client.search( + table_name=self.__table_name, + index_name=self.__index_name, + search_query=search_query, + columns_to_get=tablestore.ColumnsToGet( + return_type=tablestore.ColumnReturnType.ALL + ), + ) + logger.info( + "Tablestore search successfully. request_id:%s", + search_response.request_id, + ) + tuple_list = [] + for hit in search_response.search_hits: + row = hit.row + score = hit.score + document_id = row[0][0][1] + meta_data = {} + text = "" + for col in row[1]: + key = col[0] + val = col[1] + if key == self.__text_field: + text = val + continue + if key == self.__vector_field: + val = json.loads(val) + meta_data[key] = val + doc = Document( + id=document_id, + page_content=text, + metadata=meta_data, + ) + tuple_list.append((doc, score)) + return tuple_list + except tablestore.OTSClientError as e: + logger.exception("Tablestore search failed with client error:%s", e) + raise e + except tablestore.OTSServiceError as e: + logger.exception( + "Tablestore search failed with client error:%s, " + "http_status:%d, error_code:%s, error_message:%s, request_id:%s", + e, + e.get_http_status(), + e.get_error_code(), + e.get_error_message(), + e.get_request_id(), + ) + raise e + + def add_texts( + self, + texts: Iterable[str], + metadatas: Optional[List[dict]] = None, + ids: Optional[List[str]] = None, + **kwargs: Any, + ) -> List[str]: + ids = ids or [str(uuid.uuid4().hex) for _ in texts] + text_list = list(texts) + embeddings = self.__embedding.embed_documents(text_list) + for i in range(len(ids)): + row_id = ids[i] + text = text_list[i] + embedding_vector = embeddings[i] + metadata = dict() + if metadatas and metadatas[i]: + metadata = metadatas[i] + self.__write_row( + row_id=row_id, + content=text, + embedding_vector=embedding_vector, + meta_data=metadata, + ) + return ids + + def delete(self, ids: Optional[List[str]] = None, **kwargs: Any) -> Optional[bool]: + if ids: + for row_id in ids: + self.__delete_row(row_id) + return True + + def get_by_ids(self, ids: Sequence[str], /) -> List[Document]: + return [self.__get_row(row_id) for row_id in ids] + + def similarity_search( + self, + query: str, + k: int = 4, + tablestore_filter_query: Optional[Any] = None, + **kwargs: Any, + ) -> List[Document]: + return [ + doc + for (doc, score) in self.similarity_search_with_score( + query, k=k, tablestore_filter_query=tablestore_filter_query, **kwargs + ) + ] + + def similarity_search_with_score( + self, + query: str, + k: int = 4, + tablestore_filter_query: Optional[Any] = None, + *args: Any, + **kwargs: Any, + ) -> List[Tuple[Document, float]]: + query_embedding = self.__embedding.embed_query(query) + return self._tablestore_search( + query_embedding, + k=k, + tablestore_filter_query=tablestore_filter_query, + **kwargs, + ) + + def similarity_search_by_vector( + self, + embedding: List[float], + k: int = 4, + tablestore_filter_query: Optional[Any] = None, + **kwargs: Any, + ) -> List[Document]: + return [ + doc + for (doc, score) in self._tablestore_search( + embedding, + k=k, + tablestore_filter_query=tablestore_filter_query, + **kwargs, + ) + ] + + @classmethod + def from_texts( + cls, + texts: List[str], + embedding: Embeddings, + metadatas: Optional[List[dict]] = None, + endpoint: Optional[str] = None, + instance_name: Optional[str] = None, + access_key_id: Optional[str] = None, + access_key_secret: Optional[str] = None, + table_name: Optional[str] = "langchain_vector_store_ots_v1", + index_name: Optional[str] = "langchain_vector_store_ots_index_v1", + text_field: Optional[str] = "content", + vector_field: Optional[str] = "embedding", + vector_dimension: int = 512, + vector_metric_type: Optional[str] = "cosine", + metadata_mappings: Optional[List[Any]] = None, + **kwargs: Any, + ) -> "TablestoreVectorStore": + store = cls( + embedding=embedding, + endpoint=endpoint, + instance_name=instance_name, + access_key_id=access_key_id, + access_key_secret=access_key_secret, + table_name=table_name, + index_name=index_name, + text_field=text_field, + vector_field=vector_field, + vector_dimension=vector_dimension, + vector_metric_type=vector_metric_type, + metadata_mappings=metadata_mappings, + ) + store.create_table_if_not_exist() + store.create_search_index_if_not_exist() + store.add_texts(texts, metadatas) + return store diff --git a/libs/community/langchain_community/vectorstores/usearch.py b/libs/community/langchain_community/vectorstores/usearch.py index c59446de54da1..4f9d69d02f7ee 100644 --- a/libs/community/langchain_community/vectorstores/usearch.py +++ b/libs/community/langchain_community/vectorstores/usearch.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Any, Dict, Iterable, List, Optional, Tuple, Union +from typing import Any, Dict, Iterable, List, Optional, Tuple, Union, cast import numpy as np from langchain_core.documents import Document @@ -75,7 +75,7 @@ def add_texts( self.index.add(np.array(ids), np.array(embeddings)) self.docstore.add(dict(zip(ids, documents))) self.ids.extend(ids) - return ids.tolist() + return cast(List[str], ids.tolist()) def similarity_search_with_score( self, @@ -171,4 +171,4 @@ def from_texts( usearch = guard_import("usearch.index") index = usearch.Index(ndim=len(embeddings[0]), metric=metric) index.add(np.array(ids), np.array(embeddings)) - return cls(embedding, index, docstore, ids.tolist()) + return cls(embedding, index, docstore, cast(List[str], ids.tolist())) diff --git a/libs/community/poetry.lock b/libs/community/poetry.lock index 21b8dc4e1e690..b67efa31c209b 100644 --- a/libs/community/poetry.lock +++ b/libs/community/poetry.lock @@ -1,99 +1,99 @@ -# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. [[package]] name = "aiohappyeyeballs" -version = "2.4.3" +version = "2.4.4" description = "Happy Eyeballs for asyncio" optional = false python-versions = ">=3.8" files = [ - {file = "aiohappyeyeballs-2.4.3-py3-none-any.whl", hash = "sha256:8a7a83727b2756f394ab2895ea0765a0a8c475e3c71e98d43d76f22b4b435572"}, - {file = "aiohappyeyeballs-2.4.3.tar.gz", hash = "sha256:75cf88a15106a5002a8eb1dab212525c00d1f4c0fa96e551c9fbe6f09a621586"}, + {file = "aiohappyeyeballs-2.4.4-py3-none-any.whl", hash = "sha256:a980909d50efcd44795c4afeca523296716d50cd756ddca6af8c65b996e27de8"}, + {file = "aiohappyeyeballs-2.4.4.tar.gz", hash = "sha256:5fdd7d87889c63183afc18ce9271f9b0a7d32c2303e394468dd45d514a757745"}, ] [[package]] name = "aiohttp" -version = "3.11.7" +version = "3.11.10" description = "Async http client/server framework (asyncio)" optional = false python-versions = ">=3.9" files = [ - {file = "aiohttp-3.11.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8bedb1f6cb919af3b6353921c71281b1491f948ca64408871465d889b4ee1b66"}, - {file = "aiohttp-3.11.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f5022504adab881e2d801a88b748ea63f2a9d130e0b2c430824682a96f6534be"}, - {file = "aiohttp-3.11.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e22d1721c978a6494adc824e0916f9d187fa57baeda34b55140315fa2f740184"}, - {file = "aiohttp-3.11.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e993676c71288618eb07e20622572b1250d8713e7e00ab3aabae28cb70f3640d"}, - {file = "aiohttp-3.11.7-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e13a05db87d3b241c186d0936808d0e4e12decc267c617d54e9c643807e968b6"}, - {file = "aiohttp-3.11.7-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ba8d043fed7ffa117024d7ba66fdea011c0e7602327c6d73cacaea38abe4491"}, - {file = "aiohttp-3.11.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dda3ed0a7869d2fa16aa41f9961ade73aa2c2e3b2fcb0a352524e7b744881889"}, - {file = "aiohttp-3.11.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43bfd25113c1e98aec6c70e26d5f4331efbf4aa9037ba9ad88f090853bf64d7f"}, - {file = "aiohttp-3.11.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3dd3e7e7c9ef3e7214f014f1ae260892286647b3cf7c7f1b644a568fd410f8ca"}, - {file = "aiohttp-3.11.7-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:78c657ece7a73b976905ab9ec8be9ef2df12ed8984c24598a1791c58ce3b4ce4"}, - {file = "aiohttp-3.11.7-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:db70a47987e34494b451a334605bee57a126fe8d290511349e86810b4be53b01"}, - {file = "aiohttp-3.11.7-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:9e67531370a3b07e49b280c1f8c2df67985c790ad2834d1b288a2f13cd341c5f"}, - {file = "aiohttp-3.11.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9202f184cc0582b1db15056f2225ab4c1e3dac4d9ade50dd0613ac3c46352ac2"}, - {file = "aiohttp-3.11.7-cp310-cp310-win32.whl", hash = "sha256:2257bdd5cf54a4039a4337162cd8048f05a724380a2283df34620f55d4e29341"}, - {file = "aiohttp-3.11.7-cp310-cp310-win_amd64.whl", hash = "sha256:b7215bf2b53bc6cb35808149980c2ae80a4ae4e273890ac85459c014d5aa60ac"}, - {file = "aiohttp-3.11.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:cea52d11e02123f125f9055dfe0ccf1c3857225fb879e4a944fae12989e2aef2"}, - {file = "aiohttp-3.11.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3ce18f703b7298e7f7633efd6a90138d99a3f9a656cb52c1201e76cb5d79cf08"}, - {file = "aiohttp-3.11.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:670847ee6aeb3a569cd7cdfbe0c3bec1d44828bbfbe78c5d305f7f804870ef9e"}, - {file = "aiohttp-3.11.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4dda726f89bfa5c465ba45b76515135a3ece0088dfa2da49b8bb278f3bdeea12"}, - {file = "aiohttp-3.11.7-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c25b74a811dba37c7ea6a14d99eb9402d89c8d739d50748a75f3cf994cf19c43"}, - {file = "aiohttp-3.11.7-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5522ee72f95661e79db691310290c4618b86dff2d9b90baedf343fd7a08bf79"}, - {file = "aiohttp-3.11.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1fbf41a6bbc319a7816ae0f0177c265b62f2a59ad301a0e49b395746eb2a9884"}, - {file = "aiohttp-3.11.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:59ee1925b5a5efdf6c4e7be51deee93984d0ac14a6897bd521b498b9916f1544"}, - {file = "aiohttp-3.11.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:24054fce8c6d6f33a3e35d1c603ef1b91bbcba73e3f04a22b4f2f27dac59b347"}, - {file = "aiohttp-3.11.7-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:351849aca2c6f814575c1a485c01c17a4240413f960df1bf9f5deb0003c61a53"}, - {file = "aiohttp-3.11.7-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:12724f3a211fa243570e601f65a8831372caf1a149d2f1859f68479f07efec3d"}, - {file = "aiohttp-3.11.7-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:7ea4490360b605804bea8173d2d086b6c379d6bb22ac434de605a9cbce006e7d"}, - {file = "aiohttp-3.11.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e0bf378db07df0a713a1e32381a1b277e62ad106d0dbe17b5479e76ec706d720"}, - {file = "aiohttp-3.11.7-cp311-cp311-win32.whl", hash = "sha256:cd8d62cab363dfe713067027a5adb4907515861f1e4ce63e7be810b83668b847"}, - {file = "aiohttp-3.11.7-cp311-cp311-win_amd64.whl", hash = "sha256:bf0e6cce113596377cadda4e3ac5fb89f095bd492226e46d91b4baef1dd16f60"}, - {file = "aiohttp-3.11.7-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:4bb7493c3e3a36d3012b8564bd0e2783259ddd7ef3a81a74f0dbfa000fce48b7"}, - {file = "aiohttp-3.11.7-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e143b0ef9cb1a2b4f74f56d4fbe50caa7c2bb93390aff52f9398d21d89bc73ea"}, - {file = "aiohttp-3.11.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f7c58a240260822dc07f6ae32a0293dd5bccd618bb2d0f36d51c5dbd526f89c0"}, - {file = "aiohttp-3.11.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d20cfe63a1c135d26bde8c1d0ea46fd1200884afbc523466d2f1cf517d1fe33"}, - {file = "aiohttp-3.11.7-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12e4d45847a174f77b2b9919719203769f220058f642b08504cf8b1cf185dacf"}, - {file = "aiohttp-3.11.7-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cf4efa2d01f697a7dbd0509891a286a4af0d86902fc594e20e3b1712c28c0106"}, - {file = "aiohttp-3.11.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ee6a4cdcbf54b8083dc9723cdf5f41f722c00db40ccf9ec2616e27869151129"}, - {file = "aiohttp-3.11.7-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c6095aaf852c34f42e1bd0cf0dc32d1e4b48a90bfb5054abdbb9d64b36acadcb"}, - {file = "aiohttp-3.11.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1cf03d27885f8c5ebf3993a220cc84fc66375e1e6e812731f51aab2b2748f4a6"}, - {file = "aiohttp-3.11.7-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:1a17f6a230f81eb53282503823f59d61dff14fb2a93847bf0399dc8e87817307"}, - {file = "aiohttp-3.11.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:481f10a1a45c5f4c4a578bbd74cff22eb64460a6549819242a87a80788461fba"}, - {file = "aiohttp-3.11.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:db37248535d1ae40735d15bdf26ad43be19e3d93ab3f3dad8507eb0f85bb8124"}, - {file = "aiohttp-3.11.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9d18a8b44ec8502a7fde91446cd9c9b95ce7c49f1eacc1fb2358b8907d4369fd"}, - {file = "aiohttp-3.11.7-cp312-cp312-win32.whl", hash = "sha256:3d1c9c15d3999107cbb9b2d76ca6172e6710a12fda22434ee8bd3f432b7b17e8"}, - {file = "aiohttp-3.11.7-cp312-cp312-win_amd64.whl", hash = "sha256:018f1b04883a12e77e7fc161934c0f298865d3a484aea536a6a2ca8d909f0ba0"}, - {file = "aiohttp-3.11.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:241a6ca732d2766836d62c58c49ca7a93d08251daef0c1e3c850df1d1ca0cbc4"}, - {file = "aiohttp-3.11.7-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:aa3705a8d14de39898da0fbad920b2a37b7547c3afd2a18b9b81f0223b7d0f68"}, - {file = "aiohttp-3.11.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9acfc7f652b31853eed3b92095b0acf06fd5597eeea42e939bd23a17137679d5"}, - {file = "aiohttp-3.11.7-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcefcf2915a2dbdbce37e2fc1622129a1918abfe3d06721ce9f6cdac9b6d2eaa"}, - {file = "aiohttp-3.11.7-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c1f6490dd1862af5aae6cfcf2a274bffa9a5b32a8f5acb519a7ecf5a99a88866"}, - {file = "aiohttp-3.11.7-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1ac5462582d6561c1c1708853a9faf612ff4e5ea5e679e99be36143d6eabd8e"}, - {file = "aiohttp-3.11.7-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c1a6309005acc4b2bcc577ba3b9169fea52638709ffacbd071f3503264620da"}, - {file = "aiohttp-3.11.7-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f5b973cce96793725ef63eb449adfb74f99c043c718acb76e0d2a447ae369962"}, - {file = "aiohttp-3.11.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ce91a24aac80de6be8512fb1c4838a9881aa713f44f4e91dd7bb3b34061b497d"}, - {file = "aiohttp-3.11.7-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:875f7100ce0e74af51d4139495eec4025affa1a605280f23990b6434b81df1bd"}, - {file = "aiohttp-3.11.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c171fc35d3174bbf4787381716564042a4cbc008824d8195eede3d9b938e29a8"}, - {file = "aiohttp-3.11.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:ee9afa1b0d2293c46954f47f33e150798ad68b78925e3710044e0d67a9487791"}, - {file = "aiohttp-3.11.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8360c7cc620abb320e1b8d603c39095101391a82b1d0be05fb2225471c9c5c52"}, - {file = "aiohttp-3.11.7-cp313-cp313-win32.whl", hash = "sha256:7a9318da4b4ada9a67c1dd84d1c0834123081e746bee311a16bb449f363d965e"}, - {file = "aiohttp-3.11.7-cp313-cp313-win_amd64.whl", hash = "sha256:fc6da202068e0a268e298d7cd09b6e9f3997736cd9b060e2750963754552a0a9"}, - {file = "aiohttp-3.11.7-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:17829f37c0d31d89aa6b8b010475a10233774771f9b6dc2cc352ea4f8ce95d9a"}, - {file = "aiohttp-3.11.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d6177077a31b1aecfc3c9070bd2f11419dbb4a70f30f4c65b124714f525c2e48"}, - {file = "aiohttp-3.11.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:badda65ac99555791eed75e234afb94686ed2317670c68bff8a4498acdaee935"}, - {file = "aiohttp-3.11.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0de6466b9d742b4ee56fe1b2440706e225eb48c77c63152b1584864a236e7a50"}, - {file = "aiohttp-3.11.7-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:04b0cc74d5a882c9dacaeeccc1444f0233212b6f5be8bc90833feef1e1ce14b9"}, - {file = "aiohttp-3.11.7-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c7af3e50e5903d21d7b935aceed901cc2475463bc16ddd5587653548661fdb"}, - {file = "aiohttp-3.11.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c63f898f683d1379b9be5afc3dd139e20b30b0b1e0bf69a3fc3681f364cf1629"}, - {file = "aiohttp-3.11.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fdadc3f6a32d6eca45f9a900a254757fd7855dfb2d8f8dcf0e88f0fae3ff8eb1"}, - {file = "aiohttp-3.11.7-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:d329300fb23e14ed1f8c6d688dfd867d1dcc3b1d7cd49b7f8c5b44e797ce0932"}, - {file = "aiohttp-3.11.7-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:5578cf40440eafcb054cf859964bc120ab52ebe0e0562d2b898126d868749629"}, - {file = "aiohttp-3.11.7-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:7b2f8107a3c329789f3c00b2daad0e35f548d0a55cda6291579136622099a46e"}, - {file = "aiohttp-3.11.7-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:43dd89a6194f6ab02a3fe36b09e42e2df19c211fc2050ce37374d96f39604997"}, - {file = "aiohttp-3.11.7-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:d2fa6fc7cc865d26ff42480ac9b52b8c9b7da30a10a6442a9cdf429de840e949"}, - {file = "aiohttp-3.11.7-cp39-cp39-win32.whl", hash = "sha256:a7d9a606355655617fee25dd7e54d3af50804d002f1fd3118dd6312d26692d70"}, - {file = "aiohttp-3.11.7-cp39-cp39-win_amd64.whl", hash = "sha256:53c921b58fdc6485d6b2603e0132bb01cd59b8f0620ffc0907f525e0ba071687"}, - {file = "aiohttp-3.11.7.tar.gz", hash = "sha256:01a8aca4af3da85cea5c90141d23f4b0eee3cbecfd33b029a45a80f28c66c668"}, + {file = "aiohttp-3.11.10-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cbad88a61fa743c5d283ad501b01c153820734118b65aee2bd7dbb735475ce0d"}, + {file = "aiohttp-3.11.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:80886dac673ceaef499de2f393fc80bb4481a129e6cb29e624a12e3296cc088f"}, + {file = "aiohttp-3.11.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:61b9bae80ed1f338c42f57c16918853dc51775fb5cb61da70d590de14d8b5fb4"}, + {file = "aiohttp-3.11.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9e2e576caec5c6a6b93f41626c9c02fc87cd91538b81a3670b2e04452a63def6"}, + {file = "aiohttp-3.11.10-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:02c13415b5732fb6ee7ff64583a5e6ed1c57aa68f17d2bda79c04888dfdc2769"}, + {file = "aiohttp-3.11.10-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4cfce37f31f20800a6a6620ce2cdd6737b82e42e06e6e9bd1b36f546feb3c44f"}, + {file = "aiohttp-3.11.10-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3bbbfff4c679c64e6e23cb213f57cc2c9165c9a65d63717108a644eb5a7398df"}, + {file = "aiohttp-3.11.10-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49c7dbbc1a559ae14fc48387a115b7d4bbc84b4a2c3b9299c31696953c2a5219"}, + {file = "aiohttp-3.11.10-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:68386d78743e6570f054fe7949d6cb37ef2b672b4d3405ce91fafa996f7d9b4d"}, + {file = "aiohttp-3.11.10-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:9ef405356ba989fb57f84cac66f7b0260772836191ccefbb987f414bcd2979d9"}, + {file = "aiohttp-3.11.10-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:5d6958671b296febe7f5f859bea581a21c1d05430d1bbdcf2b393599b1cdce77"}, + {file = "aiohttp-3.11.10-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:99b7920e7165be5a9e9a3a7f1b680f06f68ff0d0328ff4079e5163990d046767"}, + {file = "aiohttp-3.11.10-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0dc49f42422163efb7e6f1df2636fe3db72713f6cd94688e339dbe33fe06d61d"}, + {file = "aiohttp-3.11.10-cp310-cp310-win32.whl", hash = "sha256:40d1c7a7f750b5648642586ba7206999650208dbe5afbcc5284bcec6579c9b91"}, + {file = "aiohttp-3.11.10-cp310-cp310-win_amd64.whl", hash = "sha256:68ff6f48b51bd78ea92b31079817aff539f6c8fc80b6b8d6ca347d7c02384e33"}, + {file = "aiohttp-3.11.10-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:77c4aa15a89847b9891abf97f3d4048f3c2d667e00f8a623c89ad2dccee6771b"}, + {file = "aiohttp-3.11.10-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:909af95a72cedbefe5596f0bdf3055740f96c1a4baa0dd11fd74ca4de0b4e3f1"}, + {file = "aiohttp-3.11.10-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:386fbe79863eb564e9f3615b959e28b222259da0c48fd1be5929ac838bc65683"}, + {file = "aiohttp-3.11.10-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3de34936eb1a647aa919655ff8d38b618e9f6b7f250cc19a57a4bf7fd2062b6d"}, + {file = "aiohttp-3.11.10-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0c9527819b29cd2b9f52033e7fb9ff08073df49b4799c89cb5754624ecd98299"}, + {file = "aiohttp-3.11.10-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65a96e3e03300b41f261bbfd40dfdbf1c301e87eab7cd61c054b1f2e7c89b9e8"}, + {file = "aiohttp-3.11.10-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98f5635f7b74bcd4f6f72fcd85bea2154b323a9f05226a80bc7398d0c90763b0"}, + {file = "aiohttp-3.11.10-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:03b6002e20938fc6ee0918c81d9e776bebccc84690e2b03ed132331cca065ee5"}, + {file = "aiohttp-3.11.10-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6362cc6c23c08d18ddbf0e8c4d5159b5df74fea1a5278ff4f2c79aed3f4e9f46"}, + {file = "aiohttp-3.11.10-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:3691ed7726fef54e928fe26344d930c0c8575bc968c3e239c2e1a04bd8cf7838"}, + {file = "aiohttp-3.11.10-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:31d5093d3acd02b31c649d3a69bb072d539d4c7659b87caa4f6d2bcf57c2fa2b"}, + {file = "aiohttp-3.11.10-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:8b3cf2dc0f0690a33f2d2b2cb15db87a65f1c609f53c37e226f84edb08d10f52"}, + {file = "aiohttp-3.11.10-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:fbbaea811a2bba171197b08eea288b9402faa2bab2ba0858eecdd0a4105753a3"}, + {file = "aiohttp-3.11.10-cp311-cp311-win32.whl", hash = "sha256:4b2c7ac59c5698a7a8207ba72d9e9c15b0fc484a560be0788b31312c2c5504e4"}, + {file = "aiohttp-3.11.10-cp311-cp311-win_amd64.whl", hash = "sha256:974d3a2cce5fcfa32f06b13ccc8f20c6ad9c51802bb7f829eae8a1845c4019ec"}, + {file = "aiohttp-3.11.10-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b78f053a7ecfc35f0451d961dacdc671f4bcbc2f58241a7c820e9d82559844cf"}, + {file = "aiohttp-3.11.10-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ab7485222db0959a87fbe8125e233b5a6f01f4400785b36e8a7878170d8c3138"}, + {file = "aiohttp-3.11.10-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cf14627232dfa8730453752e9cdc210966490992234d77ff90bc8dc0dce361d5"}, + {file = "aiohttp-3.11.10-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:076bc454a7e6fd646bc82ea7f98296be0b1219b5e3ef8a488afbdd8e81fbac50"}, + {file = "aiohttp-3.11.10-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:482cafb7dc886bebeb6c9ba7925e03591a62ab34298ee70d3dd47ba966370d2c"}, + {file = "aiohttp-3.11.10-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bf3d1a519a324af764a46da4115bdbd566b3c73fb793ffb97f9111dbc684fc4d"}, + {file = "aiohttp-3.11.10-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24213ba85a419103e641e55c27dc7ff03536c4873470c2478cce3311ba1eee7b"}, + {file = "aiohttp-3.11.10-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b99acd4730ad1b196bfb03ee0803e4adac371ae8efa7e1cbc820200fc5ded109"}, + {file = "aiohttp-3.11.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:14cdb5a9570be5a04eec2ace174a48ae85833c2aadc86de68f55541f66ce42ab"}, + {file = "aiohttp-3.11.10-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:7e97d622cb083e86f18317282084bc9fbf261801b0192c34fe4b1febd9f7ae69"}, + {file = "aiohttp-3.11.10-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:012f176945af138abc10c4a48743327a92b4ca9adc7a0e078077cdb5dbab7be0"}, + {file = "aiohttp-3.11.10-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:44224d815853962f48fe124748227773acd9686eba6dc102578defd6fc99e8d9"}, + {file = "aiohttp-3.11.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c87bf31b7fdab94ae3adbe4a48e711bfc5f89d21cf4c197e75561def39e223bc"}, + {file = "aiohttp-3.11.10-cp312-cp312-win32.whl", hash = "sha256:06a8e2ee1cbac16fe61e51e0b0c269400e781b13bcfc33f5425912391a542985"}, + {file = "aiohttp-3.11.10-cp312-cp312-win_amd64.whl", hash = "sha256:be2b516f56ea883a3e14dda17059716593526e10fb6303189aaf5503937db408"}, + {file = "aiohttp-3.11.10-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8cc5203b817b748adccb07f36390feb730b1bc5f56683445bfe924fc270b8816"}, + {file = "aiohttp-3.11.10-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5ef359ebc6949e3a34c65ce20230fae70920714367c63afd80ea0c2702902ccf"}, + {file = "aiohttp-3.11.10-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9bca390cb247dbfaec3c664326e034ef23882c3f3bfa5fbf0b56cad0320aaca5"}, + {file = "aiohttp-3.11.10-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:811f23b3351ca532af598405db1093f018edf81368e689d1b508c57dcc6b6a32"}, + {file = "aiohttp-3.11.10-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ddf5f7d877615f6a1e75971bfa5ac88609af3b74796ff3e06879e8422729fd01"}, + {file = "aiohttp-3.11.10-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6ab29b8a0beb6f8eaf1e5049252cfe74adbaafd39ba91e10f18caeb0e99ffb34"}, + {file = "aiohttp-3.11.10-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c49a76c1038c2dd116fa443eba26bbb8e6c37e924e2513574856de3b6516be99"}, + {file = "aiohttp-3.11.10-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7f3dc0e330575f5b134918976a645e79adf333c0a1439dcf6899a80776c9ab39"}, + {file = "aiohttp-3.11.10-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:efb15a17a12497685304b2d976cb4939e55137df7b09fa53f1b6a023f01fcb4e"}, + {file = "aiohttp-3.11.10-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:db1d0b28fcb7f1d35600150c3e4b490775251dea70f894bf15c678fdd84eda6a"}, + {file = "aiohttp-3.11.10-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:15fccaf62a4889527539ecb86834084ecf6e9ea70588efde86e8bc775e0e7542"}, + {file = "aiohttp-3.11.10-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:593c114a2221444f30749cc5e5f4012488f56bd14de2af44fe23e1e9894a9c60"}, + {file = "aiohttp-3.11.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7852bbcb4d0d2f0c4d583f40c3bc750ee033265d80598d0f9cb6f372baa6b836"}, + {file = "aiohttp-3.11.10-cp313-cp313-win32.whl", hash = "sha256:65e55ca7debae8faaffee0ebb4b47a51b4075f01e9b641c31e554fd376595c6c"}, + {file = "aiohttp-3.11.10-cp313-cp313-win_amd64.whl", hash = "sha256:beb39a6d60a709ae3fb3516a1581777e7e8b76933bb88c8f4420d875bb0267c6"}, + {file = "aiohttp-3.11.10-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0580f2e12de2138f34debcd5d88894786453a76e98febaf3e8fe5db62d01c9bf"}, + {file = "aiohttp-3.11.10-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a55d2ad345684e7c3dd2c20d2f9572e9e1d5446d57200ff630e6ede7612e307f"}, + {file = "aiohttp-3.11.10-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:04814571cb72d65a6899db6099e377ed00710bf2e3eafd2985166f2918beaf59"}, + {file = "aiohttp-3.11.10-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e44a9a3c053b90c6f09b1bb4edd880959f5328cf63052503f892c41ea786d99f"}, + {file = "aiohttp-3.11.10-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:502a1464ccbc800b4b1995b302efaf426e8763fadf185e933c2931df7db9a199"}, + {file = "aiohttp-3.11.10-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:613e5169f8ae77b1933e42e418a95931fb4867b2991fc311430b15901ed67079"}, + {file = "aiohttp-3.11.10-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cca22a61b7fe45da8fc73c3443150c3608750bbe27641fc7558ec5117b27fdf"}, + {file = "aiohttp-3.11.10-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:86a5dfcc39309470bd7b68c591d84056d195428d5d2e0b5ccadfbaf25b026ebc"}, + {file = "aiohttp-3.11.10-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:77ae58586930ee6b2b6f696c82cf8e78c8016ec4795c53e36718365f6959dc82"}, + {file = "aiohttp-3.11.10-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:78153314f26d5abef3239b4a9af20c229c6f3ecb97d4c1c01b22c4f87669820c"}, + {file = "aiohttp-3.11.10-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:98283b94cc0e11c73acaf1c9698dea80c830ca476492c0fe2622bd931f34b487"}, + {file = "aiohttp-3.11.10-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:53bf2097e05c2accc166c142a2090e4c6fd86581bde3fd9b2d3f9e93dda66ac1"}, + {file = "aiohttp-3.11.10-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c5532f0441fc09c119e1dca18fbc0687e64fbeb45aa4d6a87211ceaee50a74c4"}, + {file = "aiohttp-3.11.10-cp39-cp39-win32.whl", hash = "sha256:47ad15a65fb41c570cd0ad9a9ff8012489e68176e7207ec7b82a0940dddfd8be"}, + {file = "aiohttp-3.11.10-cp39-cp39-win_amd64.whl", hash = "sha256:c6b9e6d7e41656d78e37ce754813fa44b455c3d0d0dced2a047def7dc5570b74"}, + {file = "aiohttp-3.11.10.tar.gz", hash = "sha256:b1fc6b45010a8d0ff9e88f9f2418c6fd408c99c211257334aff41597ebece42e"}, ] [package.dependencies] @@ -111,13 +111,13 @@ speedups = ["Brotli", "aiodns (>=3.2.0)", "brotlicffi"] [[package]] name = "aiosignal" -version = "1.3.1" +version = "1.3.2" description = "aiosignal: a list of registered asynchronous callbacks" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ - {file = "aiosignal-1.3.1-py3-none-any.whl", hash = "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17"}, - {file = "aiosignal-1.3.1.tar.gz", hash = "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc"}, + {file = "aiosignal-1.3.2-py2.py3-none-any.whl", hash = "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5"}, + {file = "aiosignal-1.3.2.tar.gz", hash = "sha256:a8c255c66fafb1e499c9351d0bf32ff2d8a0321595ebac3b93713656d2436f54"}, ] [package.dependencies] @@ -136,24 +136,24 @@ files = [ [[package]] name = "anyio" -version = "4.6.2.post1" +version = "4.7.0" description = "High level compatibility layer for multiple asynchronous event loop implementations" optional = false python-versions = ">=3.9" files = [ - {file = "anyio-4.6.2.post1-py3-none-any.whl", hash = "sha256:6d170c36fba3bdd840c73d3868c1e777e33676a69c3a72cf0a0d5d6d8009b61d"}, - {file = "anyio-4.6.2.post1.tar.gz", hash = "sha256:4c8bc31ccdb51c7f7bd251f51c609e038d63e34219b44aa86e47576389880b4c"}, + {file = "anyio-4.7.0-py3-none-any.whl", hash = "sha256:ea60c3723ab42ba6fff7e8ccb0488c898ec538ff4df1f1d5e642c3601d07e352"}, + {file = "anyio-4.7.0.tar.gz", hash = "sha256:2f834749c602966b7d456a7567cafcb309f96482b5081d14ac93ccd457f9dd48"}, ] [package.dependencies] exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""} idna = ">=2.8" sniffio = ">=1.1" -typing-extensions = {version = ">=4.1", markers = "python_version < \"3.11\""} +typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""} [package.extras] -doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] -test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21.0b1)"] +doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx_rtd_theme"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21)"] trio = ["trio (>=0.26.1)"] [[package]] @@ -245,21 +245,18 @@ test = ["dateparser (==1.*)", "pre-commit", "pytest", "pytest-cov", "pytest-mock [[package]] name = "asttokens" -version = "2.4.1" +version = "3.0.0" description = "Annotate AST trees with source code positions" optional = false -python-versions = "*" +python-versions = ">=3.8" files = [ - {file = "asttokens-2.4.1-py2.py3-none-any.whl", hash = "sha256:051ed49c3dcae8913ea7cd08e46a606dba30b79993209636c4875bc1d637bc24"}, - {file = "asttokens-2.4.1.tar.gz", hash = "sha256:b03869718ba9a6eb027e134bfdf69f38a236d681c83c160d510768af11254ba0"}, + {file = "asttokens-3.0.0-py3-none-any.whl", hash = "sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2"}, + {file = "asttokens-3.0.0.tar.gz", hash = "sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7"}, ] -[package.dependencies] -six = ">=1.12.0" - [package.extras] -astroid = ["astroid (>=1,<2)", "astroid (>=2,<4)"] -test = ["astroid (>=1,<2)", "astroid (>=2,<4)", "pytest"] +astroid = ["astroid (>=2,<4)"] +test = ["astroid (>=2,<4)", "pytest", "pytest-cov", "pytest-xdist"] [[package]] name = "async-lru" @@ -359,13 +356,13 @@ css = ["tinycss2 (>=1.1.0,<1.5)"] [[package]] name = "certifi" -version = "2024.8.30" +version = "2024.12.14" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"}, - {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, + {file = "certifi-2024.12.14-py3-none-any.whl", hash = "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56"}, + {file = "certifi-2024.12.14.tar.gz", hash = "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db"}, ] [[package]] @@ -687,73 +684,73 @@ test = ["pytest"] [[package]] name = "coverage" -version = "7.6.8" +version = "7.6.9" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.9" files = [ - {file = "coverage-7.6.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b39e6011cd06822eb964d038d5dff5da5d98652b81f5ecd439277b32361a3a50"}, - {file = "coverage-7.6.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:63c19702db10ad79151a059d2d6336fe0c470f2e18d0d4d1a57f7f9713875dcf"}, - {file = "coverage-7.6.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3985b9be361d8fb6b2d1adc9924d01dec575a1d7453a14cccd73225cb79243ee"}, - {file = "coverage-7.6.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:644ec81edec0f4ad17d51c838a7d01e42811054543b76d4ba2c5d6af741ce2a6"}, - {file = "coverage-7.6.8-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f188a2402f8359cf0c4b1fe89eea40dc13b52e7b4fd4812450da9fcd210181d"}, - {file = "coverage-7.6.8-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e19122296822deafce89a0c5e8685704c067ae65d45e79718c92df7b3ec3d331"}, - {file = "coverage-7.6.8-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:13618bed0c38acc418896005732e565b317aa9e98d855a0e9f211a7ffc2d6638"}, - {file = "coverage-7.6.8-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:193e3bffca48ad74b8c764fb4492dd875038a2f9925530cb094db92bb5e47bed"}, - {file = "coverage-7.6.8-cp310-cp310-win32.whl", hash = "sha256:3988665ee376abce49613701336544041f2117de7b7fbfe91b93d8ff8b151c8e"}, - {file = "coverage-7.6.8-cp310-cp310-win_amd64.whl", hash = "sha256:f56f49b2553d7dd85fd86e029515a221e5c1f8cb3d9c38b470bc38bde7b8445a"}, - {file = "coverage-7.6.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:86cffe9c6dfcfe22e28027069725c7f57f4b868a3f86e81d1c62462764dc46d4"}, - {file = "coverage-7.6.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d82ab6816c3277dc962cfcdc85b1efa0e5f50fb2c449432deaf2398a2928ab94"}, - {file = "coverage-7.6.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:13690e923a3932e4fad4c0ebfb9cb5988e03d9dcb4c5150b5fcbf58fd8bddfc4"}, - {file = "coverage-7.6.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4be32da0c3827ac9132bb488d331cb32e8d9638dd41a0557c5569d57cf22c9c1"}, - {file = "coverage-7.6.8-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:44e6c85bbdc809383b509d732b06419fb4544dca29ebe18480379633623baafb"}, - {file = "coverage-7.6.8-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:768939f7c4353c0fac2f7c37897e10b1414b571fd85dd9fc49e6a87e37a2e0d8"}, - {file = "coverage-7.6.8-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e44961e36cb13c495806d4cac67640ac2866cb99044e210895b506c26ee63d3a"}, - {file = "coverage-7.6.8-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3ea8bb1ab9558374c0ab591783808511d135a833c3ca64a18ec927f20c4030f0"}, - {file = "coverage-7.6.8-cp311-cp311-win32.whl", hash = "sha256:629a1ba2115dce8bf75a5cce9f2486ae483cb89c0145795603d6554bdc83e801"}, - {file = "coverage-7.6.8-cp311-cp311-win_amd64.whl", hash = "sha256:fb9fc32399dca861584d96eccd6c980b69bbcd7c228d06fb74fe53e007aa8ef9"}, - {file = "coverage-7.6.8-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e683e6ecc587643f8cde8f5da6768e9d165cd31edf39ee90ed7034f9ca0eefee"}, - {file = "coverage-7.6.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1defe91d41ce1bd44b40fabf071e6a01a5aa14de4a31b986aa9dfd1b3e3e414a"}, - {file = "coverage-7.6.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7ad66e8e50225ebf4236368cc43c37f59d5e6728f15f6e258c8639fa0dd8e6d"}, - {file = "coverage-7.6.8-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3fe47da3e4fda5f1abb5709c156eca207eacf8007304ce3019eb001e7a7204cb"}, - {file = "coverage-7.6.8-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:202a2d645c5a46b84992f55b0a3affe4f0ba6b4c611abec32ee88358db4bb649"}, - {file = "coverage-7.6.8-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4674f0daa1823c295845b6a740d98a840d7a1c11df00d1fd62614545c1583787"}, - {file = "coverage-7.6.8-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:74610105ebd6f33d7c10f8907afed696e79c59e3043c5f20eaa3a46fddf33b4c"}, - {file = "coverage-7.6.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:37cda8712145917105e07aab96388ae76e787270ec04bcb9d5cc786d7cbb8443"}, - {file = "coverage-7.6.8-cp312-cp312-win32.whl", hash = "sha256:9e89d5c8509fbd6c03d0dd1972925b22f50db0792ce06324ba069f10787429ad"}, - {file = "coverage-7.6.8-cp312-cp312-win_amd64.whl", hash = "sha256:379c111d3558272a2cae3d8e57e6b6e6f4fe652905692d54bad5ea0ca37c5ad4"}, - {file = "coverage-7.6.8-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0b0c69f4f724c64dfbfe79f5dfb503b42fe6127b8d479b2677f2b227478db2eb"}, - {file = "coverage-7.6.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c15b32a7aca8038ed7644f854bf17b663bc38e1671b5d6f43f9a2b2bd0c46f63"}, - {file = "coverage-7.6.8-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63068a11171e4276f6ece913bde059e77c713b48c3a848814a6537f35afb8365"}, - {file = "coverage-7.6.8-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f4548c5ead23ad13fb7a2c8ea541357474ec13c2b736feb02e19a3085fac002"}, - {file = "coverage-7.6.8-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b4b4299dd0d2c67caaaf286d58aef5e75b125b95615dda4542561a5a566a1e3"}, - {file = "coverage-7.6.8-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c9ebfb2507751f7196995142f057d1324afdab56db1d9743aab7f50289abd022"}, - {file = "coverage-7.6.8-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:c1b4474beee02ede1eef86c25ad4600a424fe36cff01a6103cb4533c6bf0169e"}, - {file = "coverage-7.6.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d9fd2547e6decdbf985d579cf3fc78e4c1d662b9b0ff7cc7862baaab71c9cc5b"}, - {file = "coverage-7.6.8-cp313-cp313-win32.whl", hash = "sha256:8aae5aea53cbfe024919715eca696b1a3201886ce83790537d1c3668459c7146"}, - {file = "coverage-7.6.8-cp313-cp313-win_amd64.whl", hash = "sha256:ae270e79f7e169ccfe23284ff5ea2d52a6f401dc01b337efb54b3783e2ce3f28"}, - {file = "coverage-7.6.8-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:de38add67a0af869b0d79c525d3e4588ac1ffa92f39116dbe0ed9753f26eba7d"}, - {file = "coverage-7.6.8-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b07c25d52b1c16ce5de088046cd2432b30f9ad5e224ff17c8f496d9cb7d1d451"}, - {file = "coverage-7.6.8-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62a66ff235e4c2e37ed3b6104d8b478d767ff73838d1222132a7a026aa548764"}, - {file = "coverage-7.6.8-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09b9f848b28081e7b975a3626e9081574a7b9196cde26604540582da60235fdf"}, - {file = "coverage-7.6.8-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:093896e530c38c8e9c996901858ac63f3d4171268db2c9c8b373a228f459bbc5"}, - {file = "coverage-7.6.8-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9a7b8ac36fd688c8361cbc7bf1cb5866977ece6e0b17c34aa0df58bda4fa18a4"}, - {file = "coverage-7.6.8-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:38c51297b35b3ed91670e1e4efb702b790002e3245a28c76e627478aa3c10d83"}, - {file = "coverage-7.6.8-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2e4e0f60cb4bd7396108823548e82fdab72d4d8a65e58e2c19bbbc2f1e2bfa4b"}, - {file = "coverage-7.6.8-cp313-cp313t-win32.whl", hash = "sha256:6535d996f6537ecb298b4e287a855f37deaf64ff007162ec0afb9ab8ba3b8b71"}, - {file = "coverage-7.6.8-cp313-cp313t-win_amd64.whl", hash = "sha256:c79c0685f142ca53256722a384540832420dff4ab15fec1863d7e5bc8691bdcc"}, - {file = "coverage-7.6.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3ac47fa29d8d41059ea3df65bd3ade92f97ee4910ed638e87075b8e8ce69599e"}, - {file = "coverage-7.6.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:24eda3a24a38157eee639ca9afe45eefa8d2420d49468819ac5f88b10de84f4c"}, - {file = "coverage-7.6.8-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4c81ed2820b9023a9a90717020315e63b17b18c274a332e3b6437d7ff70abe0"}, - {file = "coverage-7.6.8-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd55f8fc8fa494958772a2a7302b0354ab16e0b9272b3c3d83cdb5bec5bd1779"}, - {file = "coverage-7.6.8-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f39e2f3530ed1626c66e7493be7a8423b023ca852aacdc91fb30162c350d2a92"}, - {file = "coverage-7.6.8-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:716a78a342679cd1177bc8c2fe957e0ab91405bd43a17094324845200b2fddf4"}, - {file = "coverage-7.6.8-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:177f01eeaa3aee4a5ffb0d1439c5952b53d5010f86e9d2667963e632e30082cc"}, - {file = "coverage-7.6.8-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:912e95017ff51dc3d7b6e2be158dedc889d9a5cc3382445589ce554f1a34c0ea"}, - {file = "coverage-7.6.8-cp39-cp39-win32.whl", hash = "sha256:4db3ed6a907b555e57cc2e6f14dc3a4c2458cdad8919e40b5357ab9b6db6c43e"}, - {file = "coverage-7.6.8-cp39-cp39-win_amd64.whl", hash = "sha256:428ac484592f780e8cd7b6b14eb568f7c85460c92e2a37cb0c0e5186e1a0d076"}, - {file = "coverage-7.6.8-pp39.pp310-none-any.whl", hash = "sha256:5c52a036535d12590c32c49209e79cabaad9f9ad8aa4cbd875b68c4d67a9cbce"}, - {file = "coverage-7.6.8.tar.gz", hash = "sha256:8b2b8503edb06822c86d82fa64a4a5cb0760bb8f31f26e138ec743f422f37cfc"}, + {file = "coverage-7.6.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:85d9636f72e8991a1706b2b55b06c27545448baf9f6dbf51c4004609aacd7dcb"}, + {file = "coverage-7.6.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:608a7fd78c67bee8936378299a6cb9f5149bb80238c7a566fc3e6717a4e68710"}, + {file = "coverage-7.6.9-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:96d636c77af18b5cb664ddf12dab9b15a0cfe9c0bde715da38698c8cea748bfa"}, + {file = "coverage-7.6.9-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d75cded8a3cff93da9edc31446872d2997e327921d8eed86641efafd350e1df1"}, + {file = "coverage-7.6.9-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7b15f589593110ae767ce997775d645b47e5cbbf54fd322f8ebea6277466cec"}, + {file = "coverage-7.6.9-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:44349150f6811b44b25574839b39ae35291f6496eb795b7366fef3bd3cf112d3"}, + {file = "coverage-7.6.9-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:d891c136b5b310d0e702e186d70cd16d1119ea8927347045124cb286b29297e5"}, + {file = "coverage-7.6.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:db1dab894cc139f67822a92910466531de5ea6034ddfd2b11c0d4c6257168073"}, + {file = "coverage-7.6.9-cp310-cp310-win32.whl", hash = "sha256:41ff7b0da5af71a51b53f501a3bac65fb0ec311ebed1632e58fc6107f03b9198"}, + {file = "coverage-7.6.9-cp310-cp310-win_amd64.whl", hash = "sha256:35371f8438028fdccfaf3570b31d98e8d9eda8bb1d6ab9473f5a390969e98717"}, + {file = "coverage-7.6.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:932fc826442132dde42ee52cf66d941f581c685a6313feebed358411238f60f9"}, + {file = "coverage-7.6.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:085161be5f3b30fd9b3e7b9a8c301f935c8313dcf928a07b116324abea2c1c2c"}, + {file = "coverage-7.6.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ccc660a77e1c2bf24ddbce969af9447a9474790160cfb23de6be4fa88e3951c7"}, + {file = "coverage-7.6.9-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c69e42c892c018cd3c8d90da61d845f50a8243062b19d228189b0224150018a9"}, + {file = "coverage-7.6.9-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0824a28ec542a0be22f60c6ac36d679e0e262e5353203bea81d44ee81fe9c6d4"}, + {file = "coverage-7.6.9-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4401ae5fc52ad8d26d2a5d8a7428b0f0c72431683f8e63e42e70606374c311a1"}, + {file = "coverage-7.6.9-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:98caba4476a6c8d59ec1eb00c7dd862ba9beca34085642d46ed503cc2d440d4b"}, + {file = "coverage-7.6.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ee5defd1733fd6ec08b168bd4f5387d5b322f45ca9e0e6c817ea6c4cd36313e3"}, + {file = "coverage-7.6.9-cp311-cp311-win32.whl", hash = "sha256:f2d1ec60d6d256bdf298cb86b78dd715980828f50c46701abc3b0a2b3f8a0dc0"}, + {file = "coverage-7.6.9-cp311-cp311-win_amd64.whl", hash = "sha256:0d59fd927b1f04de57a2ba0137166d31c1a6dd9e764ad4af552912d70428c92b"}, + {file = "coverage-7.6.9-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:99e266ae0b5d15f1ca8d278a668df6f51cc4b854513daab5cae695ed7b721cf8"}, + {file = "coverage-7.6.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9901d36492009a0a9b94b20e52ebfc8453bf49bb2b27bca2c9706f8b4f5a554a"}, + {file = "coverage-7.6.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abd3e72dd5b97e3af4246cdada7738ef0e608168de952b837b8dd7e90341f015"}, + {file = "coverage-7.6.9-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff74026a461eb0660366fb01c650c1d00f833a086b336bdad7ab00cc952072b3"}, + {file = "coverage-7.6.9-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65dad5a248823a4996724a88eb51d4b31587aa7aa428562dbe459c684e5787ae"}, + {file = "coverage-7.6.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:22be16571504c9ccea919fcedb459d5ab20d41172056206eb2994e2ff06118a4"}, + {file = "coverage-7.6.9-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f957943bc718b87144ecaee70762bc2bc3f1a7a53c7b861103546d3a403f0a6"}, + {file = "coverage-7.6.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0ae1387db4aecb1f485fb70a6c0148c6cdaebb6038f1d40089b1fc84a5db556f"}, + {file = "coverage-7.6.9-cp312-cp312-win32.whl", hash = "sha256:1a330812d9cc7ac2182586f6d41b4d0fadf9be9049f350e0efb275c8ee8eb692"}, + {file = "coverage-7.6.9-cp312-cp312-win_amd64.whl", hash = "sha256:b12c6b18269ca471eedd41c1b6a1065b2f7827508edb9a7ed5555e9a56dcfc97"}, + {file = "coverage-7.6.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:899b8cd4781c400454f2f64f7776a5d87bbd7b3e7f7bda0cb18f857bb1334664"}, + {file = "coverage-7.6.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:61f70dc68bd36810972e55bbbe83674ea073dd1dcc121040a08cdf3416c5349c"}, + {file = "coverage-7.6.9-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a289d23d4c46f1a82d5db4abeb40b9b5be91731ee19a379d15790e53031c014"}, + {file = "coverage-7.6.9-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e216d8044a356fc0337c7a2a0536d6de07888d7bcda76febcb8adc50bdbbd00"}, + {file = "coverage-7.6.9-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c026eb44f744acaa2bda7493dad903aa5bf5fc4f2554293a798d5606710055d"}, + {file = "coverage-7.6.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e77363e8425325384f9d49272c54045bbed2f478e9dd698dbc65dbc37860eb0a"}, + {file = "coverage-7.6.9-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:777abfab476cf83b5177b84d7486497e034eb9eaea0d746ce0c1268c71652077"}, + {file = "coverage-7.6.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:447af20e25fdbe16f26e84eb714ba21d98868705cb138252d28bc400381f6ffb"}, + {file = "coverage-7.6.9-cp313-cp313-win32.whl", hash = "sha256:d872ec5aeb086cbea771c573600d47944eea2dcba8be5f3ee649bfe3cb8dc9ba"}, + {file = "coverage-7.6.9-cp313-cp313-win_amd64.whl", hash = "sha256:fd1213c86e48dfdc5a0cc676551db467495a95a662d2396ecd58e719191446e1"}, + {file = "coverage-7.6.9-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:ba9e7484d286cd5a43744e5f47b0b3fb457865baf07bafc6bee91896364e1419"}, + {file = "coverage-7.6.9-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e5ea1cf0872ee455c03e5674b5bca5e3e68e159379c1af0903e89f5eba9ccc3a"}, + {file = "coverage-7.6.9-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d10e07aa2b91835d6abec555ec8b2733347956991901eea6ffac295f83a30e4"}, + {file = "coverage-7.6.9-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:13a9e2d3ee855db3dd6ea1ba5203316a1b1fd8eaeffc37c5b54987e61e4194ae"}, + {file = "coverage-7.6.9-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c38bf15a40ccf5619fa2fe8f26106c7e8e080d7760aeccb3722664c8656b030"}, + {file = "coverage-7.6.9-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:d5275455b3e4627c8e7154feaf7ee0743c2e7af82f6e3b561967b1cca755a0be"}, + {file = "coverage-7.6.9-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8f8770dfc6e2c6a2d4569f411015c8d751c980d17a14b0530da2d7f27ffdd88e"}, + {file = "coverage-7.6.9-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8d2dfa71665a29b153a9681edb1c8d9c1ea50dfc2375fb4dac99ea7e21a0bcd9"}, + {file = "coverage-7.6.9-cp313-cp313t-win32.whl", hash = "sha256:5e6b86b5847a016d0fbd31ffe1001b63355ed309651851295315031ea7eb5a9b"}, + {file = "coverage-7.6.9-cp313-cp313t-win_amd64.whl", hash = "sha256:97ddc94d46088304772d21b060041c97fc16bdda13c6c7f9d8fcd8d5ae0d8611"}, + {file = "coverage-7.6.9-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:adb697c0bd35100dc690de83154627fbab1f4f3c0386df266dded865fc50a902"}, + {file = "coverage-7.6.9-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:be57b6d56e49c2739cdf776839a92330e933dd5e5d929966fbbd380c77f060be"}, + {file = "coverage-7.6.9-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1592791f8204ae9166de22ba7e6705fa4ebd02936c09436a1bb85aabca3e599"}, + {file = "coverage-7.6.9-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4e12ae8cc979cf83d258acb5e1f1cf2f3f83524d1564a49d20b8bec14b637f08"}, + {file = "coverage-7.6.9-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb5555cff66c4d3d6213a296b360f9e1a8e323e74e0426b6c10ed7f4d021e464"}, + {file = "coverage-7.6.9-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:b9389a429e0e5142e69d5bf4a435dd688c14478a19bb901735cdf75e57b13845"}, + {file = "coverage-7.6.9-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:592ac539812e9b46046620341498caf09ca21023c41c893e1eb9dbda00a70cbf"}, + {file = "coverage-7.6.9-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a27801adef24cc30871da98a105f77995e13a25a505a0161911f6aafbd66e678"}, + {file = "coverage-7.6.9-cp39-cp39-win32.whl", hash = "sha256:8e3c3e38930cfb729cb8137d7f055e5a473ddaf1217966aa6238c88bd9fd50e6"}, + {file = "coverage-7.6.9-cp39-cp39-win_amd64.whl", hash = "sha256:e28bf44afa2b187cc9f41749138a64435bf340adfcacb5b2290c070ce99839d4"}, + {file = "coverage-7.6.9-pp39.pp310-none-any.whl", hash = "sha256:f3ca78518bc6bc92828cd11867b121891d75cae4ea9e908d72030609b996db1b"}, + {file = "coverage-7.6.9.tar.gz", hash = "sha256:4a8d8977b0c6ef5aeadcb644da9e69ae0dcfe66ec7f368c89c72e058bd71164d"}, ] [package.dependencies] @@ -828,37 +825,37 @@ typing-inspect = ">=0.4.0,<1" [[package]] name = "debugpy" -version = "1.8.9" +version = "1.8.11" description = "An implementation of the Debug Adapter Protocol for Python" optional = false python-versions = ">=3.8" files = [ - {file = "debugpy-1.8.9-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:cfe1e6c6ad7178265f74981edf1154ffce97b69005212fbc90ca22ddfe3d017e"}, - {file = "debugpy-1.8.9-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ada7fb65102a4d2c9ab62e8908e9e9f12aed9d76ef44880367bc9308ebe49a0f"}, - {file = "debugpy-1.8.9-cp310-cp310-win32.whl", hash = "sha256:c36856343cbaa448171cba62a721531e10e7ffb0abff838004701454149bc037"}, - {file = "debugpy-1.8.9-cp310-cp310-win_amd64.whl", hash = "sha256:17c5e0297678442511cf00a745c9709e928ea4ca263d764e90d233208889a19e"}, - {file = "debugpy-1.8.9-cp311-cp311-macosx_14_0_universal2.whl", hash = "sha256:b74a49753e21e33e7cf030883a92fa607bddc4ede1aa4145172debc637780040"}, - {file = "debugpy-1.8.9-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62d22dacdb0e296966d7d74a7141aaab4bec123fa43d1a35ddcb39bf9fd29d70"}, - {file = "debugpy-1.8.9-cp311-cp311-win32.whl", hash = "sha256:8138efff315cd09b8dcd14226a21afda4ca582284bf4215126d87342bba1cc66"}, - {file = "debugpy-1.8.9-cp311-cp311-win_amd64.whl", hash = "sha256:ff54ef77ad9f5c425398efb150239f6fe8e20c53ae2f68367eba7ece1e96226d"}, - {file = "debugpy-1.8.9-cp312-cp312-macosx_14_0_universal2.whl", hash = "sha256:957363d9a7a6612a37458d9a15e72d03a635047f946e5fceee74b50d52a9c8e2"}, - {file = "debugpy-1.8.9-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e565fc54b680292b418bb809f1386f17081d1346dca9a871bf69a8ac4071afe"}, - {file = "debugpy-1.8.9-cp312-cp312-win32.whl", hash = "sha256:3e59842d6c4569c65ceb3751075ff8d7e6a6ada209ceca6308c9bde932bcef11"}, - {file = "debugpy-1.8.9-cp312-cp312-win_amd64.whl", hash = "sha256:66eeae42f3137eb428ea3a86d4a55f28da9bd5a4a3d369ba95ecc3a92c1bba53"}, - {file = "debugpy-1.8.9-cp313-cp313-macosx_14_0_universal2.whl", hash = "sha256:957ecffff80d47cafa9b6545de9e016ae8c9547c98a538ee96ab5947115fb3dd"}, - {file = "debugpy-1.8.9-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1efbb3ff61487e2c16b3e033bc8595aea578222c08aaf3c4bf0f93fadbd662ee"}, - {file = "debugpy-1.8.9-cp313-cp313-win32.whl", hash = "sha256:7c4d65d03bee875bcb211c76c1d8f10f600c305dbd734beaed4077e902606fee"}, - {file = "debugpy-1.8.9-cp313-cp313-win_amd64.whl", hash = "sha256:e46b420dc1bea64e5bbedd678148be512442bc589b0111bd799367cde051e71a"}, - {file = "debugpy-1.8.9-cp38-cp38-macosx_14_0_x86_64.whl", hash = "sha256:472a3994999fe6c0756945ffa359e9e7e2d690fb55d251639d07208dbc37caea"}, - {file = "debugpy-1.8.9-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:365e556a4772d7d0d151d7eb0e77ec4db03bcd95f26b67b15742b88cacff88e9"}, - {file = "debugpy-1.8.9-cp38-cp38-win32.whl", hash = "sha256:54a7e6d3014c408eb37b0b06021366ee985f1539e12fe49ca2ee0d392d9ceca5"}, - {file = "debugpy-1.8.9-cp38-cp38-win_amd64.whl", hash = "sha256:8e99c0b1cc7bf86d83fb95d5ccdc4ad0586d4432d489d1f54e4055bcc795f693"}, - {file = "debugpy-1.8.9-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:7e8b079323a56f719977fde9d8115590cb5e7a1cba2fcee0986ef8817116e7c1"}, - {file = "debugpy-1.8.9-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6953b335b804a41f16a192fa2e7851bdcfd92173cbb2f9f777bb934f49baab65"}, - {file = "debugpy-1.8.9-cp39-cp39-win32.whl", hash = "sha256:7e646e62d4602bb8956db88b1e72fe63172148c1e25c041e03b103a25f36673c"}, - {file = "debugpy-1.8.9-cp39-cp39-win_amd64.whl", hash = "sha256:3d9755e77a2d680ce3d2c5394a444cf42be4a592caaf246dbfbdd100ffcf7ae5"}, - {file = "debugpy-1.8.9-py2.py3-none-any.whl", hash = "sha256:cc37a6c9987ad743d9c3a14fa1b1a14b7e4e6041f9dd0c8abf8895fe7a97b899"}, - {file = "debugpy-1.8.9.zip", hash = "sha256:1339e14c7d980407248f09824d1b25ff5c5616651689f1e0f0e51bdead3ea13e"}, + {file = "debugpy-1.8.11-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:2b26fefc4e31ff85593d68b9022e35e8925714a10ab4858fb1b577a8a48cb8cd"}, + {file = "debugpy-1.8.11-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61bc8b3b265e6949855300e84dc93d02d7a3a637f2aec6d382afd4ceb9120c9f"}, + {file = "debugpy-1.8.11-cp310-cp310-win32.whl", hash = "sha256:c928bbf47f65288574b78518449edaa46c82572d340e2750889bbf8cd92f3737"}, + {file = "debugpy-1.8.11-cp310-cp310-win_amd64.whl", hash = "sha256:8da1db4ca4f22583e834dcabdc7832e56fe16275253ee53ba66627b86e304da1"}, + {file = "debugpy-1.8.11-cp311-cp311-macosx_14_0_universal2.whl", hash = "sha256:85de8474ad53ad546ff1c7c7c89230db215b9b8a02754d41cb5a76f70d0be296"}, + {file = "debugpy-1.8.11-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ffc382e4afa4aee367bf413f55ed17bd91b191dcaf979890af239dda435f2a1"}, + {file = "debugpy-1.8.11-cp311-cp311-win32.whl", hash = "sha256:40499a9979c55f72f4eb2fc38695419546b62594f8af194b879d2a18439c97a9"}, + {file = "debugpy-1.8.11-cp311-cp311-win_amd64.whl", hash = "sha256:987bce16e86efa86f747d5151c54e91b3c1e36acc03ce1ddb50f9d09d16ded0e"}, + {file = "debugpy-1.8.11-cp312-cp312-macosx_14_0_universal2.whl", hash = "sha256:84e511a7545d11683d32cdb8f809ef63fc17ea2a00455cc62d0a4dbb4ed1c308"}, + {file = "debugpy-1.8.11-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce291a5aca4985d82875d6779f61375e959208cdf09fcec40001e65fb0a54768"}, + {file = "debugpy-1.8.11-cp312-cp312-win32.whl", hash = "sha256:28e45b3f827d3bf2592f3cf7ae63282e859f3259db44ed2b129093ca0ac7940b"}, + {file = "debugpy-1.8.11-cp312-cp312-win_amd64.whl", hash = "sha256:44b1b8e6253bceada11f714acf4309ffb98bfa9ac55e4fce14f9e5d4484287a1"}, + {file = "debugpy-1.8.11-cp313-cp313-macosx_14_0_universal2.whl", hash = "sha256:8988f7163e4381b0da7696f37eec7aca19deb02e500245df68a7159739bbd0d3"}, + {file = "debugpy-1.8.11-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c1f6a173d1140e557347419767d2b14ac1c9cd847e0b4c5444c7f3144697e4e"}, + {file = "debugpy-1.8.11-cp313-cp313-win32.whl", hash = "sha256:bb3b15e25891f38da3ca0740271e63ab9db61f41d4d8541745cfc1824252cb28"}, + {file = "debugpy-1.8.11-cp313-cp313-win_amd64.whl", hash = "sha256:d8768edcbeb34da9e11bcb8b5c2e0958d25218df7a6e56adf415ef262cd7b6d1"}, + {file = "debugpy-1.8.11-cp38-cp38-macosx_14_0_x86_64.whl", hash = "sha256:ad7efe588c8f5cf940f40c3de0cd683cc5b76819446abaa50dc0829a30c094db"}, + {file = "debugpy-1.8.11-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:189058d03a40103a57144752652b3ab08ff02b7595d0ce1f651b9acc3a3a35a0"}, + {file = "debugpy-1.8.11-cp38-cp38-win32.whl", hash = "sha256:32db46ba45849daed7ccf3f2e26f7a386867b077f39b2a974bb5c4c2c3b0a280"}, + {file = "debugpy-1.8.11-cp38-cp38-win_amd64.whl", hash = "sha256:116bf8342062246ca749013df4f6ea106f23bc159305843491f64672a55af2e5"}, + {file = "debugpy-1.8.11-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:654130ca6ad5de73d978057eaf9e582244ff72d4574b3e106fb8d3d2a0d32458"}, + {file = "debugpy-1.8.11-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23dc34c5e03b0212fa3c49a874df2b8b1b8fda95160bd79c01eb3ab51ea8d851"}, + {file = "debugpy-1.8.11-cp39-cp39-win32.whl", hash = "sha256:52d8a3166c9f2815bfae05f386114b0b2d274456980d41f320299a8d9a5615a7"}, + {file = "debugpy-1.8.11-cp39-cp39-win_amd64.whl", hash = "sha256:52c3cf9ecda273a19cc092961ee34eb9ba8687d67ba34cc7b79a521c1c64c4c0"}, + {file = "debugpy-1.8.11-py2.py3-none-any.whl", hash = "sha256:0e22f846f4211383e6a416d04b4c13ed174d24cc5d43f5fd52e7821d0ebc8920"}, + {file = "debugpy-1.8.11.tar.gz", hash = "sha256:6ad2688b69235c43b020e04fecccdf6a96c8943ca9c2fb340b8adc103c655e57"}, ] [[package]] @@ -990,13 +987,13 @@ tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipyth [[package]] name = "fastjsonschema" -version = "2.20.0" +version = "2.21.1" description = "Fastest Python implementation of JSON schema" optional = false python-versions = "*" files = [ - {file = "fastjsonschema-2.20.0-py3-none-any.whl", hash = "sha256:5875f0b0fa7a0043a91e93a9b8f793bcbbba9691e7fd83dca95c28ba26d21f0a"}, - {file = "fastjsonschema-2.20.0.tar.gz", hash = "sha256:3d48fc5300ee96f5d116f10fe6f28d938e6008f59a6a025c2649475b87f76a23"}, + {file = "fastjsonschema-2.21.1-py3-none-any.whl", hash = "sha256:c9e5b7e908310918cf494a434eeb31384dd84a98b57a30bcb1f535015b554667"}, + {file = "fastjsonschema-2.21.1.tar.gz", hash = "sha256:794d4f0a58f848961ba16af7b9c85a3e88cd360df008c59aac6fc5ae9323b5d4"}, ] [package.extras] @@ -1248,13 +1245,13 @@ trio = ["trio (>=0.22.0,<1.0)"] [[package]] name = "httpx" -version = "0.27.2" +version = "0.28.1" description = "The next generation HTTP client." optional = false python-versions = ">=3.8" files = [ - {file = "httpx-0.27.2-py3-none-any.whl", hash = "sha256:7bb2708e112d8fdd7829cd4243970f0c223274051cb35ee80c03301ee29a3df0"}, - {file = "httpx-0.27.2.tar.gz", hash = "sha256:f7c2be1d2f3c3c3160d441802406b206c2b76f5947b11115e6df10c6c65e66c2"}, + {file = "httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad"}, + {file = "httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc"}, ] [package.dependencies] @@ -1262,7 +1259,6 @@ anyio = "*" certifi = "*" httpcore = "==1.*" idna = "*" -sniffio = "*" [package.extras] brotli = ["brotli", "brotlicffi"] @@ -1736,13 +1732,13 @@ test = ["jupyter-server (>=2.0.0)", "pytest (>=7.0)", "pytest-jupyter[server] (> [[package]] name = "jupyterlab" -version = "4.3.1" +version = "4.3.3" description = "JupyterLab computational environment" optional = false python-versions = ">=3.8" files = [ - {file = "jupyterlab-4.3.1-py3-none-any.whl", hash = "sha256:2d9a1c305bc748e277819a17a5d5e22452e533e835f4237b2f30f3b0e491e01f"}, - {file = "jupyterlab-4.3.1.tar.gz", hash = "sha256:a4a338327556443521731d82f2a6ccf926df478914ca029616621704d47c3c65"}, + {file = "jupyterlab-4.3.3-py3-none-any.whl", hash = "sha256:32a8fd30677e734ffcc3916a4758b9dab21b02015b668c60eb36f84357b7d4b1"}, + {file = "jupyterlab-4.3.3.tar.gz", hash = "sha256:76fa39e548fdac94dc1204af5956c556f54c785f70ee26aa47ea08eda4d5bbcd"}, ] [package.dependencies] @@ -1757,7 +1753,7 @@ jupyter-server = ">=2.4.0,<3" jupyterlab-server = ">=2.27.1,<3" notebook-shim = ">=0.2" packaging = "*" -setuptools = ">=40.1.0" +setuptools = ">=40.8.0" tomli = {version = ">=1.2.2", markers = "python_version < \"3.11\""} tornado = ">=6.2.0" traitlets = "*" @@ -1819,7 +1815,7 @@ files = [ [[package]] name = "langchain" -version = "0.3.8" +version = "0.3.12" description = "Building applications with LLMs through composability" optional = false python-versions = ">=3.9,<4.0" @@ -1829,9 +1825,9 @@ develop = true [package.dependencies] aiohttp = "^3.8.3" async-timeout = {version = "^4.0.0", markers = "python_version < \"3.11\""} -langchain-core = "^0.3.21" -langchain-text-splitters = "^0.3.0" -langsmith = "^0.1.17" +langchain-core = "^0.3.25" +langchain-text-splitters = "^0.3.3" +langsmith = ">=0.1.17,<0.3" numpy = [ {version = ">=1.22.4,<2", markers = "python_version < \"3.12\""}, {version = ">=1.26.2,<3", markers = "python_version >= \"3.12\""}, @@ -1848,7 +1844,7 @@ url = "../langchain" [[package]] name = "langchain-core" -version = "0.3.21" +version = "0.3.25" description = "Building applications with LLMs through composability" optional = false python-versions = ">=3.9,<4.0" @@ -1857,7 +1853,7 @@ develop = true [package.dependencies] jsonpatch = "^1.33" -langsmith = "^0.1.125" +langsmith = ">=0.1.125,<0.3" packaging = ">=23.2,<25" pydantic = [ {version = ">=2.5.2,<3.0.0", markers = "python_full_version < \"3.12.4\""}, @@ -1873,7 +1869,7 @@ url = "../core" [[package]] name = "langchain-tests" -version = "0.3.4" +version = "0.3.7" description = "Standard tests for LangChain implementations" optional = false python-versions = ">=3.9,<4.0" @@ -1881,9 +1877,15 @@ files = [] develop = true [package.dependencies] -httpx = "^0.27.0" -langchain-core = "^0.3.19" +httpx = ">=0.25.0,<1" +langchain-core = "^0.3.22" +numpy = [ + {version = ">=1.24.0,<2.0.0", markers = "python_version < \"3.12\""}, + {version = ">=1.26.2,<3", markers = "python_version >= \"3.12\""}, +] pytest = ">=7,<9" +pytest-asyncio = ">=0.20,<1" +pytest-socket = ">=0.6.0,<1" syrupy = "^4" [package.source] @@ -1892,7 +1894,7 @@ url = "../standard-tests" [[package]] name = "langchain-text-splitters" -version = "0.3.2" +version = "0.3.3" description = "LangChain text splitting utilities" optional = false python-versions = ">=3.9,<4.0" @@ -1900,7 +1902,7 @@ files = [] develop = true [package.dependencies] -langchain-core = "^0.3.15" +langchain-core = "^0.3.25" [package.source] type = "directory" @@ -1908,13 +1910,13 @@ url = "../text-splitters" [[package]] name = "langsmith" -version = "0.1.146" +version = "0.2.3" description = "Client library to connect to the LangSmith LLM Tracing and Evaluation Platform." optional = false -python-versions = "<4.0,>=3.8.1" +python-versions = "<4.0,>=3.9" files = [ - {file = "langsmith-0.1.146-py3-none-any.whl", hash = "sha256:9d062222f1a32c9b047dab0149b24958f988989cd8d4a5f9139ff959a51e59d8"}, - {file = "langsmith-0.1.146.tar.gz", hash = "sha256:ead8b0b9d5b6cd3ac42937ec48bdf09d4afe7ca1bba22dc05eb65591a18106f8"}, + {file = "langsmith-0.2.3-py3-none-any.whl", hash = "sha256:4958b6e918f57fedba6ddc55b8534d1e06478bb44c779aa73713ce898ca6ae87"}, + {file = "langsmith-0.2.3.tar.gz", hash = "sha256:54c231b07fdeb0f8472925074a0ec0ed2cb654a0437d63c6ccf76a9da635900d"}, ] [package.dependencies] @@ -1927,6 +1929,9 @@ pydantic = [ requests = ">=2,<3" requests-toolbelt = ">=1.0.0,<2.0.0" +[package.extras] +langsmith-pyo3 = ["langsmith-pyo3 (>=0.1.0rc2,<0.2.0)"] + [[package]] name = "lark" version = "1.2.2" @@ -2243,13 +2248,13 @@ types-protobuf = ">=4.24" [[package]] name = "nbclient" -version = "0.10.0" +version = "0.10.1" description = "A client library for executing notebooks. Formerly nbconvert's ExecutePreprocessor." optional = false python-versions = ">=3.8.0" files = [ - {file = "nbclient-0.10.0-py3-none-any.whl", hash = "sha256:f13e3529332a1f1f81d82a53210322476a168bb7090a0289c795fe9cc11c9d3f"}, - {file = "nbclient-0.10.0.tar.gz", hash = "sha256:4b3f1b7dba531e498449c4db4f53da339c91d449dc11e9af3a43b4eb5c5abb09"}, + {file = "nbclient-0.10.1-py3-none-any.whl", hash = "sha256:949019b9240d66897e442888cfb618f69ef23dc71c01cb5fced8499c2cfc084d"}, + {file = "nbclient-0.10.1.tar.gz", hash = "sha256:3e93e348ab27e712acd46fccd809139e356eb9a31aab641d1a7991a6eb4e6f68"}, ] [package.dependencies] @@ -2260,7 +2265,7 @@ traitlets = ">=5.4" [package.extras] dev = ["pre-commit"] -docs = ["autodoc-traits", "mock", "moto", "myst-parser", "nbclient[test]", "sphinx (>=1.7)", "sphinx-book-theme", "sphinxcontrib-spelling"] +docs = ["autodoc-traits", "flaky", "ipykernel (>=6.19.3)", "ipython", "ipywidgets", "mock", "moto", "myst-parser", "nbconvert (>=7.0.0)", "pytest (>=7.0,<8)", "pytest-asyncio", "pytest-cov (>=4.0)", "sphinx (>=1.7)", "sphinx-book-theme", "sphinxcontrib-spelling", "testpath", "xmltodict"] test = ["flaky", "ipykernel (>=6.19.3)", "ipython", "ipywidgets", "nbconvert (>=7.0.0)", "pytest (>=7.0,<8)", "pytest-asyncio", "pytest-cov (>=4.0)", "testpath", "xmltodict"] [[package]] @@ -2335,26 +2340,26 @@ files = [ [[package]] name = "notebook" -version = "7.0.7" +version = "7.3.1" description = "Jupyter Notebook - A web-based notebook environment for interactive computing" optional = false python-versions = ">=3.8" files = [ - {file = "notebook-7.0.7-py3-none-any.whl", hash = "sha256:289b606d7e173f75a18beb1406ef411b43f97f7a9c55ba03efa3622905a62346"}, - {file = "notebook-7.0.7.tar.gz", hash = "sha256:3bcff00c17b3ac142ef5f436d50637d936b274cfa0b41f6ac0175363de9b4e09"}, + {file = "notebook-7.3.1-py3-none-any.whl", hash = "sha256:212e1486b2230fe22279043f33c7db5cf9a01d29feb063a85cb139747b7c9483"}, + {file = "notebook-7.3.1.tar.gz", hash = "sha256:84381c2a82d867517fd25b86e986dae1fe113a70b98f03edff9b94e499fec8fa"}, ] [package.dependencies] jupyter-server = ">=2.4.0,<3" -jupyterlab = ">=4.0.2,<5" -jupyterlab-server = ">=2.22.1,<3" +jupyterlab = ">=4.3.2,<4.4" +jupyterlab-server = ">=2.27.1,<3" notebook-shim = ">=0.2,<0.3" tornado = ">=6.2.0" [package.extras] dev = ["hatch", "pre-commit"] docs = ["myst-parser", "nbsphinx", "pydata-sphinx-theme", "sphinx (>=1.3.6)", "sphinxcontrib-github-alt", "sphinxcontrib-spelling"] -test = ["importlib-resources (>=5.0)", "ipykernel", "jupyter-server[test] (>=2.4.0,<3)", "jupyterlab-server[test] (>=2.22.1,<3)", "nbval", "pytest (>=7.0)", "pytest-console-scripts", "pytest-timeout", "pytest-tornasync", "requests"] +test = ["importlib-resources (>=5.0)", "ipykernel", "jupyter-server[test] (>=2.4.0,<3)", "jupyterlab-server[test] (>=2.27.1,<3)", "nbval", "pytest (>=7.0)", "pytest-console-scripts", "pytest-timeout", "pytest-tornasync", "requests"] [[package]] name = "notebook-shim" @@ -2420,66 +2425,66 @@ files = [ [[package]] name = "numpy" -version = "2.1.3" +version = "2.2.0" description = "Fundamental package for array computing in Python" optional = false python-versions = ">=3.10" files = [ - {file = "numpy-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c894b4305373b9c5576d7a12b473702afdf48ce5369c074ba304cc5ad8730dff"}, - {file = "numpy-2.1.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b47fbb433d3260adcd51eb54f92a2ffbc90a4595f8970ee00e064c644ac788f5"}, - {file = "numpy-2.1.3-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:825656d0743699c529c5943554d223c021ff0494ff1442152ce887ef4f7561a1"}, - {file = "numpy-2.1.3-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:6a4825252fcc430a182ac4dee5a505053d262c807f8a924603d411f6718b88fd"}, - {file = "numpy-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e711e02f49e176a01d0349d82cb5f05ba4db7d5e7e0defd026328e5cfb3226d3"}, - {file = "numpy-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78574ac2d1a4a02421f25da9559850d59457bac82f2b8d7a44fe83a64f770098"}, - {file = "numpy-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c7662f0e3673fe4e832fe07b65c50342ea27d989f92c80355658c7f888fcc83c"}, - {file = "numpy-2.1.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:fa2d1337dc61c8dc417fbccf20f6d1e139896a30721b7f1e832b2bb6ef4eb6c4"}, - {file = "numpy-2.1.3-cp310-cp310-win32.whl", hash = "sha256:72dcc4a35a8515d83e76b58fdf8113a5c969ccd505c8a946759b24e3182d1f23"}, - {file = "numpy-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:ecc76a9ba2911d8d37ac01de72834d8849e55473457558e12995f4cd53e778e0"}, - {file = "numpy-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4d1167c53b93f1f5d8a139a742b3c6f4d429b54e74e6b57d0eff40045187b15d"}, - {file = "numpy-2.1.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c80e4a09b3d95b4e1cac08643f1152fa71a0a821a2d4277334c88d54b2219a41"}, - {file = "numpy-2.1.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:576a1c1d25e9e02ed7fa5477f30a127fe56debd53b8d2c89d5578f9857d03ca9"}, - {file = "numpy-2.1.3-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:973faafebaae4c0aaa1a1ca1ce02434554d67e628b8d805e61f874b84e136b09"}, - {file = "numpy-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:762479be47a4863e261a840e8e01608d124ee1361e48b96916f38b119cfda04a"}, - {file = "numpy-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc6f24b3d1ecc1eebfbf5d6051faa49af40b03be1aaa781ebdadcbc090b4539b"}, - {file = "numpy-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:17ee83a1f4fef3c94d16dc1802b998668b5419362c8a4f4e8a491de1b41cc3ee"}, - {file = "numpy-2.1.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:15cb89f39fa6d0bdfb600ea24b250e5f1a3df23f901f51c8debaa6a5d122b2f0"}, - {file = "numpy-2.1.3-cp311-cp311-win32.whl", hash = "sha256:d9beb777a78c331580705326d2367488d5bc473b49a9bc3036c154832520aca9"}, - {file = "numpy-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:d89dd2b6da69c4fff5e39c28a382199ddedc3a5be5390115608345dec660b9e2"}, - {file = "numpy-2.1.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f55ba01150f52b1027829b50d70ef1dafd9821ea82905b63936668403c3b471e"}, - {file = "numpy-2.1.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:13138eadd4f4da03074851a698ffa7e405f41a0845a6b1ad135b81596e4e9958"}, - {file = "numpy-2.1.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:a6b46587b14b888e95e4a24d7b13ae91fa22386c199ee7b418f449032b2fa3b8"}, - {file = "numpy-2.1.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:0fa14563cc46422e99daef53d725d0c326e99e468a9320a240affffe87852564"}, - {file = "numpy-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8637dcd2caa676e475503d1f8fdb327bc495554e10838019651b76d17b98e512"}, - {file = "numpy-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2312b2aa89e1f43ecea6da6ea9a810d06aae08321609d8dc0d0eda6d946a541b"}, - {file = "numpy-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a38c19106902bb19351b83802531fea19dee18e5b37b36454f27f11ff956f7fc"}, - {file = "numpy-2.1.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:02135ade8b8a84011cbb67dc44e07c58f28575cf9ecf8ab304e51c05528c19f0"}, - {file = "numpy-2.1.3-cp312-cp312-win32.whl", hash = "sha256:e6988e90fcf617da2b5c78902fe8e668361b43b4fe26dbf2d7b0f8034d4cafb9"}, - {file = "numpy-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:0d30c543f02e84e92c4b1f415b7c6b5326cbe45ee7882b6b77db7195fb971e3a"}, - {file = "numpy-2.1.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96fe52fcdb9345b7cd82ecd34547fca4321f7656d500eca497eb7ea5a926692f"}, - {file = "numpy-2.1.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f653490b33e9c3a4c1c01d41bc2aef08f9475af51146e4a7710c450cf9761598"}, - {file = "numpy-2.1.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:dc258a761a16daa791081d026f0ed4399b582712e6fc887a95af09df10c5ca57"}, - {file = "numpy-2.1.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:016d0f6f5e77b0f0d45d77387ffa4bb89816b57c835580c3ce8e099ef830befe"}, - {file = "numpy-2.1.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c181ba05ce8299c7aa3125c27b9c2167bca4a4445b7ce73d5febc411ca692e43"}, - {file = "numpy-2.1.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5641516794ca9e5f8a4d17bb45446998c6554704d888f86df9b200e66bdcce56"}, - {file = "numpy-2.1.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ea4dedd6e394a9c180b33c2c872b92f7ce0f8e7ad93e9585312b0c5a04777a4a"}, - {file = "numpy-2.1.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b0df3635b9c8ef48bd3be5f862cf71b0a4716fa0e702155c45067c6b711ddcef"}, - {file = "numpy-2.1.3-cp313-cp313-win32.whl", hash = "sha256:50ca6aba6e163363f132b5c101ba078b8cbd3fa92c7865fd7d4d62d9779ac29f"}, - {file = "numpy-2.1.3-cp313-cp313-win_amd64.whl", hash = "sha256:747641635d3d44bcb380d950679462fae44f54b131be347d5ec2bce47d3df9ed"}, - {file = "numpy-2.1.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:996bb9399059c5b82f76b53ff8bb686069c05acc94656bb259b1d63d04a9506f"}, - {file = "numpy-2.1.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:45966d859916ad02b779706bb43b954281db43e185015df6eb3323120188f9e4"}, - {file = "numpy-2.1.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:baed7e8d7481bfe0874b566850cb0b85243e982388b7b23348c6db2ee2b2ae8e"}, - {file = "numpy-2.1.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:a9f7f672a3388133335589cfca93ed468509cb7b93ba3105fce780d04a6576a0"}, - {file = "numpy-2.1.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7aac50327da5d208db2eec22eb11e491e3fe13d22653dce51b0f4109101b408"}, - {file = "numpy-2.1.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4394bc0dbd074b7f9b52024832d16e019decebf86caf909d94f6b3f77a8ee3b6"}, - {file = "numpy-2.1.3-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:50d18c4358a0a8a53f12a8ba9d772ab2d460321e6a93d6064fc22443d189853f"}, - {file = "numpy-2.1.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:14e253bd43fc6b37af4921b10f6add6925878a42a0c5fe83daee390bca80bc17"}, - {file = "numpy-2.1.3-cp313-cp313t-win32.whl", hash = "sha256:08788d27a5fd867a663f6fc753fd7c3ad7e92747efc73c53bca2f19f8bc06f48"}, - {file = "numpy-2.1.3-cp313-cp313t-win_amd64.whl", hash = "sha256:2564fbdf2b99b3f815f2107c1bbc93e2de8ee655a69c261363a1172a79a257d4"}, - {file = "numpy-2.1.3-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:4f2015dfe437dfebbfce7c85c7b53d81ba49e71ba7eadbf1df40c915af75979f"}, - {file = "numpy-2.1.3-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:3522b0dfe983a575e6a9ab3a4a4dfe156c3e428468ff08ce582b9bb6bd1d71d4"}, - {file = "numpy-2.1.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c006b607a865b07cd981ccb218a04fc86b600411d83d6fc261357f1c0966755d"}, - {file = "numpy-2.1.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e14e26956e6f1696070788252dcdff11b4aca4c3e8bd166e0df1bb8f315a67cb"}, - {file = "numpy-2.1.3.tar.gz", hash = "sha256:aa08e04e08aaf974d4458def539dece0d28146d866a39da5639596f4921fd761"}, + {file = "numpy-2.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1e25507d85da11ff5066269d0bd25d06e0a0f2e908415534f3e603d2a78e4ffa"}, + {file = "numpy-2.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a62eb442011776e4036af5c8b1a00b706c5bc02dc15eb5344b0c750428c94219"}, + {file = "numpy-2.2.0-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:b606b1aaf802e6468c2608c65ff7ece53eae1a6874b3765f69b8ceb20c5fa78e"}, + {file = "numpy-2.2.0-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:36b2b43146f646642b425dd2027730f99bac962618ec2052932157e213a040e9"}, + {file = "numpy-2.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7fe8f3583e0607ad4e43a954e35c1748b553bfe9fdac8635c02058023277d1b3"}, + {file = "numpy-2.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:122fd2fcfafdefc889c64ad99c228d5a1f9692c3a83f56c292618a59aa60ae83"}, + {file = "numpy-2.2.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3f2f5cddeaa4424a0a118924b988746db6ffa8565e5829b1841a8a3bd73eb59a"}, + {file = "numpy-2.2.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7fe4bb0695fe986a9e4deec3b6857003b4cfe5c5e4aac0b95f6a658c14635e31"}, + {file = "numpy-2.2.0-cp310-cp310-win32.whl", hash = "sha256:b30042fe92dbd79f1ba7f6898fada10bdaad1847c44f2dff9a16147e00a93661"}, + {file = "numpy-2.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:54dc1d6d66f8d37843ed281773c7174f03bf7ad826523f73435deb88ba60d2d4"}, + {file = "numpy-2.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9874bc2ff574c40ab7a5cbb7464bf9b045d617e36754a7bc93f933d52bd9ffc6"}, + {file = "numpy-2.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0da8495970f6b101ddd0c38ace92edea30e7e12b9a926b57f5fabb1ecc25bb90"}, + {file = "numpy-2.2.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:0557eebc699c1c34cccdd8c3778c9294e8196df27d713706895edc6f57d29608"}, + {file = "numpy-2.2.0-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:3579eaeb5e07f3ded59298ce22b65f877a86ba8e9fe701f5576c99bb17c283da"}, + {file = "numpy-2.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40deb10198bbaa531509aad0cd2f9fadb26c8b94070831e2208e7df543562b74"}, + {file = "numpy-2.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c2aed8fcf8abc3020d6a9ccb31dbc9e7d7819c56a348cc88fd44be269b37427e"}, + {file = "numpy-2.2.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a222d764352c773aa5ebde02dd84dba3279c81c6db2e482d62a3fa54e5ece69b"}, + {file = "numpy-2.2.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4e58666988605e251d42c2818c7d3d8991555381be26399303053b58a5bbf30d"}, + {file = "numpy-2.2.0-cp311-cp311-win32.whl", hash = "sha256:4723a50e1523e1de4fccd1b9a6dcea750c2102461e9a02b2ac55ffeae09a4410"}, + {file = "numpy-2.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:16757cf28621e43e252c560d25b15f18a2f11da94fea344bf26c599b9cf54b73"}, + {file = "numpy-2.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cff210198bb4cae3f3c100444c5eaa573a823f05c253e7188e1362a5555235b3"}, + {file = "numpy-2.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:58b92a5828bd4d9aa0952492b7de803135038de47343b2aa3cc23f3b71a3dc4e"}, + {file = "numpy-2.2.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:ebe5e59545401fbb1b24da76f006ab19734ae71e703cdb4a8b347e84a0cece67"}, + {file = "numpy-2.2.0-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:e2b8cd48a9942ed3f85b95ca4105c45758438c7ed28fff1e4ce3e57c3b589d8e"}, + {file = "numpy-2.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57fcc997ffc0bef234b8875a54d4058afa92b0b0c4223fc1f62f24b3b5e86038"}, + {file = "numpy-2.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85ad7d11b309bd132d74397fcf2920933c9d1dc865487128f5c03d580f2c3d03"}, + {file = "numpy-2.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:cb24cca1968b21355cc6f3da1a20cd1cebd8a023e3c5b09b432444617949085a"}, + {file = "numpy-2.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0798b138c291d792f8ea40fe3768610f3c7dd2574389e37c3f26573757c8f7ef"}, + {file = "numpy-2.2.0-cp312-cp312-win32.whl", hash = "sha256:afe8fb968743d40435c3827632fd36c5fbde633b0423da7692e426529b1759b1"}, + {file = "numpy-2.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:3a4199f519e57d517ebd48cb76b36c82da0360781c6a0353e64c0cac30ecaad3"}, + {file = "numpy-2.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f8c8b141ef9699ae777c6278b52c706b653bf15d135d302754f6b2e90eb30367"}, + {file = "numpy-2.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0f0986e917aca18f7a567b812ef7ca9391288e2acb7a4308aa9d265bd724bdae"}, + {file = "numpy-2.2.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:1c92113619f7b272838b8d6702a7f8ebe5edea0df48166c47929611d0b4dea69"}, + {file = "numpy-2.2.0-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:5a145e956b374e72ad1dff82779177d4a3c62bc8248f41b80cb5122e68f22d13"}, + {file = "numpy-2.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18142b497d70a34b01642b9feabb70156311b326fdddd875a9981f34a369b671"}, + {file = "numpy-2.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a7d41d1612c1a82b64697e894b75db6758d4f21c3ec069d841e60ebe54b5b571"}, + {file = "numpy-2.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a98f6f20465e7618c83252c02041517bd2f7ea29be5378f09667a8f654a5918d"}, + {file = "numpy-2.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e09d40edfdb4e260cb1567d8ae770ccf3b8b7e9f0d9b5c2a9992696b30ce2742"}, + {file = "numpy-2.2.0-cp313-cp313-win32.whl", hash = "sha256:3905a5fffcc23e597ee4d9fb3fcd209bd658c352657548db7316e810ca80458e"}, + {file = "numpy-2.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:a184288538e6ad699cbe6b24859206e38ce5fba28f3bcfa51c90d0502c1582b2"}, + {file = "numpy-2.2.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:7832f9e8eb00be32f15fdfb9a981d6955ea9adc8574c521d48710171b6c55e95"}, + {file = "numpy-2.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f0dd071b95bbca244f4cb7f70b77d2ff3aaaba7fa16dc41f58d14854a6204e6c"}, + {file = "numpy-2.2.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:b0b227dcff8cdc3efbce66d4e50891f04d0a387cce282fe1e66199146a6a8fca"}, + {file = "numpy-2.2.0-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:6ab153263a7c5ccaf6dfe7e53447b74f77789f28ecb278c3b5d49db7ece10d6d"}, + {file = "numpy-2.2.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e500aba968a48e9019e42c0c199b7ec0696a97fa69037bea163b55398e390529"}, + {file = "numpy-2.2.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:440cfb3db4c5029775803794f8638fbdbf71ec702caf32735f53b008e1eaece3"}, + {file = "numpy-2.2.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a55dc7a7f0b6198b07ec0cd445fbb98b05234e8b00c5ac4874a63372ba98d4ab"}, + {file = "numpy-2.2.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4bddbaa30d78c86329b26bd6aaaea06b1e47444da99eddac7bf1e2fab717bd72"}, + {file = "numpy-2.2.0-cp313-cp313t-win32.whl", hash = "sha256:30bf971c12e4365153afb31fc73f441d4da157153f3400b82db32d04de1e4066"}, + {file = "numpy-2.2.0-cp313-cp313t-win_amd64.whl", hash = "sha256:d35717333b39d1b6bb8433fa758a55f1081543de527171543a2b710551d40881"}, + {file = "numpy-2.2.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e12c6c1ce84628c52d6367863773f7c8c8241be554e8b79686e91a43f1733773"}, + {file = "numpy-2.2.0-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:b6207dc8fb3c8cb5668e885cef9ec7f70189bec4e276f0ff70d5aa078d32c88e"}, + {file = "numpy-2.2.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a50aeff71d0f97b6450d33940c7181b08be1441c6c193e678211bff11aa725e7"}, + {file = "numpy-2.2.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:df12a1f99b99f569a7c2ae59aa2d31724e8d835fc7f33e14f4792e3071d11221"}, + {file = "numpy-2.2.0.tar.gz", hash = "sha256:140dd80ff8981a583a60980be1a655068f8adebf7a45a06a6858c873fcdcd4a0"}, ] [[package]] @@ -2599,30 +2604,41 @@ files = [ {file = "pandas-2.2.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:381175499d3802cde0eabbaf6324cce0c4f5d52ca6f8c377c29ad442f50f6348"}, {file = "pandas-2.2.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d9c45366def9a3dd85a6454c0e7908f2b3b8e9c138f5dc38fed7ce720d8453ed"}, {file = "pandas-2.2.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86976a1c5b25ae3f8ccae3a5306e443569ee3c3faf444dfd0f41cda24667ad57"}, + {file = "pandas-2.2.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b8661b0238a69d7aafe156b7fa86c44b881387509653fdf857bebc5e4008ad42"}, {file = "pandas-2.2.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:37e0aced3e8f539eccf2e099f65cdb9c8aa85109b0be6e93e2baff94264bdc6f"}, {file = "pandas-2.2.3-cp310-cp310-win_amd64.whl", hash = "sha256:56534ce0746a58afaf7942ba4863e0ef81c9c50d3f0ae93e9497d6a41a057645"}, {file = "pandas-2.2.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:66108071e1b935240e74525006034333f98bcdb87ea116de573a6a0dccb6c039"}, {file = "pandas-2.2.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7c2875855b0ff77b2a64a0365e24455d9990730d6431b9e0ee18ad8acee13dbd"}, + {file = "pandas-2.2.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd8d0c3be0515c12fed0bdbae072551c8b54b7192c7b1fda0ba56059a0179698"}, {file = "pandas-2.2.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c124333816c3a9b03fbeef3a9f230ba9a737e9e5bb4060aa2107a86cc0a497fc"}, + {file = "pandas-2.2.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:63cc132e40a2e084cf01adf0775b15ac515ba905d7dcca47e9a251819c575ef3"}, {file = "pandas-2.2.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:29401dbfa9ad77319367d36940cd8a0b3a11aba16063e39632d98b0e931ddf32"}, {file = "pandas-2.2.3-cp311-cp311-win_amd64.whl", hash = "sha256:3fc6873a41186404dad67245896a6e440baacc92f5b716ccd1bc9ed2995ab2c5"}, {file = "pandas-2.2.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b1d432e8d08679a40e2a6d8b2f9770a5c21793a6f9f47fdd52c5ce1948a5a8a9"}, {file = "pandas-2.2.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a5a1595fe639f5988ba6a8e5bc9649af3baf26df3998a0abe56c02609392e0a4"}, + {file = "pandas-2.2.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5de54125a92bb4d1c051c0659e6fcb75256bf799a732a87184e5ea503965bce3"}, {file = "pandas-2.2.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fffb8ae78d8af97f849404f21411c95062db1496aeb3e56f146f0355c9989319"}, + {file = "pandas-2.2.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6dfcb5ee8d4d50c06a51c2fffa6cff6272098ad6540aed1a76d15fb9318194d8"}, {file = "pandas-2.2.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:062309c1b9ea12a50e8ce661145c6aab431b1e99530d3cd60640e255778bd43a"}, {file = "pandas-2.2.3-cp312-cp312-win_amd64.whl", hash = "sha256:59ef3764d0fe818125a5097d2ae867ca3fa64df032331b7e0917cf5d7bf66b13"}, {file = "pandas-2.2.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f00d1345d84d8c86a63e476bb4955e46458b304b9575dcf71102b5c705320015"}, {file = "pandas-2.2.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3508d914817e153ad359d7e069d752cdd736a247c322d932eb89e6bc84217f28"}, + {file = "pandas-2.2.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22a9d949bfc9a502d320aa04e5d02feab689d61da4e7764b62c30b991c42c5f0"}, {file = "pandas-2.2.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3a255b2c19987fbbe62a9dfd6cff7ff2aa9ccab3fc75218fd4b7530f01efa24"}, + {file = "pandas-2.2.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:800250ecdadb6d9c78eae4990da62743b857b470883fa27f652db8bdde7f6659"}, {file = "pandas-2.2.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6374c452ff3ec675a8f46fd9ab25c4ad0ba590b71cf0656f8b6daa5202bca3fb"}, {file = "pandas-2.2.3-cp313-cp313-win_amd64.whl", hash = "sha256:61c5ad4043f791b61dd4752191d9f07f0ae412515d59ba8f005832a532f8736d"}, {file = "pandas-2.2.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3b71f27954685ee685317063bf13c7709a7ba74fc996b84fc6821c59b0f06468"}, {file = "pandas-2.2.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:38cf8125c40dae9d5acc10fa66af8ea6fdf760b2714ee482ca691fc66e6fcb18"}, + {file = "pandas-2.2.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ba96630bc17c875161df3818780af30e43be9b166ce51c9a18c1feae342906c2"}, {file = "pandas-2.2.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1db71525a1538b30142094edb9adc10be3f3e176748cd7acc2240c2f2e5aa3a4"}, + {file = "pandas-2.2.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:15c0e1e02e93116177d29ff83e8b1619c93ddc9c49083f237d4312337a61165d"}, {file = "pandas-2.2.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ad5b65698ab28ed8d7f18790a0dc58005c7629f227be9ecc1072aa74c0c1d43a"}, {file = "pandas-2.2.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bc6b93f9b966093cb0fd62ff1a7e4c09e6d546ad7c1de191767baffc57628f39"}, {file = "pandas-2.2.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5dbca4c1acd72e8eeef4753eeca07de9b1db4f398669d5994086f788a5d7cc30"}, + {file = "pandas-2.2.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8cd6d7cc958a3910f934ea8dbdf17b2364827bb4dafc38ce6eef6bb3d65ff09c"}, {file = "pandas-2.2.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99df71520d25fade9db7c1076ac94eb994f4d2673ef2aa2e86ee039b6746d20c"}, + {file = "pandas-2.2.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:31d0ced62d4ea3e231a9f228366919a5ea0b07440d9d4dac345376fd8e1477ea"}, {file = "pandas-2.2.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7eee9e7cea6adf3e3d24e304ac6b8300646e2a5d1cd3a3c2abed9101b0846761"}, {file = "pandas-2.2.3-cp39-cp39-win_amd64.whl", hash = "sha256:4850ba03528b6dd51d6c5d273c46f183f39a9baf3f0143e566b89450965b105e"}, {file = "pandas-2.2.3.tar.gz", hash = "sha256:4f18ba62b61d7e192368b84517265a99b4d7ee8912f8708660fb4a366cc82667"}, @@ -2736,13 +2752,13 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "prometheus-client" -version = "0.21.0" +version = "0.21.1" description = "Python client for the Prometheus monitoring system." optional = false python-versions = ">=3.8" files = [ - {file = "prometheus_client-0.21.0-py3-none-any.whl", hash = "sha256:4fa6b4dd0ac16d58bb587c04b1caae65b8c5043e85f778f42f5f632f6af2e166"}, - {file = "prometheus_client-0.21.0.tar.gz", hash = "sha256:96c83c606b71ff2b0a433c98889d275f51ffec6c5e267de37c7a2b5c9aa9233e"}, + {file = "prometheus_client-0.21.1-py3-none-any.whl", hash = "sha256:594b45c410d6f4f8888940fe80b5cc2521b305a1fafe1c58609ef715a001f301"}, + {file = "prometheus_client-0.21.1.tar.gz", hash = "sha256:252505a722ac04b0456be05c05f75f45d760c2911ffc45f2a06bcaed9f3ae3fb"}, ] [package.extras] @@ -2764,129 +2780,113 @@ wcwidth = "*" [[package]] name = "propcache" -version = "0.2.0" +version = "0.2.1" description = "Accelerated property cache" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "propcache-0.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c5869b8fd70b81835a6f187c5fdbe67917a04d7e52b6e7cc4e5fe39d55c39d58"}, - {file = "propcache-0.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:952e0d9d07609d9c5be361f33b0d6d650cd2bae393aabb11d9b719364521984b"}, - {file = "propcache-0.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:33ac8f098df0585c0b53009f039dfd913b38c1d2edafed0cedcc0c32a05aa110"}, - {file = "propcache-0.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:97e48e8875e6c13909c800fa344cd54cc4b2b0db1d5f911f840458a500fde2c2"}, - {file = "propcache-0.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:388f3217649d6d59292b722d940d4d2e1e6a7003259eb835724092a1cca0203a"}, - {file = "propcache-0.2.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f571aea50ba5623c308aa146eb650eebf7dbe0fd8c5d946e28343cb3b5aad577"}, - {file = "propcache-0.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3dfafb44f7bb35c0c06eda6b2ab4bfd58f02729e7c4045e179f9a861b07c9850"}, - {file = "propcache-0.2.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a3ebe9a75be7ab0b7da2464a77bb27febcb4fab46a34f9288f39d74833db7f61"}, - {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d2f0d0f976985f85dfb5f3d685697ef769faa6b71993b46b295cdbbd6be8cc37"}, - {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:a3dc1a4b165283bd865e8f8cb5f0c64c05001e0718ed06250d8cac9bec115b48"}, - {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:9e0f07b42d2a50c7dd2d8675d50f7343d998c64008f1da5fef888396b7f84630"}, - {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e63e3e1e0271f374ed489ff5ee73d4b6e7c60710e1f76af5f0e1a6117cd26394"}, - {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:56bb5c98f058a41bb58eead194b4db8c05b088c93d94d5161728515bd52b052b"}, - {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7665f04d0c7f26ff8bb534e1c65068409bf4687aa2534faf7104d7182debb336"}, - {file = "propcache-0.2.0-cp310-cp310-win32.whl", hash = "sha256:7cf18abf9764746b9c8704774d8b06714bcb0a63641518a3a89c7f85cc02c2ad"}, - {file = "propcache-0.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:cfac69017ef97db2438efb854edf24f5a29fd09a536ff3a992b75990720cdc99"}, - {file = "propcache-0.2.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:63f13bf09cc3336eb04a837490b8f332e0db41da66995c9fd1ba04552e516354"}, - {file = "propcache-0.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:608cce1da6f2672a56b24a015b42db4ac612ee709f3d29f27a00c943d9e851de"}, - {file = "propcache-0.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:466c219deee4536fbc83c08d09115249db301550625c7fef1c5563a584c9bc87"}, - {file = "propcache-0.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc2db02409338bf36590aa985a461b2c96fce91f8e7e0f14c50c5fcc4f229016"}, - {file = "propcache-0.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a6ed8db0a556343d566a5c124ee483ae113acc9a557a807d439bcecc44e7dfbb"}, - {file = "propcache-0.2.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:91997d9cb4a325b60d4e3f20967f8eb08dfcb32b22554d5ef78e6fd1dda743a2"}, - {file = "propcache-0.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c7dde9e533c0a49d802b4f3f218fa9ad0a1ce21f2c2eb80d5216565202acab4"}, - {file = "propcache-0.2.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffcad6c564fe6b9b8916c1aefbb37a362deebf9394bd2974e9d84232e3e08504"}, - {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:97a58a28bcf63284e8b4d7b460cbee1edaab24634e82059c7b8c09e65284f178"}, - {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:945db8ee295d3af9dbdbb698cce9bbc5c59b5c3fe328bbc4387f59a8a35f998d"}, - {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:39e104da444a34830751715f45ef9fc537475ba21b7f1f5b0f4d71a3b60d7fe2"}, - {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c5ecca8f9bab618340c8e848d340baf68bcd8ad90a8ecd7a4524a81c1764b3db"}, - {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:c436130cc779806bdf5d5fae0d848713105472b8566b75ff70048c47d3961c5b"}, - {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:191db28dc6dcd29d1a3e063c3be0b40688ed76434622c53a284e5427565bbd9b"}, - {file = "propcache-0.2.0-cp311-cp311-win32.whl", hash = "sha256:5f2564ec89058ee7c7989a7b719115bdfe2a2fb8e7a4543b8d1c0cc4cf6478c1"}, - {file = "propcache-0.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:6e2e54267980349b723cff366d1e29b138b9a60fa376664a157a342689553f71"}, - {file = "propcache-0.2.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:2ee7606193fb267be4b2e3b32714f2d58cad27217638db98a60f9efb5efeccc2"}, - {file = "propcache-0.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:91ee8fc02ca52e24bcb77b234f22afc03288e1dafbb1f88fe24db308910c4ac7"}, - {file = "propcache-0.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2e900bad2a8456d00a113cad8c13343f3b1f327534e3589acc2219729237a2e8"}, - {file = "propcache-0.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f52a68c21363c45297aca15561812d542f8fc683c85201df0bebe209e349f793"}, - {file = "propcache-0.2.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1e41d67757ff4fbc8ef2af99b338bfb955010444b92929e9e55a6d4dcc3c4f09"}, - {file = "propcache-0.2.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a64e32f8bd94c105cc27f42d3b658902b5bcc947ece3c8fe7bc1b05982f60e89"}, - {file = "propcache-0.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:55346705687dbd7ef0d77883ab4f6fabc48232f587925bdaf95219bae072491e"}, - {file = "propcache-0.2.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00181262b17e517df2cd85656fcd6b4e70946fe62cd625b9d74ac9977b64d8d9"}, - {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6994984550eaf25dd7fc7bd1b700ff45c894149341725bb4edc67f0ffa94efa4"}, - {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:56295eb1e5f3aecd516d91b00cfd8bf3a13991de5a479df9e27dd569ea23959c"}, - {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:439e76255daa0f8151d3cb325f6dd4a3e93043e6403e6491813bcaaaa8733887"}, - {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f6475a1b2ecb310c98c28d271a30df74f9dd436ee46d09236a6b750a7599ce57"}, - {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3444cdba6628accf384e349014084b1cacd866fbb88433cd9d279d90a54e0b23"}, - {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4a9d9b4d0a9b38d1c391bb4ad24aa65f306c6f01b512e10a8a34a2dc5675d348"}, - {file = "propcache-0.2.0-cp312-cp312-win32.whl", hash = "sha256:69d3a98eebae99a420d4b28756c8ce6ea5a29291baf2dc9ff9414b42676f61d5"}, - {file = "propcache-0.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:ad9c9b99b05f163109466638bd30ada1722abb01bbb85c739c50b6dc11f92dc3"}, - {file = "propcache-0.2.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ecddc221a077a8132cf7c747d5352a15ed763b674c0448d811f408bf803d9ad7"}, - {file = "propcache-0.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0e53cb83fdd61cbd67202735e6a6687a7b491c8742dfc39c9e01e80354956763"}, - {file = "propcache-0.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92fe151145a990c22cbccf9ae15cae8ae9eddabfc949a219c9f667877e40853d"}, - {file = "propcache-0.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d6a21ef516d36909931a2967621eecb256018aeb11fc48656e3257e73e2e247a"}, - {file = "propcache-0.2.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f88a4095e913f98988f5b338c1d4d5d07dbb0b6bad19892fd447484e483ba6b"}, - {file = "propcache-0.2.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a5b3bb545ead161be780ee85a2b54fdf7092815995661947812dde94a40f6fb"}, - {file = "propcache-0.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67aeb72e0f482709991aa91345a831d0b707d16b0257e8ef88a2ad246a7280bf"}, - {file = "propcache-0.2.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c997f8c44ec9b9b0bcbf2d422cc00a1d9b9c681f56efa6ca149a941e5560da2"}, - {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2a66df3d4992bc1d725b9aa803e8c5a66c010c65c741ad901e260ece77f58d2f"}, - {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:3ebbcf2a07621f29638799828b8d8668c421bfb94c6cb04269130d8de4fb7136"}, - {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1235c01ddaa80da8235741e80815ce381c5267f96cc49b1477fdcf8c047ef325"}, - {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3947483a381259c06921612550867b37d22e1df6d6d7e8361264b6d037595f44"}, - {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:d5bed7f9805cc29c780f3aee05de3262ee7ce1f47083cfe9f77471e9d6777e83"}, - {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e4a91d44379f45f5e540971d41e4626dacd7f01004826a18cb048e7da7e96544"}, - {file = "propcache-0.2.0-cp313-cp313-win32.whl", hash = "sha256:f902804113e032e2cdf8c71015651c97af6418363bea8d78dc0911d56c335032"}, - {file = "propcache-0.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:8f188cfcc64fb1266f4684206c9de0e80f54622c3f22a910cbd200478aeae61e"}, - {file = "propcache-0.2.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:53d1bd3f979ed529f0805dd35ddaca330f80a9a6d90bc0121d2ff398f8ed8861"}, - {file = "propcache-0.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:83928404adf8fb3d26793665633ea79b7361efa0287dfbd372a7e74311d51ee6"}, - {file = "propcache-0.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:77a86c261679ea5f3896ec060be9dc8e365788248cc1e049632a1be682442063"}, - {file = "propcache-0.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:218db2a3c297a3768c11a34812e63b3ac1c3234c3a086def9c0fee50d35add1f"}, - {file = "propcache-0.2.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7735e82e3498c27bcb2d17cb65d62c14f1100b71723b68362872bca7d0913d90"}, - {file = "propcache-0.2.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:20a617c776f520c3875cf4511e0d1db847a076d720714ae35ffe0df3e440be68"}, - {file = "propcache-0.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67b69535c870670c9f9b14a75d28baa32221d06f6b6fa6f77a0a13c5a7b0a5b9"}, - {file = "propcache-0.2.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4569158070180c3855e9c0791c56be3ceeb192defa2cdf6a3f39e54319e56b89"}, - {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:db47514ffdbd91ccdc7e6f8407aac4ee94cc871b15b577c1c324236b013ddd04"}, - {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:2a60ad3e2553a74168d275a0ef35e8c0a965448ffbc3b300ab3a5bb9956c2162"}, - {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:662dd62358bdeaca0aee5761de8727cfd6861432e3bb828dc2a693aa0471a563"}, - {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:25a1f88b471b3bc911d18b935ecb7115dff3a192b6fef46f0bfaf71ff4f12418"}, - {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:f60f0ac7005b9f5a6091009b09a419ace1610e163fa5deaba5ce3484341840e7"}, - {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:74acd6e291f885678631b7ebc85d2d4aec458dd849b8c841b57ef04047833bed"}, - {file = "propcache-0.2.0-cp38-cp38-win32.whl", hash = "sha256:d9b6ddac6408194e934002a69bcaadbc88c10b5f38fb9307779d1c629181815d"}, - {file = "propcache-0.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:676135dcf3262c9c5081cc8f19ad55c8a64e3f7282a21266d05544450bffc3a5"}, - {file = "propcache-0.2.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:25c8d773a62ce0451b020c7b29a35cfbc05de8b291163a7a0f3b7904f27253e6"}, - {file = "propcache-0.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:375a12d7556d462dc64d70475a9ee5982465fbb3d2b364f16b86ba9135793638"}, - {file = "propcache-0.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1ec43d76b9677637a89d6ab86e1fef70d739217fefa208c65352ecf0282be957"}, - {file = "propcache-0.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f45eec587dafd4b2d41ac189c2156461ebd0c1082d2fe7013571598abb8505d1"}, - {file = "propcache-0.2.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bc092ba439d91df90aea38168e11f75c655880c12782facf5cf9c00f3d42b562"}, - {file = "propcache-0.2.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fa1076244f54bb76e65e22cb6910365779d5c3d71d1f18b275f1dfc7b0d71b4d"}, - {file = "propcache-0.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:682a7c79a2fbf40f5dbb1eb6bfe2cd865376deeac65acf9beb607505dced9e12"}, - {file = "propcache-0.2.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8e40876731f99b6f3c897b66b803c9e1c07a989b366c6b5b475fafd1f7ba3fb8"}, - {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:363ea8cd3c5cb6679f1c2f5f1f9669587361c062e4899fce56758efa928728f8"}, - {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:140fbf08ab3588b3468932974a9331aff43c0ab8a2ec2c608b6d7d1756dbb6cb"}, - {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e70fac33e8b4ac63dfc4c956fd7d85a0b1139adcfc0d964ce288b7c527537fea"}, - {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:b33d7a286c0dc1a15f5fc864cc48ae92a846df287ceac2dd499926c3801054a6"}, - {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:f6d5749fdd33d90e34c2efb174c7e236829147a2713334d708746e94c4bde40d"}, - {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:22aa8f2272d81d9317ff5756bb108021a056805ce63dd3630e27d042c8092798"}, - {file = "propcache-0.2.0-cp39-cp39-win32.whl", hash = "sha256:73e4b40ea0eda421b115248d7e79b59214411109a5bc47d0d48e4c73e3b8fcf9"}, - {file = "propcache-0.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:9517d5e9e0731957468c29dbfd0f976736a0e55afaea843726e887f36fe017df"}, - {file = "propcache-0.2.0-py3-none-any.whl", hash = "sha256:2ccc28197af5313706511fab3a8b66dcd6da067a1331372c82ea1cb74285e036"}, - {file = "propcache-0.2.0.tar.gz", hash = "sha256:df81779732feb9d01e5d513fad0122efb3d53bbc75f61b2a4f29a020bc985e70"}, + {file = "propcache-0.2.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6b3f39a85d671436ee3d12c017f8fdea38509e4f25b28eb25877293c98c243f6"}, + {file = "propcache-0.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d51fbe4285d5db5d92a929e3e21536ea3dd43732c5b177c7ef03f918dff9f2"}, + {file = "propcache-0.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6445804cf4ec763dc70de65a3b0d9954e868609e83850a47ca4f0cb64bd79fea"}, + {file = "propcache-0.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9479aa06a793c5aeba49ce5c5692ffb51fcd9a7016e017d555d5e2b0045d212"}, + {file = "propcache-0.2.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9631c5e8b5b3a0fda99cb0d29c18133bca1e18aea9effe55adb3da1adef80d3"}, + {file = "propcache-0.2.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3156628250f46a0895f1f36e1d4fbe062a1af8718ec3ebeb746f1d23f0c5dc4d"}, + {file = "propcache-0.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b6fb63ae352e13748289f04f37868099e69dba4c2b3e271c46061e82c745634"}, + {file = "propcache-0.2.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:887d9b0a65404929641a9fabb6452b07fe4572b269d901d622d8a34a4e9043b2"}, + {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a96dc1fa45bd8c407a0af03b2d5218392729e1822b0c32e62c5bf7eeb5fb3958"}, + {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:a7e65eb5c003a303b94aa2c3852ef130230ec79e349632d030e9571b87c4698c"}, + {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:999779addc413181912e984b942fbcc951be1f5b3663cd80b2687758f434c583"}, + {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:19a0f89a7bb9d8048d9c4370c9c543c396e894c76be5525f5e1ad287f1750ddf"}, + {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:1ac2f5fe02fa75f56e1ad473f1175e11f475606ec9bd0be2e78e4734ad575034"}, + {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:574faa3b79e8ebac7cb1d7930f51184ba1ccf69adfdec53a12f319a06030a68b"}, + {file = "propcache-0.2.1-cp310-cp310-win32.whl", hash = "sha256:03ff9d3f665769b2a85e6157ac8b439644f2d7fd17615a82fa55739bc97863f4"}, + {file = "propcache-0.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:2d3af2e79991102678f53e0dbf4c35de99b6b8b58f29a27ca0325816364caaba"}, + {file = "propcache-0.2.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1ffc3cca89bb438fb9c95c13fc874012f7b9466b89328c3c8b1aa93cdcfadd16"}, + {file = "propcache-0.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f174bbd484294ed9fdf09437f889f95807e5f229d5d93588d34e92106fbf6717"}, + {file = "propcache-0.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:70693319e0b8fd35dd863e3e29513875eb15c51945bf32519ef52927ca883bc3"}, + {file = "propcache-0.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b480c6a4e1138e1aa137c0079b9b6305ec6dcc1098a8ca5196283e8a49df95a9"}, + {file = "propcache-0.2.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d27b84d5880f6d8aa9ae3edb253c59d9f6642ffbb2c889b78b60361eed449787"}, + {file = "propcache-0.2.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:857112b22acd417c40fa4595db2fe28ab900c8c5fe4670c7989b1c0230955465"}, + {file = "propcache-0.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf6c4150f8c0e32d241436526f3c3f9cbd34429492abddbada2ffcff506c51af"}, + {file = "propcache-0.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:66d4cfda1d8ed687daa4bc0274fcfd5267873db9a5bc0418c2da19273040eeb7"}, + {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c2f992c07c0fca81655066705beae35fc95a2fa7366467366db627d9f2ee097f"}, + {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:4a571d97dbe66ef38e472703067021b1467025ec85707d57e78711c085984e54"}, + {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:bb6178c241278d5fe853b3de743087be7f5f4c6f7d6d22a3b524d323eecec505"}, + {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:ad1af54a62ffe39cf34db1aa6ed1a1873bd548f6401db39d8e7cd060b9211f82"}, + {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e7048abd75fe40712005bcfc06bb44b9dfcd8e101dda2ecf2f5aa46115ad07ca"}, + {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:160291c60081f23ee43d44b08a7e5fb76681221a8e10b3139618c5a9a291b84e"}, + {file = "propcache-0.2.1-cp311-cp311-win32.whl", hash = "sha256:819ce3b883b7576ca28da3861c7e1a88afd08cc8c96908e08a3f4dd64a228034"}, + {file = "propcache-0.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:edc9fc7051e3350643ad929df55c451899bb9ae6d24998a949d2e4c87fb596d3"}, + {file = "propcache-0.2.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:081a430aa8d5e8876c6909b67bd2d937bfd531b0382d3fdedb82612c618bc41a"}, + {file = "propcache-0.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d2ccec9ac47cf4e04897619c0e0c1a48c54a71bdf045117d3a26f80d38ab1fb0"}, + {file = "propcache-0.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:14d86fe14b7e04fa306e0c43cdbeebe6b2c2156a0c9ce56b815faacc193e320d"}, + {file = "propcache-0.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:049324ee97bb67285b49632132db351b41e77833678432be52bdd0289c0e05e4"}, + {file = "propcache-0.2.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1cd9a1d071158de1cc1c71a26014dcdfa7dd3d5f4f88c298c7f90ad6f27bb46d"}, + {file = "propcache-0.2.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98110aa363f1bb4c073e8dcfaefd3a5cea0f0834c2aab23dda657e4dab2f53b5"}, + {file = "propcache-0.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:647894f5ae99c4cf6bb82a1bb3a796f6e06af3caa3d32e26d2350d0e3e3faf24"}, + {file = "propcache-0.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bfd3223c15bebe26518d58ccf9a39b93948d3dcb3e57a20480dfdd315356baff"}, + {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d71264a80f3fcf512eb4f18f59423fe82d6e346ee97b90625f283df56aee103f"}, + {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:e73091191e4280403bde6c9a52a6999d69cdfde498f1fdf629105247599b57ec"}, + {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3935bfa5fede35fb202c4b569bb9c042f337ca4ff7bd540a0aa5e37131659348"}, + {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f508b0491767bb1f2b87fdfacaba5f7eddc2f867740ec69ece6d1946d29029a6"}, + {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:1672137af7c46662a1c2be1e8dc78cb6d224319aaa40271c9257d886be4363a6"}, + {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b74c261802d3d2b85c9df2dfb2fa81b6f90deeef63c2db9f0e029a3cac50b518"}, + {file = "propcache-0.2.1-cp312-cp312-win32.whl", hash = "sha256:d09c333d36c1409d56a9d29b3a1b800a42c76a57a5a8907eacdbce3f18768246"}, + {file = "propcache-0.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:c214999039d4f2a5b2073ac506bba279945233da8c786e490d411dfc30f855c1"}, + {file = "propcache-0.2.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aca405706e0b0a44cc6bfd41fbe89919a6a56999157f6de7e182a990c36e37bc"}, + {file = "propcache-0.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:12d1083f001ace206fe34b6bdc2cb94be66d57a850866f0b908972f90996b3e9"}, + {file = "propcache-0.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d93f3307ad32a27bda2e88ec81134b823c240aa3abb55821a8da553eed8d9439"}, + {file = "propcache-0.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba278acf14471d36316159c94a802933d10b6a1e117b8554fe0d0d9b75c9d536"}, + {file = "propcache-0.2.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4e6281aedfca15301c41f74d7005e6e3f4ca143584ba696ac69df4f02f40d629"}, + {file = "propcache-0.2.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5b750a8e5a1262434fb1517ddf64b5de58327f1adc3524a5e44c2ca43305eb0b"}, + {file = "propcache-0.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf72af5e0fb40e9babf594308911436c8efde3cb5e75b6f206c34ad18be5c052"}, + {file = "propcache-0.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b2d0a12018b04f4cb820781ec0dffb5f7c7c1d2a5cd22bff7fb055a2cb19ebce"}, + {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e800776a79a5aabdb17dcc2346a7d66d0777e942e4cd251defeb084762ecd17d"}, + {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:4160d9283bd382fa6c0c2b5e017acc95bc183570cd70968b9202ad6d8fc48dce"}, + {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:30b43e74f1359353341a7adb783c8f1b1c676367b011709f466f42fda2045e95"}, + {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:58791550b27d5488b1bb52bc96328456095d96206a250d28d874fafe11b3dfaf"}, + {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:0f022d381747f0dfe27e99d928e31bc51a18b65bb9e481ae0af1380a6725dd1f"}, + {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:297878dc9d0a334358f9b608b56d02e72899f3b8499fc6044133f0d319e2ec30"}, + {file = "propcache-0.2.1-cp313-cp313-win32.whl", hash = "sha256:ddfab44e4489bd79bda09d84c430677fc7f0a4939a73d2bba3073036f487a0a6"}, + {file = "propcache-0.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:556fc6c10989f19a179e4321e5d678db8eb2924131e64652a51fe83e4c3db0e1"}, + {file = "propcache-0.2.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:6a9a8c34fb7bb609419a211e59da8887eeca40d300b5ea8e56af98f6fbbb1541"}, + {file = "propcache-0.2.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ae1aa1cd222c6d205853b3013c69cd04515f9d6ab6de4b0603e2e1c33221303e"}, + {file = "propcache-0.2.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:accb6150ce61c9c4b7738d45550806aa2b71c7668c6942f17b0ac182b6142fd4"}, + {file = "propcache-0.2.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5eee736daafa7af6d0a2dc15cc75e05c64f37fc37bafef2e00d77c14171c2097"}, + {file = "propcache-0.2.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7a31fc1e1bd362874863fdeed71aed92d348f5336fd84f2197ba40c59f061bd"}, + {file = "propcache-0.2.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba4cfa1052819d16699e1d55d18c92b6e094d4517c41dd231a8b9f87b6fa681"}, + {file = "propcache-0.2.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f089118d584e859c62b3da0892b88a83d611c2033ac410e929cb6754eec0ed16"}, + {file = "propcache-0.2.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:781e65134efaf88feb447e8c97a51772aa75e48b794352f94cb7ea717dedda0d"}, + {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:31f5af773530fd3c658b32b6bdc2d0838543de70eb9a2156c03e410f7b0d3aae"}, + {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:a7a078f5d37bee6690959c813977da5291b24286e7b962e62a94cec31aa5188b"}, + {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:cea7daf9fc7ae6687cf1e2c049752f19f146fdc37c2cc376e7d0032cf4f25347"}, + {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:8b3489ff1ed1e8315674d0775dc7d2195fb13ca17b3808721b54dbe9fd020faf"}, + {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:9403db39be1393618dd80c746cb22ccda168efce239c73af13c3763ef56ffc04"}, + {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5d97151bc92d2b2578ff7ce779cdb9174337390a535953cbb9452fb65164c587"}, + {file = "propcache-0.2.1-cp39-cp39-win32.whl", hash = "sha256:9caac6b54914bdf41bcc91e7eb9147d331d29235a7c967c150ef5df6464fd1bb"}, + {file = "propcache-0.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:92fc4500fcb33899b05ba73276dfb684a20d31caa567b7cb5252d48f896a91b1"}, + {file = "propcache-0.2.1-py3-none-any.whl", hash = "sha256:52277518d6aae65536e9cea52d4e7fd2f7a66f4aa2d30ed3f2fcea620ace3c54"}, + {file = "propcache-0.2.1.tar.gz", hash = "sha256:3f77ce728b19cb537714499928fe800c3dda29e8d9428778fc7c186da4c09a64"}, ] [[package]] name = "protobuf" -version = "5.28.3" +version = "5.29.1" description = "" optional = false python-versions = ">=3.8" files = [ - {file = "protobuf-5.28.3-cp310-abi3-win32.whl", hash = "sha256:0c4eec6f987338617072592b97943fdbe30d019c56126493111cf24344c1cc24"}, - {file = "protobuf-5.28.3-cp310-abi3-win_amd64.whl", hash = "sha256:91fba8f445723fcf400fdbe9ca796b19d3b1242cd873907979b9ed71e4afe868"}, - {file = "protobuf-5.28.3-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a3f6857551e53ce35e60b403b8a27b0295f7d6eb63d10484f12bc6879c715687"}, - {file = "protobuf-5.28.3-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:3fa2de6b8b29d12c61911505d893afe7320ce7ccba4df913e2971461fa36d584"}, - {file = "protobuf-5.28.3-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:712319fbdddb46f21abb66cd33cb9e491a5763b2febd8f228251add221981135"}, - {file = "protobuf-5.28.3-cp38-cp38-win32.whl", hash = "sha256:3e6101d095dfd119513cde7259aa703d16c6bbdfae2554dfe5cfdbe94e32d548"}, - {file = "protobuf-5.28.3-cp38-cp38-win_amd64.whl", hash = "sha256:27b246b3723692bf1068d5734ddaf2fccc2cdd6e0c9b47fe099244d80200593b"}, - {file = "protobuf-5.28.3-cp39-cp39-win32.whl", hash = "sha256:135658402f71bbd49500322c0f736145731b16fc79dc8f367ab544a17eab4535"}, - {file = "protobuf-5.28.3-cp39-cp39-win_amd64.whl", hash = "sha256:70585a70fc2dd4818c51287ceef5bdba6387f88a578c86d47bb34669b5552c36"}, - {file = "protobuf-5.28.3-py3-none-any.whl", hash = "sha256:cee1757663fa32a1ee673434fcf3bf24dd54763c79690201208bafec62f19eed"}, - {file = "protobuf-5.28.3.tar.gz", hash = "sha256:64badbc49180a5e401f373f9ce7ab1d18b63f7dd4a9cdc43c92b9f0b481cef7b"}, + {file = "protobuf-5.29.1-cp310-abi3-win32.whl", hash = "sha256:22c1f539024241ee545cbcb00ee160ad1877975690b16656ff87dde107b5f110"}, + {file = "protobuf-5.29.1-cp310-abi3-win_amd64.whl", hash = "sha256:1fc55267f086dd4050d18ef839d7bd69300d0d08c2a53ca7df3920cc271a3c34"}, + {file = "protobuf-5.29.1-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:d473655e29c0c4bbf8b69e9a8fb54645bc289dead6d753b952e7aa660254ae18"}, + {file = "protobuf-5.29.1-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:b5ba1d0e4c8a40ae0496d0e2ecfdbb82e1776928a205106d14ad6985a09ec155"}, + {file = "protobuf-5.29.1-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:8ee1461b3af56145aca2800e6a3e2f928108c749ba8feccc6f5dd0062c410c0d"}, + {file = "protobuf-5.29.1-cp38-cp38-win32.whl", hash = "sha256:50879eb0eb1246e3a5eabbbe566b44b10348939b7cc1b267567e8c3d07213853"}, + {file = "protobuf-5.29.1-cp38-cp38-win_amd64.whl", hash = "sha256:027fbcc48cea65a6b17028510fdd054147057fa78f4772eb547b9274e5219331"}, + {file = "protobuf-5.29.1-cp39-cp39-win32.whl", hash = "sha256:5a41deccfa5e745cef5c65a560c76ec0ed8e70908a67cc8f4da5fce588b50d57"}, + {file = "protobuf-5.29.1-cp39-cp39-win_amd64.whl", hash = "sha256:012ce28d862ff417fd629285aca5d9772807f15ceb1a0dbd15b88f58c776c98c"}, + {file = "protobuf-5.29.1-py3-none-any.whl", hash = "sha256:32600ddb9c2a53dedc25b8581ea0f1fd8ea04956373c0c07577ce58d312522e0"}, + {file = "protobuf-5.29.1.tar.gz", hash = "sha256:683be02ca21a6ffe80db6dd02c0b5b2892322c59ca57fd6c872d652cb80549cb"}, ] [[package]] @@ -2957,13 +2957,13 @@ files = [ [[package]] name = "pydantic" -version = "2.10.2" +version = "2.10.3" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic-2.10.2-py3-none-any.whl", hash = "sha256:cfb96e45951117c3024e6b67b25cdc33a3cb7b2fa62e239f7af1378358a1d99e"}, - {file = "pydantic-2.10.2.tar.gz", hash = "sha256:2bc2d7f17232e0841cbba4641e65ba1eb6fafb3a08de3a091ff3ce14a197c4fa"}, + {file = "pydantic-2.10.3-py3-none-any.whl", hash = "sha256:be04d85bbc7b65651c5f8e6b9976ed9c6f41782a55524cef079a34a0bb82144d"}, + {file = "pydantic-2.10.3.tar.gz", hash = "sha256:cb5ac360ce894ceacd69c403187900a02c4b20b693a9dd1d643e1effab9eadf9"}, ] [package.dependencies] @@ -3089,13 +3089,13 @@ typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" [[package]] name = "pydantic-settings" -version = "2.6.1" +version = "2.7.0" description = "Settings management using Pydantic" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic_settings-2.6.1-py3-none-any.whl", hash = "sha256:7fb0637c786a558d3103436278a7c4f1cfd29ba8973238a50c5bb9a55387da87"}, - {file = "pydantic_settings-2.6.1.tar.gz", hash = "sha256:e0f92546d8a9923cb8941689abf85d6601a8c19a23e97a34b2964a2e3f813ca0"}, + {file = "pydantic_settings-2.7.0-py3-none-any.whl", hash = "sha256:e00c05d5fa6cbbb227c84bd7487c5c1065084119b750df7c8c1a554aed236eb5"}, + {file = "pydantic_settings-2.7.0.tar.gz", hash = "sha256:ac4bfd4a36831a48dbf8b2d9325425b549a0a6f18cea118436d728eb4f1c4d66"}, ] [package.dependencies] @@ -3284,15 +3284,21 @@ cli = ["click (>=5.0)"] [[package]] name = "python-json-logger" -version = "2.0.7" -description = "A python library adding a json log formatter" +version = "3.2.0" +description = "JSON Log Formatter for the Python Logging Package" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "python-json-logger-2.0.7.tar.gz", hash = "sha256:23e7ec02d34237c5aa1e29a070193a4ea87583bb4e7f8fd06d3de8264c4b2e1c"}, - {file = "python_json_logger-2.0.7-py3-none-any.whl", hash = "sha256:f380b826a991ebbe3de4d897aeec42760035ac760345e57b812938dc8b35e2bd"}, + {file = "python_json_logger-3.2.0-py3-none-any.whl", hash = "sha256:d73522ddcfc6d0461394120feaddea9025dc64bf804d96357dd42fa878cc5fe8"}, + {file = "python_json_logger-3.2.0.tar.gz", hash = "sha256:2c11056458d3f56614480b24e9cb28f7aba69cbfbebddbb77c92f0ec0d4947ab"}, ] +[package.dependencies] +typing_extensions = {version = "*", markers = "python_version < \"3.10\""} + +[package.extras] +dev = ["backports.zoneinfo", "black", "build", "freezegun", "mdx_truly_sane_lists", "mike", "mkdocs", "mkdocs-awesome-pages-plugin", "mkdocs-gen-files", "mkdocs-literate-nav", "mkdocs-material (>=8.5)", "mkdocstrings[python]", "msgspec", "msgspec-python313-pre", "mypy", "orjson", "pylint", "pytest", "tzdata", "validate-pyproject[all]"] + [[package]] name = "pytz" version = "2024.2" @@ -3643,101 +3649,114 @@ files = [ [[package]] name = "rpds-py" -version = "0.21.0" +version = "0.22.3" description = "Python bindings to Rust's persistent data structures (rpds)" optional = false python-versions = ">=3.9" files = [ - {file = "rpds_py-0.21.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:a017f813f24b9df929674d0332a374d40d7f0162b326562daae8066b502d0590"}, - {file = "rpds_py-0.21.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:20cc1ed0bcc86d8e1a7e968cce15be45178fd16e2ff656a243145e0b439bd250"}, - {file = "rpds_py-0.21.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad116dda078d0bc4886cb7840e19811562acdc7a8e296ea6ec37e70326c1b41c"}, - {file = "rpds_py-0.21.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:808f1ac7cf3b44f81c9475475ceb221f982ef548e44e024ad5f9e7060649540e"}, - {file = "rpds_py-0.21.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de552f4a1916e520f2703ec474d2b4d3f86d41f353e7680b597512ffe7eac5d0"}, - {file = "rpds_py-0.21.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:efec946f331349dfc4ae9d0e034c263ddde19414fe5128580f512619abed05f1"}, - {file = "rpds_py-0.21.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b80b4690bbff51a034bfde9c9f6bf9357f0a8c61f548942b80f7b66356508bf5"}, - {file = "rpds_py-0.21.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:085ed25baac88953d4283e5b5bd094b155075bb40d07c29c4f073e10623f9f2e"}, - {file = "rpds_py-0.21.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:daa8efac2a1273eed2354397a51216ae1e198ecbce9036fba4e7610b308b6153"}, - {file = "rpds_py-0.21.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:95a5bad1ac8a5c77b4e658671642e4af3707f095d2b78a1fdd08af0dfb647624"}, - {file = "rpds_py-0.21.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3e53861b29a13d5b70116ea4230b5f0f3547b2c222c5daa090eb7c9c82d7f664"}, - {file = "rpds_py-0.21.0-cp310-none-win32.whl", hash = "sha256:ea3a6ac4d74820c98fcc9da4a57847ad2cc36475a8bd9683f32ab6d47a2bd682"}, - {file = "rpds_py-0.21.0-cp310-none-win_amd64.whl", hash = "sha256:b8f107395f2f1d151181880b69a2869c69e87ec079c49c0016ab96860b6acbe5"}, - {file = "rpds_py-0.21.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:5555db3e618a77034954b9dc547eae94166391a98eb867905ec8fcbce1308d95"}, - {file = "rpds_py-0.21.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:97ef67d9bbc3e15584c2f3c74bcf064af36336c10d2e21a2131e123ce0f924c9"}, - {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ab2c2a26d2f69cdf833174f4d9d86118edc781ad9a8fa13970b527bf8236027"}, - {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4e8921a259f54bfbc755c5bbd60c82bb2339ae0324163f32868f63f0ebb873d9"}, - {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8a7ff941004d74d55a47f916afc38494bd1cfd4b53c482b77c03147c91ac0ac3"}, - {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5145282a7cd2ac16ea0dc46b82167754d5e103a05614b724457cffe614f25bd8"}, - {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de609a6f1b682f70bb7163da745ee815d8f230d97276db049ab447767466a09d"}, - {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:40c91c6e34cf016fa8e6b59d75e3dbe354830777fcfd74c58b279dceb7975b75"}, - {file = "rpds_py-0.21.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d2132377f9deef0c4db89e65e8bb28644ff75a18df5293e132a8d67748397b9f"}, - {file = "rpds_py-0.21.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:0a9e0759e7be10109645a9fddaaad0619d58c9bf30a3f248a2ea57a7c417173a"}, - {file = "rpds_py-0.21.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9e20da3957bdf7824afdd4b6eeb29510e83e026473e04952dca565170cd1ecc8"}, - {file = "rpds_py-0.21.0-cp311-none-win32.whl", hash = "sha256:f71009b0d5e94c0e86533c0b27ed7cacc1239cb51c178fd239c3cfefefb0400a"}, - {file = "rpds_py-0.21.0-cp311-none-win_amd64.whl", hash = "sha256:e168afe6bf6ab7ab46c8c375606298784ecbe3ba31c0980b7dcbb9631dcba97e"}, - {file = "rpds_py-0.21.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:30b912c965b2aa76ba5168fd610087bad7fcde47f0a8367ee8f1876086ee6d1d"}, - {file = "rpds_py-0.21.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ca9989d5d9b1b300bc18e1801c67b9f6d2c66b8fd9621b36072ed1df2c977f72"}, - {file = "rpds_py-0.21.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f54e7106f0001244a5f4cf810ba8d3f9c542e2730821b16e969d6887b664266"}, - {file = "rpds_py-0.21.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fed5dfefdf384d6fe975cc026886aece4f292feaf69d0eeb716cfd3c5a4dd8be"}, - {file = "rpds_py-0.21.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:590ef88db231c9c1eece44dcfefd7515d8bf0d986d64d0caf06a81998a9e8cab"}, - {file = "rpds_py-0.21.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f983e4c2f603c95dde63df633eec42955508eefd8d0f0e6d236d31a044c882d7"}, - {file = "rpds_py-0.21.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b229ce052ddf1a01c67d68166c19cb004fb3612424921b81c46e7ea7ccf7c3bf"}, - {file = "rpds_py-0.21.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ebf64e281a06c904a7636781d2e973d1f0926a5b8b480ac658dc0f556e7779f4"}, - {file = "rpds_py-0.21.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:998a8080c4495e4f72132f3d66ff91f5997d799e86cec6ee05342f8f3cda7dca"}, - {file = "rpds_py-0.21.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:98486337f7b4f3c324ab402e83453e25bb844f44418c066623db88e4c56b7c7b"}, - {file = "rpds_py-0.21.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a78d8b634c9df7f8d175451cfeac3810a702ccb85f98ec95797fa98b942cea11"}, - {file = "rpds_py-0.21.0-cp312-none-win32.whl", hash = "sha256:a58ce66847711c4aa2ecfcfaff04cb0327f907fead8945ffc47d9407f41ff952"}, - {file = "rpds_py-0.21.0-cp312-none-win_amd64.whl", hash = "sha256:e860f065cc4ea6f256d6f411aba4b1251255366e48e972f8a347cf88077b24fd"}, - {file = "rpds_py-0.21.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:ee4eafd77cc98d355a0d02f263efc0d3ae3ce4a7c24740010a8b4012bbb24937"}, - {file = "rpds_py-0.21.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:688c93b77e468d72579351a84b95f976bd7b3e84aa6686be6497045ba84be560"}, - {file = "rpds_py-0.21.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c38dbf31c57032667dd5a2f0568ccde66e868e8f78d5a0d27dcc56d70f3fcd3b"}, - {file = "rpds_py-0.21.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2d6129137f43f7fa02d41542ffff4871d4aefa724a5fe38e2c31a4e0fd343fb0"}, - {file = "rpds_py-0.21.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:520ed8b99b0bf86a176271f6fe23024323862ac674b1ce5b02a72bfeff3fff44"}, - {file = "rpds_py-0.21.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aaeb25ccfb9b9014a10eaf70904ebf3f79faaa8e60e99e19eef9f478651b9b74"}, - {file = "rpds_py-0.21.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af04ac89c738e0f0f1b913918024c3eab6e3ace989518ea838807177d38a2e94"}, - {file = "rpds_py-0.21.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b9b76e2afd585803c53c5b29e992ecd183f68285b62fe2668383a18e74abe7a3"}, - {file = "rpds_py-0.21.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5afb5efde74c54724e1a01118c6e5c15e54e642c42a1ba588ab1f03544ac8c7a"}, - {file = "rpds_py-0.21.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:52c041802a6efa625ea18027a0723676a778869481d16803481ef6cc02ea8cb3"}, - {file = "rpds_py-0.21.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ee1e4fc267b437bb89990b2f2abf6c25765b89b72dd4a11e21934df449e0c976"}, - {file = "rpds_py-0.21.0-cp313-none-win32.whl", hash = "sha256:0c025820b78817db6a76413fff6866790786c38f95ea3f3d3c93dbb73b632202"}, - {file = "rpds_py-0.21.0-cp313-none-win_amd64.whl", hash = "sha256:320c808df533695326610a1b6a0a6e98f033e49de55d7dc36a13c8a30cfa756e"}, - {file = "rpds_py-0.21.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:2c51d99c30091f72a3c5d126fad26236c3f75716b8b5e5cf8effb18889ced928"}, - {file = "rpds_py-0.21.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cbd7504a10b0955ea287114f003b7ad62330c9e65ba012c6223dba646f6ffd05"}, - {file = "rpds_py-0.21.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6dcc4949be728ede49e6244eabd04064336012b37f5c2200e8ec8eb2988b209c"}, - {file = "rpds_py-0.21.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f414da5c51bf350e4b7960644617c130140423882305f7574b6cf65a3081cecb"}, - {file = "rpds_py-0.21.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9afe42102b40007f588666bc7de82451e10c6788f6f70984629db193849dced1"}, - {file = "rpds_py-0.21.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b929c2bb6e29ab31f12a1117c39f7e6d6450419ab7464a4ea9b0b417174f044"}, - {file = "rpds_py-0.21.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8404b3717da03cbf773a1d275d01fec84ea007754ed380f63dfc24fb76ce4592"}, - {file = "rpds_py-0.21.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e12bb09678f38b7597b8346983d2323a6482dcd59e423d9448108c1be37cac9d"}, - {file = "rpds_py-0.21.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:58a0e345be4b18e6b8501d3b0aa540dad90caeed814c515e5206bb2ec26736fd"}, - {file = "rpds_py-0.21.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:c3761f62fcfccf0864cc4665b6e7c3f0c626f0380b41b8bd1ce322103fa3ef87"}, - {file = "rpds_py-0.21.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c2b2f71c6ad6c2e4fc9ed9401080badd1469fa9889657ec3abea42a3d6b2e1ed"}, - {file = "rpds_py-0.21.0-cp39-none-win32.whl", hash = "sha256:b21747f79f360e790525e6f6438c7569ddbfb1b3197b9e65043f25c3c9b489d8"}, - {file = "rpds_py-0.21.0-cp39-none-win_amd64.whl", hash = "sha256:0626238a43152918f9e72ede9a3b6ccc9e299adc8ade0d67c5e142d564c9a83d"}, - {file = "rpds_py-0.21.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:6b4ef7725386dc0762857097f6b7266a6cdd62bfd209664da6712cb26acef035"}, - {file = "rpds_py-0.21.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:6bc0e697d4d79ab1aacbf20ee5f0df80359ecf55db33ff41481cf3e24f206919"}, - {file = "rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da52d62a96e61c1c444f3998c434e8b263c384f6d68aca8274d2e08d1906325c"}, - {file = "rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:98e4fe5db40db87ce1c65031463a760ec7906ab230ad2249b4572c2fc3ef1f9f"}, - {file = "rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:30bdc973f10d28e0337f71d202ff29345320f8bc49a31c90e6c257e1ccef4333"}, - {file = "rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:faa5e8496c530f9c71f2b4e1c49758b06e5f4055e17144906245c99fa6d45356"}, - {file = "rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32eb88c30b6a4f0605508023b7141d043a79b14acb3b969aa0b4f99b25bc7d4a"}, - {file = "rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a89a8ce9e4e75aeb7fa5d8ad0f3fecdee813802592f4f46a15754dcb2fd6b061"}, - {file = "rpds_py-0.21.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:241e6c125568493f553c3d0fdbb38c74babf54b45cef86439d4cd97ff8feb34d"}, - {file = "rpds_py-0.21.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:3b766a9f57663396e4f34f5140b3595b233a7b146e94777b97a8413a1da1be18"}, - {file = "rpds_py-0.21.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:af4a644bf890f56e41e74be7d34e9511e4954894d544ec6b8efe1e21a1a8da6c"}, - {file = "rpds_py-0.21.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:3e30a69a706e8ea20444b98a49f386c17b26f860aa9245329bab0851ed100677"}, - {file = "rpds_py-0.21.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:031819f906bb146561af051c7cef4ba2003d28cff07efacef59da973ff7969ba"}, - {file = "rpds_py-0.21.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:b876f2bc27ab5954e2fd88890c071bd0ed18b9c50f6ec3de3c50a5ece612f7a6"}, - {file = "rpds_py-0.21.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc5695c321e518d9f03b7ea6abb5ea3af4567766f9852ad1560f501b17588c7b"}, - {file = "rpds_py-0.21.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b4de1da871b5c0fd5537b26a6fc6814c3cc05cabe0c941db6e9044ffbb12f04a"}, - {file = "rpds_py-0.21.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:878f6fea96621fda5303a2867887686d7a198d9e0f8a40be100a63f5d60c88c9"}, - {file = "rpds_py-0.21.0-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8eeec67590e94189f434c6d11c426892e396ae59e4801d17a93ac96b8c02a6c"}, - {file = "rpds_py-0.21.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ff2eba7f6c0cb523d7e9cff0903f2fe1feff8f0b2ceb6bd71c0e20a4dcee271"}, - {file = "rpds_py-0.21.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a429b99337062877d7875e4ff1a51fe788424d522bd64a8c0a20ef3021fdb6ed"}, - {file = "rpds_py-0.21.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:d167e4dbbdac48bd58893c7e446684ad5d425b407f9336e04ab52e8b9194e2ed"}, - {file = "rpds_py-0.21.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:4eb2de8a147ffe0626bfdc275fc6563aa7bf4b6db59cf0d44f0ccd6ca625a24e"}, - {file = "rpds_py-0.21.0-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:e78868e98f34f34a88e23ee9ccaeeec460e4eaf6db16d51d7a9b883e5e785a5e"}, - {file = "rpds_py-0.21.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:4991ca61656e3160cdaca4851151fd3f4a92e9eba5c7a530ab030d6aee96ec89"}, - {file = "rpds_py-0.21.0.tar.gz", hash = "sha256:ed6378c9d66d0de903763e7706383d60c33829581f0adff47b6535f1802fa6db"}, + {file = "rpds_py-0.22.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:6c7b99ca52c2c1752b544e310101b98a659b720b21db00e65edca34483259967"}, + {file = "rpds_py-0.22.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:be2eb3f2495ba669d2a985f9b426c1797b7d48d6963899276d22f23e33d47e37"}, + {file = "rpds_py-0.22.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70eb60b3ae9245ddea20f8a4190bd79c705a22f8028aaf8bbdebe4716c3fab24"}, + {file = "rpds_py-0.22.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4041711832360a9b75cfb11b25a6a97c8fb49c07b8bd43d0d02b45d0b499a4ff"}, + {file = "rpds_py-0.22.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:64607d4cbf1b7e3c3c8a14948b99345eda0e161b852e122c6bb71aab6d1d798c"}, + {file = "rpds_py-0.22.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e69b0a0e2537f26d73b4e43ad7bc8c8efb39621639b4434b76a3de50c6966e"}, + {file = "rpds_py-0.22.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc27863442d388870c1809a87507727b799c8460573cfbb6dc0eeaef5a11b5ec"}, + {file = "rpds_py-0.22.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e79dd39f1e8c3504be0607e5fc6e86bb60fe3584bec8b782578c3b0fde8d932c"}, + {file = "rpds_py-0.22.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e0fa2d4ec53dc51cf7d3bb22e0aa0143966119f42a0c3e4998293a3dd2856b09"}, + {file = "rpds_py-0.22.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fda7cb070f442bf80b642cd56483b5548e43d366fe3f39b98e67cce780cded00"}, + {file = "rpds_py-0.22.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cff63a0272fcd259dcc3be1657b07c929c466b067ceb1c20060e8d10af56f5bf"}, + {file = "rpds_py-0.22.3-cp310-cp310-win32.whl", hash = "sha256:9bd7228827ec7bb817089e2eb301d907c0d9827a9e558f22f762bb690b131652"}, + {file = "rpds_py-0.22.3-cp310-cp310-win_amd64.whl", hash = "sha256:9beeb01d8c190d7581a4d59522cd3d4b6887040dcfc744af99aa59fef3e041a8"}, + {file = "rpds_py-0.22.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d20cfb4e099748ea39e6f7b16c91ab057989712d31761d3300d43134e26e165f"}, + {file = "rpds_py-0.22.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:68049202f67380ff9aa52f12e92b1c30115f32e6895cd7198fa2a7961621fc5a"}, + {file = "rpds_py-0.22.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb4f868f712b2dd4bcc538b0a0c1f63a2b1d584c925e69a224d759e7070a12d5"}, + {file = "rpds_py-0.22.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bc51abd01f08117283c5ebf64844a35144a0843ff7b2983e0648e4d3d9f10dbb"}, + {file = "rpds_py-0.22.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0f3cec041684de9a4684b1572fe28c7267410e02450f4561700ca5a3bc6695a2"}, + {file = "rpds_py-0.22.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7ef9d9da710be50ff6809fed8f1963fecdfecc8b86656cadfca3bc24289414b0"}, + {file = "rpds_py-0.22.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59f4a79c19232a5774aee369a0c296712ad0e77f24e62cad53160312b1c1eaa1"}, + {file = "rpds_py-0.22.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a60bce91f81ddaac922a40bbb571a12c1070cb20ebd6d49c48e0b101d87300d"}, + {file = "rpds_py-0.22.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e89391e6d60251560f0a8f4bd32137b077a80d9b7dbe6d5cab1cd80d2746f648"}, + {file = "rpds_py-0.22.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e3fb866d9932a3d7d0c82da76d816996d1667c44891bd861a0f97ba27e84fc74"}, + {file = "rpds_py-0.22.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1352ae4f7c717ae8cba93421a63373e582d19d55d2ee2cbb184344c82d2ae55a"}, + {file = "rpds_py-0.22.3-cp311-cp311-win32.whl", hash = "sha256:b0b4136a252cadfa1adb705bb81524eee47d9f6aab4f2ee4fa1e9d3cd4581f64"}, + {file = "rpds_py-0.22.3-cp311-cp311-win_amd64.whl", hash = "sha256:8bd7c8cfc0b8247c8799080fbff54e0b9619e17cdfeb0478ba7295d43f635d7c"}, + {file = "rpds_py-0.22.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:27e98004595899949bd7a7b34e91fa7c44d7a97c40fcaf1d874168bb652ec67e"}, + {file = "rpds_py-0.22.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1978d0021e943aae58b9b0b196fb4895a25cc53d3956b8e35e0b7682eefb6d56"}, + {file = "rpds_py-0.22.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:655ca44a831ecb238d124e0402d98f6212ac527a0ba6c55ca26f616604e60a45"}, + {file = "rpds_py-0.22.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:feea821ee2a9273771bae61194004ee2fc33f8ec7db08117ef9147d4bbcbca8e"}, + {file = "rpds_py-0.22.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:22bebe05a9ffc70ebfa127efbc429bc26ec9e9b4ee4d15a740033efda515cf3d"}, + {file = "rpds_py-0.22.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3af6e48651c4e0d2d166dc1b033b7042ea3f871504b6805ba5f4fe31581d8d38"}, + {file = "rpds_py-0.22.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e67ba3c290821343c192f7eae1d8fd5999ca2dc99994114643e2f2d3e6138b15"}, + {file = "rpds_py-0.22.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:02fbb9c288ae08bcb34fb41d516d5eeb0455ac35b5512d03181d755d80810059"}, + {file = "rpds_py-0.22.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f56a6b404f74ab372da986d240e2e002769a7d7102cc73eb238a4f72eec5284e"}, + {file = "rpds_py-0.22.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0a0461200769ab3b9ab7e513f6013b7a97fdeee41c29b9db343f3c5a8e2b9e61"}, + {file = "rpds_py-0.22.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8633e471c6207a039eff6aa116e35f69f3156b3989ea3e2d755f7bc41754a4a7"}, + {file = "rpds_py-0.22.3-cp312-cp312-win32.whl", hash = "sha256:593eba61ba0c3baae5bc9be2f5232430453fb4432048de28399ca7376de9c627"}, + {file = "rpds_py-0.22.3-cp312-cp312-win_amd64.whl", hash = "sha256:d115bffdd417c6d806ea9069237a4ae02f513b778e3789a359bc5856e0404cc4"}, + {file = "rpds_py-0.22.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:ea7433ce7e4bfc3a85654aeb6747babe3f66eaf9a1d0c1e7a4435bbdf27fea84"}, + {file = "rpds_py-0.22.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6dd9412824c4ce1aca56c47b0991e65bebb7ac3f4edccfd3f156150c96a7bf25"}, + {file = "rpds_py-0.22.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20070c65396f7373f5df4005862fa162db5d25d56150bddd0b3e8214e8ef45b4"}, + {file = "rpds_py-0.22.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0b09865a9abc0ddff4e50b5ef65467cd94176bf1e0004184eb915cbc10fc05c5"}, + {file = "rpds_py-0.22.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3453e8d41fe5f17d1f8e9c383a7473cd46a63661628ec58e07777c2fff7196dc"}, + {file = "rpds_py-0.22.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f5d36399a1b96e1a5fdc91e0522544580dbebeb1f77f27b2b0ab25559e103b8b"}, + {file = "rpds_py-0.22.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:009de23c9c9ee54bf11303a966edf4d9087cd43a6003672e6aa7def643d06518"}, + {file = "rpds_py-0.22.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1aef18820ef3e4587ebe8b3bc9ba6e55892a6d7b93bac6d29d9f631a3b4befbd"}, + {file = "rpds_py-0.22.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f60bd8423be1d9d833f230fdbccf8f57af322d96bcad6599e5a771b151398eb2"}, + {file = "rpds_py-0.22.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:62d9cfcf4948683a18a9aff0ab7e1474d407b7bab2ca03116109f8464698ab16"}, + {file = "rpds_py-0.22.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9253fc214112405f0afa7db88739294295f0e08466987f1d70e29930262b4c8f"}, + {file = "rpds_py-0.22.3-cp313-cp313-win32.whl", hash = "sha256:fb0ba113b4983beac1a2eb16faffd76cb41e176bf58c4afe3e14b9c681f702de"}, + {file = "rpds_py-0.22.3-cp313-cp313-win_amd64.whl", hash = "sha256:c58e2339def52ef6b71b8f36d13c3688ea23fa093353f3a4fee2556e62086ec9"}, + {file = "rpds_py-0.22.3-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:f82a116a1d03628a8ace4859556fb39fd1424c933341a08ea3ed6de1edb0283b"}, + {file = "rpds_py-0.22.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3dfcbc95bd7992b16f3f7ba05af8a64ca694331bd24f9157b49dadeeb287493b"}, + {file = "rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59259dc58e57b10e7e18ce02c311804c10c5a793e6568f8af4dead03264584d1"}, + {file = "rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5725dd9cc02068996d4438d397e255dcb1df776b7ceea3b9cb972bdb11260a83"}, + {file = "rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99b37292234e61325e7a5bb9689e55e48c3f5f603af88b1642666277a81f1fbd"}, + {file = "rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:27b1d3b3915a99208fee9ab092b8184c420f2905b7d7feb4aeb5e4a9c509b8a1"}, + {file = "rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f612463ac081803f243ff13cccc648578e2279295048f2a8d5eb430af2bae6e3"}, + {file = "rpds_py-0.22.3-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f73d3fef726b3243a811121de45193c0ca75f6407fe66f3f4e183c983573e130"}, + {file = "rpds_py-0.22.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:3f21f0495edea7fdbaaa87e633a8689cd285f8f4af5c869f27bc8074638ad69c"}, + {file = "rpds_py-0.22.3-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:1e9663daaf7a63ceccbbb8e3808fe90415b0757e2abddbfc2e06c857bf8c5e2b"}, + {file = "rpds_py-0.22.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a76e42402542b1fae59798fab64432b2d015ab9d0c8c47ba7addddbaf7952333"}, + {file = "rpds_py-0.22.3-cp313-cp313t-win32.whl", hash = "sha256:69803198097467ee7282750acb507fba35ca22cc3b85f16cf45fb01cb9097730"}, + {file = "rpds_py-0.22.3-cp313-cp313t-win_amd64.whl", hash = "sha256:f5cf2a0c2bdadf3791b5c205d55a37a54025c6e18a71c71f82bb536cf9a454bf"}, + {file = "rpds_py-0.22.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:378753b4a4de2a7b34063d6f95ae81bfa7b15f2c1a04a9518e8644e81807ebea"}, + {file = "rpds_py-0.22.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3445e07bf2e8ecfeef6ef67ac83de670358abf2996916039b16a218e3d95e97e"}, + {file = "rpds_py-0.22.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b2513ba235829860b13faa931f3b6846548021846ac808455301c23a101689d"}, + {file = "rpds_py-0.22.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eaf16ae9ae519a0e237a0f528fd9f0197b9bb70f40263ee57ae53c2b8d48aeb3"}, + {file = "rpds_py-0.22.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:583f6a1993ca3369e0f80ba99d796d8e6b1a3a2a442dd4e1a79e652116413091"}, + {file = "rpds_py-0.22.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4617e1915a539a0d9a9567795023de41a87106522ff83fbfaf1f6baf8e85437e"}, + {file = "rpds_py-0.22.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c150c7a61ed4a4f4955a96626574e9baf1adf772c2fb61ef6a5027e52803543"}, + {file = "rpds_py-0.22.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2fa4331c200c2521512595253f5bb70858b90f750d39b8cbfd67465f8d1b596d"}, + {file = "rpds_py-0.22.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:214b7a953d73b5e87f0ebece4a32a5bd83c60a3ecc9d4ec8f1dca968a2d91e99"}, + {file = "rpds_py-0.22.3-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:f47ad3d5f3258bd7058d2d506852217865afefe6153a36eb4b6928758041d831"}, + {file = "rpds_py-0.22.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:f276b245347e6e36526cbd4a266a417796fc531ddf391e43574cf6466c492520"}, + {file = "rpds_py-0.22.3-cp39-cp39-win32.whl", hash = "sha256:bbb232860e3d03d544bc03ac57855cd82ddf19c7a07651a7c0fdb95e9efea8b9"}, + {file = "rpds_py-0.22.3-cp39-cp39-win_amd64.whl", hash = "sha256:cfbc454a2880389dbb9b5b398e50d439e2e58669160f27b60e5eca11f68ae17c"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:d48424e39c2611ee1b84ad0f44fb3b2b53d473e65de061e3f460fc0be5f1939d"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:24e8abb5878e250f2eb0d7859a8e561846f98910326d06c0d51381fed59357bd"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b232061ca880db21fa14defe219840ad9b74b6158adb52ddf0e87bead9e8493"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac0a03221cdb5058ce0167ecc92a8c89e8d0decdc9e99a2ec23380793c4dcb96"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eb0c341fa71df5a4595f9501df4ac5abfb5a09580081dffbd1ddd4654e6e9123"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bf9db5488121b596dbfc6718c76092fda77b703c1f7533a226a5a9f65248f8ad"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b8db6b5b2d4491ad5b6bdc2bc7c017eec108acbf4e6785f42a9eb0ba234f4c9"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b3d504047aba448d70cf6fa22e06cb09f7cbd761939fdd47604f5e007675c24e"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:e61b02c3f7a1e0b75e20c3978f7135fd13cb6cf551bf4a6d29b999a88830a338"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:e35ba67d65d49080e8e5a1dd40101fccdd9798adb9b050ff670b7d74fa41c566"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:26fd7cac7dd51011a245f29a2cc6489c4608b5a8ce8d75661bb4a1066c52dfbe"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:177c7c0fce2855833819c98e43c262007f42ce86651ffbb84f37883308cb0e7d"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:bb47271f60660803ad11f4c61b42242b8c1312a31c98c578f79ef9387bbde21c"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:70fb28128acbfd264eda9bf47015537ba3fe86e40d046eb2963d75024be4d055"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:44d61b4b7d0c2c9ac019c314e52d7cbda0ae31078aabd0f22e583af3e0d79723"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f0e260eaf54380380ac3808aa4ebe2d8ca28b9087cf411649f96bad6900c728"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b25bc607423935079e05619d7de556c91fb6adeae9d5f80868dde3468657994b"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fb6116dfb8d1925cbdb52595560584db42a7f664617a1f7d7f6e32f138cdf37d"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a63cbdd98acef6570c62b92a1e43266f9e8b21e699c363c0fef13bd530799c11"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2b8f60e1b739a74bab7e01fcbe3dddd4657ec685caa04681df9d562ef15b625f"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:2e8b55d8517a2fda8d95cb45d62a5a8bbf9dd0ad39c5b25c8833efea07b880ca"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:2de29005e11637e7a2361fa151f780ff8eb2543a0da1413bb951e9f14b699ef3"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:666ecce376999bf619756a24ce15bb14c5bfaf04bf00abc7e663ce17c3f34fe7"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:5246b14ca64a8675e0a7161f7af68fe3e910e6b90542b4bfb5439ba752191df6"}, + {file = "rpds_py-0.22.3.tar.gz", hash = "sha256:e32fee8ab45d3c2db6da19a5323bc3362237c8b653c70194414b892fd06a080d"}, ] [[package]] @@ -3801,13 +3820,13 @@ testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs ( [[package]] name = "six" -version = "1.16.0" +version = "1.17.0" description = "Python 2 and 3 compatibility utilities" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" files = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, + {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, + {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, ] [[package]] @@ -4027,13 +4046,43 @@ files = [ [[package]] name = "tomli" -version = "2.1.0" +version = "2.2.1" description = "A lil' TOML parser" optional = false python-versions = ">=3.8" files = [ - {file = "tomli-2.1.0-py3-none-any.whl", hash = "sha256:a5c57c3d1c56f5ccdf89f6523458f60ef716e210fc47c4cfb188c5ba473e0391"}, - {file = "tomli-2.1.0.tar.gz", hash = "sha256:3f646cae2aec94e17d04973e4249548320197cfabdf130015d023de4b74d8ab8"}, + {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, + {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"}, + {file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"}, + {file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"}, + {file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"}, + {file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"}, + {file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"}, + {file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"}, + {file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"}, + {file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"}, + {file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"}, + {file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"}, + {file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"}, + {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"}, ] [[package]] @@ -4098,13 +4147,13 @@ files = [ [[package]] name = "types-protobuf" -version = "5.28.3.20241030" +version = "5.29.1.20241207" description = "Typing stubs for protobuf" optional = false python-versions = ">=3.8" files = [ - {file = "types-protobuf-5.28.3.20241030.tar.gz", hash = "sha256:f7e6b45845d75393fb41c0b3ce82c46d775f9771fae2097414a1dbfe5b51a988"}, - {file = "types_protobuf-5.28.3.20241030-py3-none-any.whl", hash = "sha256:f3dae16adf342d4fb5bb3673cabb22549a6252e5dd66fc52d8310b1a39c64ba9"}, + {file = "types_protobuf-5.29.1.20241207-py3-none-any.whl", hash = "sha256:92893c42083e9b718c678badc0af7a9a1307b92afe1599e5cba5f3d35b668b2f"}, + {file = "types_protobuf-5.29.1.20241207.tar.gz", hash = "sha256:2ebcadb8ab3ef2e3e2f067e0882906d64ba0dc65fc5b0fd7a8b692315b4a0be9"}, ] [[package]] @@ -4124,13 +4173,13 @@ types-cffi = "*" [[package]] name = "types-python-dateutil" -version = "2.9.0.20241003" +version = "2.9.0.20241206" description = "Typing stubs for python-dateutil" optional = false python-versions = ">=3.8" files = [ - {file = "types-python-dateutil-2.9.0.20241003.tar.gz", hash = "sha256:58cb85449b2a56d6684e41aeefb4c4280631246a0da1a719bdbe6f3fb0317446"}, - {file = "types_python_dateutil-2.9.0.20241003-py3-none-any.whl", hash = "sha256:250e1d8e80e7bbc3a6c99b907762711d1a1cdd00e978ad39cb5940f6f0a87f3d"}, + {file = "types_python_dateutil-2.9.0.20241206-py3-none-any.whl", hash = "sha256:e248a4bc70a486d3e3ec84d0dc30eec3a5f979d6e7ee4123ae043eedbb987f53"}, + {file = "types_python_dateutil-2.9.0.20241206.tar.gz", hash = "sha256:18f493414c26ffba692a72369fea7a154c502646301ebfe3d56a04b3767284cb"}, ] [[package]] @@ -4516,93 +4565,93 @@ files = [ [[package]] name = "yarl" -version = "1.18.0" +version = "1.18.3" description = "Yet another URL library" optional = false python-versions = ">=3.9" files = [ - {file = "yarl-1.18.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:074fee89caab89a97e18ef5f29060ef61ba3cae6cd77673acc54bfdd3214b7b7"}, - {file = "yarl-1.18.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b026cf2c32daf48d90c0c4e406815c3f8f4cfe0c6dfccb094a9add1ff6a0e41a"}, - {file = "yarl-1.18.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ae38bd86eae3ba3d2ce5636cc9e23c80c9db2e9cb557e40b98153ed102b5a736"}, - {file = "yarl-1.18.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:685cc37f3f307c6a8e879986c6d85328f4c637f002e219f50e2ef66f7e062c1d"}, - {file = "yarl-1.18.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8254dbfce84ee5d1e81051ee7a0f1536c108ba294c0fdb5933476398df0654f3"}, - {file = "yarl-1.18.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:20de4a8b04de70c49698dc2390b7fd2d18d424d3b876371f9b775e2b462d4b41"}, - {file = "yarl-1.18.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b0a2074a37285570d54b55820687de3d2f2b9ecf1b714e482e48c9e7c0402038"}, - {file = "yarl-1.18.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3f576ed278860df2721a5d57da3381040176ef1d07def9688a385c8330db61a1"}, - {file = "yarl-1.18.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3a3709450a574d61be6ac53d582496014342ea34876af8dc17cc16da32826c9a"}, - {file = "yarl-1.18.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:bd80ed29761490c622edde5dd70537ca8c992c2952eb62ed46984f8eff66d6e8"}, - {file = "yarl-1.18.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:32141e13a1d5a48525e519c9197d3f4d9744d818d5c7d6547524cc9eccc8971e"}, - {file = "yarl-1.18.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8b8d3e4e014fb4274f1c5bf61511d2199e263909fb0b8bda2a7428b0894e8dc6"}, - {file = "yarl-1.18.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:701bb4a8f4de191c8c0cc9a1e6d5142f4df880e9d1210e333b829ca9425570ed"}, - {file = "yarl-1.18.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a45d94075ac0647621eaaf693c8751813a3eccac455d423f473ffed38c8ac5c9"}, - {file = "yarl-1.18.0-cp310-cp310-win32.whl", hash = "sha256:34176bfb082add67cb2a20abd85854165540891147f88b687a5ed0dc225750a0"}, - {file = "yarl-1.18.0-cp310-cp310-win_amd64.whl", hash = "sha256:73553bbeea7d6ec88c08ad8027f4e992798f0abc459361bf06641c71972794dc"}, - {file = "yarl-1.18.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b8e8c516dc4e1a51d86ac975b0350735007e554c962281c432eaa5822aa9765c"}, - {file = "yarl-1.18.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2e6b4466714a73f5251d84b471475850954f1fa6acce4d3f404da1d55d644c34"}, - {file = "yarl-1.18.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c893f8c1a6d48b25961e00922724732d00b39de8bb0b451307482dc87bddcd74"}, - {file = "yarl-1.18.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:13aaf2bdbc8c86ddce48626b15f4987f22e80d898818d735b20bd58f17292ee8"}, - {file = "yarl-1.18.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dd21c0128e301851de51bc607b0a6da50e82dc34e9601f4b508d08cc89ee7929"}, - {file = "yarl-1.18.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:205de377bd23365cd85562c9c6c33844050a93661640fda38e0567d2826b50df"}, - {file = "yarl-1.18.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed69af4fe2a0949b1ea1d012bf065c77b4c7822bad4737f17807af2adb15a73c"}, - {file = "yarl-1.18.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8e1c18890091aa3cc8a77967943476b729dc2016f4cfe11e45d89b12519d4a93"}, - {file = "yarl-1.18.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:91b8fb9427e33f83ca2ba9501221ffaac1ecf0407f758c4d2f283c523da185ee"}, - {file = "yarl-1.18.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:536a7a8a53b75b2e98ff96edb2dfb91a26b81c4fed82782035767db5a465be46"}, - {file = "yarl-1.18.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:a64619a9c47c25582190af38e9eb382279ad42e1f06034f14d794670796016c0"}, - {file = "yarl-1.18.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c73a6bbc97ba1b5a0c3c992ae93d721c395bdbb120492759b94cc1ac71bc6350"}, - {file = "yarl-1.18.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:a173401d7821a2a81c7b47d4e7d5c4021375a1441af0c58611c1957445055056"}, - {file = "yarl-1.18.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7520e799b1f84e095cce919bd6c23c9d49472deeef25fe1ef960b04cca51c3fc"}, - {file = "yarl-1.18.0-cp311-cp311-win32.whl", hash = "sha256:c4cb992d8090d5ae5f7afa6754d7211c578be0c45f54d3d94f7781c495d56716"}, - {file = "yarl-1.18.0-cp311-cp311-win_amd64.whl", hash = "sha256:52c136f348605974c9b1c878addd6b7a60e3bf2245833e370862009b86fa4689"}, - {file = "yarl-1.18.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1ece25e2251c28bab737bdf0519c88189b3dd9492dc086a1d77336d940c28ced"}, - {file = "yarl-1.18.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:454902dc1830d935c90b5b53c863ba2a98dcde0fbaa31ca2ed1ad33b2a7171c6"}, - {file = "yarl-1.18.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:01be8688fc211dc237e628fcc209dda412d35de7642453059a0553747018d075"}, - {file = "yarl-1.18.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d26f1fa9fa2167bb238f6f4b20218eb4e88dd3ef21bb8f97439fa6b5313e30d"}, - {file = "yarl-1.18.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b234a4a9248a9f000b7a5dfe84b8cb6210ee5120ae70eb72a4dcbdb4c528f72f"}, - {file = "yarl-1.18.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe94d1de77c4cd8caff1bd5480e22342dbd54c93929f5943495d9c1e8abe9f42"}, - {file = "yarl-1.18.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b4c90c5363c6b0a54188122b61edb919c2cd1119684999d08cd5e538813a28e"}, - {file = "yarl-1.18.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49a98ecadc5a241c9ba06de08127ee4796e1009555efd791bac514207862b43d"}, - {file = "yarl-1.18.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9106025c7f261f9f5144f9aa7681d43867eed06349a7cfb297a1bc804de2f0d1"}, - {file = "yarl-1.18.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:f275ede6199d0f1ed4ea5d55a7b7573ccd40d97aee7808559e1298fe6efc8dbd"}, - {file = "yarl-1.18.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:f7edeb1dcc7f50a2c8e08b9dc13a413903b7817e72273f00878cb70e766bdb3b"}, - {file = "yarl-1.18.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c083f6dd6951b86e484ebfc9c3524b49bcaa9c420cb4b2a78ef9f7a512bfcc85"}, - {file = "yarl-1.18.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:80741ec5b471fbdfb997821b2842c59660a1c930ceb42f8a84ba8ca0f25a66aa"}, - {file = "yarl-1.18.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b1a3297b9cad594e1ff0c040d2881d7d3a74124a3c73e00c3c71526a1234a9f7"}, - {file = "yarl-1.18.0-cp312-cp312-win32.whl", hash = "sha256:cd6ab7d6776c186f544f893b45ee0c883542b35e8a493db74665d2e594d3ca75"}, - {file = "yarl-1.18.0-cp312-cp312-win_amd64.whl", hash = "sha256:039c299a0864d1f43c3e31570045635034ea7021db41bf4842693a72aca8df3a"}, - {file = "yarl-1.18.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6fb64dd45453225f57d82c4764818d7a205ee31ce193e9f0086e493916bd4f72"}, - {file = "yarl-1.18.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3adaaf9c6b1b4fc258584f4443f24d775a2086aee82d1387e48a8b4f3d6aecf6"}, - {file = "yarl-1.18.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:da206d1ec78438a563c5429ab808a2b23ad7bc025c8adbf08540dde202be37d5"}, - {file = "yarl-1.18.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:576d258b21c1db4c6449b1c572c75d03f16a482eb380be8003682bdbe7db2f28"}, - {file = "yarl-1.18.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c60e547c0a375c4bfcdd60eef82e7e0e8698bf84c239d715f5c1278a73050393"}, - {file = "yarl-1.18.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e3818eabaefb90adeb5e0f62f047310079d426387991106d4fbf3519eec7d90a"}, - {file = "yarl-1.18.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5f72421246c21af6a92fbc8c13b6d4c5427dfd949049b937c3b731f2f9076bd"}, - {file = "yarl-1.18.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7fa7d37f2ada0f42e0723632993ed422f2a679af0e200874d9d861720a54f53e"}, - {file = "yarl-1.18.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:42ba84e2ac26a3f252715f8ec17e6fdc0cbf95b9617c5367579fafcd7fba50eb"}, - {file = "yarl-1.18.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:6a49ad0102c0f0ba839628d0bf45973c86ce7b590cdedf7540d5b1833ddc6f00"}, - {file = "yarl-1.18.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:96404e8d5e1bbe36bdaa84ef89dc36f0e75939e060ca5cd45451aba01db02902"}, - {file = "yarl-1.18.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:a0509475d714df8f6d498935b3f307cd122c4ca76f7d426c7e1bb791bcd87eda"}, - {file = "yarl-1.18.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:1ff116f0285b5c8b3b9a2680aeca29a858b3b9e0402fc79fd850b32c2bcb9f8b"}, - {file = "yarl-1.18.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e2580c1d7e66e6d29d6e11855e3b1c6381971e0edd9a5066e6c14d79bc8967af"}, - {file = "yarl-1.18.0-cp313-cp313-win32.whl", hash = "sha256:14408cc4d34e202caba7b5ac9cc84700e3421a9e2d1b157d744d101b061a4a88"}, - {file = "yarl-1.18.0-cp313-cp313-win_amd64.whl", hash = "sha256:1db1537e9cb846eb0ff206eac667f627794be8b71368c1ab3207ec7b6f8c5afc"}, - {file = "yarl-1.18.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:fa2c9cb607e0f660d48c54a63de7a9b36fef62f6b8bd50ff592ce1137e73ac7d"}, - {file = "yarl-1.18.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c0f4808644baf0a434a3442df5e0bedf8d05208f0719cedcd499e168b23bfdc4"}, - {file = "yarl-1.18.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7db9584235895a1dffca17e1c634b13870852094f6389b68dcc6338086aa7b08"}, - {file = "yarl-1.18.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:309f8d27d6f93ceeeb80aa6980e883aa57895270f7f41842b92247e65d7aeddf"}, - {file = "yarl-1.18.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:609ffd44fed2ed88d9b4ef62ee860cf86446cf066333ad4ce4123505b819e581"}, - {file = "yarl-1.18.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f172b8b2c72a13a06ea49225a9c47079549036ad1b34afa12d5491b881f5b993"}, - {file = "yarl-1.18.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d89ae7de94631b60d468412c18290d358a9d805182373d804ec839978b120422"}, - {file = "yarl-1.18.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:466d31fd043ef9af822ee3f1df8fdff4e8c199a7f4012c2642006af240eade17"}, - {file = "yarl-1.18.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:7609b8462351c4836b3edce4201acb6dd46187b207c589b30a87ffd1813b48dc"}, - {file = "yarl-1.18.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:d9d4f5e471e8dc49b593a80766c2328257e405f943c56a3dc985c125732bc4cf"}, - {file = "yarl-1.18.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:67b336c15e564d76869c9a21316f90edf546809a5796a083b8f57c845056bc01"}, - {file = "yarl-1.18.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:b212452b80cae26cb767aa045b051740e464c5129b7bd739c58fbb7deb339e7b"}, - {file = "yarl-1.18.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:38b39b7b3e692b6c92b986b00137a3891eddb66311b229d1940dcbd4f025083c"}, - {file = "yarl-1.18.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a7ee6884a8848792d58b854946b685521f41d8871afa65e0d4a774954e9c9e89"}, - {file = "yarl-1.18.0-cp39-cp39-win32.whl", hash = "sha256:b4095c5019bb889aa866bf12ed4c85c0daea5aafcb7c20d1519f02a1e738f07f"}, - {file = "yarl-1.18.0-cp39-cp39-win_amd64.whl", hash = "sha256:2d90f2e4d16a5b0915ee065218b435d2ef619dd228973b1b47d262a6f7cd8fa5"}, - {file = "yarl-1.18.0-py3-none-any.whl", hash = "sha256:dbf53db46f7cf176ee01d8d98c39381440776fcda13779d269a8ba664f69bec0"}, - {file = "yarl-1.18.0.tar.gz", hash = "sha256:20d95535e7d833889982bfe7cc321b7f63bf8879788fee982c76ae2b24cfb715"}, + {file = "yarl-1.18.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7df647e8edd71f000a5208fe6ff8c382a1de8edfbccdbbfe649d263de07d8c34"}, + {file = "yarl-1.18.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c69697d3adff5aa4f874b19c0e4ed65180ceed6318ec856ebc423aa5850d84f7"}, + {file = "yarl-1.18.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:602d98f2c2d929f8e697ed274fbadc09902c4025c5a9963bf4e9edfc3ab6f7ed"}, + {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c654d5207c78e0bd6d749f6dae1dcbbfde3403ad3a4b11f3c5544d9906969dde"}, + {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5094d9206c64181d0f6e76ebd8fb2f8fe274950a63890ee9e0ebfd58bf9d787b"}, + {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35098b24e0327fc4ebdc8ffe336cee0a87a700c24ffed13161af80124b7dc8e5"}, + {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3236da9272872443f81fedc389bace88408f64f89f75d1bdb2256069a8730ccc"}, + {file = "yarl-1.18.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2c08cc9b16f4f4bc522771d96734c7901e7ebef70c6c5c35dd0f10845270bcd"}, + {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:80316a8bd5109320d38eef8833ccf5f89608c9107d02d2a7f985f98ed6876990"}, + {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:c1e1cc06da1491e6734f0ea1e6294ce00792193c463350626571c287c9a704db"}, + {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fea09ca13323376a2fdfb353a5fa2e59f90cd18d7ca4eaa1fd31f0a8b4f91e62"}, + {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e3b9fd71836999aad54084906f8663dffcd2a7fb5cdafd6c37713b2e72be1760"}, + {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:757e81cae69244257d125ff31663249b3013b5dc0a8520d73694aed497fb195b"}, + {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b1771de9944d875f1b98a745bc547e684b863abf8f8287da8466cf470ef52690"}, + {file = "yarl-1.18.3-cp310-cp310-win32.whl", hash = "sha256:8874027a53e3aea659a6d62751800cf6e63314c160fd607489ba5c2edd753cf6"}, + {file = "yarl-1.18.3-cp310-cp310-win_amd64.whl", hash = "sha256:93b2e109287f93db79210f86deb6b9bbb81ac32fc97236b16f7433db7fc437d8"}, + {file = "yarl-1.18.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8503ad47387b8ebd39cbbbdf0bf113e17330ffd339ba1144074da24c545f0069"}, + {file = "yarl-1.18.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:02ddb6756f8f4517a2d5e99d8b2f272488e18dd0bfbc802f31c16c6c20f22193"}, + {file = "yarl-1.18.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:67a283dd2882ac98cc6318384f565bffc751ab564605959df4752d42483ad889"}, + {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d980e0325b6eddc81331d3f4551e2a333999fb176fd153e075c6d1c2530aa8a8"}, + {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b643562c12680b01e17239be267bc306bbc6aac1f34f6444d1bded0c5ce438ca"}, + {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c017a3b6df3a1bd45b9fa49a0f54005e53fbcad16633870104b66fa1a30a29d8"}, + {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75674776d96d7b851b6498f17824ba17849d790a44d282929c42dbb77d4f17ae"}, + {file = "yarl-1.18.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ccaa3a4b521b780a7e771cc336a2dba389a0861592bbce09a476190bb0c8b4b3"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2d06d3005e668744e11ed80812e61efd77d70bb7f03e33c1598c301eea20efbb"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:9d41beda9dc97ca9ab0b9888cb71f7539124bc05df02c0cff6e5acc5a19dcc6e"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ba23302c0c61a9999784e73809427c9dbedd79f66a13d84ad1b1943802eaaf59"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:6748dbf9bfa5ba1afcc7556b71cda0d7ce5f24768043a02a58846e4a443d808d"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0b0cad37311123211dc91eadcb322ef4d4a66008d3e1bdc404808992260e1a0e"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0fb2171a4486bb075316ee754c6d8382ea6eb8b399d4ec62fde2b591f879778a"}, + {file = "yarl-1.18.3-cp311-cp311-win32.whl", hash = "sha256:61b1a825a13bef4a5f10b1885245377d3cd0bf87cba068e1d9a88c2ae36880e1"}, + {file = "yarl-1.18.3-cp311-cp311-win_amd64.whl", hash = "sha256:b9d60031cf568c627d028239693fd718025719c02c9f55df0a53e587aab951b5"}, + {file = "yarl-1.18.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1dd4bdd05407ced96fed3d7f25dbbf88d2ffb045a0db60dbc247f5b3c5c25d50"}, + {file = "yarl-1.18.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7c33dd1931a95e5d9a772d0ac5e44cac8957eaf58e3c8da8c1414de7dd27c576"}, + {file = "yarl-1.18.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:25b411eddcfd56a2f0cd6a384e9f4f7aa3efee14b188de13048c25b5e91f1640"}, + {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:436c4fc0a4d66b2badc6c5fc5ef4e47bb10e4fd9bf0c79524ac719a01f3607c2"}, + {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e35ef8683211db69ffe129a25d5634319a677570ab6b2eba4afa860f54eeaf75"}, + {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:84b2deecba4a3f1a398df819151eb72d29bfeb3b69abb145a00ddc8d30094512"}, + {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00e5a1fea0fd4f5bfa7440a47eff01d9822a65b4488f7cff83155a0f31a2ecba"}, + {file = "yarl-1.18.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d0e883008013c0e4aef84dcfe2a0b172c4d23c2669412cf5b3371003941f72bb"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5a3f356548e34a70b0172d8890006c37be92995f62d95a07b4a42e90fba54272"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ccd17349166b1bee6e529b4add61727d3f55edb7babbe4069b5764c9587a8cc6"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b958ddd075ddba5b09bb0be8a6d9906d2ce933aee81100db289badbeb966f54e"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c7d79f7d9aabd6011004e33b22bc13056a3e3fb54794d138af57f5ee9d9032cb"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:4891ed92157e5430874dad17b15eb1fda57627710756c27422200c52d8a4e393"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ce1af883b94304f493698b00d0f006d56aea98aeb49d75ec7d98cd4a777e9285"}, + {file = "yarl-1.18.3-cp312-cp312-win32.whl", hash = "sha256:f91c4803173928a25e1a55b943c81f55b8872f0018be83e3ad4938adffb77dd2"}, + {file = "yarl-1.18.3-cp312-cp312-win_amd64.whl", hash = "sha256:7e2ee16578af3b52ac2f334c3b1f92262f47e02cc6193c598502bd46f5cd1477"}, + {file = "yarl-1.18.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:90adb47ad432332d4f0bc28f83a5963f426ce9a1a8809f5e584e704b82685dcb"}, + {file = "yarl-1.18.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:913829534200eb0f789d45349e55203a091f45c37a2674678744ae52fae23efa"}, + {file = "yarl-1.18.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ef9f7768395923c3039055c14334ba4d926f3baf7b776c923c93d80195624782"}, + {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88a19f62ff30117e706ebc9090b8ecc79aeb77d0b1f5ec10d2d27a12bc9f66d0"}, + {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e17c9361d46a4d5addf777c6dd5eab0715a7684c2f11b88c67ac37edfba6c482"}, + {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1a74a13a4c857a84a845505fd2d68e54826a2cd01935a96efb1e9d86c728e186"}, + {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41f7ce59d6ee7741af71d82020346af364949314ed3d87553763a2df1829cc58"}, + {file = "yarl-1.18.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f52a265001d830bc425f82ca9eabda94a64a4d753b07d623a9f2863fde532b53"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:82123d0c954dc58db301f5021a01854a85bf1f3bb7d12ae0c01afc414a882ca2"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:2ec9bbba33b2d00999af4631a3397d1fd78290c48e2a3e52d8dd72db3a067ac8"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:fbd6748e8ab9b41171bb95c6142faf068f5ef1511935a0aa07025438dd9a9bc1"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:877d209b6aebeb5b16c42cbb377f5f94d9e556626b1bfff66d7b0d115be88d0a"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b464c4ab4bfcb41e3bfd3f1c26600d038376c2de3297760dfe064d2cb7ea8e10"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8d39d351e7faf01483cc7ff7c0213c412e38e5a340238826be7e0e4da450fdc8"}, + {file = "yarl-1.18.3-cp313-cp313-win32.whl", hash = "sha256:61ee62ead9b68b9123ec24bc866cbef297dd266175d53296e2db5e7f797f902d"}, + {file = "yarl-1.18.3-cp313-cp313-win_amd64.whl", hash = "sha256:578e281c393af575879990861823ef19d66e2b1d0098414855dd367e234f5b3c"}, + {file = "yarl-1.18.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:61e5e68cb65ac8f547f6b5ef933f510134a6bf31bb178be428994b0cb46c2a04"}, + {file = "yarl-1.18.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fe57328fbc1bfd0bd0514470ac692630f3901c0ee39052ae47acd1d90a436719"}, + {file = "yarl-1.18.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a440a2a624683108a1b454705ecd7afc1c3438a08e890a1513d468671d90a04e"}, + {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09c7907c8548bcd6ab860e5f513e727c53b4a714f459b084f6580b49fa1b9cee"}, + {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b4f6450109834af88cb4cc5ecddfc5380ebb9c228695afc11915a0bf82116789"}, + {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9ca04806f3be0ac6d558fffc2fdf8fcef767e0489d2684a21912cc4ed0cd1b8"}, + {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77a6e85b90a7641d2e07184df5557132a337f136250caafc9ccaa4a2a998ca2c"}, + {file = "yarl-1.18.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6333c5a377c8e2f5fae35e7b8f145c617b02c939d04110c76f29ee3676b5f9a5"}, + {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0b3c92fa08759dbf12b3a59579a4096ba9af8dd344d9a813fc7f5070d86bbab1"}, + {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:4ac515b860c36becb81bb84b667466885096b5fc85596948548b667da3bf9f24"}, + {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:045b8482ce9483ada4f3f23b3774f4e1bf4f23a2d5c912ed5170f68efb053318"}, + {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:a4bb030cf46a434ec0225bddbebd4b89e6471814ca851abb8696170adb163985"}, + {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:54d6921f07555713b9300bee9c50fb46e57e2e639027089b1d795ecd9f7fa910"}, + {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1d407181cfa6e70077df3377938c08012d18893f9f20e92f7d2f314a437c30b1"}, + {file = "yarl-1.18.3-cp39-cp39-win32.whl", hash = "sha256:ac36703a585e0929b032fbaab0707b75dc12703766d0b53486eabd5139ebadd5"}, + {file = "yarl-1.18.3-cp39-cp39-win_amd64.whl", hash = "sha256:ba87babd629f8af77f557b61e49e7c7cac36f22f871156b91e10a6e9d4f829e9"}, + {file = "yarl-1.18.3-py3-none-any.whl", hash = "sha256:b57f4f58099328dfb26c6a771d09fb20dbbae81d20cfb66141251ea063bd101b"}, + {file = "yarl-1.18.3.tar.gz", hash = "sha256:ac1801c45cbf77b6c99242eeff4fffb5e4e73a800b5c4ad4fc0be5def634d2e1"}, ] [package.dependencies] @@ -4632,4 +4681,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = ">=3.9,<4.0" -content-hash = "0244ee785378f72a3826faa0b3b25f95fc06abb01f7c7f40f0c5b15db9cafff5" +content-hash = "5124ac44f45015639bc7e6fd1121aeca0084256ca3594e16470a2efd53462452" diff --git a/libs/community/pyproject.toml b/libs/community/pyproject.toml index f19f23b62de25..664d7c1d9f3a6 100644 --- a/libs/community/pyproject.toml +++ b/libs/community/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api" [tool.poetry] name = "langchain-community" -version = "0.3.9" +version = "0.3.12" description = "Community contributed LangChain integrations." authors = [] license = "MIT" @@ -30,8 +30,8 @@ ignore-words-list = "momento,collison,ned,foor,reworkd,parth,whats,aapply,mysogy [tool.poetry.dependencies] python = ">=3.9,<4.0" -langchain-core = "^0.3.21" -langchain = "^0.3.8" +langchain-core = "^0.3.25" +langchain = "^0.3.12" SQLAlchemy = ">=1.4,<3" requests = "^2" PyYAML = ">=5.3" @@ -39,7 +39,7 @@ aiohttp = "^3.8.3" tenacity = ">=8.1.0,!=8.4.0,<10" dataclasses-json = ">= 0.5.7, < 0.7" pydantic-settings = "^2.4.0" -langsmith = "^0.1.125" +langsmith = ">=0.1.125,<0.3" httpx-sse = "^0.4.0" [[tool.poetry.dependencies.numpy]] version = ">=1.22.4,<2" diff --git a/libs/community/scripts/check_pydantic.sh b/libs/community/scripts/check_pydantic.sh index ca83c483d515a..5e2ce64432f81 100755 --- a/libs/community/scripts/check_pydantic.sh +++ b/libs/community/scripts/check_pydantic.sh @@ -20,7 +20,7 @@ count=$(git grep -E '(@root_validator)|(@validator)|(@field_validator)|(@pre_ini # PRs that increase the current count will not be accepted. # PRs that decrease update the code in the repository # and allow decreasing the count of are welcome! -current_count=125 +current_count=124 if [ "$count" -gt "$current_count" ]; then echo "The PR seems to be introducing new usage of @root_validator and/or @field_validator." diff --git a/libs/community/tests/integration_tests/document_loaders/test_json_loader.py b/libs/community/tests/integration_tests/document_loaders/test_json_loader.py index 8f85d9b01911a..620cb16b4a632 100644 --- a/libs/community/tests/integration_tests/document_loaders/test_json_loader.py +++ b/libs/community/tests/integration_tests/document_loaders/test_json_loader.py @@ -1,12 +1,20 @@ from pathlib import Path +from typing import Dict from langchain_community.document_loaders import JSONLoader +def call_back(sample: Dict, additional_fields: Dict) -> Dict: + metadata = additional_fields.copy() + metadata["source"] += f"#seq_num={metadata['seq_num']}" + return metadata + + def test_json_loader() -> None: """Test unstructured loader.""" file_path = Path(__file__).parent.parent / "examples/example.json" - loader = JSONLoader(str(file_path), ".messages[].content") + + loader = JSONLoader(file_path, ".messages[].content", metadata_func=call_back) docs = loader.load() # Check that the correct number of documents are loaded. diff --git a/libs/community/tests/integration_tests/graphs/test_memgraph.py b/libs/community/tests/integration_tests/graphs/test_memgraph.py index 663f974d3f106..c005eec55ef3a 100644 --- a/libs/community/tests/integration_tests/graphs/test_memgraph.py +++ b/libs/community/tests/integration_tests/graphs/test_memgraph.py @@ -1,24 +1,44 @@ import os +from langchain_core.documents import Document + from langchain_community.graphs import MemgraphGraph +from langchain_community.graphs.graph_document import GraphDocument, Node, Relationship +from langchain_community.graphs.memgraph_graph import NODE_PROPERTIES_QUERY, REL_QUERY + +test_data = [ + GraphDocument( + nodes=[Node(id="foo", type="foo"), Node(id="bar", type="bar")], + relationships=[ + Relationship( + source=Node(id="foo", type="foo"), + target=Node(id="bar", type="bar"), + type="REL", + ) + ], + source=Document(page_content="source document"), + ) +] def test_cypher_return_correct_schema() -> None: """Test that chain returns direct results.""" + url = os.environ.get("MEMGRAPH_URI", "bolt://localhost:7687") username = os.environ.get("MEMGRAPH_USERNAME", "") password = os.environ.get("MEMGRAPH_PASSWORD", "") + assert url is not None assert username is not None assert password is not None - graph = MemgraphGraph( - url=url, - username=username, - password=password, - ) - # Delete all nodes in the graph - graph.query("MATCH (n) DETACH DELETE n") + graph = MemgraphGraph(url=url, username=username, password=password) + + # Drop graph + graph.query("STORAGE MODE IN_MEMORY_ANALYTICAL") + graph.query("DROP GRAPH") + graph.query("STORAGE MODE IN_MEMORY_TRANSACTIONAL") + # Create two nodes and a relationship graph.query( """ @@ -31,32 +51,123 @@ def test_cypher_return_correct_schema() -> None: ) # Refresh schema information graph.refresh_schema() - relationships = graph.query( - "CALL llm_util.schema('raw') YIELD schema " - "WITH schema.relationships AS relationships " - "UNWIND relationships AS relationship " - "RETURN relationship['start'] AS start, " - "relationship['type'] AS type, " - "relationship['end'] AS end " - "ORDER BY start, type, end;" - ) - node_props = graph.query( - "CALL llm_util.schema('raw') YIELD schema " - "WITH schema.node_props AS nodes " - "WITH nodes['LabelA'] AS properties " - "UNWIND properties AS property " - "RETURN property['property'] AS prop, " - "property['type'] AS type " - "ORDER BY prop ASC;" - ) + node_properties = graph.query(NODE_PROPERTIES_QUERY) + relationships = graph.query(REL_QUERY) + + expected_node_properties = [ + { + "output": { + "labels": ":`LabelA`", + "properties": [{"key": "property_a", "types": ["String"]}], + } + }, + {"output": {"labels": ":`LabelB`", "properties": [{"key": "", "types": []}]}}, + {"output": {"labels": ":`LabelC`", "properties": [{"key": "", "types": []}]}}, + ] expected_relationships = [ - {"start": "LabelA", "type": "REL_TYPE", "end": "LabelB"}, - {"start": "LabelA", "type": "REL_TYPE", "end": "LabelC"}, + { + "start_node_labels": ["LabelA"], + "rel_type": "REL_TYPE", + "end_node_labels": ["LabelC"], + "properties_info": [["rel_prop", "STRING"]], + }, + { + "start_node_labels": ["LabelA"], + "rel_type": "REL_TYPE", + "end_node_labels": ["LabelB"], + "properties_info": [], + }, ] - expected_node_props = [{"prop": "property_a", "type": "str"}] + graph.close() + assert node_properties == expected_node_properties assert relationships == expected_relationships - assert node_props == expected_node_props + + +def test_add_graph_documents() -> None: + """Test that Memgraph correctly imports graph document.""" + url = os.environ.get("MEMGRAPH_URI", "bolt://localhost:7687") + username = os.environ.get("MEMGRAPH_USERNAME", "") + password = os.environ.get("MEMGRAPH_PASSWORD", "") + + assert url is not None + assert username is not None + assert password is not None + + graph = MemgraphGraph( + url=url, username=username, password=password, refresh_schema=False + ) + # Drop graph + graph.query("STORAGE MODE IN_MEMORY_ANALYTICAL") + graph.query("DROP GRAPH") + graph.query("STORAGE MODE IN_MEMORY_TRANSACTIONAL") + # Create KG + graph.add_graph_documents(test_data) + output = graph.query("MATCH (n) RETURN labels(n) AS label, count(*) AS count") + # Close the connection + graph.close() + assert output == [{"label": ["bar"], "count": 1}, {"label": ["foo"], "count": 1}] + + +def test_add_graph_documents_base_entity() -> None: + """Test that Memgraph correctly imports graph document with Entity label.""" + url = os.environ.get("MEMGRAPH_URI", "bolt://localhost:7687") + username = os.environ.get("MEMGRAPH_USERNAME", "") + password = os.environ.get("MEMGRAPH_PASSWORD", "") + + assert url is not None + assert username is not None + assert password is not None + + graph = MemgraphGraph( + url=url, username=username, password=password, refresh_schema=False + ) + # Drop graph + graph.query("STORAGE MODE IN_MEMORY_ANALYTICAL") + graph.query("DROP GRAPH") + graph.query("STORAGE MODE IN_MEMORY_TRANSACTIONAL") + # Create KG + graph.add_graph_documents(test_data, baseEntityLabel=True) + output = graph.query("MATCH (n) RETURN labels(n) AS label, count(*) AS count") + + # Close the connection + graph.close() + + assert output == [ + {"label": ["__Entity__", "bar"], "count": 1}, + {"label": ["__Entity__", "foo"], "count": 1}, + ] + + +def test_add_graph_documents_include_source() -> None: + """Test that Memgraph correctly imports graph document with source included.""" + url = os.environ.get("MEMGRAPH_URI", "bolt://localhost:7687") + username = os.environ.get("MEMGRAPH_USERNAME", "") + password = os.environ.get("MEMGRAPH_PASSWORD", "") + + assert url is not None + assert username is not None + assert password is not None + + graph = MemgraphGraph( + url=url, username=username, password=password, refresh_schema=False + ) + # Drop graph + graph.query("STORAGE MODE IN_MEMORY_ANALYTICAL") + graph.query("DROP GRAPH") + graph.query("STORAGE MODE IN_MEMORY_TRANSACTIONAL") + # Create KG + graph.add_graph_documents(test_data, include_source=True) + output = graph.query("MATCH (n) RETURN labels(n) AS label, count(*) AS count") + + # Close the connection + graph.close() + + assert output == [ + {"label": ["bar"], "count": 1}, + {"label": ["foo"], "count": 1}, + {"label": ["Document"], "count": 1}, + ] diff --git a/libs/community/tests/integration_tests/vectorstores/test_aperturedb.py b/libs/community/tests/integration_tests/vectorstores/test_aperturedb.py index 15d65de90b136..8b6bc19912a79 100644 --- a/libs/community/tests/integration_tests/vectorstores/test_aperturedb.py +++ b/libs/community/tests/integration_tests/vectorstores/test_aperturedb.py @@ -3,27 +3,15 @@ import uuid import pytest -from langchain_tests.integration_tests.vectorstores import ( - AsyncReadWriteTestSuite, - ReadWriteTestSuite, -) +from langchain_tests.integration_tests.vectorstores import VectorStoreIntegrationTests from langchain_community.vectorstores import ApertureDB -class TestApertureDBReadWriteTestSuite(ReadWriteTestSuite): +class TestApertureStandard(VectorStoreIntegrationTests): @pytest.fixture def vectorstore(self) -> ApertureDB: descriptor_set = uuid.uuid4().hex # Fresh descriptor set for each test return ApertureDB( embeddings=self.get_embeddings(), descriptor_set=descriptor_set ) - - -class TestAsyncApertureDBReadWriteTestSuite(AsyncReadWriteTestSuite): - @pytest.fixture - async def vectorstore(self) -> ApertureDB: - descriptor_set = uuid.uuid4().hex # Fresh descriptor set for each test - return ApertureDB( - embeddings=self.get_embeddings(), descriptor_set=descriptor_set - ) diff --git a/libs/community/tests/integration_tests/vectorstores/test_azure_cosmos_db.py b/libs/community/tests/integration_tests/vectorstores/test_azure_cosmos_db.py index b76bba231a2b2..b75c920224a2f 100644 --- a/libs/community/tests/integration_tests/vectorstores/test_azure_cosmos_db.py +++ b/libs/community/tests/integration_tests/vectorstores/test_azure_cosmos_db.py @@ -8,7 +8,7 @@ import pytest from langchain_core.documents import Document -from langchain_community.embeddings import OpenAIEmbeddings +from langchain_community.embeddings import AzureOpenAIEmbeddings from langchain_community.vectorstores.azure_cosmos_db import ( AzureCosmosDBVectorSearch, CosmosDBSimilarityType, @@ -24,6 +24,7 @@ INDEX_NAME = "langchain-test-index" INDEX_NAME_VECTOR_HNSW = "langchain-test-index-hnsw" +INDEX_NAME_VECTOR_DISKANN = "langchain-test-index-diskann" NAMESPACE = "langchain_test_db.langchain_test_collection" CONNECTION_STRING: str = os.environ.get("MONGODB_VCORE_URI", "") DB_NAME, COLLECTION_NAME = NAMESPACE.split(".") @@ -36,6 +37,9 @@ ef_construction = 64 ef_search = 40 score_threshold = 0.1 +maxDegree = 50 +lBuild = 40 +lSearch = 100 application_name = "LANGCHAIN_PYTHON" @@ -53,8 +57,9 @@ def collection() -> Any: @pytest.fixture() def azure_openai_embeddings() -> Any: - openai_embeddings: OpenAIEmbeddings = OpenAIEmbeddings( - deployment=model_deployment, model=model_name, chunk_size=1 + openai_embeddings: AzureOpenAIEmbeddings = AzureOpenAIEmbeddings( + model=model_name, + chunk_size=1, ) return openai_embeddings @@ -70,8 +75,12 @@ def azure_openai_embeddings() -> Any: class TestAzureCosmosDBVectorSearch: @classmethod def setup_class(cls) -> None: - if not os.getenv("OPENAI_API_KEY"): - raise ValueError("OPENAI_API_KEY environment variable is not set") + if not os.getenv("AZURE_OPENAI_API_KEY"): + raise ValueError("AZURE_OPENAI_API_KEY environment variable is not set") + if not os.getenv("AZURE_OPENAI_ENDPOINT"): + raise ValueError("AZURE_OPENAI_ENDPOINT environment variable is not set") + if not os.getenv("AZURE_OPENAI_API_VERSION"): + raise ValueError("AZURE_OPENAI_API_VERSION environment variable is not set") # insure the test collection is empty collection = prepare_collection() @@ -95,7 +104,7 @@ def cosmos_db_url(self) -> Union[str, Generator[str, None, None]]: return "805.555.1212" def test_from_documents_cosine_distance( - self, azure_openai_embeddings: OpenAIEmbeddings, collection: Any + self, azure_openai_embeddings: AzureOpenAIEmbeddings, collection: Any ) -> None: """Test end to end construction and search.""" documents = [ @@ -135,7 +144,7 @@ def test_from_documents_cosine_distance( vectorstore.delete_index() def test_from_documents_inner_product( - self, azure_openai_embeddings: OpenAIEmbeddings, collection: Any + self, azure_openai_embeddings: AzureOpenAIEmbeddings, collection: Any ) -> None: """Test end to end construction and search.""" documents = [ @@ -174,7 +183,7 @@ def test_from_documents_inner_product( vectorstore.delete_index() def test_from_texts_cosine_distance( - self, azure_openai_embeddings: OpenAIEmbeddings, collection: Any + self, azure_openai_embeddings: AzureOpenAIEmbeddings, collection: Any ) -> None: texts = [ "Dogs are tough.", @@ -208,7 +217,7 @@ def test_from_texts_cosine_distance( vectorstore.delete_index() def test_from_texts_with_metadatas_cosine_distance( - self, azure_openai_embeddings: OpenAIEmbeddings, collection: Any + self, azure_openai_embeddings: AzureOpenAIEmbeddings, collection: Any ) -> None: texts = [ "Dogs are tough.", @@ -246,7 +255,7 @@ def test_from_texts_with_metadatas_cosine_distance( vectorstore.delete_index() def test_from_texts_with_metadatas_delete_one( - self, azure_openai_embeddings: OpenAIEmbeddings, collection: Any + self, azure_openai_embeddings: AzureOpenAIEmbeddings, collection: Any ) -> None: texts = [ "Dogs are tough.", @@ -280,7 +289,6 @@ def test_from_texts_with_metadatas_delete_one( assert output assert output[0].page_content == "What is a sandwich?" assert output[0].metadata["c"] == 1 - first_document_id_object = output[0].metadata["_id"] first_document_id = str(first_document_id_object) @@ -300,7 +308,7 @@ def test_from_texts_with_metadatas_delete_one( vectorstore.delete_index() def test_from_texts_with_metadatas_delete_multiple( - self, azure_openai_embeddings: OpenAIEmbeddings, collection: Any + self, azure_openai_embeddings: AzureOpenAIEmbeddings, collection: Any ) -> None: texts = [ "Dogs are tough.", @@ -359,7 +367,7 @@ def test_from_texts_with_metadatas_delete_multiple( vectorstore.delete_index() def test_from_texts_with_metadatas_inner_product( - self, azure_openai_embeddings: OpenAIEmbeddings, collection: Any + self, azure_openai_embeddings: AzureOpenAIEmbeddings, collection: Any ) -> None: texts = [ "Dogs are tough.", @@ -397,7 +405,7 @@ def test_from_texts_with_metadatas_inner_product( vectorstore.delete_index() def test_from_texts_with_metadatas_euclidean_distance( - self, azure_openai_embeddings: OpenAIEmbeddings, collection: Any + self, azure_openai_embeddings: AzureOpenAIEmbeddings, collection: Any ) -> None: texts = [ "Dogs are tough.", @@ -435,7 +443,7 @@ def test_from_texts_with_metadatas_euclidean_distance( vectorstore.delete_index() def test_max_marginal_relevance_cosine_distance( - self, azure_openai_embeddings: OpenAIEmbeddings, collection: Any + self, azure_openai_embeddings: AzureOpenAIEmbeddings, collection: Any ) -> None: texts = ["foo", "foo", "fou", "foy"] vectorstore = AzureCosmosDBVectorSearch.from_texts( @@ -453,7 +461,12 @@ def test_max_marginal_relevance_cosine_distance( query = "foo" output = vectorstore.max_marginal_relevance_search( - query, k=10, kind=kind, lambda_mult=0.1, score_threshold=score_threshold + query, + k=10, + kind=kind, + lambda_mult=0.1, + score_threshold=score_threshold, + with_embedding=True, ) assert len(output) == len(texts) @@ -463,7 +476,7 @@ def test_max_marginal_relevance_cosine_distance( vectorstore.delete_index() def test_max_marginal_relevance_inner_product( - self, azure_openai_embeddings: OpenAIEmbeddings, collection: Any + self, azure_openai_embeddings: AzureOpenAIEmbeddings, collection: Any ) -> None: texts = ["foo", "foo", "fou", "foy"] vectorstore = AzureCosmosDBVectorSearch.from_texts( @@ -481,7 +494,12 @@ def test_max_marginal_relevance_inner_product( query = "foo" output = vectorstore.max_marginal_relevance_search( - query, k=10, kind=kind, lambda_mult=0.1, score_threshold=score_threshold + query, + k=10, + kind=kind, + lambda_mult=0.1, + score_threshold=score_threshold, + with_embedding=True, ) assert len(output) == len(texts) @@ -495,7 +513,7 @@ def test_max_marginal_relevance_inner_product( """ def test_from_documents_cosine_distance_vector_hnsw( - self, azure_openai_embeddings: OpenAIEmbeddings, collection: Any + self, azure_openai_embeddings: AzureOpenAIEmbeddings, collection: Any ) -> None: """Test end to end construction and search.""" documents = [ @@ -539,7 +557,7 @@ def test_from_documents_cosine_distance_vector_hnsw( vectorstore.delete_index() def test_from_documents_inner_product_vector_hnsw( - self, azure_openai_embeddings: OpenAIEmbeddings, collection: Any + self, azure_openai_embeddings: AzureOpenAIEmbeddings, collection: Any ) -> None: """Test end to end construction and search.""" documents = [ @@ -583,7 +601,7 @@ def test_from_documents_inner_product_vector_hnsw( vectorstore.delete_index() def test_from_texts_cosine_distance_vector_hnsw( - self, azure_openai_embeddings: OpenAIEmbeddings, collection: Any + self, azure_openai_embeddings: AzureOpenAIEmbeddings, collection: Any ) -> None: texts = [ "Dogs are tough.", @@ -622,7 +640,7 @@ def test_from_texts_cosine_distance_vector_hnsw( vectorstore.delete_index() def test_from_texts_with_metadatas_cosine_distance_vector_hnsw( - self, azure_openai_embeddings: OpenAIEmbeddings, collection: Any + self, azure_openai_embeddings: AzureOpenAIEmbeddings, collection: Any ) -> None: texts = [ "Dogs are tough.", @@ -665,7 +683,7 @@ def test_from_texts_with_metadatas_cosine_distance_vector_hnsw( vectorstore.delete_index() def test_from_texts_with_metadatas_delete_one_vector_hnsw( - self, azure_openai_embeddings: OpenAIEmbeddings, collection: Any + self, azure_openai_embeddings: AzureOpenAIEmbeddings, collection: Any ) -> None: texts = [ "Dogs are tough.", @@ -724,7 +742,7 @@ def test_from_texts_with_metadatas_delete_one_vector_hnsw( vectorstore.delete_index() def test_from_texts_with_metadatas_delete_multiple_vector_hnsw( - self, azure_openai_embeddings: OpenAIEmbeddings, collection: Any + self, azure_openai_embeddings: AzureOpenAIEmbeddings, collection: Any ) -> None: texts = [ "Dogs are tough.", @@ -788,7 +806,7 @@ def test_from_texts_with_metadatas_delete_multiple_vector_hnsw( vectorstore.delete_index() def test_from_texts_with_metadatas_inner_product_vector_hnsw( - self, azure_openai_embeddings: OpenAIEmbeddings, collection: Any + self, azure_openai_embeddings: AzureOpenAIEmbeddings, collection: Any ) -> None: texts = [ "Dogs are tough.", @@ -831,7 +849,7 @@ def test_from_texts_with_metadatas_inner_product_vector_hnsw( vectorstore.delete_index() def test_max_marginal_relevance_cosine_distance_vector_hnsw( - self, azure_openai_embeddings: OpenAIEmbeddings, collection: Any + self, azure_openai_embeddings: AzureOpenAIEmbeddings, collection: Any ) -> None: texts = ["foo", "foo", "fou", "foy"] vectorstore = AzureCosmosDBVectorSearch.from_texts( @@ -859,6 +877,7 @@ def test_max_marginal_relevance_cosine_distance_vector_hnsw( kind=CosmosDBVectorSearchType.VECTOR_HNSW, lambda_mult=0.1, score_threshold=score_threshold, + with_embedding=True, ) assert len(output) == len(texts) @@ -868,7 +887,7 @@ def test_max_marginal_relevance_cosine_distance_vector_hnsw( vectorstore.delete_index() def test_max_marginal_relevance_inner_product_vector_hnsw( - self, azure_openai_embeddings: OpenAIEmbeddings, collection: Any + self, azure_openai_embeddings: AzureOpenAIEmbeddings, collection: Any ) -> None: texts = ["foo", "foo", "fou", "foy"] vectorstore = AzureCosmosDBVectorSearch.from_texts( @@ -896,6 +915,405 @@ def test_max_marginal_relevance_inner_product_vector_hnsw( kind=CosmosDBVectorSearchType.VECTOR_HNSW, lambda_mult=0.1, score_threshold=score_threshold, + with_embedding=True, + ) + + assert len(output) == len(texts) + assert output[0].page_content == "foo" + assert output[1].page_content != "foo" + + vectorstore.delete_index() + + """ + Test cases for the similarity algorithm using vector-diskann + """ + + def test_from_documents_cosine_distance_vector_diskann( + self, azure_openai_embeddings: AzureOpenAIEmbeddings, collection: Any + ) -> None: + """Test end to end construction and search.""" + documents = [ + Document(page_content="Dogs are tough.", metadata={"a": 1}), + Document(page_content="Cats have fluff.", metadata={"b": 1}), + Document(page_content="What is a sandwich?", metadata={"c": 1}), + Document(page_content="That fence is purple.", metadata={"d": 1, "e": 2}), + ] + + vectorstore = AzureCosmosDBVectorSearch.from_documents( + documents, + azure_openai_embeddings, + collection=collection, + index_name=INDEX_NAME_VECTOR_DISKANN, + ) + sleep(1) # waits for Cosmos DB to save contents to the collection + + # Create the DiskANN index that will be leveraged later for vector search + vectorstore.create_index( + dimensions=dimensions, + similarity=similarity_algorithm, + kind=CosmosDBVectorSearchType.VECTOR_DISKANN, + max_degree=maxDegree, + l_build=lBuild, + ) + sleep(2) # waits for the index to be set up + + output = vectorstore.similarity_search( + "Sandwich", + k=1, + kind=CosmosDBVectorSearchType.VECTOR_DISKANN, + lSearch=lSearch, + ) + + assert output + assert output[0].page_content == "What is a sandwich?" + assert output[0].metadata["c"] == 1 + + vectorstore.delete_index() + + def test_from_documents_inner_product_vector_diskann( + self, azure_openai_embeddings: AzureOpenAIEmbeddings, collection: Any + ) -> None: + """Test end to end construction and search.""" + documents = [ + Document(page_content="Dogs are tough.", metadata={"a": 1}), + Document(page_content="Cats have fluff.", metadata={"b": 1}), + Document(page_content="What is a sandwich?", metadata={"c": 1}), + Document(page_content="That fence is purple.", metadata={"d": 1, "e": 2}), + ] + + vectorstore = AzureCosmosDBVectorSearch.from_documents( + documents, + azure_openai_embeddings, + collection=collection, + index_name=INDEX_NAME_VECTOR_DISKANN, + ) + sleep(1) # waits for Cosmos DB to save contents to the collection + + # Create the DiskANN index that will be leveraged later for vector search + vectorstore.create_index( + dimensions=dimensions, + similarity=similarity_algorithm, + kind=CosmosDBVectorSearchType.VECTOR_DISKANN, + max_degree=maxDegree, + l_build=lBuild, + ) + sleep(2) # waits for the index to be set up + + output = vectorstore.similarity_search( + "Sandwich", + k=1, + kind=CosmosDBVectorSearchType.VECTOR_DISKANN, + lSearch=lSearch, + ) + + assert output + assert output[0].page_content == "What is a sandwich?" + assert output[0].metadata["c"] == 1 + + vectorstore.delete_index() + + def test_from_texts_cosine_distance_vector_diskann( + self, azure_openai_embeddings: AzureOpenAIEmbeddings, collection: Any + ) -> None: + texts = [ + "Dogs are tough.", + "Cats have fluff.", + "What is a sandwich?", + "That fence is purple.", + ] + vectorstore = AzureCosmosDBVectorSearch.from_texts( + texts, + azure_openai_embeddings, + collection=collection, + index_name=INDEX_NAME_VECTOR_DISKANN, + ) + + # Create the DiskANN index that will be leveraged later for vector search + vectorstore.create_index( + dimensions=dimensions, + similarity=similarity_algorithm, + kind=CosmosDBVectorSearchType.VECTOR_DISKANN, + max_degree=maxDegree, + l_build=lBuild, + ) + sleep(2) # waits for the index to be set up + + output = vectorstore.similarity_search( + "Sandwich", + k=1, + kind=CosmosDBVectorSearchType.VECTOR_DISKANN, + lSearch=lSearch, + ) + + assert output[0].page_content == "What is a sandwich?" + + vectorstore.delete_index() + + def test_from_texts_with_metadatas_cosine_distance_vector_diskann( + self, azure_openai_embeddings: AzureOpenAIEmbeddings, collection: Any + ) -> None: + texts = [ + "Dogs are tough.", + "Cats have fluff.", + "What is a sandwich?", + "The fence is purple.", + ] + metadatas = [{"a": 1}, {"b": 1}, {"c": 1}, {"d": 1, "e": 2}] + vectorstore = AzureCosmosDBVectorSearch.from_texts( + texts, + azure_openai_embeddings, + metadatas=metadatas, + collection=collection, + index_name=INDEX_NAME_VECTOR_DISKANN, + ) + + # Create the DiskANN index that will be leveraged later for vector search + vectorstore.create_index( + dimensions=dimensions, + similarity=similarity_algorithm, + kind=CosmosDBVectorSearchType.VECTOR_DISKANN, + max_degree=maxDegree, + l_build=lBuild, + ) + sleep(2) # waits for the index to be set up + + output = vectorstore.similarity_search( + "Sandwich", + k=1, + kind=CosmosDBVectorSearchType.VECTOR_DISKANN, + lSearch=lSearch, + ) + + assert output + assert output[0].page_content == "What is a sandwich?" + assert output[0].metadata["c"] == 1 + + vectorstore.delete_index() + + def test_from_texts_with_metadatas_delete_one_vector_diskann( + self, azure_openai_embeddings: AzureOpenAIEmbeddings, collection: Any + ) -> None: + texts = [ + "Dogs are tough.", + "Cats have fluff.", + "What is a sandwich?", + "The fence is purple.", + ] + metadatas = [{"a": 1}, {"b": 1}, {"c": 1}, {"d": 1, "e": 2}] + vectorstore = AzureCosmosDBVectorSearch.from_texts( + texts, + azure_openai_embeddings, + metadatas=metadatas, + collection=collection, + index_name=INDEX_NAME_VECTOR_DISKANN, + ) + + # Create the DiskANN index that will be leveraged later for vector search + vectorstore.create_index( + dimensions=dimensions, + similarity=similarity_algorithm, + kind=CosmosDBVectorSearchType.VECTOR_DISKANN, + max_degree=maxDegree, + l_build=lBuild, + ) + sleep(2) # waits for the index to be set up + + output = vectorstore.similarity_search( + "Sandwich", + k=1, + kind=CosmosDBVectorSearchType.VECTOR_DISKANN, + lSearch=lSearch, + ) + + assert output + assert output[0].page_content == "What is a sandwich?" + assert output[0].metadata["c"] == 1 + + first_document_id_object = output[0].metadata["_id"] + first_document_id = str(first_document_id_object) + + vectorstore.delete_document_by_id(first_document_id) + sleep(2) # waits for the index to be updated + + output2 = vectorstore.similarity_search( + "Sandwich", + k=1, + kind=CosmosDBVectorSearchType.VECTOR_DISKANN, + lSearch=lSearch, + ) + assert output2 + assert output2[0].page_content != "What is a sandwich?" + + vectorstore.delete_index() + + def test_from_texts_with_metadatas_delete_multiple_vector_diskann( + self, azure_openai_embeddings: AzureOpenAIEmbeddings, collection: Any + ) -> None: + texts = [ + "Dogs are tough.", + "Cats have fluff.", + "What is a sandwich?", + "The fence is purple.", + ] + metadatas = [{"a": 1}, {"b": 1}, {"c": 1}, {"d": 1, "e": 2}] + vectorstore = AzureCosmosDBVectorSearch.from_texts( + texts, + azure_openai_embeddings, + metadatas=metadatas, + collection=collection, + index_name=INDEX_NAME_VECTOR_DISKANN, + ) + + # Create the DiskANN index that will be leveraged later for vector search + vectorstore.create_index( + dimensions=dimensions, + similarity=similarity_algorithm, + kind=CosmosDBVectorSearchType.VECTOR_DISKANN, + max_degree=maxDegree, + l_build=lBuild, + ) + sleep(2) # waits for the index to be set up + + output = vectorstore.similarity_search( + "Sandwich", + k=5, + kind=CosmosDBVectorSearchType.VECTOR_DISKANN, + lSearch=lSearch, + ) + + first_document_id = str(output[0].metadata["_id"]) + + second_document_id = str(output[1].metadata["_id"]) + + third_document_id = str(output[2].metadata["_id"]) + + document_ids = [first_document_id, second_document_id, third_document_id] + vectorstore.delete(document_ids) + sleep(2) # waits for the index to be updated + + output_2 = vectorstore.similarity_search( + "Sandwich", + k=5, + kind=CosmosDBVectorSearchType.VECTOR_DISKANN, + lSearch=lSearch, + ) + assert output + assert output_2 + + assert len(output) == 4 # we should see all the four documents + assert ( + len(output_2) == 1 + ) # we should see only one document left after three have been deleted + + vectorstore.delete_index() + + def test_from_texts_with_metadatas_inner_product_vector_diskann( + self, azure_openai_embeddings: AzureOpenAIEmbeddings, collection: Any + ) -> None: + texts = [ + "Dogs are tough.", + "Cats have fluff.", + "What is a sandwich?", + "The fence is purple.", + ] + metadatas = [{"a": 1}, {"b": 1}, {"c": 1}, {"d": 1, "e": 2}] + vectorstore = AzureCosmosDBVectorSearch.from_texts( + texts, + azure_openai_embeddings, + metadatas=metadatas, + collection=collection, + index_name=INDEX_NAME_VECTOR_DISKANN, + ) + + # Create the DiskANN index that will be leveraged later for vector search + vectorstore.create_index( + dimensions=dimensions, + similarity=similarity_algorithm, + kind=CosmosDBVectorSearchType.VECTOR_DISKANN, + max_degree=maxDegree, + l_build=lBuild, + ) + sleep(2) # waits for the index to be set up + + output = vectorstore.similarity_search( + "Sandwich", + k=1, + kind=CosmosDBVectorSearchType.VECTOR_DISKANN, + lSearch=lSearch, + ) + + assert output + assert output[0].page_content == "What is a sandwich?" + assert output[0].metadata["c"] == 1 + + vectorstore.delete_index() + + def test_max_marginal_relevance_cosine_distance_vector_diskann( + self, azure_openai_embeddings: AzureOpenAIEmbeddings, collection: Any + ) -> None: + texts = ["foo", "foo", "fou", "foy"] + vectorstore = AzureCosmosDBVectorSearch.from_texts( + texts, + azure_openai_embeddings, + collection=collection, + index_name=INDEX_NAME_VECTOR_DISKANN, + ) + + # Create the IVF index that will be leveraged later for vector search + vectorstore.create_index( + dimensions=dimensions, + similarity=similarity_algorithm, + kind=CosmosDBVectorSearchType.VECTOR_DISKANN, + max_degree=maxDegree, + l_build=lBuild, + ) + sleep(2) # waits for the index to be set up + + query = "foo" + output = vectorstore.max_marginal_relevance_search( + query, + k=10, + kind=CosmosDBVectorSearchType.VECTOR_DISKANN, + lambda_mult=0.1, + lSearch=lSearch, + with_embedding=True, + ) + + assert len(output) == len(texts) + assert output[0].page_content == "foo" + assert output[1].page_content != "foo" + + vectorstore.delete_index() + + def test_max_marginal_relevance_inner_product_vector_diskann( + self, azure_openai_embeddings: AzureOpenAIEmbeddings, collection: Any + ) -> None: + texts = ["foo", "foo", "fou", "foy"] + vectorstore = AzureCosmosDBVectorSearch.from_texts( + texts, + azure_openai_embeddings, + collection=collection, + index_name=INDEX_NAME_VECTOR_DISKANN, + ) + + # Create the DiskANN index that will be leveraged later for vector search + vectorstore.create_index( + dimensions=dimensions, + similarity=similarity_algorithm, + kind=CosmosDBVectorSearchType.VECTOR_DISKANN, + max_degree=maxDegree, + l_build=lBuild, + ) + sleep(2) # waits for the index to be set up + + query = "foo" + output = vectorstore.max_marginal_relevance_search( + query, + k=10, + kind=CosmosDBVectorSearchType.VECTOR_DISKANN, + lambda_mult=0.1, + lSearch=lSearch, + with_embedding=True, ) assert len(output) == len(texts) @@ -906,7 +1324,7 @@ def test_max_marginal_relevance_inner_product_vector_hnsw( @staticmethod def invoke_delete_with_no_args( - azure_openai_embeddings: OpenAIEmbeddings, collection: Any + azure_openai_embeddings: AzureOpenAIEmbeddings, collection: Any ) -> Optional[bool]: vectorstore: AzureCosmosDBVectorSearch = ( AzureCosmosDBVectorSearch.from_connection_string( @@ -922,7 +1340,7 @@ def invoke_delete_with_no_args( @staticmethod def invoke_delete_by_id_with_no_args( - azure_openai_embeddings: OpenAIEmbeddings, collection: Any + azure_openai_embeddings: AzureOpenAIEmbeddings, collection: Any ) -> None: vectorstore: AzureCosmosDBVectorSearch = ( AzureCosmosDBVectorSearch.from_connection_string( @@ -937,14 +1355,14 @@ def invoke_delete_by_id_with_no_args( vectorstore.delete_document_by_id() def test_invalid_arguments_to_delete( - self, azure_openai_embeddings: OpenAIEmbeddings, collection: Any + self, azure_openai_embeddings: AzureOpenAIEmbeddings, collection: Any ) -> None: with pytest.raises(ValueError) as exception_info: self.invoke_delete_with_no_args(azure_openai_embeddings, collection) assert str(exception_info.value) == "No document ids provided to delete." def test_no_arguments_to_delete_by_id( - self, azure_openai_embeddings: OpenAIEmbeddings, collection: Any + self, azure_openai_embeddings: AzureOpenAIEmbeddings, collection: Any ) -> None: with pytest.raises(Exception) as exception_info: self.invoke_delete_by_id_with_no_args( diff --git a/libs/community/tests/integration_tests/vectorstores/test_hanavector.py b/libs/community/tests/integration_tests/vectorstores/test_hanavector.py index fd50baf529077..e1ffcd7af0eac 100644 --- a/libs/community/tests/integration_tests/vectorstores/test_hanavector.py +++ b/libs/community/tests/integration_tests/vectorstores/test_hanavector.py @@ -1432,3 +1432,193 @@ def test_preexisting_specific_columns_for_returned_metadata_completeness( assert docs[0].metadata["quality"] == "good" assert docs[0].metadata["ready"] assert "NonExisting" not in docs[0].metadata.keys() + + +@pytest.mark.skipif(not hanadb_installed, reason="hanadb not installed") +def test_create_hnsw_index_with_default_values(texts: List[str]) -> None: + table_name = "TEST_TABLE_HNSW_INDEX_DEFAULT" + + # Delete table if it exists (cleanup from previous tests) + drop_table(test_setup.conn, table_name) + + # Create table and insert data + vectorDB = HanaDB.from_texts( + connection=test_setup.conn, + texts=texts, + embedding=embedding, + table_name=table_name, + ) + + # Test the creation of HNSW index + try: + vectorDB.create_hnsw_index() + except Exception as e: + pytest.fail(f"Failed to create HNSW index: {e}") + + # Perform a search using the index to confirm its correctness + search_result = vectorDB.max_marginal_relevance_search(texts[0], k=2, fetch_k=20) + + assert len(search_result) == 2 + assert search_result[0].page_content == texts[0] + assert search_result[1].page_content != texts[0] + + +@pytest.mark.skipif(not hanadb_installed, reason="hanadb not installed") +def test_create_hnsw_index_with_defined_values(texts: List[str]) -> None: + table_name = "TEST_TABLE_HNSW_INDEX_DEFINED" + + # Delete table if it exists (cleanup from previous tests) + drop_table(test_setup.conn, table_name) + + # Create table and insert data + vectorDB = HanaDB.from_texts( + connection=test_setup.conn, + texts=texts, + embedding=embedding, + table_name=table_name, + distance_strategy=DistanceStrategy.EUCLIDEAN_DISTANCE, + ) + + # Test the creation of HNSW index with specific values + try: + vectorDB.create_hnsw_index( + index_name="my_L2_index", ef_search=500, m=100, ef_construction=200 + ) + except Exception as e: + pytest.fail(f"Failed to create HNSW index with defined values: {e}") + + # Perform a search using the index to confirm its correctness + search_result = vectorDB.max_marginal_relevance_search(texts[0], k=2, fetch_k=20) + + assert len(search_result) == 2 + assert search_result[0].page_content == texts[0] + assert search_result[1].page_content != texts[0] + + +@pytest.mark.skipif(not hanadb_installed, reason="hanadb not installed") +def test_create_hnsw_index_after_initialization(texts: List[str]) -> None: + table_name = "TEST_TABLE_HNSW_INDEX_AFTER_INIT" + + drop_table(test_setup.conn, table_name) + + # Initialize HanaDB without adding documents yet + vectorDB = HanaDB( + connection=test_setup.conn, + embedding=embedding, + table_name=table_name, + ) + + # Create HNSW index before adding documents + vectorDB.create_hnsw_index( + index_name="index_pre_add", ef_search=400, m=50, ef_construction=150 + ) + + # Add texts after index creation + vectorDB.add_texts(texts=texts) + + # Perform similarity search using the index + search_result = vectorDB.similarity_search(texts[0], k=3) + + # Assert that search result is valid and has expected length + assert len(search_result) == 3 + assert search_result[0].page_content == texts[0] + assert search_result[1].page_content != texts[0] + + +@pytest.mark.skipif(not hanadb_installed, reason="hanadb not installed") +def test_duplicate_hnsw_index_creation(texts: List[str]) -> None: + table_name = "TEST_TABLE_HNSW_DUPLICATE_INDEX" + + # Delete table if it exists (cleanup from previous tests) + drop_table(test_setup.conn, table_name) + + # Create table and insert data + vectorDB = HanaDB.from_texts( + connection=test_setup.conn, + texts=texts, + embedding=embedding, + table_name=table_name, + ) + + # Create HNSW index for the first time + vectorDB.create_hnsw_index( + index_name="index_cosine", + ef_search=300, + m=80, + ef_construction=100, + ) + + with pytest.raises(Exception): + vectorDB.create_hnsw_index(ef_search=300, m=80, ef_construction=100) + + +@pytest.mark.skipif(not hanadb_installed, reason="hanadb not installed") +def test_create_hnsw_index_invalid_m_value(texts: List[str]) -> None: + table_name = "TEST_TABLE_HNSW_INVALID_M" + + # Cleanup: drop the table if it exists + drop_table(test_setup.conn, table_name) + + # Create table and insert data + vectorDB = HanaDB.from_texts( + connection=test_setup.conn, + texts=texts, + embedding=embedding, + table_name=table_name, + ) + + # Test invalid `m` value (too low) + with pytest.raises(ValueError): + vectorDB.create_hnsw_index(m=3) + + # Test invalid `m` value (too high) + with pytest.raises(ValueError): + vectorDB.create_hnsw_index(m=1001) + + +@pytest.mark.skipif(not hanadb_installed, reason="hanadb not installed") +def test_create_hnsw_index_invalid_ef_construction(texts: List[str]) -> None: + table_name = "TEST_TABLE_HNSW_INVALID_EF_CONSTRUCTION" + + # Cleanup: drop the table if it exists + drop_table(test_setup.conn, table_name) + + # Create table and insert data + vectorDB = HanaDB.from_texts( + connection=test_setup.conn, + texts=texts, + embedding=embedding, + table_name=table_name, + ) + + # Test invalid `ef_construction` value (too low) + with pytest.raises(ValueError): + vectorDB.create_hnsw_index(ef_construction=0) + + # Test invalid `ef_construction` value (too high) + with pytest.raises(ValueError): + vectorDB.create_hnsw_index(ef_construction=100001) + + +@pytest.mark.skipif(not hanadb_installed, reason="hanadb not installed") +def test_create_hnsw_index_invalid_ef_search(texts: List[str]) -> None: + table_name = "TEST_TABLE_HNSW_INVALID_EF_SEARCH" + + # Cleanup: drop the table if it exists + drop_table(test_setup.conn, table_name) + + # Create table and insert data + vectorDB = HanaDB.from_texts( + connection=test_setup.conn, + texts=texts, + embedding=embedding, + table_name=table_name, + ) + + # Test invalid `ef_search` value (too low) + with pytest.raises(ValueError): + vectorDB.create_hnsw_index(ef_search=0) + + # Test invalid `ef_search` value (too high) + with pytest.raises(ValueError): + vectorDB.create_hnsw_index(ef_search=100001) diff --git a/libs/community/tests/integration_tests/vectorstores/test_lancedb.py b/libs/community/tests/integration_tests/vectorstores/test_lancedb.py index 7152c93bfa117..7ba3a00466388 100644 --- a/libs/community/tests/integration_tests/vectorstores/test_lancedb.py +++ b/libs/community/tests/integration_tests/vectorstores/test_lancedb.py @@ -84,6 +84,16 @@ def test_lancedb_delete() -> None: assert store.get_table().count_rows() == 2 +@pytest.mark.requires("lancedb") +def test_lancedb_delete_by_ids() -> None: + embeddings = FakeEmbeddings() + + store = LanceDB(embedding=embeddings, id_key="pk") + ids = store.add_texts(["text 1", "text 2", "item 3"]) + store.delete(ids=ids) + assert store.get_table().count_rows() == 0 + + @pytest.mark.requires("lancedb") def test_lancedb_all_searches() -> None: embeddings = FakeEmbeddings() diff --git a/libs/community/tests/integration_tests/vectorstores/test_opensearch.py b/libs/community/tests/integration_tests/vectorstores/test_opensearch.py index 22bff5200738c..bae4e0454a798 100644 --- a/libs/community/tests/integration_tests/vectorstores/test_opensearch.py +++ b/libs/community/tests/integration_tests/vectorstores/test_opensearch.py @@ -4,6 +4,7 @@ from langchain_core.documents import Document from langchain_community.vectorstores.opensearch_vector_search import ( + HYBRID_SEARCH, PAINLESS_SCRIPTING_SEARCH, SCRIPT_SCORING_SEARCH, OpenSearchVectorSearch, @@ -75,6 +76,112 @@ def test_opensearch_with_custom_field_name() -> None: assert output == [Document(page_content="foo", id="id_foo")] +def test_configure_search_pipeline() -> None: + """Test configure search pipeline functionality.""" + test_search_pipeline_name = "test_search_pipeline" + keyword_weight = 0.7 + vector_weight = 0.3 + + docsearch = OpenSearchVectorSearch.from_texts( + texts, FakeEmbeddings(), opensearch_url=DEFAULT_OPENSEARCH_URL + ) + docsearch.configure_search_pipelines( + pipeline_name=test_search_pipeline_name, + keyword_weight=keyword_weight, + vector_weight=vector_weight, + ) + assert docsearch.search_pipeline_exists(test_search_pipeline_name) + + +def test_get_search_pipeline_info() -> None: + """Test get search pipeline info functionality.""" + test_search_pipeline_name = "test_search_pipeline" + + docsearch = OpenSearchVectorSearch.from_texts( + texts, FakeEmbeddings(), opensearch_url=DEFAULT_OPENSEARCH_URL + ) + test_pipeline_info = docsearch.get_search_pipeline_info(test_search_pipeline_name) + assert test_pipeline_info == { + "test_search_pipeline": { + "description": "Post processor for hybrid search", + "phase_results_processors": [ + { + "normalization-processor": { + "normalization": {"technique": "min_max"}, + "combination": { + "technique": "arithmetic_mean", + "parameters": {"weights": [0.7, 0.3]}, + }, + } + } + ], + } + } + + +def test_hybrid_search() -> None: + """Test hybrid search functionality.""" + metadatas = [{"page": i} for i in range(len(texts))] + docsearch = OpenSearchVectorSearch.from_texts( + texts, + ConsistentFakeEmbeddings(), + metadatas=metadatas, + opensearch_url=DEFAULT_OPENSEARCH_URL, + ) + output = docsearch.similarity_search( + query="foo", + k=2, + search_type=HYBRID_SEARCH, + search_pipeline="test_search_pipeline", + ) + + assert output == [ + Document(page_content="foo", metadata={"page": 0}), + Document(page_content="bar", metadata={"page": 1}), + ] + + +def test_hybrid_search_with_score() -> None: + """Test hybrid search with score functionality.""" + metadatas = [{"page": i} for i in range(len(texts))] + docsearch = OpenSearchVectorSearch.from_texts( + texts, + ConsistentFakeEmbeddings(), + metadatas=metadatas, + opensearch_url=DEFAULT_OPENSEARCH_URL, + ) + output = docsearch.similarity_search_with_score( + query="foo", + k=2, + search_type=HYBRID_SEARCH, + search_pipeline="test_search_pipeline", + ) + assert output == [ + (Document(page_content="foo", metadata={"page": 0}), 1.0), + (Document(page_content="bar", metadata={"page": 1}), 0.0003), + ] + + +def test_hybrid_search_with_post_filter() -> None: + """Test hybrid search with post filter functionality.""" + metadatas = [{"page": i} for i in range(len(texts))] + docsearch = OpenSearchVectorSearch.from_texts( + texts, + ConsistentFakeEmbeddings(), + metadatas=metadatas, + opensearch_url=DEFAULT_OPENSEARCH_URL, + ) + output = docsearch.similarity_search( + query="foo", + k=2, + search_type="hybrid_search", + search_pipeline="test_search_pipeline", + post_filter={"bool": {"filter": {"term": {"metadata.page": 1}}}}, + ) + + assert output == [Document(page_content="bar", metadata={"page": 1})] + + def test_opensearch_with_metadatas() -> None: """Test end to end indexing and search with metadata.""" metadatas = [{"page": i} for i in range(len(texts))] diff --git a/libs/community/tests/integration_tests/vectorstores/test_singlestoredb.py b/libs/community/tests/integration_tests/vectorstores/test_singlestoredb.py index fe8e3ca3004bf..8308adaa7603f 100644 --- a/libs/community/tests/integration_tests/vectorstores/test_singlestoredb.py +++ b/libs/community/tests/integration_tests/vectorstores/test_singlestoredb.py @@ -3,7 +3,7 @@ import math import os import tempfile -from typing import List +from typing import List, cast import numpy as np import pytest @@ -60,13 +60,13 @@ class RandomEmbeddings(Embeddings): """Fake embeddings with random vectors. For testing purposes.""" def embed_documents(self, texts: List[str]) -> List[List[float]]: - return [np.random.rand(100).tolist() for _ in texts] + return [cast(list[float], np.random.rand(100).tolist()) for _ in texts] def embed_query(self, text: str) -> List[float]: - return np.random.rand(100).tolist() + return cast(list[float], np.random.rand(100).tolist()) def embed_image(self, uris: List[str]) -> List[List[float]]: - return [np.random.rand(100).tolist() for _ in uris] + return [cast(list[float], np.random.rand(100).tolist()) for _ in uris] class IncrementalEmbeddings(Embeddings): diff --git a/libs/community/tests/integration_tests/vectorstores/test_tablestore.py b/libs/community/tests/integration_tests/vectorstores/test_tablestore.py new file mode 100644 index 0000000000000..4af879485d725 --- /dev/null +++ b/libs/community/tests/integration_tests/vectorstores/test_tablestore.py @@ -0,0 +1,93 @@ +"""Test tablestore functionality.""" + +import os + +import pytest +from langchain_core.documents import Document + +from langchain_community.embeddings import FakeEmbeddings +from langchain_community.vectorstores.tablestore import TablestoreVectorStore + + +def test_tablestore() -> None: + """Test end to end construction and search.""" + test_embedding_dimension_size = 4 + embeddings = FakeEmbeddings(size=test_embedding_dimension_size) + + end_point = os.getenv("end_point") + instance_name = os.getenv("instance_name") + access_key_id = os.getenv("access_key_id") + access_key_secret = os.getenv("access_key_secret") + if ( + end_point is None + or instance_name is None + or access_key_id is None + or access_key_secret is None + ): + pytest.skip( + "end_point is None or instance_name is None or " + "access_key_id is None or access_key_secret is None" + ) + """ + 1. create vector store + """ + store = TablestoreVectorStore( + embedding=embeddings, + endpoint=end_point, + instance_name=instance_name, + access_key_id=access_key_id, + access_key_secret=access_key_secret, + vector_dimension=test_embedding_dimension_size, + ) + + """ + 2. create table and index. (only needs to be run once) + """ + store.create_table_if_not_exist() + store.create_search_index_if_not_exist() + + """ + 3. add document + """ + store.add_documents( + [ + Document( + id="1", + page_content="1 hello world", + metadata={"type": "pc", "time": 2000}, + ), + Document( + id="2", page_content="abc world", metadata={"type": "pc", "time": 2009} + ), + Document( + id="3", + page_content="3 text world", + metadata={"type": "sky", "time": 2010}, + ), + Document( + id="4", page_content="hi world", metadata={"type": "sky", "time": 2030} + ), + Document( + id="5", page_content="hi world", metadata={"type": "sky", "time": 2030} + ), + ] + ) + + """ + 4. delete document + """ + assert store.delete(["3"]) + + """ + 5. get document + """ + get_docs = store.get_by_ids(["1", "4"]) + assert len(get_docs) == 2 + assert get_docs[0].id == "1" + assert get_docs[1].id == "4" + + """ + 6. similarity_search + """ + search_result = store.similarity_search_with_score(query="hello world", k=2) + assert len(search_result) == 2 diff --git a/libs/community/tests/unit_tests/document_loaders/test_confluence.py b/libs/community/tests/unit_tests/document_loaders/test_confluence.py index feecb1588b571..abb47326beef7 100644 --- a/libs/community/tests/unit_tests/document_loaders/test_confluence.py +++ b/libs/community/tests/unit_tests/document_loaders/test_confluence.py @@ -195,6 +195,36 @@ def test_confluence_loader_when_content_format_and_keep_markdown_format_enabled( assert mock_confluence.cql.call_count == 0 assert mock_confluence.get_page_child_by_type.call_count == 0 + @pytest.mark.requires("markdownify") + def test_confluence_loader_when_include_lables_set_to_true( + self, mock_confluence: MagicMock + ) -> None: + # one response with two pages + mock_confluence.get_all_pages_from_space.return_value = [ + self._get_mock_page("123", include_labels=True), + self._get_mock_page("456", include_labels=False), + ] + mock_confluence.get_all_restrictions_for_content.side_effect = [ + self._get_mock_page_restrictions("123"), + self._get_mock_page_restrictions("456"), + ] + + conflence_loader = self._get_mock_confluence_loader( + mock_confluence, + space_key=self.MOCK_SPACE_KEY, + include_labels=True, + max_pages=2, + ) + + documents = conflence_loader.load() + + assert mock_confluence.get_all_pages_from_space.call_count == 1 + + assert len(documents) == 2 + assert all(isinstance(doc, Document) for doc in documents) + assert documents[0].metadata["labels"] == ["l1", "l2"] + assert documents[1].metadata["labels"] == [] + def _get_mock_confluence_loader( self, mock_confluence: MagicMock, **kwargs: Any ) -> ConfluenceLoader: @@ -208,7 +238,10 @@ def _get_mock_confluence_loader( return confluence_loader def _get_mock_page( - self, page_id: str, content_format: ContentFormat = ContentFormat.STORAGE + self, + page_id: str, + content_format: ContentFormat = ContentFormat.STORAGE, + include_labels: bool = False, ) -> Dict: return { "id": f"{page_id}", @@ -216,6 +249,20 @@ def _get_mock_page( "body": { f"{content_format.name.lower()}": {"value": f"

      Content {page_id}

      "} }, + **( + { + "metadata": { + "labels": { + "results": [ + {"prefix": "global", "name": "l1", "id": "111"}, + {"prefix": "global", "name": "l2", "id": "222"}, + ] + } + } + if include_labels + else {}, + } + ), "status": "current", "type": "page", "_links": { diff --git a/libs/community/tests/unit_tests/document_loaders/test_json_loader.py b/libs/community/tests/unit_tests/document_loaders/test_json_loader.py index c4b1df4f2a30f..2f4e066e304b5 100644 --- a/libs/community/tests/unit_tests/document_loaders/test_json_loader.py +++ b/libs/community/tests/unit_tests/document_loaders/test_json_loader.py @@ -1,4 +1,5 @@ import io +from pathlib import Path from typing import Any, Dict import pytest @@ -12,7 +13,7 @@ def test_load_valid_string_content(mocker: MockerFixture) -> None: - file_path = "/workspaces/langchain/test.json" + file_path = str(Path("/workspaces/langchain/test.json").resolve()) expected_docs = [ Document( page_content="value1", @@ -37,7 +38,7 @@ def test_load_valid_string_content(mocker: MockerFixture) -> None: def test_load_valid_dict_content(mocker: MockerFixture) -> None: - file_path = "/workspaces/langchain/test.json" + file_path = str(Path("/workspaces/langchain/test.json").resolve()) expected_docs = [ Document( page_content='{"text": "value1"}', @@ -64,7 +65,7 @@ def test_load_valid_dict_content(mocker: MockerFixture) -> None: def test_load_valid_bool_content(mocker: MockerFixture) -> None: - file_path = "/workspaces/langchain/test.json" + file_path = str(Path("/workspaces/langchain/test.json").resolve()) expected_docs = [ Document( page_content="False", @@ -93,7 +94,7 @@ def test_load_valid_bool_content(mocker: MockerFixture) -> None: def test_load_valid_numeric_content(mocker: MockerFixture) -> None: - file_path = "/workspaces/langchain/test.json" + file_path = str(Path("/workspaces/langchain/test.json").resolve()) expected_docs = [ Document( page_content="99", @@ -122,7 +123,7 @@ def test_load_valid_numeric_content(mocker: MockerFixture) -> None: def test_load_invalid_test_content(mocker: MockerFixture) -> None: - file_path = "/workspaces/langchain/test.json" + file_path = str(Path("/workspaces/langchain/test.json").resolve()) mocker.patch("builtins.open", mocker.mock_open()) mocker.patch( @@ -139,7 +140,7 @@ def test_load_invalid_test_content(mocker: MockerFixture) -> None: def test_load_jsonlines(mocker: MockerFixture) -> None: - file_path = "/workspaces/langchain/test.json" + file_path = str(Path("/workspaces/langchain/test.json").resolve()) expected_docs = [ Document( page_content="value1", @@ -177,7 +178,7 @@ def test_load_jsonlines(mocker: MockerFixture) -> None: ), ) def test_load_jsonlines_list(params: Dict, mocker: MockerFixture) -> None: - file_path = "/workspaces/langchain/test.json" + file_path = str(Path("/workspaces/langchain/test.json").resolve()) expected_docs = [ Document( page_content="value1", @@ -250,7 +251,7 @@ def test_json_meta_01( mocker.patch("builtins.open", mocker.mock_open()) mocker.patch(patch_func, return_value=patch_func_value) - file_path = "/workspaces/langchain/test.json" + file_path = str(Path("/workspaces/langchain/test.json").resolve()) expected_docs = [ Document( page_content="value1", @@ -300,7 +301,7 @@ def test_json_meta_02( mocker.patch("builtins.open", mocker.mock_open()) mocker.patch(patch_func, return_value=patch_func_value) - file_path = "/workspaces/langchain/test.json" + file_path = str(Path("/workspaces/langchain/test.json").resolve()) expected_docs = [ Document( page_content="value1", @@ -336,7 +337,7 @@ def metadata_func(record: Dict, metadata: Dict) -> Dict: def test_load_json_with_jq_parsable_content_key( params: Dict, mocker: MockerFixture ) -> None: - file_path = "/workspaces/langchain/test.json" + file_path = str(Path("/workspaces/langchain/test.json").resolve()) expected_docs = [ Document( page_content="value1", @@ -364,7 +365,7 @@ def test_load_json_with_jq_parsable_content_key( def test_load_json_with_nested_jq_parsable_content_key(mocker: MockerFixture) -> None: - file_path = "/workspaces/langchain/test.json" + file_path = str(Path("/workspaces/langchain/test.json").resolve()) expected_docs = [ Document( page_content="message1", @@ -401,7 +402,7 @@ def test_load_json_with_nested_jq_parsable_content_key(mocker: MockerFixture) -> def test_load_json_with_nested_jq_parsable_content_key_with_metadata( mocker: MockerFixture, ) -> None: - file_path = "/workspaces/langchain/test.json" + file_path = str(Path("/workspaces/langchain/test.json").resolve()) expected_docs = [ Document( page_content="message1", diff --git a/libs/community/tests/unit_tests/document_transformers/test_markdownify.py b/libs/community/tests/unit_tests/document_transformers/test_markdownify.py index 32ae9dda691eb..1ce407289dd4a 100644 --- a/libs/community/tests/unit_tests/document_transformers/test_markdownify.py +++ b/libs/community/tests/unit_tests/document_transformers/test_markdownify.py @@ -50,7 +50,8 @@ def test_extract_html() -> None: documents = [Document(page_content=basic_html)] docs_transformed = markdownify.transform_documents(documents) assert docs_transformed[0].page_content == ( - "Simple Test Page # Test Header\n\n " + "Simple Test Page " + "# Test Header\n\n " "First paragraph.\n\n " "Second paragraph.\n\n " "[Example Link](https://example.com)" @@ -105,7 +106,8 @@ def test_convert_tags() -> None: assert docs_transformed[0].page_content == ( "Header " "**1st paragraph.** " - "2nd paragraph. Here is [link](http://example.com) " + "2nd paragraph. " + "Here is [link](http://example.com) " "Ignore at end" ) @@ -137,3 +139,139 @@ def test_strip_convert_conflict_error() -> None: ) documents = [Document(page_content=paragraphs_html)] markdownify.transform_documents(documents) + + +# Async variants: exact duplicates of the above functions, using atransform_documents() +@pytest.mark.requires("markdownify") +async def test_empty_html_async() -> None: + markdownify = MarkdownifyTransformer() + empty_html = "" + documents = [Document(page_content=empty_html)] + docs_transformed = await markdownify.atransform_documents(documents) + assert docs_transformed[0].page_content == "" + + +@pytest.mark.requires("markdownify") +async def test_extract_paragraphs_async() -> None: + markdownify = MarkdownifyTransformer() + paragraphs_html = ( + "

      Header

      First paragraph.

      " + "

      Second paragraph.

      Ignore at end

      " + ) + documents = [Document(page_content=paragraphs_html)] + docs_transformed = await markdownify.atransform_documents(documents) + assert docs_transformed[0].page_content == ( + "# Header\n\n" "First paragraph.\n\n" "Second paragraph.\n\n" "# Ignore at end" + ) + + +@pytest.mark.requires("markdownify") +async def test_extract_html_async() -> None: + markdownify = MarkdownifyTransformer(skip="title") + basic_html = ( + "" + '' + "" + ' ' + " Simple Test Page" + "" + "" + "

      Test Header

      " + "

      First paragraph.

      " + "

      Second paragraph.

      " + ' Example Link' + "" + "" + ) + documents = [Document(page_content=basic_html)] + docs_transformed = await markdownify.atransform_documents(documents) + assert docs_transformed[0].page_content == ( + "Simple Test Page " + "# Test Header\n\n " + "First paragraph.\n\n " + "Second paragraph.\n\n " + "[Example Link](https://example.com)" + ) + + +@pytest.mark.requires("markdownify") +async def test_strip_tags_async() -> None: + markdownify = MarkdownifyTransformer(strip="strong") + paragraphs_html = ( + "" + "

      Header

      " + "

      1st paragraph.

      " + '

      2nd paragraph. Here is link

      ' + ' Sample Image' + "

      Ignore at end

      " + ) + documents = [Document(page_content=paragraphs_html)] + docs_transformed = await markdownify.atransform_documents(documents) + assert docs_transformed[0].page_content == ( + "# Header\n\n " + "1st paragraph.\n\n " + "2nd paragraph. Here is [link](http://example.com)\n\n " + "![Sample Image](image.jpg)" + "# Ignore at end" + ) + + markdownify = MarkdownifyTransformer(strip=["strong", "a", "img"]) + documents = [Document(page_content=paragraphs_html)] + docs_transformed = await markdownify.atransform_documents(documents) + assert docs_transformed[0].page_content == ( + "# Header\n\n " + "1st paragraph.\n\n " + "2nd paragraph. Here is link\n\n " + "# Ignore at end" + ) + + +@pytest.mark.requires("markdownify") +async def test_convert_tags_async() -> None: + markdownify = MarkdownifyTransformer(convert=["strong", "a"]) + paragraphs_html = ( + "" + "

      Header

      " + "

      1st paragraph.

      " + '

      2nd paragraph. Here is link

      ' + ' Sample Image' + "

      Ignore at end

      " + ) + documents = [Document(page_content=paragraphs_html)] + docs_transformed = await markdownify.atransform_documents(documents) + assert docs_transformed[0].page_content == ( + "Header " + "**1st paragraph.** " + "2nd paragraph. " + "Here is [link](http://example.com) " + "Ignore at end" + ) + + markdownify = MarkdownifyTransformer(convert="p") + documents = [Document(page_content=paragraphs_html)] + docs_transformed = await markdownify.atransform_documents(documents) + assert docs_transformed[0].page_content == ( + "Header " + "1st paragraph.\n\n " + "2nd paragraph. Here is link\n\n " + "Ignore at end" + ) + + +@pytest.mark.requires("markdownify") +async def test_strip_convert_conflict_error_async() -> None: + with pytest.raises( + ValueError, + match="You may specify either tags to strip or tags to convert, but not both.", + ): + markdownify = MarkdownifyTransformer(strip="h1", convert=["strong", "a"]) + paragraphs_html = ( + "" + "

      Header

      " + "

      1st paragraph.

      " + '

      2nd paragraph. Here is link

      ' + ' Sample Image' + "

      Ignore at end

      " + ) + documents = [Document(page_content=paragraphs_html)] + await markdownify.atransform_documents(documents) diff --git a/libs/community/tests/unit_tests/embeddings/test_imports.py b/libs/community/tests/unit_tests/embeddings/test_imports.py index a6f26ce0c3fa6..ba9e9590f8103 100644 --- a/libs/community/tests/unit_tests/embeddings/test_imports.py +++ b/libs/community/tests/unit_tests/embeddings/test_imports.py @@ -26,6 +26,7 @@ "MlflowAIGatewayEmbeddings", "MlflowEmbeddings", "MlflowCohereEmbeddings", + "Model2vecEmbeddings", "ModelScopeEmbeddings", "TensorflowHubEmbeddings", "SagemakerEndpointEmbeddings", diff --git a/libs/community/tests/unit_tests/embeddings/test_model2vec.py b/libs/community/tests/unit_tests/embeddings/test_model2vec.py new file mode 100644 index 0000000000000..f1f6d0788dfba --- /dev/null +++ b/libs/community/tests/unit_tests/embeddings/test_model2vec.py @@ -0,0 +1,11 @@ +from langchain_community.embeddings.model2vec import Model2vecEmbeddings + + +def test_hugginggface_inferenceapi_embedding_documents_init() -> None: + """Test model2vec embeddings.""" + try: + embedding = Model2vecEmbeddings("minishlab/potion-base-8M") + assert len(embedding.embed_query("hi")) == 256 + except Exception: + # model2vec is not installed + assert True diff --git a/libs/community/tests/unit_tests/graph_vectorstores/test_visualize.py b/libs/community/tests/unit_tests/graph_vectorstores/test_visualize.py new file mode 100644 index 0000000000000..89615c0bfe6b9 --- /dev/null +++ b/libs/community/tests/unit_tests/graph_vectorstores/test_visualize.py @@ -0,0 +1,113 @@ +import pytest +from langchain_core.documents import Document + +from langchain_community.graph_vectorstores.links import METADATA_LINKS_KEY, Link +from langchain_community.graph_vectorstores.visualize import render_graphviz + + +@pytest.mark.requires("graphviz") +def test_visualize_simple_graph() -> None: + doc1 = Document( + id="a", + page_content="some content", + metadata={ + METADATA_LINKS_KEY: [ + Link.incoming("href", "a"), + Link.bidir("kw", "foo"), + ] + }, + ) + doc2 = Document( + id="b", + page_content="", + metadata={ + METADATA_LINKS_KEY: [ + Link.incoming("href", "b"), + Link.outgoing("href", "a"), + Link.bidir("kw", "foo"), + Link.bidir("kw", "bar"), + ] + }, + ) + + assert render_graphviz([doc1, doc2]).source == ( + "digraph {\n" + "\trankdir=LR\n" + "\tnode [style=filled]\n" + '\ta [label="a\nsome content" shape=note tooltip="some content"]\n' + '\ttag_0 [label="href:a"]\n' + "\ta -> tag_0 [dir=back]\n" + '\ttag_1 [label="kw:foo"]\n' + "\ta -> tag_1 [dir=both]\n" + '\tb [label="b\n" ' + 'shape=note tooltip=""]\n' + '\ttag_2 [label="href:b"]\n' + "\tb -> tag_2 [dir=back]\n" + "\tb -> tag_0 [dir=forward]\n" + "\tb -> tag_1 [dir=both]\n" + '\ttag_3 [label="kw:bar"]\n' + "\tb -> tag_3 [dir=both]\n" + "}\n" + ) + + assert render_graphviz([doc1, doc2], engine="fdp").engine == "fdp" + + assert render_graphviz([doc1, doc2], node_colors={"a": "gold"}).source == ( + "digraph {\n" + "\trankdir=LR\n" + "\tnode [style=filled]\n" + '\ta [label="a\nsome content" fillcolor=gold ' + 'shape=note tooltip="some content"]\n' + '\ttag_0 [label="href:a"]\n' + "\ta -> tag_0 [dir=back]\n" + '\ttag_1 [label="kw:foo"]\n' + "\ta -> tag_1 [dir=both]\n" + '\tb [label="b\n" ' + 'shape=note tooltip=""]\n' + '\ttag_2 [label="href:b"]\n' + "\tb -> tag_2 [dir=back]\n" + "\tb -> tag_0 [dir=forward]\n" + "\tb -> tag_1 [dir=both]\n" + '\ttag_3 [label="kw:bar"]\n' + "\tb -> tag_3 [dir=both]\n" + "}\n" + ) + + assert render_graphviz( + [doc1, doc2], node_color="gold", node_colors={"a": None} + ).source == ( + "digraph {\n" + "\trankdir=LR\n" + "\tnode [style=filled]\n" + '\ta [label="a\nsome content" shape=note tooltip="some content"]\n' + '\ttag_0 [label="href:a"]\n' + "\ta -> tag_0 [dir=back]\n" + '\ttag_1 [label="kw:foo"]\n' + "\ta -> tag_1 [dir=both]\n" + '\tb [label="b\n" fillcolor=gold ' + 'shape=note tooltip=""]\n' + '\ttag_2 [label="href:b"]\n' + "\tb -> tag_2 [dir=back]\n" + "\tb -> tag_0 [dir=forward]\n" + "\tb -> tag_1 [dir=both]\n" + '\ttag_3 [label="kw:bar"]\n' + "\tb -> tag_3 [dir=both]\n" + "}\n" + ) + + assert render_graphviz([doc1, doc2], skip_tags=[("kw", "foo")]).source == ( + "digraph {\n" + "\trankdir=LR\n" + "\tnode [style=filled]\n" + '\ta [label="a\nsome content" shape=note tooltip="some content"]\n' + '\ttag_0 [label="href:a"]\n' + "\ta -> tag_0 [dir=back]\n" + '\tb [label="b\n" ' + 'shape=note tooltip=""]\n' + '\ttag_1 [label="href:b"]\n' + "\tb -> tag_1 [dir=back]\n" + "\tb -> tag_0 [dir=forward]\n" + '\ttag_2 [label="kw:bar"]\n' + "\tb -> tag_2 [dir=both]\n" + "}\n" + ) diff --git a/libs/community/tests/unit_tests/tools/test_exported.py b/libs/community/tests/unit_tests/tools/test_exported.py index e5503369873ed..6e004f1bac6bb 100644 --- a/libs/community/tests/unit_tests/tools/test_exported.py +++ b/libs/community/tests/unit_tests/tools/test_exported.py @@ -41,4 +41,5 @@ def test_tool_names_unique() -> None: tool_classes = _get_tool_classes(skip_tools_without_default_names=True) names = sorted([tool_cls.model_fields["name"].default for tool_cls in tool_classes]) duplicated_names = [name for name in names if names.count(name) > 1] - assert not duplicated_names + # only one duplicate currently exists + assert duplicated_names == ["sql_db_query", "sql_db_query"] diff --git a/libs/community/tests/unit_tests/tools/test_imports.py b/libs/community/tests/unit_tests/tools/test_imports.py index 0af4f3ab7ce48..e93d224c27876 100644 --- a/libs/community/tests/unit_tests/tools/test_imports.py +++ b/libs/community/tests/unit_tests/tools/test_imports.py @@ -105,6 +105,7 @@ "QueryCheckerTool", "QueryPowerBITool", "QuerySQLCheckerTool", + "QuerySQLDatabaseTool", "QuerySQLDataBaseTool", "QuerySparkSQLTool", "ReadFileTool", diff --git a/libs/community/tests/unit_tests/vectorstores/test_faiss.py b/libs/community/tests/unit_tests/vectorstores/test_faiss.py index 99b4ba6e69948..3fd9b5ee5297d 100644 --- a/libs/community/tests/unit_tests/vectorstores/test_faiss.py +++ b/libs/community/tests/unit_tests/vectorstores/test_faiss.py @@ -322,6 +322,808 @@ def test_faiss_mmr_with_metadatas_and_filter() -> None: ) +@pytest.mark.requires("faiss") +def test_faiss_mmr_with_metadatas_and_comparison_operators_filter_eq() -> None: + texts = ["foo", "foo", "fou", "foy"] + metadatas = [{"page": i} for i in range(len(texts))] + docsearch = FAISS.from_texts(texts, FakeEmbeddings(), metadatas=metadatas) + query_vec = FakeEmbeddings().embed_query(text="foo") + output = docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, k=10, lambda_mult=0.1, filter={"page": {"$eq": 1}} + ) + assert len(output) == 1 + assert output[0][0] == Document(page_content="foo", metadata={"page": 1}) + assert output[0][1] == 0.0 + assert output == docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, k=10, lambda_mult=0.1, filter=lambda di: di["page"] == 1 + ) + + +@pytest.mark.requires("faiss") +def test_faiss_mmr_with_metadatas_and_comparison_operators_filter_neq() -> None: + texts = ["foo", "foo", "fou", "foy"] + metadatas = [{"page": i} for i in range(len(texts))] + docsearch = FAISS.from_texts(texts, FakeEmbeddings(), metadatas=metadatas) + query_vec = FakeEmbeddings().embed_query(text="foo") + output = docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, k=10, lambda_mult=0.1, filter={"page": {"$neq": 1}} + ) + assert len(output) == 3 + assert output[0][0] == Document(page_content="foo", metadata={"page": 0}) + assert output[0][1] == 0.0 + assert output[1][0] != Document(page_content="foo", metadata={"page": 0}) + assert output[2][0] != Document(page_content="foo", metadata={"page": 0}) + assert output == docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, k=10, lambda_mult=0.1, filter=lambda di: di["page"] != 1 + ) + + +@pytest.mark.requires("faiss") +def test_faiss_mmr_with_metadatas_and_comparison_operators_filter_gt() -> None: + texts = ["foo", "foo", "fou", "foy"] + metadatas = [{"page": i} for i in range(len(texts))] + docsearch = FAISS.from_texts(texts, FakeEmbeddings(), metadatas=metadatas) + query_vec = FakeEmbeddings().embed_query(text="foo") + output = docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, k=10, lambda_mult=0.1, filter={"page": {"$gt": 0}} + ) + assert len(output) == 3 + assert output[0][0] == Document(page_content="foo", metadata={"page": 1}) + assert output[0][1] == 0.0 + assert output[1][0] != Document(page_content="foo", metadata={"page": 1}) + assert output[2][0] != Document(page_content="foo", metadata={"page": 1}) + assert output == docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, k=10, lambda_mult=0.1, filter=lambda di: di["page"] > 0 + ) + + +@pytest.mark.requires("faiss") +def test_faiss_mmr_with_metadatas_and_comparison_operators_filter_lt() -> None: + texts = ["foo", "foo", "fou", "foy"] + metadatas = [{"page": i} for i in range(len(texts))] + docsearch = FAISS.from_texts(texts, FakeEmbeddings(), metadatas=metadatas) + query_vec = FakeEmbeddings().embed_query(text="foo") + output = docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, k=10, lambda_mult=0.1, filter={"page": {"$lt": 2}} + ) + assert len(output) == 2 + assert output[0][0] == Document(page_content="foo", metadata={"page": 0}) + assert output[0][1] == 0.0 + assert output[1][0] == Document(page_content="foo", metadata={"page": 1}) + assert output[1][1] == 1.0 + assert output == docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, k=10, lambda_mult=0.1, filter=lambda di: di["page"] < 2 + ) + + +@pytest.mark.requires("faiss") +def test_faiss_mmr_with_metadatas_and_comparison_operators_filter_gte() -> None: + texts = ["foo", "foo", "fou", "foy"] + metadatas = [{"page": i} for i in range(len(texts))] + docsearch = FAISS.from_texts(texts, FakeEmbeddings(), metadatas=metadatas) + query_vec = FakeEmbeddings().embed_query(text="foo") + output = docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, k=10, lambda_mult=0.1, filter={"page": {"$gte": 1}} + ) + assert len(output) == 3 + assert output[0][0] == Document(page_content="foo", metadata={"page": 1}) + assert output[0][1] == 0.0 + assert output[1][0] != Document(page_content="foo", metadata={"page": 1}) + assert output[2][0] != Document(page_content="foo", metadata={"page": 1}) + assert output == docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, k=10, lambda_mult=0.1, filter=lambda di: di["page"] >= 1 + ) + + +@pytest.mark.requires("faiss") +def test_faiss_mmr_with_metadatas_and_comparison_operators_filter_lte() -> None: + texts = ["fou", "fou", "fouu", "fouuu"] + metadatas = [{"page": i} for i in range(len(texts))] + docsearch = FAISS.from_texts(texts, FakeEmbeddings(), metadatas=metadatas) + query_vec = FakeEmbeddings().embed_query(text="foo") + output = docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, k=10, lambda_mult=0.1, filter={"page": {"$lte": 0}} + ) + assert len(output) == 1 + assert output[0][0] == Document(page_content="fou", metadata={"page": 0}) + assert output[0][1] == 0.0 + assert output == docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, k=10, lambda_mult=0.1, filter=lambda di: di["page"] <= 0 + ) + + +@pytest.mark.requires("faiss") +def test_faiss_mmr_with_metadatas_and_comparison_operators_filter_in_1() -> None: + texts = ["foo", "foo", "fou", "foy"] + metadatas = [{"page": i} for i in range(len(texts))] + docsearch = FAISS.from_texts(texts, FakeEmbeddings(), metadatas=metadatas) + query_vec = FakeEmbeddings().embed_query(text="foo") + output = docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, k=10, lambda_mult=0.1, filter={"page": {"$in": [0]}} + ) + assert len(output) == 1 + assert output[0][0] == Document(page_content="foo", metadata={"page": 0}) + assert output[0][1] == 0.0 + assert output == docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, k=10, lambda_mult=0.1, filter=lambda di: di["page"] in [0] + ) + + +@pytest.mark.requires("faiss") +def test_faiss_mmr_with_metadatas_and_comparison_operators_filter_in_2() -> None: + texts = ["foo", "foo", "fou", "foy"] + metadatas = [{"page": i} for i in range(len(texts))] + docsearch = FAISS.from_texts(texts, FakeEmbeddings(), metadatas=metadatas) + query_vec = FakeEmbeddings().embed_query(text="foo") + output = docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, k=10, lambda_mult=0.1, filter={"page": {"$in": [1, 2]}} + ) + assert len(output) == 2 + assert output[0][0] == Document(page_content="foo", metadata={"page": 1}) + assert output[0][1] == 0.0 + assert output[1][0] == Document(page_content="fou", metadata={"page": 2}) + assert output == docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, k=10, lambda_mult=0.1, filter=lambda di: di["page"] in [1, 2] + ) + + +@pytest.mark.requires("faiss") +def test_faiss_mmr_with_metadatas_and_comparison_operators_filter_nin_1() -> None: + texts = ["foo", "foo", "fou", "foy"] + metadatas = [{"page": i} for i in range(len(texts))] + docsearch = FAISS.from_texts(texts, FakeEmbeddings(), metadatas=metadatas) + query_vec = FakeEmbeddings().embed_query(text="foo") + output = docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, k=10, lambda_mult=0.1, filter={"page": {"$nin": [0, 1]}} + ) + assert len(output) == 2 + assert output[0][0] == Document(page_content="fou", metadata={"page": 2}) + assert output[0][1] == 0.0 + assert output[1][0] == Document(page_content="foy", metadata={"page": 3}) + assert output == docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, k=10, lambda_mult=0.1, filter=lambda di: di["page"] not in [0, 1] + ) + + +@pytest.mark.requires("faiss") +def test_faiss_mmr_with_metadatas_and_comparison_operators_filter_nin_2() -> None: + texts = ["foo", "foo", "fou", "foy"] + metadatas = [{"page": i} for i in range(len(texts))] + docsearch = FAISS.from_texts(texts, FakeEmbeddings(), metadatas=metadatas) + query_vec = FakeEmbeddings().embed_query(text="foo") + output = docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, k=10, lambda_mult=0.1, filter={"page": {"$nin": [0, 1, 2]}} + ) + assert len(output) == 1 + assert output[0][0] == Document(page_content="foy", metadata={"page": 3}) + assert output[0][1] == 0.0 + assert output == docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, k=10, lambda_mult=0.1, filter=lambda di: di["page"] not in [0, 1, 2] + ) + + +@pytest.mark.requires("faiss") +def test_faiss_mmr_with_metadatas_and_logical_operators_filter_not() -> None: + texts = ["foo", "foo", "fou", "foy"] + metadatas = [{"page": i} for i in range(len(texts))] + docsearch = FAISS.from_texts(texts, FakeEmbeddings(), metadatas=metadatas) + query_vec = FakeEmbeddings().embed_query(text="foo") + output = docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, k=10, lambda_mult=0.1, filter={"$not": {"page": 1}} + ) + assert len(output) == 3 + assert output[0][0] == Document(page_content="foo", metadata={"page": 0}) + assert output[0][1] == 0.0 + assert output[1][0] == Document(page_content="foy", metadata={"page": 3}) + assert output[2][0] == Document(page_content="fou", metadata={"page": 2}) + assert output == docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, k=10, lambda_mult=0.1, filter=lambda di: not di["page"] == 1 + ) + + +@pytest.mark.requires("faiss") +def test_faiss_mmr_with_metadatas_and_logical_operators_filter_or_1() -> None: + texts = ["foo", "foo", "fou", "foy"] + metadatas = [{"page": i} for i in range(len(texts))] + docsearch = FAISS.from_texts(texts, FakeEmbeddings(), metadatas=metadatas) + query_vec = FakeEmbeddings().embed_query(text="foo") + output = docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, k=10, lambda_mult=0.1, filter={"$or": [{"page": 0}]} + ) + assert len(output) == 1 + assert output[0][0] == Document(page_content="foo", metadata={"page": 0}) + assert output[0][1] == 0.0 + assert output == docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, k=10, lambda_mult=0.1, filter=lambda di: (di["page"] == 0) + ) + + +@pytest.mark.requires("faiss") +def test_faiss_mmr_with_metadatas_and_logical_operators_filter_or_2() -> None: + texts = ["foo", "foo", "fou", "foy"] + metadatas = [{"page": i} for i in range(len(texts))] + docsearch = FAISS.from_texts(texts, FakeEmbeddings(), metadatas=metadatas) + query_vec = FakeEmbeddings().embed_query(text="foo") + output = docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, k=10, lambda_mult=0.1, filter={"$or": [{"page": 0}, {"page": 1}]} + ) + assert len(output) == 2 + assert output[0][0] == Document(page_content="foo", metadata={"page": 0}) + assert output[0][1] == 0.0 + assert output[1][0] == Document(page_content="foo", metadata={"page": 1}) + assert output[1][1] == 1.0 + assert output == docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, + k=10, + lambda_mult=0.1, + filter=lambda di: (di["page"] == 0) or (di["page"] == 1), + ) + + +@pytest.mark.requires("faiss") +def test_faiss_mmr_with_metadatas_and_logical_operators_filter_or_3() -> None: + texts = ["foo", "foo", "fou", "foy"] + metadatas = [{"page": i} for i in range(len(texts))] + docsearch = FAISS.from_texts(texts, FakeEmbeddings(), metadatas=metadatas) + query_vec = FakeEmbeddings().embed_query(text="foo") + output = docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, + k=10, + lambda_mult=0.1, + filter={"$or": [{"page": 0}, {"page": 1}, {"page": 2}]}, + ) + assert len(output) == 3 + assert output[0][0] == Document(page_content="foo", metadata={"page": 0}) + assert output[0][1] == 0.0 + assert output[1][0] != Document(page_content="foo", metadata={"page": 0}) + assert output[2][0] != Document(page_content="foo", metadata={"page": 0}) + assert output == docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, + k=10, + lambda_mult=0.1, + filter=lambda di: (di["page"] == 0) or (di["page"] == 1) or (di["page"] == 2), + ) + + +@pytest.mark.requires("faiss") +def test_faiss_mmr_with_metadatas_and_logical_operators_filter_and_1() -> None: + texts = ["foo", "foo", "fou", "foy"] + metadatas = [{"page": i} for i in range(len(texts))] + docsearch = FAISS.from_texts(texts, FakeEmbeddings(), metadatas=metadatas) + query_vec = FakeEmbeddings().embed_query(text="foo") + output = docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, k=10, lambda_mult=0.1, filter={"$and": [{"page": 0}]} + ) + assert len(output) == 1 + assert output[0][0] == Document(page_content="foo", metadata={"page": 0}) + assert output[0][1] == 0.0 + assert output == docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, k=10, lambda_mult=0.1, filter=lambda di: (di["page"] == 0) + ) + + +@pytest.mark.requires("faiss") +def test_faiss_mmr_with_metadatas_and_logical_operators_filter_and_2() -> None: + texts = ["foo", "foo", "fou", "foy"] + metadatas = [{"page": i} for i in range(len(texts))] + docsearch = FAISS.from_texts(texts, FakeEmbeddings(), metadatas=metadatas) + query_vec = FakeEmbeddings().embed_query(text="foo") + output = docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, k=10, lambda_mult=0.1, filter={"$and": [{"page": 0}, {"page": 1}]} + ) + assert len(output) == 0 + assert output == docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, + k=10, + lambda_mult=0.1, + filter=lambda di: (di["page"] == 0) and (di["page"] == 1), + ) + + +@pytest.mark.requires("faiss") +def test_faiss_mmr_with_metadatas_and_logical_operators_filter_and_3() -> None: + texts = ["foo", "foo", "fou", "foy"] + metadatas = [{"page": i} for i in range(len(texts))] + docsearch = FAISS.from_texts(texts, FakeEmbeddings(), metadatas=metadatas) + query_vec = FakeEmbeddings().embed_query(text="foo") + output = docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, + k=10, + lambda_mult=0.1, + filter={"$and": [{"page": 0}, {"page": 1}, {"page": 2}]}, + ) + assert len(output) == 0 + assert output == docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, + k=10, + lambda_mult=0.1, + filter=lambda di: (di["page"] == 0) and (di["page"] == 1) and (di["page"] == 2), + ) + + +@pytest.mark.requires("faiss") +def test_faiss_mmr_with_metadatas_and_logical_operators_filter_and_4() -> None: + texts = ["foo", "foo", "fou", "foy"] + metadatas = [{"page": i} for i in range(len(texts))] + docsearch = FAISS.from_texts(texts, FakeEmbeddings(), metadatas=metadatas) + query_vec = FakeEmbeddings().embed_query(text="foo") + output = docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, + k=10, + lambda_mult=0.1, + filter={"$and": [{"page": 0}, {"page": 0}, {"page": 0}]}, + ) + assert len(output) == 1 + assert output[0][0] == Document(page_content="foo", metadata={"page": 0}) + assert output[0][1] == 0.0 + assert output == docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, + k=10, + lambda_mult=0.1, + filter=lambda di: (di["page"] == 0) and (di["page"] == 0) and (di["page"] == 0), + ) + + +@pytest.mark.requires("faiss") +def test_faiss_mmr_with_metadatas_and_nested_logical_operators_filter_1() -> None: + texts = ["foo", "foo", "fou", "foy"] + metadatas = [{"page": i} for i in range(len(texts))] + docsearch = FAISS.from_texts(texts, FakeEmbeddings(), metadatas=metadatas) + query_vec = FakeEmbeddings().embed_query(text="foo") + output = docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, + k=10, + lambda_mult=0.1, + filter={"$and": [{"$or": [{"page": 1}, {"page": 2}]}, {"$not": {"page": 1}}]}, + ) + assert len(output) == 1 + assert output[0][0] == Document(page_content="fou", metadata={"page": 2}) + assert output == docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, + k=10, + lambda_mult=0.1, + filter=lambda di: (di["page"] == 1 or di["page"] == 2) + and (not di["page"] == 1), + ) + + +@pytest.mark.requires("faiss") +def test_faiss_mmr_with_metadatas_and_nested_logical_operators_filter_2() -> None: + texts = ["foo", "foo", "fou", "foy"] + metadatas = [{"page": i} for i in range(len(texts))] + docsearch = FAISS.from_texts(texts, FakeEmbeddings(), metadatas=metadatas) + query_vec = FakeEmbeddings().embed_query(text="foo") + output = docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, + k=10, + lambda_mult=0.1, + filter={ + "$and": [ + {"$or": [{"page": 1}, {"page": 2}]}, + {"$or": [{"page": 3}, {"page": 2}, {"page": 0}]}, + ] + }, + ) + assert len(output) == 1 + assert output[0][0] == Document(page_content="fou", metadata={"page": 2}) + assert output == docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, + k=10, + lambda_mult=0.1, + filter=lambda di: (di["page"] == 1 or di["page"] == 2) + and (di["page"] == 3 or di["page"] == 2 or di["page"] == 0), + ) + + +@pytest.mark.requires("faiss") +def test_faiss_mmr_with_metadatas_and_nested_logical_operators_filter_3() -> None: + texts = ["foo", "foo", "fou", "foy"] + metadatas = [{"page": i} for i in range(len(texts))] + docsearch = FAISS.from_texts(texts, FakeEmbeddings(), metadatas=metadatas) + query_vec = FakeEmbeddings().embed_query(text="foo") + output = docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, + k=10, + lambda_mult=0.1, + filter={ + "$or": [ + {"$and": [{"page": 1}, {"page": 2}]}, + {"$and": [{"page": 0}, {"page": 2}]}, + ] + }, + ) + assert len(output) == 0 + assert output == docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, + k=10, + lambda_mult=0.1, + filter=lambda di: (di["page"] == 1 and di["page"] == 2) + or (di["page"] == 0 and di["page"] == 2), + ) + + +@pytest.mark.requires("faiss") +def test_faiss_mmr_with_metadatas_and_logical_comparsion_operators_filter_1() -> None: + texts = ["foo", "foo", "fou", "foy"] + metadatas = [{"page": i} for i in range(len(texts))] + docsearch = FAISS.from_texts(texts, FakeEmbeddings(), metadatas=metadatas) + query_vec = FakeEmbeddings().embed_query(text="foo") + output = docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, + k=10, + lambda_mult=0.1, + filter={"$or": [{"page": {"$lt": 1}}, {"page": {"$gt": 2}}]}, + ) + assert len(output) == 2 + assert output[0][0] == Document(page_content="foo", metadata={"page": 0}) + assert output[0][1] == 0.0 + assert output[1][0] == Document(page_content="foy", metadata={"page": 3}) + assert output == docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, + k=10, + lambda_mult=0.1, + filter=lambda di: di["page"] < 1 or di["page"] > 2, + ) + + +@pytest.mark.requires("faiss") +def test_faiss_mmr_with_metadatas_and_logical_comparsion_operators_filter_2() -> None: + texts = ["foo", "foo", "fou", "foy"] + metadatas = [{"page": i} for i in range(len(texts))] + docsearch = FAISS.from_texts(texts, FakeEmbeddings(), metadatas=metadatas) + query_vec = FakeEmbeddings().embed_query(text="foo") + output = docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, k=10, lambda_mult=0.1, filter={"$not": {"page": {"$lt": 1}}} + ) + assert len(output) == 3 + assert output[0][0] == Document(page_content="foo", metadata={"page": 1}) + assert output[0][1] == 0.0 + assert output[1][0] == Document(page_content="foy", metadata={"page": 3}) + assert output[2][0] == Document(page_content="fou", metadata={"page": 2}) + assert output == docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, k=10, lambda_mult=0.1, filter=lambda di: not di["page"] < 1 + ) + + +@pytest.mark.requires("faiss") +def test_faiss_mmr_with_metadatas_and_nested_logical_comparsion_ops_filter_1() -> None: + texts = ["foo", "foo", "fou", "foy"] + metadatas = [{"page": i} for i in range(len(texts))] + docsearch = FAISS.from_texts(texts, FakeEmbeddings(), metadatas=metadatas) + query_vec = FakeEmbeddings().embed_query(text="foo") + output = docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, + k=10, + lambda_mult=0.1, + filter={ + "$and": [ + {"$or": [{"page": {"$lt": 1}}, {"page": {"$gt": 2}}]}, + {"$or": [{"page": {"$eq": 0}}, {"page": {"$eq": 1}}]}, + ] + }, + ) + assert len(output) == 1 + assert output[0][0] == Document(page_content="foo", metadata={"page": 0}) + assert output == docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, + k=10, + lambda_mult=0.1, + filter=lambda di: (di["page"] < 1 or di["page"] > 2) + and (di["page"] == 0 or di["page"] == 1), + ) + + +@pytest.mark.requires("faiss") +def test_faiss_mmr_with_metadatas_and_nested_logical_comparsion_ops_filter_2() -> None: + texts = ["foo", "foo", "fou", "foy"] + metadatas = [{"page": i} for i in range(len(texts))] + docsearch = FAISS.from_texts(texts, FakeEmbeddings(), metadatas=metadatas) + query_vec = FakeEmbeddings().embed_query(text="foo") + output = docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, + k=10, + lambda_mult=0.1, + filter={ + "$and": [ + {"$or": [{"page": {"$lt": 1}}, {"page": {"$gt": 2}}]}, + {"$not": {"page": {"$in": [0]}}}, + {"page": {"$neq": 3}}, + ] + }, + ) + assert len(output) == 0 + assert output == docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, + k=10, + lambda_mult=0.1, + filter=lambda di: (di["page"] < 1 or di["page"] > 2) + and (di["page"] not in [0]) + and (di["page"] != 3), + ) + + +@pytest.mark.requires("faiss") +def test_faiss_mmr_with_metadatas_and_nested_logical_comparsion_ops_filter_3() -> None: + texts = ["foo", "foo", "fou", "foy"] + metadatas = [{"page": i} for i in range(len(texts))] + docsearch = FAISS.from_texts(texts, FakeEmbeddings(), metadatas=metadatas) + query_vec = FakeEmbeddings().embed_query(text="foo") + output = docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, + k=10, + lambda_mult=0.1, + filter={ + "$or": [ + {"$and": [{"page": {"$lt": 1}}, {"page": {"$gt": 2}}]}, + {"$not": {"page": {"$nin": [0]}}}, + {"page": {"$eq": 3}}, + ] + }, + ) + assert len(output) == 2 + assert output[0][0] == Document(page_content="foo", metadata={"page": 0}) + assert output[0][1] == 0.0 + assert output[1][0] == Document(page_content="foy", metadata={"page": 3}) + assert output == docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, + k=10, + lambda_mult=0.1, + filter=lambda di: (di["page"] < 1 and di["page"] > 2) + or (not di["page"] not in [0]) + or (di["page"] == 3), + ) + + +@pytest.mark.requires("faiss") +def test_faiss_mmr_with_metadatas_and_empty_conditions() -> None: + """Test with an empty filter condition.""" + texts = ["foo", "bar", "baz"] + metadatas = [{"page": i} for i in range(len(texts))] + docsearch = FAISS.from_texts(texts, FakeEmbeddings(), metadatas=metadatas) + query_vec = FakeEmbeddings().embed_query(text="foo") + + output = docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, k=10, lambda_mult=0.1, filter={} + ) + + assert len(output) == 3 + assert all(doc[0].page_content in ["foo", "bar", "baz"] for doc in output) + + +@pytest.mark.requires("faiss") +def test_faiss_mmr_with_metadates_and_empty_and_operator() -> None: + """Test with an empty $and operator.""" + texts = ["foo", "bar", "baz"] + metadatas = [{"page": i} for i in range(len(texts))] + docsearch = FAISS.from_texts(texts, FakeEmbeddings(), metadatas=metadatas) + query_vec = FakeEmbeddings().embed_query(text="foo") + + # Using an empty $and filter + output = docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, k=10, lambda_mult=0.1, filter={"$and": []} + ) + + assert len(output) == 3 + assert all(doc[0].page_content in ["foo", "bar", "baz"] for doc in output) + + +@pytest.mark.requires("faiss") +def test_faiss_mmr_with_metadatas_and_empty_or_operator() -> None: + """Test with an empty $or operator.""" + texts = ["foo", "bar", "baz"] + metadatas = [{"page": i} for i in range(len(texts))] + docsearch = FAISS.from_texts(texts, FakeEmbeddings(), metadatas=metadatas) + query_vec = FakeEmbeddings().embed_query(text="foo") + + # Using an empty $or filter + output = docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, k=10, lambda_mult=0.1, filter={"$or": []} + ) + + assert len(output) == 0 + + +@pytest.mark.requires("faiss") +def test_faiss_mmr_with_metadatas_and_nonexistent_field() -> None: + """Test with a non-existent field in the metadata.""" + texts = ["foo", "bar", "baz"] + metadatas = [{"page": i} for i in range(len(texts))] + docsearch = FAISS.from_texts(texts, FakeEmbeddings(), metadatas=metadatas) + query_vec = FakeEmbeddings().embed_query(text="foo") + + # Using a filter with a non-existent field + output = docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, k=10, lambda_mult=0.1, filter={"nonexistent_field": {"$eq": 1}} + ) + + assert len(output) == 0 # Expecting no documents to match + + +@pytest.mark.requires("faiss") +def test_faiss_mmr_with_metadatas_and_invalid_logical_operator() -> None: + """Test with an invalid logical operator key.""" + texts = ["foo", "bar", "baz"] + metadatas = [{"page": i} for i in range(len(texts))] + docsearch = FAISS.from_texts(texts, FakeEmbeddings(), metadatas=metadatas) + query_vec = FakeEmbeddings().embed_query(text="foo") + + # Using a filter with an invalid logical operator + with pytest.raises(ValueError, match="unsupported operator"): + docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, k=10, lambda_mult=0.1, filter={"$unknown": [{"page": 1}]} + ) + + +@pytest.mark.requires("faiss") +def test_faiss_mmr_with_metadatas_and_invalid_comparison_operator() -> None: + """Test with an invalid comparison operator key.""" + texts = ["foo", "bar", "baz"] + metadatas = [{"page": i} for i in range(len(texts))] + docsearch = FAISS.from_texts(texts, FakeEmbeddings(), metadatas=metadatas) + query_vec = FakeEmbeddings().embed_query(text="foo") + + # Using a filter with an invalid comparison operator + with pytest.raises(ValueError, match="unsupported operator"): + docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, k=10, lambda_mult=0.1, filter={"page": {"$invalid_operator": 1}} + ) + + +@pytest.mark.requires("faiss") +def test_faiss_mmr_with_metadatas_and_valid_invalid_fields() -> None: + """Test with logical operators combining valid and invalid field.""" + texts = ["foo", "bar", "baz"] + metadatas = [{"page": i} for i in range(len(texts))] + docsearch = FAISS.from_texts(texts, FakeEmbeddings(), metadatas=metadatas) + query_vec = FakeEmbeddings().embed_query(text="foo") + + # Using a filter with $and combining valid and invalid fields + output = docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, + k=10, + lambda_mult=0.1, + filter={ + "$and": [ + {"page": {"$eq": 1}}, # Valid field + {"invalid_field": {"$eq": 1}}, # Invalid field + ] + }, + ) + # Expecting no documents to match due to the invalid field + assert len(output) == 0 + + +@pytest.mark.requires("faiss") +def test_faiss_mmr_with_metadatas_and_valid_and_invalid_operators() -> None: + """Test with logical operators combining valid and invalid operators.""" + texts = ["foo", "bar", "baz"] + metadatas = [{"page": i} for i in range(len(texts))] + docsearch = FAISS.from_texts(texts, FakeEmbeddings(), metadatas=metadatas) + query_vec = FakeEmbeddings().embed_query(text="foo") + + # Using a filter with $and combining valid and invalid operators + with pytest.raises(ValueError, match="unsupported operator"): + docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, + k=10, + lambda_mult=0.1, + filter={ + "$and": [ + {"page": {"$eq": 1}}, # Valid condition + {"page": {"$unknown": 2}}, # Invalid operator + ] + }, + ) + + +@pytest.mark.requires("faiss") +def test_faiss_mmr_with_metadatas_and_multiple_nested_logical_operators() -> None: + """Test with multiple nested logical operators.""" + texts = ["foo", "bar", "baz", "qux", "quux"] + metadatas = [ + {"page": 1, "chapter": 1, "section": 3}, + {"page": 2, "chapter": 2, "section": 4}, + {"page": 1, "chapter": 3, "section": 6}, + {"page": 3, "chapter": 2, "section": 5}, + {"page": 4, "chapter": 1, "section": 2}, + ] + docsearch = FAISS.from_texts(texts, FakeEmbeddings(), metadatas=metadatas) + query_vec = FakeEmbeddings().embed_query(text="foo") + + # Using a filter with multiple nested logical operators + output = docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, + k=10, + lambda_mult=0.1, + filter={ + "$and": [ + {"$or": [{"page": {"$eq": 1}}, {"chapter": {"$gt": 2}}]}, + {"$not": {"section": {"$lte": 5}}}, + ] + }, + ) + assert len(output) > 0 + assert output == docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, + k=10, + lambda_mult=0.1, + filter=lambda di: ( + (di["page"] == 1 or di["chapter"] > 2) and di["section"] > 5 + ), + ) + + +@pytest.mark.requires("faiss") +def test_faiss_mmr_with_metadatas_and_mixed_data_types() -> None: + """Test with metadata containing mixed data types (numbers, strings, booleans).""" + texts = ["foo", "bar", "baz", "qux", "quux"] + metadatas: list[dict] = [ + {"page": "1", "isActive": True, "priority": 2.5}, + {"page": 2, "isActive": False, "priority": 3.0}, + {"page": 3, "isActive": True, "priority": 1.5}, + {"page": 1, "isActive": True, "priority": 4.0}, + {"page": 4, "isActive": False, "priority": 2.0}, + ] + docsearch = FAISS.from_texts(texts, FakeEmbeddings(), metadatas=metadatas) + query_vec = FakeEmbeddings().embed_query(text="foo") + + # Using a filter with mixed data types + output = docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, + k=10, + lambda_mult=0.1, + filter={ + "page": {"$eq": "1"}, # String comparison + "isActive": {"$eq": True}, # Boolean comparison + "priority": {"$gt": 2.0}, # Numeric comparison + }, + ) + # Assert output matches expected results based on the filter conditions + assert output == docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, + k=10, + lambda_mult=0.1, + filter=lambda di: ( + di["page"] == "1" and di["isActive"] is True and di["priority"] > 2.0 + ), + ) + + +@pytest.mark.requires("faiss") +def test_faiss_mmr_with_metadatas_and_conflicting_conditions() -> None: + """Test with conflicting conditions in filters.""" + texts = ["foo", "bar", "baz"] + metadatas = [{"page": i} for i in range(len(texts))] + docsearch = FAISS.from_texts(texts, FakeEmbeddings(), metadatas=metadatas) + query_vec = FakeEmbeddings().embed_query(text="foo") + + output = docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, + k=10, + lambda_mult=0.1, + filter={"$and": [{"page": {"$eq": 1}}, {"page": {"$eq": 2}}]}, + ) + # Assert that the output is empty due to conflicting conditions + assert len(output) == 0 + + +@pytest.mark.requires("faiss") +def test_faiss_mmr_with_metadatas_and_null_field_values() -> None: + """Test with fields that have null or undefined values.""" + texts = ["foo", "bar", "baz", "qux"] + metadatas: list[dict] = [{"page": 1}, {"page": None}, {"page": 2}, {"page": None}] + docsearch = FAISS.from_texts(texts, FakeEmbeddings(), metadatas=metadatas) + query_vec = FakeEmbeddings().embed_query(text="foo") + # Using a filter to find documents where page is null + output = docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, k=10, lambda_mult=0.1, filter={"page": {"$eq": None}} + ) + assert len(output) == 2 + assert output == docsearch.max_marginal_relevance_search_with_score_by_vector( + query_vec, k=10, lambda_mult=0.1, filter=lambda di: di["page"] is None + ) + + @pytest.mark.requires("faiss") async def test_faiss_async_mmr_with_metadatas_and_filter() -> None: texts = ["foo", "foo", "fou", "foy"] diff --git a/libs/community/tests/unit_tests/vectorstores/test_imports.py b/libs/community/tests/unit_tests/vectorstores/test_imports.py index 5ac0ca72b49c5..d65d9d41a1316 100644 --- a/libs/community/tests/unit_tests/vectorstores/test_imports.py +++ b/libs/community/tests/unit_tests/vectorstores/test_imports.py @@ -84,6 +84,7 @@ "StarRocks", "SupabaseVectorStore", "SurrealDBStore", + "TablestoreVectorStore", "Tair", "TencentVectorDB", "TiDBVectorStore", diff --git a/libs/community/tests/unit_tests/vectorstores/test_indexing_docs.py b/libs/community/tests/unit_tests/vectorstores/test_indexing_docs.py index 041f4172b2dcb..b7da470ef2636 100644 --- a/libs/community/tests/unit_tests/vectorstores/test_indexing_docs.py +++ b/libs/community/tests/unit_tests/vectorstores/test_indexing_docs.py @@ -88,6 +88,7 @@ def check_compatibility(vector_store: VectorStore) -> bool: "SingleStoreDB", "SupabaseVectorStore", "SurrealDBStore", + "TablestoreVectorStore", "TileDB", "TimescaleVector", "TencentVectorDB", diff --git a/libs/community/tests/unit_tests/vectorstores/test_inmemory.py b/libs/community/tests/unit_tests/vectorstores/test_inmemory.py index b650cda14fdd1..a2bebcd426982 100644 --- a/libs/community/tests/unit_tests/vectorstores/test_inmemory.py +++ b/libs/community/tests/unit_tests/vectorstores/test_inmemory.py @@ -3,10 +3,7 @@ import pytest from langchain_core.documents import Document -from langchain_tests.integration_tests.vectorstores import ( - AsyncReadWriteTestSuite, - ReadWriteTestSuite, -) +from langchain_tests.integration_tests.vectorstores import VectorStoreIntegrationTests from langchain_community.vectorstores.inmemory import InMemoryVectorStore from tests.integration_tests.vectorstores.fake_embeddings import ( @@ -26,18 +23,12 @@ def _AnyDocument(**kwargs: Any) -> Document: return doc -class TestInMemoryReadWriteTestSuite(ReadWriteTestSuite): +class TestInMemoryStandard(VectorStoreIntegrationTests): @pytest.fixture def vectorstore(self) -> InMemoryVectorStore: return InMemoryVectorStore(embedding=self.get_embeddings()) -class TestAsyncInMemoryReadWriteTestSuite(AsyncReadWriteTestSuite): - @pytest.fixture - async def vectorstore(self) -> InMemoryVectorStore: - return InMemoryVectorStore(embedding=self.get_embeddings()) - - async def test_inmemory() -> None: """Test end to end construction and search.""" store = await InMemoryVectorStore.afrom_texts( diff --git a/libs/community/tests/unit_tests/vectorstores/test_utils.py b/libs/community/tests/unit_tests/vectorstores/test_utils.py index c9714df6aa6e7..5d4e97d068b0e 100644 --- a/libs/community/tests/unit_tests/vectorstores/test_utils.py +++ b/libs/community/tests/unit_tests/vectorstores/test_utils.py @@ -1,5 +1,7 @@ """Test vector store utility functions.""" +from typing import cast + import numpy as np from langchain_core.documents import Document @@ -53,7 +55,7 @@ def test_maximal_marginal_relevance() -> None: def test_maximal_marginal_relevance_query_dim() -> None: query_embedding = np.random.random(size=5) query_embedding_2d = query_embedding.reshape((1, 5)) - embedding_list = np.random.random(size=(4, 5)).tolist() + embedding_list = cast(list[list[float]], np.random.random(size=(4, 5)).tolist()) first = maximal_marginal_relevance(query_embedding, embedding_list) second = maximal_marginal_relevance(query_embedding_2d, embedding_list) assert first == second diff --git a/libs/core/langchain_core/_api/deprecation.py b/libs/core/langchain_core/_api/deprecation.py index ab6e47d31590a..c7d1e87b3bac8 100644 --- a/libs/core/langchain_core/_api/deprecation.py +++ b/libs/core/langchain_core/_api/deprecation.py @@ -364,8 +364,15 @@ def finalize(wrapper: Callable[..., Any], new_doc: str) -> T: _package or _name.split(".")[0].replace("_", "-") if "." in _name else None ) since_str = f"{package}=={since}" if package else since + if removal: + if removal.startswith("1.") and package and package.startswith("langchain"): + removal_str = f"It will not be removed until {package}=={removal}." + else: + removal_str = f"It will be removed in {package}=={removal}." + else: + removal_str = "" new_doc = f"""\ -.. deprecated:: {since_str} {details} +.. deprecated:: {since_str} {details} {removal_str} {old_doc}\ """ diff --git a/libs/core/langchain_core/indexing/api.py b/libs/core/langchain_core/indexing/api.py index c310deb241516..3a8d859379fd8 100644 --- a/libs/core/langchain_core/indexing/api.py +++ b/libs/core/langchain_core/indexing/api.py @@ -22,6 +22,7 @@ from langchain_core.document_loaders.base import BaseLoader from langchain_core.documents import Document +from langchain_core.exceptions import LangChainException from langchain_core.indexing.base import DocumentIndex, RecordManager from langchain_core.vectorstores import VectorStore @@ -175,6 +176,32 @@ def _deduplicate_in_order( yield hashed_doc +class IndexingException(LangChainException): + """Raised when an indexing operation fails.""" + + +def _delete( + vector_store: Union[VectorStore, DocumentIndex], + ids: list[str], +) -> None: + if isinstance(vector_store, VectorStore): + delete_ok = vector_store.delete(ids) + if delete_ok is not None and delete_ok is False: + msg = "The delete operation to VectorStore failed." + raise IndexingException(msg) + elif isinstance(vector_store, DocumentIndex): + delete_response = vector_store.delete(ids) + if "num_failed" in delete_response and delete_response["num_failed"] > 0: + msg = "The delete operation to DocumentIndex failed." + raise IndexingException(msg) + else: + msg = ( + f"Vectorstore should be either a VectorStore or a DocumentIndex. " + f"Got {type(vector_store)}." + ) + raise TypeError(msg) + + # PUBLIC API @@ -197,7 +224,7 @@ def index( vector_store: Union[VectorStore, DocumentIndex], *, batch_size: int = 100, - cleanup: Literal["incremental", "full", None] = None, + cleanup: Literal["incremental", "full", "scoped_full", None] = None, source_id_key: Union[str, Callable[[Document], str], None] = None, cleanup_batch_size: int = 1_000, force_update: bool = False, @@ -215,7 +242,7 @@ def index( are not able to specify the uid of the document. IMPORTANT: - * if auto_cleanup is set to True, the loader should be returning + * In full mode, the loader should be returning the entire dataset, and not just a subset of the dataset. Otherwise, the auto_cleanup will remove documents that it is not supposed to. @@ -227,6 +254,11 @@ def index( chunks, and we index them using a batch size of 5, we'll have 3 batches all with the same source id. In general, to avoid doing too much redundant work select as big a batch size as possible. + * The `scoped_full` mode is suitable if determining an appropriate batch size + is challenging or if your data loader cannot return the entire dataset at + once. This mode keeps track of source IDs in memory, which should be fine + for most use cases. If your dataset is large (10M+ docs), you will likely + need to parallelize the indexing process regardless. Args: docs_source: Data loader or iterable of documents to index. @@ -235,16 +267,19 @@ def index( vector_store: VectorStore or DocumentIndex to index the documents into. batch_size: Batch size to use when indexing. Default is 100. cleanup: How to handle clean up of documents. Default is None. - - Incremental: Cleans up all documents that haven't been updated AND + - incremental: Cleans up all documents that haven't been updated AND that are associated with source ids that were seen during indexing. Clean up is done continuously during indexing helping to minimize the probability of users seeing duplicated content. - - Full: Delete all documents that have not been returned by the loader + - full: Delete all documents that have not been returned by the loader during this run of indexing. Clean up runs after all documents have been indexed. This means that users may see duplicated content during indexing. + - scoped_full: Similar to Full, but only deletes all documents + that haven't been updated AND that are associated with + source ids that were seen during indexing. - None: Do not delete any documents. source_id_key: Optional key that helps identify the original source of the document. Default is None. @@ -270,16 +305,22 @@ def index( ValueError: If vectorstore does not have "delete" and "add_documents" required methods. ValueError: If source_id_key is not None, but is not a string or callable. + + .. version_modified:: 0.3.25 + + * Added `scoped_full` cleanup mode. """ - if cleanup not in {"incremental", "full", None}: + if cleanup not in {"incremental", "full", "scoped_full", None}: msg = ( - f"cleanup should be one of 'incremental', 'full' or None. " + f"cleanup should be one of 'incremental', 'full', 'scoped_full' or None. " f"Got {cleanup}." ) raise ValueError(msg) - if cleanup == "incremental" and source_id_key is None: - msg = "Source id key is required when cleanup mode is incremental." + if (cleanup == "incremental" or cleanup == "scoped_full") and source_id_key is None: + msg = ( + "Source id key is required when cleanup mode is incremental or scoped_full." + ) raise ValueError(msg) destination = vector_store # Renaming internally for clarity @@ -326,6 +367,7 @@ def index( num_skipped = 0 num_updated = 0 num_deleted = 0 + scoped_full_cleanup_source_ids: set[str] = set() for doc_batch in _batch(batch_size, doc_iterator): hashed_docs = list( @@ -338,17 +380,20 @@ def index( source_id_assigner(doc) for doc in hashed_docs ] - if cleanup == "incremental": - # If the cleanup mode is incremental, source ids are required. + if cleanup == "incremental" or cleanup == "scoped_full": + # source ids are required. for source_id, hashed_doc in zip(source_ids, hashed_docs): if source_id is None: msg = ( - "Source ids are required when cleanup mode is incremental. " + f"Source ids are required when cleanup mode is " + f"incremental or scoped_full. " f"Document that starts with " f"content: {hashed_doc.page_content[:100]} was not assigned " f"as source id." ) raise ValueError(msg) + if cleanup == "scoped_full": + scoped_full_cleanup_source_ids.add(source_id) # source ids cannot be None after for loop above. source_ids = cast(Sequence[str], source_ids) # type: ignore[assignment] @@ -408,30 +453,35 @@ def index( # mypy isn't good enough to determine that source ids cannot be None # here due to a check that's happening above, so we check again. - if any(source_id is None for source_id in source_ids): - msg = "Source ids cannot be if cleanup=='incremental'." - raise AssertionError(msg) + for source_id in source_ids: + if source_id is None: + msg = ( + "source_id cannot be None at this point. " + "Reached unreachable code." + ) + raise AssertionError(msg) - indexed_source_ids = cast( - Sequence[str], [source_id_assigner(doc) for doc in docs_to_index] - ) + _source_ids = cast(Sequence[str], source_ids) uids_to_delete = record_manager.list_keys( - group_ids=indexed_source_ids, before=index_start_dt + group_ids=_source_ids, before=index_start_dt ) - if indexed_source_ids and uids_to_delete: + if uids_to_delete: # Then delete from vector store. - destination.delete(uids_to_delete) + _delete(destination, uids_to_delete) # First delete from record store. record_manager.delete_keys(uids_to_delete) num_deleted += len(uids_to_delete) - if cleanup == "full": + if cleanup == "full" or cleanup == "scoped_full": + delete_group_ids: Optional[Sequence[str]] = None + if cleanup == "scoped_full": + delete_group_ids = list(scoped_full_cleanup_source_ids) while uids_to_delete := record_manager.list_keys( - before=index_start_dt, limit=cleanup_batch_size + group_ids=delete_group_ids, before=index_start_dt, limit=cleanup_batch_size ): # First delete from record store. - destination.delete(uids_to_delete) + _delete(destination, uids_to_delete) # Then delete from record manager. record_manager.delete_keys(uids_to_delete) num_deleted += len(uids_to_delete) @@ -451,13 +501,35 @@ async def _to_async_iterator(iterator: Iterable[T]) -> AsyncIterator[T]: yield item +async def _adelete( + vector_store: Union[VectorStore, DocumentIndex], + ids: list[str], +) -> None: + if isinstance(vector_store, VectorStore): + delete_ok = await vector_store.adelete(ids) + if delete_ok is not None and delete_ok is False: + msg = "The delete operation to VectorStore failed." + raise IndexingException(msg) + elif isinstance(vector_store, DocumentIndex): + delete_response = await vector_store.adelete(ids) + if "num_failed" in delete_response and delete_response["num_failed"] > 0: + msg = "The delete operation to DocumentIndex failed." + raise IndexingException(msg) + else: + msg = ( + f"Vectorstore should be either a VectorStore or a DocumentIndex. " + f"Got {type(vector_store)}." + ) + raise TypeError(msg) + + async def aindex( docs_source: Union[BaseLoader, Iterable[Document], AsyncIterator[Document]], record_manager: RecordManager, vector_store: Union[VectorStore, DocumentIndex], *, batch_size: int = 100, - cleanup: Literal["incremental", "full", None] = None, + cleanup: Literal["incremental", "full", "scoped_full", None] = None, source_id_key: Union[str, Callable[[Document], str], None] = None, cleanup_batch_size: int = 1_000, force_update: bool = False, @@ -475,10 +547,23 @@ async def aindex( are not able to specify the uid of the document. IMPORTANT: - if auto_cleanup is set to True, the loader should be returning - the entire dataset, and not just a subset of the dataset. - Otherwise, the auto_cleanup will remove documents that it is not - supposed to. + * In full mode, the loader should be returning + the entire dataset, and not just a subset of the dataset. + Otherwise, the auto_cleanup will remove documents that it is not + supposed to. + * In incremental mode, if documents associated with a particular + source id appear across different batches, the indexing API + will do some redundant work. This will still result in the + correct end state of the index, but will unfortunately not be + 100% efficient. For example, if a given document is split into 15 + chunks, and we index them using a batch size of 5, we'll have 3 batches + all with the same source id. In general, to avoid doing too much + redundant work select as big a batch size as possible. + * The `scoped_full` mode is suitable if determining an appropriate batch size + is challenging or if your data loader cannot return the entire dataset at + once. This mode keeps track of source IDs in memory, which should be fine + for most use cases. If your dataset is large (10M+ docs), you will likely + need to parallelize the indexing process regardless. Args: docs_source: Data loader or iterable of documents to index. @@ -487,15 +572,18 @@ async def aindex( vector_store: VectorStore or DocumentIndex to index the documents into. batch_size: Batch size to use when indexing. Default is 100. cleanup: How to handle clean up of documents. Default is None. - - Incremental: Cleans up all documents that haven't been updated AND + - incremental: Cleans up all documents that haven't been updated AND that are associated with source ids that were seen during indexing. Clean up is done continuously during indexing helping to minimize the probability of users seeing duplicated content. - - Full: Delete all documents that haven to been returned by the loader. + - full: Delete all documents that haven to been returned by the loader. Clean up runs after all documents have been indexed. This means that users may see duplicated content during indexing. + - scoped_full: Similar to Full, but only deletes all documents + that haven't been updated AND that are associated with + source ids that were seen during indexing. - None: Do not delete any documents. source_id_key: Optional key that helps identify the original source of the document. Default is None. @@ -521,17 +609,23 @@ async def aindex( ValueError: If vectorstore does not have "adelete" and "aadd_documents" required methods. ValueError: If source_id_key is not None, but is not a string or callable. + + .. version_modified:: 0.3.25 + + * Added `scoped_full` cleanup mode. """ - if cleanup not in {"incremental", "full", None}: + if cleanup not in {"incremental", "full", "scoped_full", None}: msg = ( - f"cleanup should be one of 'incremental', 'full' or None. " + f"cleanup should be one of 'incremental', 'full', 'scoped_full' or None. " f"Got {cleanup}." ) raise ValueError(msg) - if cleanup == "incremental" and source_id_key is None: - msg = "Source id key is required when cleanup mode is incremental." + if (cleanup == "incremental" or cleanup == "scoped_full") and source_id_key is None: + msg = ( + "Source id key is required when cleanup mode is incremental or scoped_full." + ) raise ValueError(msg) destination = vector_store # Renaming internally for clarity @@ -587,6 +681,7 @@ async def aindex( num_skipped = 0 num_updated = 0 num_deleted = 0 + scoped_full_cleanup_source_ids: set[str] = set() async for doc_batch in _abatch(batch_size, async_doc_iterator): hashed_docs = list( @@ -599,17 +694,20 @@ async def aindex( source_id_assigner(doc) for doc in hashed_docs ] - if cleanup == "incremental": + if cleanup == "incremental" or cleanup == "scoped_full": # If the cleanup mode is incremental, source ids are required. for source_id, hashed_doc in zip(source_ids, hashed_docs): if source_id is None: msg = ( - "Source ids are required when cleanup mode is incremental. " + f"Source ids are required when cleanup mode is " + f"incremental or scoped_full. " f"Document that starts with " f"content: {hashed_doc.page_content[:100]} was not assigned " f"as source id." ) raise ValueError(msg) + if cleanup == "scoped_full": + scoped_full_cleanup_source_ids.add(source_id) # source ids cannot be None after for loop above. source_ids = cast(Sequence[str], source_ids) @@ -669,30 +767,35 @@ async def aindex( # mypy isn't good enough to determine that source ids cannot be None # here due to a check that's happening above, so we check again. - if any(source_id is None for source_id in source_ids): - msg = "Source ids cannot be if cleanup=='incremental'." - raise AssertionError(msg) + for source_id in source_ids: + if source_id is None: + msg = ( + "source_id cannot be None at this point. " + "Reached unreachable code." + ) + raise AssertionError(msg) - indexed_source_ids = cast( - Sequence[str], [source_id_assigner(doc) for doc in docs_to_index] - ) + _source_ids = cast(Sequence[str], source_ids) uids_to_delete = await record_manager.alist_keys( - group_ids=indexed_source_ids, before=index_start_dt + group_ids=_source_ids, before=index_start_dt ) - if indexed_source_ids and uids_to_delete: + if uids_to_delete: # Then delete from vector store. - await destination.adelete(uids_to_delete) + await _adelete(destination, uids_to_delete) # First delete from record store. await record_manager.adelete_keys(uids_to_delete) num_deleted += len(uids_to_delete) - if cleanup == "full": + if cleanup == "full" or cleanup == "scoped_full": + delete_group_ids: Optional[Sequence[str]] = None + if cleanup == "scoped_full": + delete_group_ids = list(scoped_full_cleanup_source_ids) while uids_to_delete := await record_manager.alist_keys( - before=index_start_dt, limit=cleanup_batch_size + group_ids=delete_group_ids, before=index_start_dt, limit=cleanup_batch_size ): # First delete from record store. - await destination.adelete(uids_to_delete) + await _adelete(destination, uids_to_delete) # Then delete from record manager. await record_manager.adelete_keys(uids_to_delete) num_deleted += len(uids_to_delete) diff --git a/libs/core/langchain_core/messages/tool.py b/libs/core/langchain_core/messages/tool.py index 653dd838f860e..873f872cef268 100644 --- a/libs/core/langchain_core/messages/tool.py +++ b/libs/core/langchain_core/messages/tool.py @@ -9,7 +9,16 @@ from langchain_core.utils._merge import merge_dicts, merge_obj -class ToolMessage(BaseMessage): +class ToolOutputMixin: + """Mixin for objects that tools can return directly. + + If a custom BaseTool is invoked with a ToolCall and the output of custom code is + not an instance of ToolOutputMixin, the output will automatically be coerced to a + string and wrapped in a ToolMessage. + """ + + +class ToolMessage(BaseMessage, ToolOutputMixin): """Message for passing the result of executing a tool back to a model. ToolMessages contain the result of a tool invocation. Typically, the result diff --git a/libs/core/langchain_core/prompts/pipeline.py b/libs/core/langchain_core/prompts/pipeline.py index e25a0a7f72461..f316ba3d12175 100644 --- a/libs/core/langchain_core/prompts/pipeline.py +++ b/libs/core/langchain_core/prompts/pipeline.py @@ -3,6 +3,7 @@ from pydantic import model_validator +from langchain_core._api.deprecation import deprecated from langchain_core.prompt_values import PromptValue from langchain_core.prompts.base import BasePromptTemplate from langchain_core.prompts.chat import BaseChatPromptTemplate @@ -12,8 +13,28 @@ def _get_inputs(inputs: dict, input_variables: list[str]) -> dict: return {k: inputs[k] for k in input_variables} +@deprecated( + since="0.3.22", + removal="1.0", + message=( + "This class is deprecated. Please see the docstring below or at the link" + " for a replacement option: " + "https://python.langchain.com/api_reference/core/prompts/langchain_core.prompts.pipeline.PipelinePromptTemplate.html" + ), +) class PipelinePromptTemplate(BasePromptTemplate): - """Prompt template for composing multiple prompt templates together. + """ + This has been deprecated in favor of chaining individual prompts together in your + code. E.g. using a for loop, you could do: + + .. code-block:: python + + my_input = {"key": "value"} + for name, prompt in pipeline_prompts: + my_input[name] = prompt.invoke(my_input).to_string() + my_output = final_prompt.invoke(my_input) + + Prompt template for composing multiple prompt templates together. This can be useful when you want to reuse parts of prompts. diff --git a/libs/core/langchain_core/retrievers.py b/libs/core/langchain_core/retrievers.py index 7462569ddd30d..e1be4588081a2 100644 --- a/libs/core/langchain_core/retrievers.py +++ b/libs/core/langchain_core/retrievers.py @@ -27,7 +27,7 @@ from typing import TYPE_CHECKING, Any, Optional from pydantic import ConfigDict -from typing_extensions import TypedDict +from typing_extensions import Self, TypedDict from langchain_core._api import deprecated from langchain_core.documents import Document @@ -180,6 +180,18 @@ def __init_subclass__(cls, **kwargs: Any) -> None: cls._aget_relevant_documents = aswap # type: ignore[assignment] parameters = signature(cls._get_relevant_documents).parameters cls._new_arg_supported = parameters.get("run_manager") is not None + if ( + not cls._new_arg_supported + and cls._aget_relevant_documents == BaseRetriever._aget_relevant_documents + ): + # we need to tolerate no run_manager in _aget_relevant_documents signature + async def _aget_relevant_documents( + self: Self, query: str + ) -> list[Document]: + return await run_in_executor(None, self._get_relevant_documents, query) # type: ignore + + cls._aget_relevant_documents = _aget_relevant_documents # type: ignore[assignment] + # If a V1 retriever broke the interface and expects additional arguments cls._expects_other_args = ( len(set(parameters.keys()) - {"self", "query", "run_manager"}) > 0 diff --git a/libs/core/langchain_core/runnables/graph.py b/libs/core/langchain_core/runnables/graph.py index 2cb57c4e0fec5..d0e69e0b481b5 100644 --- a/libs/core/langchain_core/runnables/graph.py +++ b/libs/core/langchain_core/runnables/graph.py @@ -470,14 +470,22 @@ def trim_first_node(self) -> None: """Remove the first node if it exists and has a single outgoing edge, i.e., if removing it would not leave the graph without a "first" node.""" first_node = self.first_node() - if first_node and _first_node(self, exclude=[first_node.id]): + if ( + first_node + and _first_node(self, exclude=[first_node.id]) + and len({e for e in self.edges if e.source == first_node.id}) == 1 + ): self.remove_node(first_node) def trim_last_node(self) -> None: """Remove the last node if it exists and has a single incoming edge, i.e., if removing it would not leave the graph without a "last" node.""" last_node = self.last_node() - if last_node and _last_node(self, exclude=[last_node.id]): + if ( + last_node + and _last_node(self, exclude=[last_node.id]) + and len({e for e in self.edges if e.target == last_node.id}) == 1 + ): self.remove_node(last_node) def draw_ascii(self) -> str: diff --git a/libs/core/langchain_core/tools/base.py b/libs/core/langchain_core/tools/base.py index 9782234dfb1a0..ff264edac3284 100644 --- a/libs/core/langchain_core/tools/base.py +++ b/libs/core/langchain_core/tools/base.py @@ -45,7 +45,7 @@ CallbackManager, Callbacks, ) -from langchain_core.messages.tool import ToolCall, ToolMessage +from langchain_core.messages.tool import ToolCall, ToolMessage, ToolOutputMixin from langchain_core.runnables import ( RunnableConfig, RunnableSerializable, @@ -494,7 +494,9 @@ async def ainvoke( # --- Tool --- - def _parse_input(self, tool_input: Union[str, dict]) -> Union[str, dict[str, Any]]: + def _parse_input( + self, tool_input: Union[str, dict], tool_call_id: Optional[str] + ) -> Union[str, dict[str, Any]]: """Convert tool input to a pydantic model. Args: @@ -512,9 +514,39 @@ def _parse_input(self, tool_input: Union[str, dict]) -> Union[str, dict[str, Any else: if input_args is not None: if issubclass(input_args, BaseModel): + for k, v in get_all_basemodel_annotations(input_args).items(): + if ( + _is_injected_arg_type(v, injected_type=InjectedToolCallId) + and k not in tool_input + ): + if tool_call_id is None: + msg = ( + "When tool includes an InjectedToolCallId " + "argument, tool must always be invoked with a full " + "model ToolCall of the form: {'args': {...}, " + "'name': '...', 'type': 'tool_call', " + "'tool_call_id': '...'}" + ) + raise ValueError(msg) + tool_input[k] = tool_call_id result = input_args.model_validate(tool_input) result_dict = result.model_dump() elif issubclass(input_args, BaseModelV1): + for k, v in get_all_basemodel_annotations(input_args).items(): + if ( + _is_injected_arg_type(v, injected_type=InjectedToolCallId) + and k not in tool_input + ): + if tool_call_id is None: + msg = ( + "When tool includes an InjectedToolCallId " + "argument, tool must always be invoked with a full " + "model ToolCall of the form: {'args': {...}, " + "'name': '...', 'type': 'tool_call', " + "'tool_call_id': '...'}" + ) + raise ValueError(msg) + tool_input[k] = tool_call_id result = input_args.parse_obj(tool_input) result_dict = result.dict() else: @@ -570,8 +602,10 @@ async def _arun(self, *args: Any, **kwargs: Any) -> Any: kwargs["run_manager"] = kwargs["run_manager"].get_sync() return await run_in_executor(None, self._run, *args, **kwargs) - def _to_args_and_kwargs(self, tool_input: Union[str, dict]) -> tuple[tuple, dict]: - tool_input = self._parse_input(tool_input) + def _to_args_and_kwargs( + self, tool_input: Union[str, dict], tool_call_id: Optional[str] + ) -> tuple[tuple, dict]: + tool_input = self._parse_input(tool_input, tool_call_id) # For backwards compatibility, if run_input is a string, # pass as a positional argument. if isinstance(tool_input, str): @@ -609,7 +643,7 @@ def run( run_id: The id of the run. Defaults to None. config: The configuration for the tool. Defaults to None. tool_call_id: The id of the tool call. Defaults to None. - kwargs: Additional arguments to pass to the tool + kwargs: Keyword arguments to be passed to tool callbacks Returns: The output of the tool. @@ -648,10 +682,9 @@ def run( child_config = patch_config(config, callbacks=run_manager.get_child()) context = copy_context() context.run(_set_config_context, child_config) - tool_args, tool_kwargs = self._to_args_and_kwargs(tool_input) + tool_args, tool_kwargs = self._to_args_and_kwargs(tool_input, tool_call_id) if signature(self._run).parameters.get("run_manager"): tool_kwargs["run_manager"] = run_manager - if config_param := _get_runnable_config_param(self._run): tool_kwargs[config_param] = config response = context.run(self._run, *tool_args, **tool_kwargs) @@ -721,7 +754,7 @@ async def arun( run_id: The id of the run. Defaults to None. config: The configuration for the tool. Defaults to None. tool_call_id: The id of the tool call. Defaults to None. - kwargs: Additional arguments to pass to the tool + kwargs: Keyword arguments to be passed to tool callbacks Returns: The output of the tool. @@ -755,7 +788,7 @@ async def arun( artifact = None error_to_raise: Optional[Union[Exception, KeyboardInterrupt]] = None try: - tool_args, tool_kwargs = self._to_args_and_kwargs(tool_input) + tool_args, tool_kwargs = self._to_args_and_kwargs(tool_input, tool_call_id) child_config = patch_config(config, callbacks=run_manager.get_child()) context = copy_context() context.run(_set_config_context, child_config) @@ -889,20 +922,23 @@ def _prep_run_args( def _format_output( - content: Any, artifact: Any, tool_call_id: Optional[str], name: str, status: str -) -> Union[ToolMessage, Any]: - if tool_call_id: - if not _is_message_content_type(content): - content = _stringify(content) - return ToolMessage( - content, - artifact=artifact, - tool_call_id=tool_call_id, - name=name, - status=status, - ) - else: + content: Any, + artifact: Any, + tool_call_id: Optional[str], + name: str, + status: str, +) -> Union[ToolOutputMixin, Any]: + if isinstance(content, ToolOutputMixin) or not tool_call_id: return content + if not _is_message_content_type(content): + content = _stringify(content) + return ToolMessage( + content, + artifact=artifact, + tool_call_id=tool_call_id, + name=name, + status=status, + ) def _is_message_content_type(obj: Any) -> bool: @@ -954,10 +990,31 @@ class InjectedToolArg: """Annotation for a Tool arg that is **not** meant to be generated by a model.""" -def _is_injected_arg_type(type_: type) -> bool: +class InjectedToolCallId(InjectedToolArg): + r'''Annotation for injecting the tool_call_id. + + Example: + ..code-block:: python + + from typing_extensions import Annotated + + from langchain_core.messages import ToolMessage + from langchain_core.tools import tool, InjectedToolCallID + + @tool + def foo(x: int, tool_call_id: Annotated[str, InjectedToolCallID]) -> ToolMessage: + """Return x.""" + return ToolMessage(str(x), artifact=x, name="foo", tool_call_id=tool_call_id) + ''' # noqa: E501 + + +def _is_injected_arg_type( + type_: type, injected_type: Optional[type[InjectedToolArg]] = None +) -> bool: + injected_type = injected_type or InjectedToolArg return any( - isinstance(arg, InjectedToolArg) - or (isinstance(arg, type) and issubclass(arg, InjectedToolArg)) + isinstance(arg, injected_type) + or (isinstance(arg, type) and issubclass(arg, injected_type)) for arg in get_args(type_)[1:] ) diff --git a/libs/core/langchain_core/tools/simple.py b/libs/core/langchain_core/tools/simple.py index 118c8b39f6db3..d9e38ba227c8b 100644 --- a/libs/core/langchain_core/tools/simple.py +++ b/libs/core/langchain_core/tools/simple.py @@ -62,9 +62,11 @@ def args(self) -> dict: # assume it takes a single string input. return {"tool_input": {"type": "string"}} - def _to_args_and_kwargs(self, tool_input: Union[str, dict]) -> tuple[tuple, dict]: + def _to_args_and_kwargs( + self, tool_input: Union[str, dict], tool_call_id: Optional[str] + ) -> tuple[tuple, dict]: """Convert tool input to pydantic model.""" - args, kwargs = super()._to_args_and_kwargs(tool_input) + args, kwargs = super()._to_args_and_kwargs(tool_input, tool_call_id) # For backwards compatibility. The tool must be run with a single input all_args = list(args) + list(kwargs.values()) if len(all_args) != 1: diff --git a/libs/core/langchain_core/tracers/context.py b/libs/core/langchain_core/tracers/context.py index 295c68552a3a8..3694db7188b06 100644 --- a/libs/core/langchain_core/tracers/context.py +++ b/libs/core/langchain_core/tracers/context.py @@ -6,6 +6,7 @@ from typing import ( TYPE_CHECKING, Any, + Literal, Optional, Union, cast, @@ -141,7 +142,7 @@ def _get_trace_callbacks( return cb -def _tracing_v2_is_enabled() -> bool: +def _tracing_v2_is_enabled() -> Union[bool, Literal["local"]]: if tracing_v2_callback_var.get() is not None: return True return ls_utils.tracing_is_enabled() diff --git a/libs/core/langchain_core/utils/function_calling.py b/libs/core/langchain_core/utils/function_calling.py index d0252603ed440..77bcaa69cccfb 100644 --- a/libs/core/langchain_core/utils/function_calling.py +++ b/libs/core/langchain_core/utils/function_calling.py @@ -639,9 +639,13 @@ def _parse_google_docstring( for line in args_block.split("\n")[1:]: if ":" in line: arg, desc = line.split(":", maxsplit=1) - arg_descriptions[arg.strip()] = desc.strip() + arg = arg.strip() + arg_name, _, _annotations = arg.partition(" ") + if _annotations.startswith("(") and _annotations.endswith(")"): + arg = arg_name + arg_descriptions[arg] = desc.strip() elif arg: - arg_descriptions[arg.strip()] += " " + line.strip() + arg_descriptions[arg] += " " + line.strip() return description, arg_descriptions def _py_38_safe_origin(origin: type) -> type: diff --git a/libs/core/poetry.lock b/libs/core/poetry.lock index dfc6f8a05c59f..b7fe070395602 100644 --- a/libs/core/poetry.lock +++ b/libs/core/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. [[package]] name = "annotated-types" @@ -13,24 +13,24 @@ files = [ [[package]] name = "anyio" -version = "4.6.2.post1" +version = "4.7.0" description = "High level compatibility layer for multiple asynchronous event loop implementations" optional = false python-versions = ">=3.9" files = [ - {file = "anyio-4.6.2.post1-py3-none-any.whl", hash = "sha256:6d170c36fba3bdd840c73d3868c1e777e33676a69c3a72cf0a0d5d6d8009b61d"}, - {file = "anyio-4.6.2.post1.tar.gz", hash = "sha256:4c8bc31ccdb51c7f7bd251f51c609e038d63e34219b44aa86e47576389880b4c"}, + {file = "anyio-4.7.0-py3-none-any.whl", hash = "sha256:ea60c3723ab42ba6fff7e8ccb0488c898ec538ff4df1f1d5e642c3601d07e352"}, + {file = "anyio-4.7.0.tar.gz", hash = "sha256:2f834749c602966b7d456a7567cafcb309f96482b5081d14ac93ccd457f9dd48"}, ] [package.dependencies] exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""} idna = ">=2.8" sniffio = ">=1.1" -typing-extensions = {version = ">=4.1", markers = "python_version < \"3.11\""} +typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""} [package.extras] -doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] -test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21.0b1)"] +doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx_rtd_theme"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21)"] trio = ["trio (>=0.26.1)"] [[package]] @@ -122,21 +122,18 @@ test = ["dateparser (==1.*)", "pre-commit", "pytest", "pytest-cov", "pytest-mock [[package]] name = "asttokens" -version = "2.4.1" +version = "3.0.0" description = "Annotate AST trees with source code positions" optional = false -python-versions = "*" +python-versions = ">=3.8" files = [ - {file = "asttokens-2.4.1-py2.py3-none-any.whl", hash = "sha256:051ed49c3dcae8913ea7cd08e46a606dba30b79993209636c4875bc1d637bc24"}, - {file = "asttokens-2.4.1.tar.gz", hash = "sha256:b03869718ba9a6eb027e134bfdf69f38a236d681c83c160d510768af11254ba0"}, + {file = "asttokens-3.0.0-py3-none-any.whl", hash = "sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2"}, + {file = "asttokens-3.0.0.tar.gz", hash = "sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7"}, ] -[package.dependencies] -six = ">=1.12.0" - [package.extras] -astroid = ["astroid (>=1,<2)", "astroid (>=2,<4)"] -test = ["astroid (>=1,<2)", "astroid (>=2,<4)", "pytest"] +astroid = ["astroid (>=2,<4)"] +test = ["astroid (>=2,<4)", "pytest", "pytest-cov", "pytest-xdist"] [[package]] name = "async-lru" @@ -457,37 +454,37 @@ test = ["pytest"] [[package]] name = "debugpy" -version = "1.8.8" +version = "1.8.11" description = "An implementation of the Debug Adapter Protocol for Python" optional = false python-versions = ">=3.8" files = [ - {file = "debugpy-1.8.8-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:e59b1607c51b71545cb3496876544f7186a7a27c00b436a62f285603cc68d1c6"}, - {file = "debugpy-1.8.8-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6531d952b565b7cb2fbd1ef5df3d333cf160b44f37547a4e7cf73666aca5d8d"}, - {file = "debugpy-1.8.8-cp310-cp310-win32.whl", hash = "sha256:b01f4a5e5c5fb1d34f4ccba99a20ed01eabc45a4684f4948b5db17a319dfb23f"}, - {file = "debugpy-1.8.8-cp310-cp310-win_amd64.whl", hash = "sha256:535f4fb1c024ddca5913bb0eb17880c8f24ba28aa2c225059db145ee557035e9"}, - {file = "debugpy-1.8.8-cp311-cp311-macosx_14_0_universal2.whl", hash = "sha256:c399023146e40ae373753a58d1be0a98bf6397fadc737b97ad612886b53df318"}, - {file = "debugpy-1.8.8-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:09cc7b162586ea2171eea055985da2702b0723f6f907a423c9b2da5996ad67ba"}, - {file = "debugpy-1.8.8-cp311-cp311-win32.whl", hash = "sha256:eea8821d998ebeb02f0625dd0d76839ddde8cbf8152ebbe289dd7acf2cdc6b98"}, - {file = "debugpy-1.8.8-cp311-cp311-win_amd64.whl", hash = "sha256:d4483836da2a533f4b1454dffc9f668096ac0433de855f0c22cdce8c9f7e10c4"}, - {file = "debugpy-1.8.8-cp312-cp312-macosx_14_0_universal2.whl", hash = "sha256:0cc94186340be87b9ac5a707184ec8f36547fb66636d1029ff4f1cc020e53996"}, - {file = "debugpy-1.8.8-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64674e95916e53c2e9540a056e5f489e0ad4872645399d778f7c598eacb7b7f9"}, - {file = "debugpy-1.8.8-cp312-cp312-win32.whl", hash = "sha256:5c6e885dbf12015aed73770f29dec7023cb310d0dc2ba8bfbeb5c8e43f80edc9"}, - {file = "debugpy-1.8.8-cp312-cp312-win_amd64.whl", hash = "sha256:19ffbd84e757a6ca0113574d1bf5a2298b3947320a3e9d7d8dc3377f02d9f864"}, - {file = "debugpy-1.8.8-cp313-cp313-macosx_14_0_universal2.whl", hash = "sha256:705cd123a773d184860ed8dae99becd879dfec361098edbefb5fc0d3683eb804"}, - {file = "debugpy-1.8.8-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:890fd16803f50aa9cb1a9b9b25b5ec321656dd6b78157c74283de241993d086f"}, - {file = "debugpy-1.8.8-cp313-cp313-win32.whl", hash = "sha256:90244598214bbe704aa47556ec591d2f9869ff9e042e301a2859c57106649add"}, - {file = "debugpy-1.8.8-cp313-cp313-win_amd64.whl", hash = "sha256:4b93e4832fd4a759a0c465c967214ed0c8a6e8914bced63a28ddb0dd8c5f078b"}, - {file = "debugpy-1.8.8-cp38-cp38-macosx_14_0_x86_64.whl", hash = "sha256:143ef07940aeb8e7316de48f5ed9447644da5203726fca378f3a6952a50a9eae"}, - {file = "debugpy-1.8.8-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f95651bdcbfd3b27a408869a53fbefcc2bcae13b694daee5f1365b1b83a00113"}, - {file = "debugpy-1.8.8-cp38-cp38-win32.whl", hash = "sha256:26b461123a030e82602a750fb24d7801776aa81cd78404e54ab60e8b5fecdad5"}, - {file = "debugpy-1.8.8-cp38-cp38-win_amd64.whl", hash = "sha256:f3cbf1833e644a3100eadb6120f25be8a532035e8245584c4f7532937edc652a"}, - {file = "debugpy-1.8.8-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:53709d4ec586b525724819dc6af1a7703502f7e06f34ded7157f7b1f963bb854"}, - {file = "debugpy-1.8.8-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a9c013077a3a0000e83d97cf9cc9328d2b0bbb31f56b0e99ea3662d29d7a6a2"}, - {file = "debugpy-1.8.8-cp39-cp39-win32.whl", hash = "sha256:ffe94dd5e9a6739a75f0b85316dc185560db3e97afa6b215628d1b6a17561cb2"}, - {file = "debugpy-1.8.8-cp39-cp39-win_amd64.whl", hash = "sha256:5c0e5a38c7f9b481bf31277d2f74d2109292179081f11108e668195ef926c0f9"}, - {file = "debugpy-1.8.8-py2.py3-none-any.whl", hash = "sha256:ec684553aba5b4066d4de510859922419febc710df7bba04fe9e7ef3de15d34f"}, - {file = "debugpy-1.8.8.zip", hash = "sha256:e6355385db85cbd666be703a96ab7351bc9e6c61d694893206f8001e22aee091"}, + {file = "debugpy-1.8.11-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:2b26fefc4e31ff85593d68b9022e35e8925714a10ab4858fb1b577a8a48cb8cd"}, + {file = "debugpy-1.8.11-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61bc8b3b265e6949855300e84dc93d02d7a3a637f2aec6d382afd4ceb9120c9f"}, + {file = "debugpy-1.8.11-cp310-cp310-win32.whl", hash = "sha256:c928bbf47f65288574b78518449edaa46c82572d340e2750889bbf8cd92f3737"}, + {file = "debugpy-1.8.11-cp310-cp310-win_amd64.whl", hash = "sha256:8da1db4ca4f22583e834dcabdc7832e56fe16275253ee53ba66627b86e304da1"}, + {file = "debugpy-1.8.11-cp311-cp311-macosx_14_0_universal2.whl", hash = "sha256:85de8474ad53ad546ff1c7c7c89230db215b9b8a02754d41cb5a76f70d0be296"}, + {file = "debugpy-1.8.11-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ffc382e4afa4aee367bf413f55ed17bd91b191dcaf979890af239dda435f2a1"}, + {file = "debugpy-1.8.11-cp311-cp311-win32.whl", hash = "sha256:40499a9979c55f72f4eb2fc38695419546b62594f8af194b879d2a18439c97a9"}, + {file = "debugpy-1.8.11-cp311-cp311-win_amd64.whl", hash = "sha256:987bce16e86efa86f747d5151c54e91b3c1e36acc03ce1ddb50f9d09d16ded0e"}, + {file = "debugpy-1.8.11-cp312-cp312-macosx_14_0_universal2.whl", hash = "sha256:84e511a7545d11683d32cdb8f809ef63fc17ea2a00455cc62d0a4dbb4ed1c308"}, + {file = "debugpy-1.8.11-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce291a5aca4985d82875d6779f61375e959208cdf09fcec40001e65fb0a54768"}, + {file = "debugpy-1.8.11-cp312-cp312-win32.whl", hash = "sha256:28e45b3f827d3bf2592f3cf7ae63282e859f3259db44ed2b129093ca0ac7940b"}, + {file = "debugpy-1.8.11-cp312-cp312-win_amd64.whl", hash = "sha256:44b1b8e6253bceada11f714acf4309ffb98bfa9ac55e4fce14f9e5d4484287a1"}, + {file = "debugpy-1.8.11-cp313-cp313-macosx_14_0_universal2.whl", hash = "sha256:8988f7163e4381b0da7696f37eec7aca19deb02e500245df68a7159739bbd0d3"}, + {file = "debugpy-1.8.11-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c1f6a173d1140e557347419767d2b14ac1c9cd847e0b4c5444c7f3144697e4e"}, + {file = "debugpy-1.8.11-cp313-cp313-win32.whl", hash = "sha256:bb3b15e25891f38da3ca0740271e63ab9db61f41d4d8541745cfc1824252cb28"}, + {file = "debugpy-1.8.11-cp313-cp313-win_amd64.whl", hash = "sha256:d8768edcbeb34da9e11bcb8b5c2e0958d25218df7a6e56adf415ef262cd7b6d1"}, + {file = "debugpy-1.8.11-cp38-cp38-macosx_14_0_x86_64.whl", hash = "sha256:ad7efe588c8f5cf940f40c3de0cd683cc5b76819446abaa50dc0829a30c094db"}, + {file = "debugpy-1.8.11-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:189058d03a40103a57144752652b3ab08ff02b7595d0ce1f651b9acc3a3a35a0"}, + {file = "debugpy-1.8.11-cp38-cp38-win32.whl", hash = "sha256:32db46ba45849daed7ccf3f2e26f7a386867b077f39b2a974bb5c4c2c3b0a280"}, + {file = "debugpy-1.8.11-cp38-cp38-win_amd64.whl", hash = "sha256:116bf8342062246ca749013df4f6ea106f23bc159305843491f64672a55af2e5"}, + {file = "debugpy-1.8.11-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:654130ca6ad5de73d978057eaf9e582244ff72d4574b3e106fb8d3d2a0d32458"}, + {file = "debugpy-1.8.11-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23dc34c5e03b0212fa3c49a874df2b8b1b8fda95160bd79c01eb3ab51ea8d851"}, + {file = "debugpy-1.8.11-cp39-cp39-win32.whl", hash = "sha256:52d8a3166c9f2815bfae05f386114b0b2d274456980d41f320299a8d9a5615a7"}, + {file = "debugpy-1.8.11-cp39-cp39-win_amd64.whl", hash = "sha256:52c3cf9ecda273a19cc092961ee34eb9ba8687d67ba34cc7b79a521c1c64c4c0"}, + {file = "debugpy-1.8.11-py2.py3-none-any.whl", hash = "sha256:0e22f846f4211383e6a416d04b4c13ed174d24cc5d43f5fd52e7821d0ebc8920"}, + {file = "debugpy-1.8.11.tar.gz", hash = "sha256:6ad2688b69235c43b020e04fecccdf6a96c8943ca9c2fb340b8adc103c655e57"}, ] [[package]] @@ -542,13 +539,13 @@ tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipyth [[package]] name = "fastjsonschema" -version = "2.20.0" +version = "2.21.1" description = "Fastest Python implementation of JSON schema" optional = false python-versions = "*" files = [ - {file = "fastjsonschema-2.20.0-py3-none-any.whl", hash = "sha256:5875f0b0fa7a0043a91e93a9b8f793bcbbba9691e7fd83dca95c28ba26d21f0a"}, - {file = "fastjsonschema-2.20.0.tar.gz", hash = "sha256:3d48fc5300ee96f5d116f10fe6f28d938e6008f59a6a025c2649475b87f76a23"}, + {file = "fastjsonschema-2.21.1-py3-none-any.whl", hash = "sha256:c9e5b7e908310918cf494a434eeb31384dd84a98b57a30bcb1f535015b554667"}, + {file = "fastjsonschema-2.21.1.tar.gz", hash = "sha256:794d4f0a58f848961ba16af7b9c85a3e88cd360df008c59aac6fc5ae9323b5d4"}, ] [package.extras] @@ -630,13 +627,13 @@ trio = ["trio (>=0.22.0,<1.0)"] [[package]] name = "httpx" -version = "0.27.2" +version = "0.28.1" description = "The next generation HTTP client." optional = false python-versions = ">=3.8" files = [ - {file = "httpx-0.27.2-py3-none-any.whl", hash = "sha256:7bb2708e112d8fdd7829cd4243970f0c223274051cb35ee80c03301ee29a3df0"}, - {file = "httpx-0.27.2.tar.gz", hash = "sha256:f7c2be1d2f3c3c3160d441802406b206c2b76f5947b11115e6df10c6c65e66c2"}, + {file = "httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad"}, + {file = "httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc"}, ] [package.dependencies] @@ -644,7 +641,6 @@ anyio = "*" certifi = "*" httpcore = "==1.*" idna = "*" -sniffio = "*" [package.extras] brotli = ["brotli", "brotlicffi"] @@ -844,13 +840,13 @@ i18n = ["Babel (>=2.7)"] [[package]] name = "json5" -version = "0.9.28" +version = "0.10.0" description = "A Python implementation of the JSON5 data format." optional = false python-versions = ">=3.8.0" files = [ - {file = "json5-0.9.28-py3-none-any.whl", hash = "sha256:29c56f1accdd8bc2e037321237662034a7e07921e2b7223281a5ce2c46f0c4df"}, - {file = "json5-0.9.28.tar.gz", hash = "sha256:1f82f36e615bc5b42f1bbd49dbc94b12563c56408c6ffa06414ea310890e9a6e"}, + {file = "json5-0.10.0-py3-none-any.whl", hash = "sha256:19b23410220a7271e8377f81ba8aacba2fdd56947fbb137ee5977cbe1f5e8dfa"}, + {file = "json5-0.10.0.tar.gz", hash = "sha256:e66941c8f0a02026943c52c2eb34ebeb2a6f819a0be05920a6f5243cd30fd559"}, ] [package.extras] @@ -1107,13 +1103,13 @@ test = ["jupyter-server (>=2.0.0)", "pytest (>=7.0)", "pytest-jupyter[server] (> [[package]] name = "jupyterlab" -version = "4.3.0" +version = "4.3.3" description = "JupyterLab computational environment" optional = false python-versions = ">=3.8" files = [ - {file = "jupyterlab-4.3.0-py3-none-any.whl", hash = "sha256:f67e1095ad61ae04349024f0b40345062ab108a0c6998d9810fec6a3c1a70cd5"}, - {file = "jupyterlab-4.3.0.tar.gz", hash = "sha256:7c6835cbf8df0af0ec8a39332e85ff11693fb9a468205343b4fc0bfbc74817e5"}, + {file = "jupyterlab-4.3.3-py3-none-any.whl", hash = "sha256:32a8fd30677e734ffcc3916a4758b9dab21b02015b668c60eb36f84357b7d4b1"}, + {file = "jupyterlab-4.3.3.tar.gz", hash = "sha256:76fa39e548fdac94dc1204af5956c556f54c785f70ee26aa47ea08eda4d5bbcd"}, ] [package.dependencies] @@ -1128,7 +1124,7 @@ jupyter-server = ">=2.4.0,<3" jupyterlab-server = ">=2.27.1,<3" notebook-shim = ">=0.2" packaging = "*" -setuptools = ">=40.1.0" +setuptools = ">=40.8.0" tomli = {version = ">=1.2.2", markers = "python_version < \"3.11\""} tornado = ">=6.2.0" traitlets = "*" @@ -1190,7 +1186,7 @@ files = [ [[package]] name = "langchain-tests" -version = "0.3.4" +version = "0.3.7" description = "Standard tests for LangChain implementations" optional = false python-versions = ">=3.9,<4.0" @@ -1198,9 +1194,15 @@ files = [] develop = true [package.dependencies] -httpx = "^0.27.0" -langchain-core = "^0.3.19" +httpx = ">=0.25.0,<1" +langchain-core = "^0.3.22" +numpy = [ + {version = ">=1.24.0,<2.0.0", markers = "python_version < \"3.12\""}, + {version = ">=1.26.2,<3", markers = "python_version >= \"3.12\""}, +] pytest = ">=7,<9" +pytest-asyncio = ">=0.20,<1" +pytest-socket = ">=0.6.0,<1" syrupy = "^4" [package.source] @@ -1225,18 +1227,18 @@ url = "../text-splitters" [[package]] name = "langsmith" -version = "0.1.143" +version = "0.2.3" description = "Client library to connect to the LangSmith LLM Tracing and Evaluation Platform." optional = false -python-versions = "<4.0,>=3.8.1" +python-versions = "<4.0,>=3.9" files = [ - {file = "langsmith-0.1.143-py3-none-any.whl", hash = "sha256:ba0d827269e9b03a90fababe41fa3e4e3f833300b95add10184f7e67167dde6f"}, - {file = "langsmith-0.1.143.tar.gz", hash = "sha256:4c5159e5cd84b3f8499433009e72d2076dd2daf6c044ac8a3611b30d0d0161c5"}, + {file = "langsmith-0.2.3-py3-none-any.whl", hash = "sha256:4958b6e918f57fedba6ddc55b8534d1e06478bb44c779aa73713ce898ca6ae87"}, + {file = "langsmith-0.2.3.tar.gz", hash = "sha256:54c231b07fdeb0f8472925074a0ec0ed2cb654a0437d63c6ccf76a9da635900d"}, ] [package.dependencies] httpx = ">=0.23.0,<1" -orjson = ">=3.9.14,<4.0.0" +orjson = {version = ">=3.9.14,<4.0.0", markers = "platform_python_implementation != \"PyPy\""} pydantic = [ {version = ">=1,<3", markers = "python_full_version < \"3.12.4\""}, {version = ">=2.7.4,<3.0.0", markers = "python_full_version >= \"3.12.4\""}, @@ -1244,6 +1246,9 @@ pydantic = [ requests = ">=2,<3" requests-toolbelt = ">=1.0.0,<2.0.0" +[package.extras] +langsmith-pyo3 = ["langsmith-pyo3 (>=0.1.0rc2,<0.2.0)"] + [[package]] name = "markupsafe" version = "3.0.2" @@ -1399,13 +1404,13 @@ files = [ [[package]] name = "nbclient" -version = "0.10.0" +version = "0.10.1" description = "A client library for executing notebooks. Formerly nbconvert's ExecutePreprocessor." optional = false python-versions = ">=3.8.0" files = [ - {file = "nbclient-0.10.0-py3-none-any.whl", hash = "sha256:f13e3529332a1f1f81d82a53210322476a168bb7090a0289c795fe9cc11c9d3f"}, - {file = "nbclient-0.10.0.tar.gz", hash = "sha256:4b3f1b7dba531e498449c4db4f53da339c91d449dc11e9af3a43b4eb5c5abb09"}, + {file = "nbclient-0.10.1-py3-none-any.whl", hash = "sha256:949019b9240d66897e442888cfb618f69ef23dc71c01cb5fced8499c2cfc084d"}, + {file = "nbclient-0.10.1.tar.gz", hash = "sha256:3e93e348ab27e712acd46fccd809139e356eb9a31aab641d1a7991a6eb4e6f68"}, ] [package.dependencies] @@ -1416,7 +1421,7 @@ traitlets = ">=5.4" [package.extras] dev = ["pre-commit"] -docs = ["autodoc-traits", "mock", "moto", "myst-parser", "nbclient[test]", "sphinx (>=1.7)", "sphinx-book-theme", "sphinxcontrib-spelling"] +docs = ["autodoc-traits", "flaky", "ipykernel (>=6.19.3)", "ipython", "ipywidgets", "mock", "moto", "myst-parser", "nbconvert (>=7.0.0)", "pytest (>=7.0,<8)", "pytest-asyncio", "pytest-cov (>=4.0)", "sphinx (>=1.7)", "sphinx-book-theme", "sphinxcontrib-spelling", "testpath", "xmltodict"] test = ["flaky", "ipykernel (>=6.19.3)", "ipython", "ipywidgets", "nbconvert (>=7.0.0)", "pytest (>=7.0,<8)", "pytest-asyncio", "pytest-cov (>=4.0)", "testpath", "xmltodict"] [[package]] @@ -1491,26 +1496,26 @@ files = [ [[package]] name = "notebook" -version = "7.0.7" +version = "7.3.1" description = "Jupyter Notebook - A web-based notebook environment for interactive computing" optional = false python-versions = ">=3.8" files = [ - {file = "notebook-7.0.7-py3-none-any.whl", hash = "sha256:289b606d7e173f75a18beb1406ef411b43f97f7a9c55ba03efa3622905a62346"}, - {file = "notebook-7.0.7.tar.gz", hash = "sha256:3bcff00c17b3ac142ef5f436d50637d936b274cfa0b41f6ac0175363de9b4e09"}, + {file = "notebook-7.3.1-py3-none-any.whl", hash = "sha256:212e1486b2230fe22279043f33c7db5cf9a01d29feb063a85cb139747b7c9483"}, + {file = "notebook-7.3.1.tar.gz", hash = "sha256:84381c2a82d867517fd25b86e986dae1fe113a70b98f03edff9b94e499fec8fa"}, ] [package.dependencies] jupyter-server = ">=2.4.0,<3" -jupyterlab = ">=4.0.2,<5" -jupyterlab-server = ">=2.22.1,<3" +jupyterlab = ">=4.3.2,<4.4" +jupyterlab-server = ">=2.27.1,<3" notebook-shim = ">=0.2,<0.3" tornado = ">=6.2.0" [package.extras] dev = ["hatch", "pre-commit"] docs = ["myst-parser", "nbsphinx", "pydata-sphinx-theme", "sphinx (>=1.3.6)", "sphinxcontrib-github-alt", "sphinxcontrib-spelling"] -test = ["importlib-resources (>=5.0)", "ipykernel", "jupyter-server[test] (>=2.4.0,<3)", "jupyterlab-server[test] (>=2.22.1,<3)", "nbval", "pytest (>=7.0)", "pytest-console-scripts", "pytest-timeout", "pytest-tornasync", "requests"] +test = ["importlib-resources (>=5.0)", "ipykernel", "jupyter-server[test] (>=2.4.0,<3)", "jupyterlab-server[test] (>=2.27.1,<3)", "nbval", "pytest (>=7.0)", "pytest-console-scripts", "pytest-timeout", "pytest-tornasync", "requests"] [[package]] name = "notebook-shim" @@ -1576,133 +1581,150 @@ files = [ [[package]] name = "numpy" -version = "2.1.3" +version = "2.2.0" description = "Fundamental package for array computing in Python" optional = false python-versions = ">=3.10" files = [ - {file = "numpy-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c894b4305373b9c5576d7a12b473702afdf48ce5369c074ba304cc5ad8730dff"}, - {file = "numpy-2.1.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b47fbb433d3260adcd51eb54f92a2ffbc90a4595f8970ee00e064c644ac788f5"}, - {file = "numpy-2.1.3-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:825656d0743699c529c5943554d223c021ff0494ff1442152ce887ef4f7561a1"}, - {file = "numpy-2.1.3-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:6a4825252fcc430a182ac4dee5a505053d262c807f8a924603d411f6718b88fd"}, - {file = "numpy-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e711e02f49e176a01d0349d82cb5f05ba4db7d5e7e0defd026328e5cfb3226d3"}, - {file = "numpy-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78574ac2d1a4a02421f25da9559850d59457bac82f2b8d7a44fe83a64f770098"}, - {file = "numpy-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c7662f0e3673fe4e832fe07b65c50342ea27d989f92c80355658c7f888fcc83c"}, - {file = "numpy-2.1.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:fa2d1337dc61c8dc417fbccf20f6d1e139896a30721b7f1e832b2bb6ef4eb6c4"}, - {file = "numpy-2.1.3-cp310-cp310-win32.whl", hash = "sha256:72dcc4a35a8515d83e76b58fdf8113a5c969ccd505c8a946759b24e3182d1f23"}, - {file = "numpy-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:ecc76a9ba2911d8d37ac01de72834d8849e55473457558e12995f4cd53e778e0"}, - {file = "numpy-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4d1167c53b93f1f5d8a139a742b3c6f4d429b54e74e6b57d0eff40045187b15d"}, - {file = "numpy-2.1.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c80e4a09b3d95b4e1cac08643f1152fa71a0a821a2d4277334c88d54b2219a41"}, - {file = "numpy-2.1.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:576a1c1d25e9e02ed7fa5477f30a127fe56debd53b8d2c89d5578f9857d03ca9"}, - {file = "numpy-2.1.3-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:973faafebaae4c0aaa1a1ca1ce02434554d67e628b8d805e61f874b84e136b09"}, - {file = "numpy-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:762479be47a4863e261a840e8e01608d124ee1361e48b96916f38b119cfda04a"}, - {file = "numpy-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc6f24b3d1ecc1eebfbf5d6051faa49af40b03be1aaa781ebdadcbc090b4539b"}, - {file = "numpy-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:17ee83a1f4fef3c94d16dc1802b998668b5419362c8a4f4e8a491de1b41cc3ee"}, - {file = "numpy-2.1.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:15cb89f39fa6d0bdfb600ea24b250e5f1a3df23f901f51c8debaa6a5d122b2f0"}, - {file = "numpy-2.1.3-cp311-cp311-win32.whl", hash = "sha256:d9beb777a78c331580705326d2367488d5bc473b49a9bc3036c154832520aca9"}, - {file = "numpy-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:d89dd2b6da69c4fff5e39c28a382199ddedc3a5be5390115608345dec660b9e2"}, - {file = "numpy-2.1.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f55ba01150f52b1027829b50d70ef1dafd9821ea82905b63936668403c3b471e"}, - {file = "numpy-2.1.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:13138eadd4f4da03074851a698ffa7e405f41a0845a6b1ad135b81596e4e9958"}, - {file = "numpy-2.1.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:a6b46587b14b888e95e4a24d7b13ae91fa22386c199ee7b418f449032b2fa3b8"}, - {file = "numpy-2.1.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:0fa14563cc46422e99daef53d725d0c326e99e468a9320a240affffe87852564"}, - {file = "numpy-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8637dcd2caa676e475503d1f8fdb327bc495554e10838019651b76d17b98e512"}, - {file = "numpy-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2312b2aa89e1f43ecea6da6ea9a810d06aae08321609d8dc0d0eda6d946a541b"}, - {file = "numpy-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a38c19106902bb19351b83802531fea19dee18e5b37b36454f27f11ff956f7fc"}, - {file = "numpy-2.1.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:02135ade8b8a84011cbb67dc44e07c58f28575cf9ecf8ab304e51c05528c19f0"}, - {file = "numpy-2.1.3-cp312-cp312-win32.whl", hash = "sha256:e6988e90fcf617da2b5c78902fe8e668361b43b4fe26dbf2d7b0f8034d4cafb9"}, - {file = "numpy-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:0d30c543f02e84e92c4b1f415b7c6b5326cbe45ee7882b6b77db7195fb971e3a"}, - {file = "numpy-2.1.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96fe52fcdb9345b7cd82ecd34547fca4321f7656d500eca497eb7ea5a926692f"}, - {file = "numpy-2.1.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f653490b33e9c3a4c1c01d41bc2aef08f9475af51146e4a7710c450cf9761598"}, - {file = "numpy-2.1.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:dc258a761a16daa791081d026f0ed4399b582712e6fc887a95af09df10c5ca57"}, - {file = "numpy-2.1.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:016d0f6f5e77b0f0d45d77387ffa4bb89816b57c835580c3ce8e099ef830befe"}, - {file = "numpy-2.1.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c181ba05ce8299c7aa3125c27b9c2167bca4a4445b7ce73d5febc411ca692e43"}, - {file = "numpy-2.1.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5641516794ca9e5f8a4d17bb45446998c6554704d888f86df9b200e66bdcce56"}, - {file = "numpy-2.1.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ea4dedd6e394a9c180b33c2c872b92f7ce0f8e7ad93e9585312b0c5a04777a4a"}, - {file = "numpy-2.1.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b0df3635b9c8ef48bd3be5f862cf71b0a4716fa0e702155c45067c6b711ddcef"}, - {file = "numpy-2.1.3-cp313-cp313-win32.whl", hash = "sha256:50ca6aba6e163363f132b5c101ba078b8cbd3fa92c7865fd7d4d62d9779ac29f"}, - {file = "numpy-2.1.3-cp313-cp313-win_amd64.whl", hash = "sha256:747641635d3d44bcb380d950679462fae44f54b131be347d5ec2bce47d3df9ed"}, - {file = "numpy-2.1.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:996bb9399059c5b82f76b53ff8bb686069c05acc94656bb259b1d63d04a9506f"}, - {file = "numpy-2.1.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:45966d859916ad02b779706bb43b954281db43e185015df6eb3323120188f9e4"}, - {file = "numpy-2.1.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:baed7e8d7481bfe0874b566850cb0b85243e982388b7b23348c6db2ee2b2ae8e"}, - {file = "numpy-2.1.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:a9f7f672a3388133335589cfca93ed468509cb7b93ba3105fce780d04a6576a0"}, - {file = "numpy-2.1.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7aac50327da5d208db2eec22eb11e491e3fe13d22653dce51b0f4109101b408"}, - {file = "numpy-2.1.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4394bc0dbd074b7f9b52024832d16e019decebf86caf909d94f6b3f77a8ee3b6"}, - {file = "numpy-2.1.3-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:50d18c4358a0a8a53f12a8ba9d772ab2d460321e6a93d6064fc22443d189853f"}, - {file = "numpy-2.1.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:14e253bd43fc6b37af4921b10f6add6925878a42a0c5fe83daee390bca80bc17"}, - {file = "numpy-2.1.3-cp313-cp313t-win32.whl", hash = "sha256:08788d27a5fd867a663f6fc753fd7c3ad7e92747efc73c53bca2f19f8bc06f48"}, - {file = "numpy-2.1.3-cp313-cp313t-win_amd64.whl", hash = "sha256:2564fbdf2b99b3f815f2107c1bbc93e2de8ee655a69c261363a1172a79a257d4"}, - {file = "numpy-2.1.3-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:4f2015dfe437dfebbfce7c85c7b53d81ba49e71ba7eadbf1df40c915af75979f"}, - {file = "numpy-2.1.3-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:3522b0dfe983a575e6a9ab3a4a4dfe156c3e428468ff08ce582b9bb6bd1d71d4"}, - {file = "numpy-2.1.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c006b607a865b07cd981ccb218a04fc86b600411d83d6fc261357f1c0966755d"}, - {file = "numpy-2.1.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e14e26956e6f1696070788252dcdff11b4aca4c3e8bd166e0df1bb8f315a67cb"}, - {file = "numpy-2.1.3.tar.gz", hash = "sha256:aa08e04e08aaf974d4458def539dece0d28146d866a39da5639596f4921fd761"}, + {file = "numpy-2.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1e25507d85da11ff5066269d0bd25d06e0a0f2e908415534f3e603d2a78e4ffa"}, + {file = "numpy-2.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a62eb442011776e4036af5c8b1a00b706c5bc02dc15eb5344b0c750428c94219"}, + {file = "numpy-2.2.0-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:b606b1aaf802e6468c2608c65ff7ece53eae1a6874b3765f69b8ceb20c5fa78e"}, + {file = "numpy-2.2.0-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:36b2b43146f646642b425dd2027730f99bac962618ec2052932157e213a040e9"}, + {file = "numpy-2.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7fe8f3583e0607ad4e43a954e35c1748b553bfe9fdac8635c02058023277d1b3"}, + {file = "numpy-2.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:122fd2fcfafdefc889c64ad99c228d5a1f9692c3a83f56c292618a59aa60ae83"}, + {file = "numpy-2.2.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3f2f5cddeaa4424a0a118924b988746db6ffa8565e5829b1841a8a3bd73eb59a"}, + {file = "numpy-2.2.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7fe4bb0695fe986a9e4deec3b6857003b4cfe5c5e4aac0b95f6a658c14635e31"}, + {file = "numpy-2.2.0-cp310-cp310-win32.whl", hash = "sha256:b30042fe92dbd79f1ba7f6898fada10bdaad1847c44f2dff9a16147e00a93661"}, + {file = "numpy-2.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:54dc1d6d66f8d37843ed281773c7174f03bf7ad826523f73435deb88ba60d2d4"}, + {file = "numpy-2.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9874bc2ff574c40ab7a5cbb7464bf9b045d617e36754a7bc93f933d52bd9ffc6"}, + {file = "numpy-2.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0da8495970f6b101ddd0c38ace92edea30e7e12b9a926b57f5fabb1ecc25bb90"}, + {file = "numpy-2.2.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:0557eebc699c1c34cccdd8c3778c9294e8196df27d713706895edc6f57d29608"}, + {file = "numpy-2.2.0-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:3579eaeb5e07f3ded59298ce22b65f877a86ba8e9fe701f5576c99bb17c283da"}, + {file = "numpy-2.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40deb10198bbaa531509aad0cd2f9fadb26c8b94070831e2208e7df543562b74"}, + {file = "numpy-2.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c2aed8fcf8abc3020d6a9ccb31dbc9e7d7819c56a348cc88fd44be269b37427e"}, + {file = "numpy-2.2.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a222d764352c773aa5ebde02dd84dba3279c81c6db2e482d62a3fa54e5ece69b"}, + {file = "numpy-2.2.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4e58666988605e251d42c2818c7d3d8991555381be26399303053b58a5bbf30d"}, + {file = "numpy-2.2.0-cp311-cp311-win32.whl", hash = "sha256:4723a50e1523e1de4fccd1b9a6dcea750c2102461e9a02b2ac55ffeae09a4410"}, + {file = "numpy-2.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:16757cf28621e43e252c560d25b15f18a2f11da94fea344bf26c599b9cf54b73"}, + {file = "numpy-2.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cff210198bb4cae3f3c100444c5eaa573a823f05c253e7188e1362a5555235b3"}, + {file = "numpy-2.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:58b92a5828bd4d9aa0952492b7de803135038de47343b2aa3cc23f3b71a3dc4e"}, + {file = "numpy-2.2.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:ebe5e59545401fbb1b24da76f006ab19734ae71e703cdb4a8b347e84a0cece67"}, + {file = "numpy-2.2.0-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:e2b8cd48a9942ed3f85b95ca4105c45758438c7ed28fff1e4ce3e57c3b589d8e"}, + {file = "numpy-2.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57fcc997ffc0bef234b8875a54d4058afa92b0b0c4223fc1f62f24b3b5e86038"}, + {file = "numpy-2.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85ad7d11b309bd132d74397fcf2920933c9d1dc865487128f5c03d580f2c3d03"}, + {file = "numpy-2.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:cb24cca1968b21355cc6f3da1a20cd1cebd8a023e3c5b09b432444617949085a"}, + {file = "numpy-2.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0798b138c291d792f8ea40fe3768610f3c7dd2574389e37c3f26573757c8f7ef"}, + {file = "numpy-2.2.0-cp312-cp312-win32.whl", hash = "sha256:afe8fb968743d40435c3827632fd36c5fbde633b0423da7692e426529b1759b1"}, + {file = "numpy-2.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:3a4199f519e57d517ebd48cb76b36c82da0360781c6a0353e64c0cac30ecaad3"}, + {file = "numpy-2.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f8c8b141ef9699ae777c6278b52c706b653bf15d135d302754f6b2e90eb30367"}, + {file = "numpy-2.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0f0986e917aca18f7a567b812ef7ca9391288e2acb7a4308aa9d265bd724bdae"}, + {file = "numpy-2.2.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:1c92113619f7b272838b8d6702a7f8ebe5edea0df48166c47929611d0b4dea69"}, + {file = "numpy-2.2.0-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:5a145e956b374e72ad1dff82779177d4a3c62bc8248f41b80cb5122e68f22d13"}, + {file = "numpy-2.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18142b497d70a34b01642b9feabb70156311b326fdddd875a9981f34a369b671"}, + {file = "numpy-2.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a7d41d1612c1a82b64697e894b75db6758d4f21c3ec069d841e60ebe54b5b571"}, + {file = "numpy-2.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a98f6f20465e7618c83252c02041517bd2f7ea29be5378f09667a8f654a5918d"}, + {file = "numpy-2.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e09d40edfdb4e260cb1567d8ae770ccf3b8b7e9f0d9b5c2a9992696b30ce2742"}, + {file = "numpy-2.2.0-cp313-cp313-win32.whl", hash = "sha256:3905a5fffcc23e597ee4d9fb3fcd209bd658c352657548db7316e810ca80458e"}, + {file = "numpy-2.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:a184288538e6ad699cbe6b24859206e38ce5fba28f3bcfa51c90d0502c1582b2"}, + {file = "numpy-2.2.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:7832f9e8eb00be32f15fdfb9a981d6955ea9adc8574c521d48710171b6c55e95"}, + {file = "numpy-2.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f0dd071b95bbca244f4cb7f70b77d2ff3aaaba7fa16dc41f58d14854a6204e6c"}, + {file = "numpy-2.2.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:b0b227dcff8cdc3efbce66d4e50891f04d0a387cce282fe1e66199146a6a8fca"}, + {file = "numpy-2.2.0-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:6ab153263a7c5ccaf6dfe7e53447b74f77789f28ecb278c3b5d49db7ece10d6d"}, + {file = "numpy-2.2.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e500aba968a48e9019e42c0c199b7ec0696a97fa69037bea163b55398e390529"}, + {file = "numpy-2.2.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:440cfb3db4c5029775803794f8638fbdbf71ec702caf32735f53b008e1eaece3"}, + {file = "numpy-2.2.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a55dc7a7f0b6198b07ec0cd445fbb98b05234e8b00c5ac4874a63372ba98d4ab"}, + {file = "numpy-2.2.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4bddbaa30d78c86329b26bd6aaaea06b1e47444da99eddac7bf1e2fab717bd72"}, + {file = "numpy-2.2.0-cp313-cp313t-win32.whl", hash = "sha256:30bf971c12e4365153afb31fc73f441d4da157153f3400b82db32d04de1e4066"}, + {file = "numpy-2.2.0-cp313-cp313t-win_amd64.whl", hash = "sha256:d35717333b39d1b6bb8433fa758a55f1081543de527171543a2b710551d40881"}, + {file = "numpy-2.2.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e12c6c1ce84628c52d6367863773f7c8c8241be554e8b79686e91a43f1733773"}, + {file = "numpy-2.2.0-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:b6207dc8fb3c8cb5668e885cef9ec7f70189bec4e276f0ff70d5aa078d32c88e"}, + {file = "numpy-2.2.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a50aeff71d0f97b6450d33940c7181b08be1441c6c193e678211bff11aa725e7"}, + {file = "numpy-2.2.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:df12a1f99b99f569a7c2ae59aa2d31724e8d835fc7f33e14f4792e3071d11221"}, + {file = "numpy-2.2.0.tar.gz", hash = "sha256:140dd80ff8981a583a60980be1a655068f8adebf7a45a06a6858c873fcdcd4a0"}, ] [[package]] name = "orjson" -version = "3.10.11" +version = "3.10.12" description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" optional = false python-versions = ">=3.8" files = [ - {file = "orjson-3.10.11-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:6dade64687f2bd7c090281652fe18f1151292d567a9302b34c2dbb92a3872f1f"}, - {file = "orjson-3.10.11-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82f07c550a6ccd2b9290849b22316a609023ed851a87ea888c0456485a7d196a"}, - {file = "orjson-3.10.11-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bd9a187742d3ead9df2e49240234d728c67c356516cf4db018833a86f20ec18c"}, - {file = "orjson-3.10.11-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:77b0fed6f209d76c1c39f032a70df2d7acf24b1812ca3e6078fd04e8972685a3"}, - {file = "orjson-3.10.11-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:63fc9d5fe1d4e8868f6aae547a7b8ba0a2e592929245fff61d633f4caccdcdd6"}, - {file = "orjson-3.10.11-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65cd3e3bb4fbb4eddc3c1e8dce10dc0b73e808fcb875f9fab40c81903dd9323e"}, - {file = "orjson-3.10.11-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6f67c570602300c4befbda12d153113b8974a3340fdcf3d6de095ede86c06d92"}, - {file = "orjson-3.10.11-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1f39728c7f7d766f1f5a769ce4d54b5aaa4c3f92d5b84817053cc9995b977acc"}, - {file = "orjson-3.10.11-cp310-none-win32.whl", hash = "sha256:1789d9db7968d805f3d94aae2c25d04014aae3a2fa65b1443117cd462c6da647"}, - {file = "orjson-3.10.11-cp310-none-win_amd64.whl", hash = "sha256:5576b1e5a53a5ba8f8df81872bb0878a112b3ebb1d392155f00f54dd86c83ff6"}, - {file = "orjson-3.10.11-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:1444f9cb7c14055d595de1036f74ecd6ce15f04a715e73f33bb6326c9cef01b6"}, - {file = "orjson-3.10.11-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdec57fe3b4bdebcc08a946db3365630332dbe575125ff3d80a3272ebd0ddafe"}, - {file = "orjson-3.10.11-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4eed32f33a0ea6ef36ccc1d37f8d17f28a1d6e8eefae5928f76aff8f1df85e67"}, - {file = "orjson-3.10.11-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80df27dd8697242b904f4ea54820e2d98d3f51f91e97e358fc13359721233e4b"}, - {file = "orjson-3.10.11-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:705f03cee0cb797256d54de6695ef219e5bc8c8120b6654dd460848d57a9af3d"}, - {file = "orjson-3.10.11-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03246774131701de8e7059b2e382597da43144a9a7400f178b2a32feafc54bd5"}, - {file = "orjson-3.10.11-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8b5759063a6c940a69c728ea70d7c33583991c6982915a839c8da5f957e0103a"}, - {file = "orjson-3.10.11-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:677f23e32491520eebb19c99bb34675daf5410c449c13416f7f0d93e2cf5f981"}, - {file = "orjson-3.10.11-cp311-none-win32.whl", hash = "sha256:a11225d7b30468dcb099498296ffac36b4673a8398ca30fdaec1e6c20df6aa55"}, - {file = "orjson-3.10.11-cp311-none-win_amd64.whl", hash = "sha256:df8c677df2f9f385fcc85ab859704045fa88d4668bc9991a527c86e710392bec"}, - {file = "orjson-3.10.11-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:360a4e2c0943da7c21505e47cf6bd725588962ff1d739b99b14e2f7f3545ba51"}, - {file = "orjson-3.10.11-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:496e2cb45de21c369079ef2d662670a4892c81573bcc143c4205cae98282ba97"}, - {file = "orjson-3.10.11-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7dfa8db55c9792d53c5952900c6a919cfa377b4f4534c7a786484a6a4a350c19"}, - {file = "orjson-3.10.11-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:51f3382415747e0dbda9dade6f1e1a01a9d37f630d8c9049a8ed0e385b7a90c0"}, - {file = "orjson-3.10.11-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f35a1b9f50a219f470e0e497ca30b285c9f34948d3c8160d5ad3a755d9299433"}, - {file = "orjson-3.10.11-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2f3b7c5803138e67028dde33450e054c87e0703afbe730c105f1fcd873496d5"}, - {file = "orjson-3.10.11-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f91d9eb554310472bd09f5347950b24442600594c2edc1421403d7610a0998fd"}, - {file = "orjson-3.10.11-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dfbb2d460a855c9744bbc8e36f9c3a997c4b27d842f3d5559ed54326e6911f9b"}, - {file = "orjson-3.10.11-cp312-none-win32.whl", hash = "sha256:d4a62c49c506d4d73f59514986cadebb7e8d186ad510c518f439176cf8d5359d"}, - {file = "orjson-3.10.11-cp312-none-win_amd64.whl", hash = "sha256:f1eec3421a558ff7a9b010a6c7effcfa0ade65327a71bb9b02a1c3b77a247284"}, - {file = "orjson-3.10.11-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:c46294faa4e4d0eb73ab68f1a794d2cbf7bab33b1dda2ac2959ffb7c61591899"}, - {file = "orjson-3.10.11-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52e5834d7d6e58a36846e059d00559cb9ed20410664f3ad156cd2cc239a11230"}, - {file = "orjson-3.10.11-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2fc947e5350fdce548bfc94f434e8760d5cafa97fb9c495d2fef6757aa02ec0"}, - {file = "orjson-3.10.11-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0efabbf839388a1dab5b72b5d3baedbd6039ac83f3b55736eb9934ea5494d258"}, - {file = "orjson-3.10.11-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a3f29634260708c200c4fe148e42b4aae97d7b9fee417fbdd74f8cfc265f15b0"}, - {file = "orjson-3.10.11-cp313-none-win32.whl", hash = "sha256:1a1222ffcee8a09476bbdd5d4f6f33d06d0d6642df2a3d78b7a195ca880d669b"}, - {file = "orjson-3.10.11-cp313-none-win_amd64.whl", hash = "sha256:bc274ac261cc69260913b2d1610760e55d3c0801bb3457ba7b9004420b6b4270"}, - {file = "orjson-3.10.11-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:19b3763e8bbf8ad797df6b6b5e0fc7c843ec2e2fc0621398534e0c6400098f87"}, - {file = "orjson-3.10.11-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1be83a13312e5e58d633580c5eb8d0495ae61f180da2722f20562974188af205"}, - {file = "orjson-3.10.11-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:afacfd1ab81f46dedd7f6001b6d4e8de23396e4884cd3c3436bd05defb1a6446"}, - {file = "orjson-3.10.11-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cb4d0bea56bba596723d73f074c420aec3b2e5d7d30698bc56e6048066bd560c"}, - {file = "orjson-3.10.11-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96ed1de70fcb15d5fed529a656df29f768187628727ee2788344e8a51e1c1350"}, - {file = "orjson-3.10.11-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4bfb30c891b530f3f80e801e3ad82ef150b964e5c38e1fb8482441c69c35c61c"}, - {file = "orjson-3.10.11-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d496c74fc2b61341e3cefda7eec21b7854c5f672ee350bc55d9a4997a8a95204"}, - {file = "orjson-3.10.11-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:655a493bac606655db9a47fe94d3d84fc7f3ad766d894197c94ccf0c5408e7d3"}, - {file = "orjson-3.10.11-cp38-none-win32.whl", hash = "sha256:b9546b278c9fb5d45380f4809e11b4dd9844ca7aaf1134024503e134ed226161"}, - {file = "orjson-3.10.11-cp38-none-win_amd64.whl", hash = "sha256:b592597fe551d518f42c5a2eb07422eb475aa8cfdc8c51e6da7054b836b26782"}, - {file = "orjson-3.10.11-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:c95f2ecafe709b4e5c733b5e2768ac569bed308623c85806c395d9cca00e08af"}, - {file = "orjson-3.10.11-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:80c00d4acded0c51c98754fe8218cb49cb854f0f7eb39ea4641b7f71732d2cb7"}, - {file = "orjson-3.10.11-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:461311b693d3d0a060439aa669c74f3603264d4e7a08faa68c47ae5a863f352d"}, - {file = "orjson-3.10.11-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:52ca832f17d86a78cbab86cdc25f8c13756ebe182b6fc1a97d534051c18a08de"}, - {file = "orjson-3.10.11-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4c57ea78a753812f528178aa2f1c57da633754c91d2124cb28991dab4c79a54"}, - {file = "orjson-3.10.11-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7fcfc6f7ca046383fb954ba528587e0f9336828b568282b27579c49f8e16aad"}, - {file = "orjson-3.10.11-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:86b9dd983857970c29e4c71bb3e95ff085c07d3e83e7c46ebe959bac07ebd80b"}, - {file = "orjson-3.10.11-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:4d83f87582d223e54efb2242a79547611ba4ebae3af8bae1e80fa9a0af83bb7f"}, - {file = "orjson-3.10.11-cp39-none-win32.whl", hash = "sha256:9fd0ad1c129bc9beb1154c2655f177620b5beaf9a11e0d10bac63ef3fce96950"}, - {file = "orjson-3.10.11-cp39-none-win_amd64.whl", hash = "sha256:10f416b2a017c8bd17f325fb9dee1fb5cdd7a54e814284896b7c3f2763faa017"}, - {file = "orjson-3.10.11.tar.gz", hash = "sha256:e35b6d730de6384d5b2dab5fd23f0d76fae8bbc8c353c2f78210aa5fa4beb3ef"}, + {file = "orjson-3.10.12-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:ece01a7ec71d9940cc654c482907a6b65df27251255097629d0dea781f255c6d"}, + {file = "orjson-3.10.12-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c34ec9aebc04f11f4b978dd6caf697a2df2dd9b47d35aa4cc606cabcb9df69d7"}, + {file = "orjson-3.10.12-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fd6ec8658da3480939c79b9e9e27e0db31dffcd4ba69c334e98c9976ac29140e"}, + {file = "orjson-3.10.12-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f17e6baf4cf01534c9de8a16c0c611f3d94925d1701bf5f4aff17003677d8ced"}, + {file = "orjson-3.10.12-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6402ebb74a14ef96f94a868569f5dccf70d791de49feb73180eb3c6fda2ade56"}, + {file = "orjson-3.10.12-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0000758ae7c7853e0a4a6063f534c61656ebff644391e1f81698c1b2d2fc8cd2"}, + {file = "orjson-3.10.12-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:888442dcee99fd1e5bd37a4abb94930915ca6af4db50e23e746cdf4d1e63db13"}, + {file = "orjson-3.10.12-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c1f7a3ce79246aa0e92f5458d86c54f257fb5dfdc14a192651ba7ec2c00f8a05"}, + {file = "orjson-3.10.12-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:802a3935f45605c66fb4a586488a38af63cb37aaad1c1d94c982c40dcc452e85"}, + {file = "orjson-3.10.12-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:1da1ef0113a2be19bb6c557fb0ec2d79c92ebd2fed4cfb1b26bab93f021fb885"}, + {file = "orjson-3.10.12-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7a3273e99f367f137d5b3fecb5e9f45bcdbfac2a8b2f32fbc72129bbd48789c2"}, + {file = "orjson-3.10.12-cp310-none-win32.whl", hash = "sha256:475661bf249fd7907d9b0a2a2421b4e684355a77ceef85b8352439a9163418c3"}, + {file = "orjson-3.10.12-cp310-none-win_amd64.whl", hash = "sha256:87251dc1fb2b9e5ab91ce65d8f4caf21910d99ba8fb24b49fd0c118b2362d509"}, + {file = "orjson-3.10.12-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a734c62efa42e7df94926d70fe7d37621c783dea9f707a98cdea796964d4cf74"}, + {file = "orjson-3.10.12-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:750f8b27259d3409eda8350c2919a58b0cfcd2054ddc1bd317a643afc646ef23"}, + {file = "orjson-3.10.12-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bb52c22bfffe2857e7aa13b4622afd0dd9d16ea7cc65fd2bf318d3223b1b6252"}, + {file = "orjson-3.10.12-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:440d9a337ac8c199ff8251e100c62e9488924c92852362cd27af0e67308c16ef"}, + {file = "orjson-3.10.12-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9e15c06491c69997dfa067369baab3bf094ecb74be9912bdc4339972323f252"}, + {file = "orjson-3.10.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:362d204ad4b0b8724cf370d0cd917bb2dc913c394030da748a3bb632445ce7c4"}, + {file = "orjson-3.10.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2b57cbb4031153db37b41622eac67329c7810e5f480fda4cfd30542186f006ae"}, + {file = "orjson-3.10.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:165c89b53ef03ce0d7c59ca5c82fa65fe13ddf52eeb22e859e58c237d4e33b9b"}, + {file = "orjson-3.10.12-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:5dee91b8dfd54557c1a1596eb90bcd47dbcd26b0baaed919e6861f076583e9da"}, + {file = "orjson-3.10.12-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:77a4e1cfb72de6f905bdff061172adfb3caf7a4578ebf481d8f0530879476c07"}, + {file = "orjson-3.10.12-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:038d42c7bc0606443459b8fe2d1f121db474c49067d8d14c6a075bbea8bf14dd"}, + {file = "orjson-3.10.12-cp311-none-win32.whl", hash = "sha256:03b553c02ab39bed249bedd4abe37b2118324d1674e639b33fab3d1dafdf4d79"}, + {file = "orjson-3.10.12-cp311-none-win_amd64.whl", hash = "sha256:8b8713b9e46a45b2af6b96f559bfb13b1e02006f4242c156cbadef27800a55a8"}, + {file = "orjson-3.10.12-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:53206d72eb656ca5ac7d3a7141e83c5bbd3ac30d5eccfe019409177a57634b0d"}, + {file = "orjson-3.10.12-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac8010afc2150d417ebda810e8df08dd3f544e0dd2acab5370cfa6bcc0662f8f"}, + {file = "orjson-3.10.12-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed459b46012ae950dd2e17150e838ab08215421487371fa79d0eced8d1461d70"}, + {file = "orjson-3.10.12-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8dcb9673f108a93c1b52bfc51b0af422c2d08d4fc710ce9c839faad25020bb69"}, + {file = "orjson-3.10.12-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:22a51ae77680c5c4652ebc63a83d5255ac7d65582891d9424b566fb3b5375ee9"}, + {file = "orjson-3.10.12-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:910fdf2ac0637b9a77d1aad65f803bac414f0b06f720073438a7bd8906298192"}, + {file = "orjson-3.10.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:24ce85f7100160936bc2116c09d1a8492639418633119a2224114f67f63a4559"}, + {file = "orjson-3.10.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8a76ba5fc8dd9c913640292df27bff80a685bed3a3c990d59aa6ce24c352f8fc"}, + {file = "orjson-3.10.12-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ff70ef093895fd53f4055ca75f93f047e088d1430888ca1229393a7c0521100f"}, + {file = "orjson-3.10.12-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:f4244b7018b5753ecd10a6d324ec1f347da130c953a9c88432c7fbc8875d13be"}, + {file = "orjson-3.10.12-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:16135ccca03445f37921fa4b585cff9a58aa8d81ebcb27622e69bfadd220b32c"}, + {file = "orjson-3.10.12-cp312-none-win32.whl", hash = "sha256:2d879c81172d583e34153d524fcba5d4adafbab8349a7b9f16ae511c2cee8708"}, + {file = "orjson-3.10.12-cp312-none-win_amd64.whl", hash = "sha256:fc23f691fa0f5c140576b8c365bc942d577d861a9ee1142e4db468e4e17094fb"}, + {file = "orjson-3.10.12-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:47962841b2a8aa9a258b377f5188db31ba49af47d4003a32f55d6f8b19006543"}, + {file = "orjson-3.10.12-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6334730e2532e77b6054e87ca84f3072bee308a45a452ea0bffbbbc40a67e296"}, + {file = "orjson-3.10.12-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:accfe93f42713c899fdac2747e8d0d5c659592df2792888c6c5f829472e4f85e"}, + {file = "orjson-3.10.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a7974c490c014c48810d1dede6c754c3cc46598da758c25ca3b4001ac45b703f"}, + {file = "orjson-3.10.12-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:3f250ce7727b0b2682f834a3facff88e310f52f07a5dcfd852d99637d386e79e"}, + {file = "orjson-3.10.12-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f31422ff9486ae484f10ffc51b5ab2a60359e92d0716fcce1b3593d7bb8a9af6"}, + {file = "orjson-3.10.12-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5f29c5d282bb2d577c2a6bbde88d8fdcc4919c593f806aac50133f01b733846e"}, + {file = "orjson-3.10.12-cp313-none-win32.whl", hash = "sha256:f45653775f38f63dc0e6cd4f14323984c3149c05d6007b58cb154dd080ddc0dc"}, + {file = "orjson-3.10.12-cp313-none-win_amd64.whl", hash = "sha256:229994d0c376d5bdc91d92b3c9e6be2f1fbabd4cc1b59daae1443a46ee5e9825"}, + {file = "orjson-3.10.12-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:7d69af5b54617a5fac5c8e5ed0859eb798e2ce8913262eb522590239db6c6763"}, + {file = "orjson-3.10.12-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ed119ea7d2953365724a7059231a44830eb6bbb0cfead33fcbc562f5fd8f935"}, + {file = "orjson-3.10.12-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9c5fc1238ef197e7cad5c91415f524aaa51e004be5a9b35a1b8a84ade196f73f"}, + {file = "orjson-3.10.12-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:43509843990439b05f848539d6f6198d4ac86ff01dd024b2f9a795c0daeeab60"}, + {file = "orjson-3.10.12-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f72e27a62041cfb37a3de512247ece9f240a561e6c8662276beaf4d53d406db4"}, + {file = "orjson-3.10.12-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a904f9572092bb6742ab7c16c623f0cdccbad9eeb2d14d4aa06284867bddd31"}, + {file = "orjson-3.10.12-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:855c0833999ed5dc62f64552db26f9be767434917d8348d77bacaab84f787d7b"}, + {file = "orjson-3.10.12-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:897830244e2320f6184699f598df7fb9db9f5087d6f3f03666ae89d607e4f8ed"}, + {file = "orjson-3.10.12-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:0b32652eaa4a7539f6f04abc6243619c56f8530c53bf9b023e1269df5f7816dd"}, + {file = "orjson-3.10.12-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:36b4aa31e0f6a1aeeb6f8377769ca5d125db000f05c20e54163aef1d3fe8e833"}, + {file = "orjson-3.10.12-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:5535163054d6cbf2796f93e4f0dbc800f61914c0e3c4ed8499cf6ece22b4a3da"}, + {file = "orjson-3.10.12-cp38-none-win32.whl", hash = "sha256:90a5551f6f5a5fa07010bf3d0b4ca2de21adafbbc0af6cb700b63cd767266cb9"}, + {file = "orjson-3.10.12-cp38-none-win_amd64.whl", hash = "sha256:703a2fb35a06cdd45adf5d733cf613cbc0cb3ae57643472b16bc22d325b5fb6c"}, + {file = "orjson-3.10.12-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:f29de3ef71a42a5822765def1febfb36e0859d33abf5c2ad240acad5c6a1b78d"}, + {file = "orjson-3.10.12-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:de365a42acc65d74953f05e4772c974dad6c51cfc13c3240899f534d611be967"}, + {file = "orjson-3.10.12-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:91a5a0158648a67ff0004cb0df5df7dcc55bfc9ca154d9c01597a23ad54c8d0c"}, + {file = "orjson-3.10.12-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c47ce6b8d90fe9646a25b6fb52284a14ff215c9595914af63a5933a49972ce36"}, + {file = "orjson-3.10.12-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0eee4c2c5bfb5c1b47a5db80d2ac7aaa7e938956ae88089f098aff2c0f35d5d8"}, + {file = "orjson-3.10.12-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:35d3081bbe8b86587eb5c98a73b97f13d8f9fea685cf91a579beddacc0d10566"}, + {file = "orjson-3.10.12-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:73c23a6e90383884068bc2dba83d5222c9fcc3b99a0ed2411d38150734236755"}, + {file = "orjson-3.10.12-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5472be7dc3269b4b52acba1433dac239215366f89dc1d8d0e64029abac4e714e"}, + {file = "orjson-3.10.12-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:7319cda750fca96ae5973efb31b17d97a5c5225ae0bc79bf5bf84df9e1ec2ab6"}, + {file = "orjson-3.10.12-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:74d5ca5a255bf20b8def6a2b96b1e18ad37b4a122d59b154c458ee9494377f80"}, + {file = "orjson-3.10.12-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:ff31d22ecc5fb85ef62c7d4afe8301d10c558d00dd24274d4bbe464380d3cd69"}, + {file = "orjson-3.10.12-cp39-none-win32.whl", hash = "sha256:c22c3ea6fba91d84fcb4cda30e64aff548fcf0c44c876e681f47d61d24b12e6b"}, + {file = "orjson-3.10.12-cp39-none-win_amd64.whl", hash = "sha256:be604f60d45ace6b0b33dd990a66b4526f1a7a186ac411c942674625456ca548"}, + {file = "orjson-3.10.12.tar.gz", hash = "sha256:0a78bbda3aea0f9f079057ee1ee8a1ecf790d4f1af88dd67493c6b8ee52506ff"}, ] [[package]] @@ -1800,13 +1822,13 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "prometheus-client" -version = "0.21.0" +version = "0.21.1" description = "Python client for the Prometheus monitoring system." optional = false python-versions = ">=3.8" files = [ - {file = "prometheus_client-0.21.0-py3-none-any.whl", hash = "sha256:4fa6b4dd0ac16d58bb587c04b1caae65b8c5043e85f778f42f5f632f6af2e166"}, - {file = "prometheus_client-0.21.0.tar.gz", hash = "sha256:96c83c606b71ff2b0a433c98889d275f51ffec6c5e267de37c7a2b5c9aa9233e"}, + {file = "prometheus_client-0.21.1-py3-none-any.whl", hash = "sha256:594b45c410d6f4f8888940fe80b5cc2521b305a1fafe1c58609ef715a001f301"}, + {file = "prometheus_client-0.21.1.tar.gz", hash = "sha256:252505a722ac04b0456be05c05f75f45d760c2911ffc45f2a06bcaed9f3ae3fb"}, ] [package.extras] @@ -1894,13 +1916,13 @@ files = [ [[package]] name = "pydantic" -version = "2.10.1" +version = "2.10.3" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic-2.10.1-py3-none-any.whl", hash = "sha256:a8d20db84de64cf4a7d59e899c2caf0fe9d660c7cfc482528e7020d7dd189a7e"}, - {file = "pydantic-2.10.1.tar.gz", hash = "sha256:a4daca2dc0aa429555e0656d6bf94873a7dc5f54ee42b1f5873d666fb3f35560"}, + {file = "pydantic-2.10.3-py3-none-any.whl", hash = "sha256:be04d85bbc7b65651c5f8e6b9976ed9c6f41782a55524cef079a34a0bb82144d"}, + {file = "pydantic-2.10.3.tar.gz", hash = "sha256:cb5ac360ce894ceacd69c403187900a02c4b20b693a9dd1d643e1effab9eadf9"}, ] [package.dependencies] @@ -2054,13 +2076,13 @@ diagrams = ["jinja2", "railroad-diagrams"] [[package]] name = "pytest" -version = "8.3.3" +version = "8.3.4" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2"}, - {file = "pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181"}, + {file = "pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6"}, + {file = "pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761"}, ] [package.dependencies] @@ -2154,15 +2176,21 @@ six = ">=1.5" [[package]] name = "python-json-logger" -version = "2.0.7" -description = "A python library adding a json log formatter" +version = "3.2.0" +description = "JSON Log Formatter for the Python Logging Package" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "python-json-logger-2.0.7.tar.gz", hash = "sha256:23e7ec02d34237c5aa1e29a070193a4ea87583bb4e7f8fd06d3de8264c4b2e1c"}, - {file = "python_json_logger-2.0.7-py3-none-any.whl", hash = "sha256:f380b826a991ebbe3de4d897aeec42760035ac760345e57b812938dc8b35e2bd"}, + {file = "python_json_logger-3.2.0-py3-none-any.whl", hash = "sha256:d73522ddcfc6d0461394120feaddea9025dc64bf804d96357dd42fa878cc5fe8"}, + {file = "python_json_logger-3.2.0.tar.gz", hash = "sha256:2c11056458d3f56614480b24e9cb28f7aba69cbfbebddbb77c92f0ec0d4947ab"}, ] +[package.dependencies] +typing_extensions = {version = "*", markers = "python_version < \"3.10\""} + +[package.extras] +dev = ["backports.zoneinfo", "black", "build", "freezegun", "mdx_truly_sane_lists", "mike", "mkdocs", "mkdocs-awesome-pages-plugin", "mkdocs-gen-files", "mkdocs-literate-nav", "mkdocs-material (>=8.5)", "mkdocstrings[python]", "msgspec", "msgspec-python313-pre", "mypy", "orjson", "pylint", "pytest", "tzdata", "validate-pyproject[all]"] + [[package]] name = "pywin32" version = "308" @@ -2484,101 +2512,114 @@ files = [ [[package]] name = "rpds-py" -version = "0.21.0" +version = "0.22.3" description = "Python bindings to Rust's persistent data structures (rpds)" optional = false python-versions = ">=3.9" files = [ - {file = "rpds_py-0.21.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:a017f813f24b9df929674d0332a374d40d7f0162b326562daae8066b502d0590"}, - {file = "rpds_py-0.21.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:20cc1ed0bcc86d8e1a7e968cce15be45178fd16e2ff656a243145e0b439bd250"}, - {file = "rpds_py-0.21.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad116dda078d0bc4886cb7840e19811562acdc7a8e296ea6ec37e70326c1b41c"}, - {file = "rpds_py-0.21.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:808f1ac7cf3b44f81c9475475ceb221f982ef548e44e024ad5f9e7060649540e"}, - {file = "rpds_py-0.21.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de552f4a1916e520f2703ec474d2b4d3f86d41f353e7680b597512ffe7eac5d0"}, - {file = "rpds_py-0.21.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:efec946f331349dfc4ae9d0e034c263ddde19414fe5128580f512619abed05f1"}, - {file = "rpds_py-0.21.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b80b4690bbff51a034bfde9c9f6bf9357f0a8c61f548942b80f7b66356508bf5"}, - {file = "rpds_py-0.21.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:085ed25baac88953d4283e5b5bd094b155075bb40d07c29c4f073e10623f9f2e"}, - {file = "rpds_py-0.21.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:daa8efac2a1273eed2354397a51216ae1e198ecbce9036fba4e7610b308b6153"}, - {file = "rpds_py-0.21.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:95a5bad1ac8a5c77b4e658671642e4af3707f095d2b78a1fdd08af0dfb647624"}, - {file = "rpds_py-0.21.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3e53861b29a13d5b70116ea4230b5f0f3547b2c222c5daa090eb7c9c82d7f664"}, - {file = "rpds_py-0.21.0-cp310-none-win32.whl", hash = "sha256:ea3a6ac4d74820c98fcc9da4a57847ad2cc36475a8bd9683f32ab6d47a2bd682"}, - {file = "rpds_py-0.21.0-cp310-none-win_amd64.whl", hash = "sha256:b8f107395f2f1d151181880b69a2869c69e87ec079c49c0016ab96860b6acbe5"}, - {file = "rpds_py-0.21.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:5555db3e618a77034954b9dc547eae94166391a98eb867905ec8fcbce1308d95"}, - {file = "rpds_py-0.21.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:97ef67d9bbc3e15584c2f3c74bcf064af36336c10d2e21a2131e123ce0f924c9"}, - {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ab2c2a26d2f69cdf833174f4d9d86118edc781ad9a8fa13970b527bf8236027"}, - {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4e8921a259f54bfbc755c5bbd60c82bb2339ae0324163f32868f63f0ebb873d9"}, - {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8a7ff941004d74d55a47f916afc38494bd1cfd4b53c482b77c03147c91ac0ac3"}, - {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5145282a7cd2ac16ea0dc46b82167754d5e103a05614b724457cffe614f25bd8"}, - {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de609a6f1b682f70bb7163da745ee815d8f230d97276db049ab447767466a09d"}, - {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:40c91c6e34cf016fa8e6b59d75e3dbe354830777fcfd74c58b279dceb7975b75"}, - {file = "rpds_py-0.21.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d2132377f9deef0c4db89e65e8bb28644ff75a18df5293e132a8d67748397b9f"}, - {file = "rpds_py-0.21.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:0a9e0759e7be10109645a9fddaaad0619d58c9bf30a3f248a2ea57a7c417173a"}, - {file = "rpds_py-0.21.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9e20da3957bdf7824afdd4b6eeb29510e83e026473e04952dca565170cd1ecc8"}, - {file = "rpds_py-0.21.0-cp311-none-win32.whl", hash = "sha256:f71009b0d5e94c0e86533c0b27ed7cacc1239cb51c178fd239c3cfefefb0400a"}, - {file = "rpds_py-0.21.0-cp311-none-win_amd64.whl", hash = "sha256:e168afe6bf6ab7ab46c8c375606298784ecbe3ba31c0980b7dcbb9631dcba97e"}, - {file = "rpds_py-0.21.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:30b912c965b2aa76ba5168fd610087bad7fcde47f0a8367ee8f1876086ee6d1d"}, - {file = "rpds_py-0.21.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ca9989d5d9b1b300bc18e1801c67b9f6d2c66b8fd9621b36072ed1df2c977f72"}, - {file = "rpds_py-0.21.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f54e7106f0001244a5f4cf810ba8d3f9c542e2730821b16e969d6887b664266"}, - {file = "rpds_py-0.21.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fed5dfefdf384d6fe975cc026886aece4f292feaf69d0eeb716cfd3c5a4dd8be"}, - {file = "rpds_py-0.21.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:590ef88db231c9c1eece44dcfefd7515d8bf0d986d64d0caf06a81998a9e8cab"}, - {file = "rpds_py-0.21.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f983e4c2f603c95dde63df633eec42955508eefd8d0f0e6d236d31a044c882d7"}, - {file = "rpds_py-0.21.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b229ce052ddf1a01c67d68166c19cb004fb3612424921b81c46e7ea7ccf7c3bf"}, - {file = "rpds_py-0.21.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ebf64e281a06c904a7636781d2e973d1f0926a5b8b480ac658dc0f556e7779f4"}, - {file = "rpds_py-0.21.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:998a8080c4495e4f72132f3d66ff91f5997d799e86cec6ee05342f8f3cda7dca"}, - {file = "rpds_py-0.21.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:98486337f7b4f3c324ab402e83453e25bb844f44418c066623db88e4c56b7c7b"}, - {file = "rpds_py-0.21.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a78d8b634c9df7f8d175451cfeac3810a702ccb85f98ec95797fa98b942cea11"}, - {file = "rpds_py-0.21.0-cp312-none-win32.whl", hash = "sha256:a58ce66847711c4aa2ecfcfaff04cb0327f907fead8945ffc47d9407f41ff952"}, - {file = "rpds_py-0.21.0-cp312-none-win_amd64.whl", hash = "sha256:e860f065cc4ea6f256d6f411aba4b1251255366e48e972f8a347cf88077b24fd"}, - {file = "rpds_py-0.21.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:ee4eafd77cc98d355a0d02f263efc0d3ae3ce4a7c24740010a8b4012bbb24937"}, - {file = "rpds_py-0.21.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:688c93b77e468d72579351a84b95f976bd7b3e84aa6686be6497045ba84be560"}, - {file = "rpds_py-0.21.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c38dbf31c57032667dd5a2f0568ccde66e868e8f78d5a0d27dcc56d70f3fcd3b"}, - {file = "rpds_py-0.21.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2d6129137f43f7fa02d41542ffff4871d4aefa724a5fe38e2c31a4e0fd343fb0"}, - {file = "rpds_py-0.21.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:520ed8b99b0bf86a176271f6fe23024323862ac674b1ce5b02a72bfeff3fff44"}, - {file = "rpds_py-0.21.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aaeb25ccfb9b9014a10eaf70904ebf3f79faaa8e60e99e19eef9f478651b9b74"}, - {file = "rpds_py-0.21.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af04ac89c738e0f0f1b913918024c3eab6e3ace989518ea838807177d38a2e94"}, - {file = "rpds_py-0.21.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b9b76e2afd585803c53c5b29e992ecd183f68285b62fe2668383a18e74abe7a3"}, - {file = "rpds_py-0.21.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5afb5efde74c54724e1a01118c6e5c15e54e642c42a1ba588ab1f03544ac8c7a"}, - {file = "rpds_py-0.21.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:52c041802a6efa625ea18027a0723676a778869481d16803481ef6cc02ea8cb3"}, - {file = "rpds_py-0.21.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ee1e4fc267b437bb89990b2f2abf6c25765b89b72dd4a11e21934df449e0c976"}, - {file = "rpds_py-0.21.0-cp313-none-win32.whl", hash = "sha256:0c025820b78817db6a76413fff6866790786c38f95ea3f3d3c93dbb73b632202"}, - {file = "rpds_py-0.21.0-cp313-none-win_amd64.whl", hash = "sha256:320c808df533695326610a1b6a0a6e98f033e49de55d7dc36a13c8a30cfa756e"}, - {file = "rpds_py-0.21.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:2c51d99c30091f72a3c5d126fad26236c3f75716b8b5e5cf8effb18889ced928"}, - {file = "rpds_py-0.21.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cbd7504a10b0955ea287114f003b7ad62330c9e65ba012c6223dba646f6ffd05"}, - {file = "rpds_py-0.21.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6dcc4949be728ede49e6244eabd04064336012b37f5c2200e8ec8eb2988b209c"}, - {file = "rpds_py-0.21.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f414da5c51bf350e4b7960644617c130140423882305f7574b6cf65a3081cecb"}, - {file = "rpds_py-0.21.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9afe42102b40007f588666bc7de82451e10c6788f6f70984629db193849dced1"}, - {file = "rpds_py-0.21.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b929c2bb6e29ab31f12a1117c39f7e6d6450419ab7464a4ea9b0b417174f044"}, - {file = "rpds_py-0.21.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8404b3717da03cbf773a1d275d01fec84ea007754ed380f63dfc24fb76ce4592"}, - {file = "rpds_py-0.21.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e12bb09678f38b7597b8346983d2323a6482dcd59e423d9448108c1be37cac9d"}, - {file = "rpds_py-0.21.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:58a0e345be4b18e6b8501d3b0aa540dad90caeed814c515e5206bb2ec26736fd"}, - {file = "rpds_py-0.21.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:c3761f62fcfccf0864cc4665b6e7c3f0c626f0380b41b8bd1ce322103fa3ef87"}, - {file = "rpds_py-0.21.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c2b2f71c6ad6c2e4fc9ed9401080badd1469fa9889657ec3abea42a3d6b2e1ed"}, - {file = "rpds_py-0.21.0-cp39-none-win32.whl", hash = "sha256:b21747f79f360e790525e6f6438c7569ddbfb1b3197b9e65043f25c3c9b489d8"}, - {file = "rpds_py-0.21.0-cp39-none-win_amd64.whl", hash = "sha256:0626238a43152918f9e72ede9a3b6ccc9e299adc8ade0d67c5e142d564c9a83d"}, - {file = "rpds_py-0.21.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:6b4ef7725386dc0762857097f6b7266a6cdd62bfd209664da6712cb26acef035"}, - {file = "rpds_py-0.21.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:6bc0e697d4d79ab1aacbf20ee5f0df80359ecf55db33ff41481cf3e24f206919"}, - {file = "rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da52d62a96e61c1c444f3998c434e8b263c384f6d68aca8274d2e08d1906325c"}, - {file = "rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:98e4fe5db40db87ce1c65031463a760ec7906ab230ad2249b4572c2fc3ef1f9f"}, - {file = "rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:30bdc973f10d28e0337f71d202ff29345320f8bc49a31c90e6c257e1ccef4333"}, - {file = "rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:faa5e8496c530f9c71f2b4e1c49758b06e5f4055e17144906245c99fa6d45356"}, - {file = "rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32eb88c30b6a4f0605508023b7141d043a79b14acb3b969aa0b4f99b25bc7d4a"}, - {file = "rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a89a8ce9e4e75aeb7fa5d8ad0f3fecdee813802592f4f46a15754dcb2fd6b061"}, - {file = "rpds_py-0.21.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:241e6c125568493f553c3d0fdbb38c74babf54b45cef86439d4cd97ff8feb34d"}, - {file = "rpds_py-0.21.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:3b766a9f57663396e4f34f5140b3595b233a7b146e94777b97a8413a1da1be18"}, - {file = "rpds_py-0.21.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:af4a644bf890f56e41e74be7d34e9511e4954894d544ec6b8efe1e21a1a8da6c"}, - {file = "rpds_py-0.21.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:3e30a69a706e8ea20444b98a49f386c17b26f860aa9245329bab0851ed100677"}, - {file = "rpds_py-0.21.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:031819f906bb146561af051c7cef4ba2003d28cff07efacef59da973ff7969ba"}, - {file = "rpds_py-0.21.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:b876f2bc27ab5954e2fd88890c071bd0ed18b9c50f6ec3de3c50a5ece612f7a6"}, - {file = "rpds_py-0.21.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc5695c321e518d9f03b7ea6abb5ea3af4567766f9852ad1560f501b17588c7b"}, - {file = "rpds_py-0.21.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b4de1da871b5c0fd5537b26a6fc6814c3cc05cabe0c941db6e9044ffbb12f04a"}, - {file = "rpds_py-0.21.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:878f6fea96621fda5303a2867887686d7a198d9e0f8a40be100a63f5d60c88c9"}, - {file = "rpds_py-0.21.0-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8eeec67590e94189f434c6d11c426892e396ae59e4801d17a93ac96b8c02a6c"}, - {file = "rpds_py-0.21.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ff2eba7f6c0cb523d7e9cff0903f2fe1feff8f0b2ceb6bd71c0e20a4dcee271"}, - {file = "rpds_py-0.21.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a429b99337062877d7875e4ff1a51fe788424d522bd64a8c0a20ef3021fdb6ed"}, - {file = "rpds_py-0.21.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:d167e4dbbdac48bd58893c7e446684ad5d425b407f9336e04ab52e8b9194e2ed"}, - {file = "rpds_py-0.21.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:4eb2de8a147ffe0626bfdc275fc6563aa7bf4b6db59cf0d44f0ccd6ca625a24e"}, - {file = "rpds_py-0.21.0-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:e78868e98f34f34a88e23ee9ccaeeec460e4eaf6db16d51d7a9b883e5e785a5e"}, - {file = "rpds_py-0.21.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:4991ca61656e3160cdaca4851151fd3f4a92e9eba5c7a530ab030d6aee96ec89"}, - {file = "rpds_py-0.21.0.tar.gz", hash = "sha256:ed6378c9d66d0de903763e7706383d60c33829581f0adff47b6535f1802fa6db"}, + {file = "rpds_py-0.22.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:6c7b99ca52c2c1752b544e310101b98a659b720b21db00e65edca34483259967"}, + {file = "rpds_py-0.22.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:be2eb3f2495ba669d2a985f9b426c1797b7d48d6963899276d22f23e33d47e37"}, + {file = "rpds_py-0.22.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70eb60b3ae9245ddea20f8a4190bd79c705a22f8028aaf8bbdebe4716c3fab24"}, + {file = "rpds_py-0.22.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4041711832360a9b75cfb11b25a6a97c8fb49c07b8bd43d0d02b45d0b499a4ff"}, + {file = "rpds_py-0.22.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:64607d4cbf1b7e3c3c8a14948b99345eda0e161b852e122c6bb71aab6d1d798c"}, + {file = "rpds_py-0.22.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e69b0a0e2537f26d73b4e43ad7bc8c8efb39621639b4434b76a3de50c6966e"}, + {file = "rpds_py-0.22.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc27863442d388870c1809a87507727b799c8460573cfbb6dc0eeaef5a11b5ec"}, + {file = "rpds_py-0.22.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e79dd39f1e8c3504be0607e5fc6e86bb60fe3584bec8b782578c3b0fde8d932c"}, + {file = "rpds_py-0.22.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e0fa2d4ec53dc51cf7d3bb22e0aa0143966119f42a0c3e4998293a3dd2856b09"}, + {file = "rpds_py-0.22.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fda7cb070f442bf80b642cd56483b5548e43d366fe3f39b98e67cce780cded00"}, + {file = "rpds_py-0.22.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cff63a0272fcd259dcc3be1657b07c929c466b067ceb1c20060e8d10af56f5bf"}, + {file = "rpds_py-0.22.3-cp310-cp310-win32.whl", hash = "sha256:9bd7228827ec7bb817089e2eb301d907c0d9827a9e558f22f762bb690b131652"}, + {file = "rpds_py-0.22.3-cp310-cp310-win_amd64.whl", hash = "sha256:9beeb01d8c190d7581a4d59522cd3d4b6887040dcfc744af99aa59fef3e041a8"}, + {file = "rpds_py-0.22.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d20cfb4e099748ea39e6f7b16c91ab057989712d31761d3300d43134e26e165f"}, + {file = "rpds_py-0.22.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:68049202f67380ff9aa52f12e92b1c30115f32e6895cd7198fa2a7961621fc5a"}, + {file = "rpds_py-0.22.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb4f868f712b2dd4bcc538b0a0c1f63a2b1d584c925e69a224d759e7070a12d5"}, + {file = "rpds_py-0.22.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bc51abd01f08117283c5ebf64844a35144a0843ff7b2983e0648e4d3d9f10dbb"}, + {file = "rpds_py-0.22.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0f3cec041684de9a4684b1572fe28c7267410e02450f4561700ca5a3bc6695a2"}, + {file = "rpds_py-0.22.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7ef9d9da710be50ff6809fed8f1963fecdfecc8b86656cadfca3bc24289414b0"}, + {file = "rpds_py-0.22.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59f4a79c19232a5774aee369a0c296712ad0e77f24e62cad53160312b1c1eaa1"}, + {file = "rpds_py-0.22.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a60bce91f81ddaac922a40bbb571a12c1070cb20ebd6d49c48e0b101d87300d"}, + {file = "rpds_py-0.22.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e89391e6d60251560f0a8f4bd32137b077a80d9b7dbe6d5cab1cd80d2746f648"}, + {file = "rpds_py-0.22.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e3fb866d9932a3d7d0c82da76d816996d1667c44891bd861a0f97ba27e84fc74"}, + {file = "rpds_py-0.22.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1352ae4f7c717ae8cba93421a63373e582d19d55d2ee2cbb184344c82d2ae55a"}, + {file = "rpds_py-0.22.3-cp311-cp311-win32.whl", hash = "sha256:b0b4136a252cadfa1adb705bb81524eee47d9f6aab4f2ee4fa1e9d3cd4581f64"}, + {file = "rpds_py-0.22.3-cp311-cp311-win_amd64.whl", hash = "sha256:8bd7c8cfc0b8247c8799080fbff54e0b9619e17cdfeb0478ba7295d43f635d7c"}, + {file = "rpds_py-0.22.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:27e98004595899949bd7a7b34e91fa7c44d7a97c40fcaf1d874168bb652ec67e"}, + {file = "rpds_py-0.22.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1978d0021e943aae58b9b0b196fb4895a25cc53d3956b8e35e0b7682eefb6d56"}, + {file = "rpds_py-0.22.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:655ca44a831ecb238d124e0402d98f6212ac527a0ba6c55ca26f616604e60a45"}, + {file = "rpds_py-0.22.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:feea821ee2a9273771bae61194004ee2fc33f8ec7db08117ef9147d4bbcbca8e"}, + {file = "rpds_py-0.22.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:22bebe05a9ffc70ebfa127efbc429bc26ec9e9b4ee4d15a740033efda515cf3d"}, + {file = "rpds_py-0.22.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3af6e48651c4e0d2d166dc1b033b7042ea3f871504b6805ba5f4fe31581d8d38"}, + {file = "rpds_py-0.22.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e67ba3c290821343c192f7eae1d8fd5999ca2dc99994114643e2f2d3e6138b15"}, + {file = "rpds_py-0.22.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:02fbb9c288ae08bcb34fb41d516d5eeb0455ac35b5512d03181d755d80810059"}, + {file = "rpds_py-0.22.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f56a6b404f74ab372da986d240e2e002769a7d7102cc73eb238a4f72eec5284e"}, + {file = "rpds_py-0.22.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0a0461200769ab3b9ab7e513f6013b7a97fdeee41c29b9db343f3c5a8e2b9e61"}, + {file = "rpds_py-0.22.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8633e471c6207a039eff6aa116e35f69f3156b3989ea3e2d755f7bc41754a4a7"}, + {file = "rpds_py-0.22.3-cp312-cp312-win32.whl", hash = "sha256:593eba61ba0c3baae5bc9be2f5232430453fb4432048de28399ca7376de9c627"}, + {file = "rpds_py-0.22.3-cp312-cp312-win_amd64.whl", hash = "sha256:d115bffdd417c6d806ea9069237a4ae02f513b778e3789a359bc5856e0404cc4"}, + {file = "rpds_py-0.22.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:ea7433ce7e4bfc3a85654aeb6747babe3f66eaf9a1d0c1e7a4435bbdf27fea84"}, + {file = "rpds_py-0.22.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6dd9412824c4ce1aca56c47b0991e65bebb7ac3f4edccfd3f156150c96a7bf25"}, + {file = "rpds_py-0.22.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20070c65396f7373f5df4005862fa162db5d25d56150bddd0b3e8214e8ef45b4"}, + {file = "rpds_py-0.22.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0b09865a9abc0ddff4e50b5ef65467cd94176bf1e0004184eb915cbc10fc05c5"}, + {file = "rpds_py-0.22.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3453e8d41fe5f17d1f8e9c383a7473cd46a63661628ec58e07777c2fff7196dc"}, + {file = "rpds_py-0.22.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f5d36399a1b96e1a5fdc91e0522544580dbebeb1f77f27b2b0ab25559e103b8b"}, + {file = "rpds_py-0.22.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:009de23c9c9ee54bf11303a966edf4d9087cd43a6003672e6aa7def643d06518"}, + {file = "rpds_py-0.22.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1aef18820ef3e4587ebe8b3bc9ba6e55892a6d7b93bac6d29d9f631a3b4befbd"}, + {file = "rpds_py-0.22.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f60bd8423be1d9d833f230fdbccf8f57af322d96bcad6599e5a771b151398eb2"}, + {file = "rpds_py-0.22.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:62d9cfcf4948683a18a9aff0ab7e1474d407b7bab2ca03116109f8464698ab16"}, + {file = "rpds_py-0.22.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9253fc214112405f0afa7db88739294295f0e08466987f1d70e29930262b4c8f"}, + {file = "rpds_py-0.22.3-cp313-cp313-win32.whl", hash = "sha256:fb0ba113b4983beac1a2eb16faffd76cb41e176bf58c4afe3e14b9c681f702de"}, + {file = "rpds_py-0.22.3-cp313-cp313-win_amd64.whl", hash = "sha256:c58e2339def52ef6b71b8f36d13c3688ea23fa093353f3a4fee2556e62086ec9"}, + {file = "rpds_py-0.22.3-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:f82a116a1d03628a8ace4859556fb39fd1424c933341a08ea3ed6de1edb0283b"}, + {file = "rpds_py-0.22.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3dfcbc95bd7992b16f3f7ba05af8a64ca694331bd24f9157b49dadeeb287493b"}, + {file = "rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59259dc58e57b10e7e18ce02c311804c10c5a793e6568f8af4dead03264584d1"}, + {file = "rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5725dd9cc02068996d4438d397e255dcb1df776b7ceea3b9cb972bdb11260a83"}, + {file = "rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99b37292234e61325e7a5bb9689e55e48c3f5f603af88b1642666277a81f1fbd"}, + {file = "rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:27b1d3b3915a99208fee9ab092b8184c420f2905b7d7feb4aeb5e4a9c509b8a1"}, + {file = "rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f612463ac081803f243ff13cccc648578e2279295048f2a8d5eb430af2bae6e3"}, + {file = "rpds_py-0.22.3-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f73d3fef726b3243a811121de45193c0ca75f6407fe66f3f4e183c983573e130"}, + {file = "rpds_py-0.22.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:3f21f0495edea7fdbaaa87e633a8689cd285f8f4af5c869f27bc8074638ad69c"}, + {file = "rpds_py-0.22.3-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:1e9663daaf7a63ceccbbb8e3808fe90415b0757e2abddbfc2e06c857bf8c5e2b"}, + {file = "rpds_py-0.22.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a76e42402542b1fae59798fab64432b2d015ab9d0c8c47ba7addddbaf7952333"}, + {file = "rpds_py-0.22.3-cp313-cp313t-win32.whl", hash = "sha256:69803198097467ee7282750acb507fba35ca22cc3b85f16cf45fb01cb9097730"}, + {file = "rpds_py-0.22.3-cp313-cp313t-win_amd64.whl", hash = "sha256:f5cf2a0c2bdadf3791b5c205d55a37a54025c6e18a71c71f82bb536cf9a454bf"}, + {file = "rpds_py-0.22.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:378753b4a4de2a7b34063d6f95ae81bfa7b15f2c1a04a9518e8644e81807ebea"}, + {file = "rpds_py-0.22.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3445e07bf2e8ecfeef6ef67ac83de670358abf2996916039b16a218e3d95e97e"}, + {file = "rpds_py-0.22.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b2513ba235829860b13faa931f3b6846548021846ac808455301c23a101689d"}, + {file = "rpds_py-0.22.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eaf16ae9ae519a0e237a0f528fd9f0197b9bb70f40263ee57ae53c2b8d48aeb3"}, + {file = "rpds_py-0.22.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:583f6a1993ca3369e0f80ba99d796d8e6b1a3a2a442dd4e1a79e652116413091"}, + {file = "rpds_py-0.22.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4617e1915a539a0d9a9567795023de41a87106522ff83fbfaf1f6baf8e85437e"}, + {file = "rpds_py-0.22.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c150c7a61ed4a4f4955a96626574e9baf1adf772c2fb61ef6a5027e52803543"}, + {file = "rpds_py-0.22.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2fa4331c200c2521512595253f5bb70858b90f750d39b8cbfd67465f8d1b596d"}, + {file = "rpds_py-0.22.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:214b7a953d73b5e87f0ebece4a32a5bd83c60a3ecc9d4ec8f1dca968a2d91e99"}, + {file = "rpds_py-0.22.3-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:f47ad3d5f3258bd7058d2d506852217865afefe6153a36eb4b6928758041d831"}, + {file = "rpds_py-0.22.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:f276b245347e6e36526cbd4a266a417796fc531ddf391e43574cf6466c492520"}, + {file = "rpds_py-0.22.3-cp39-cp39-win32.whl", hash = "sha256:bbb232860e3d03d544bc03ac57855cd82ddf19c7a07651a7c0fdb95e9efea8b9"}, + {file = "rpds_py-0.22.3-cp39-cp39-win_amd64.whl", hash = "sha256:cfbc454a2880389dbb9b5b398e50d439e2e58669160f27b60e5eca11f68ae17c"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:d48424e39c2611ee1b84ad0f44fb3b2b53d473e65de061e3f460fc0be5f1939d"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:24e8abb5878e250f2eb0d7859a8e561846f98910326d06c0d51381fed59357bd"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b232061ca880db21fa14defe219840ad9b74b6158adb52ddf0e87bead9e8493"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac0a03221cdb5058ce0167ecc92a8c89e8d0decdc9e99a2ec23380793c4dcb96"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eb0c341fa71df5a4595f9501df4ac5abfb5a09580081dffbd1ddd4654e6e9123"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bf9db5488121b596dbfc6718c76092fda77b703c1f7533a226a5a9f65248f8ad"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b8db6b5b2d4491ad5b6bdc2bc7c017eec108acbf4e6785f42a9eb0ba234f4c9"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b3d504047aba448d70cf6fa22e06cb09f7cbd761939fdd47604f5e007675c24e"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:e61b02c3f7a1e0b75e20c3978f7135fd13cb6cf551bf4a6d29b999a88830a338"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:e35ba67d65d49080e8e5a1dd40101fccdd9798adb9b050ff670b7d74fa41c566"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:26fd7cac7dd51011a245f29a2cc6489c4608b5a8ce8d75661bb4a1066c52dfbe"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:177c7c0fce2855833819c98e43c262007f42ce86651ffbb84f37883308cb0e7d"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:bb47271f60660803ad11f4c61b42242b8c1312a31c98c578f79ef9387bbde21c"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:70fb28128acbfd264eda9bf47015537ba3fe86e40d046eb2963d75024be4d055"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:44d61b4b7d0c2c9ac019c314e52d7cbda0ae31078aabd0f22e583af3e0d79723"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f0e260eaf54380380ac3808aa4ebe2d8ca28b9087cf411649f96bad6900c728"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b25bc607423935079e05619d7de556c91fb6adeae9d5f80868dde3468657994b"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fb6116dfb8d1925cbdb52595560584db42a7f664617a1f7d7f6e32f138cdf37d"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a63cbdd98acef6570c62b92a1e43266f9e8b21e699c363c0fef13bd530799c11"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2b8f60e1b739a74bab7e01fcbe3dddd4657ec685caa04681df9d562ef15b625f"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:2e8b55d8517a2fda8d95cb45d62a5a8bbf9dd0ad39c5b25c8833efea07b880ca"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:2de29005e11637e7a2361fa151f780ff8eb2543a0da1413bb951e9f14b699ef3"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:666ecce376999bf619756a24ce15bb14c5bfaf04bf00abc7e663ce17c3f34fe7"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:5246b14ca64a8675e0a7161f7af68fe3e910e6b90542b4bfb5439ba752191df6"}, + {file = "rpds_py-0.22.3.tar.gz", hash = "sha256:e32fee8ab45d3c2db6da19a5323bc3362237c8b653c70194414b892fd06a080d"}, ] [[package]] @@ -2642,13 +2683,13 @@ testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs ( [[package]] name = "six" -version = "1.16.0" +version = "1.17.0" description = "Python 2 and 3 compatibility utilities" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" files = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, + {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, + {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, ] [[package]] @@ -2694,13 +2735,13 @@ tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"] [[package]] name = "syrupy" -version = "4.7.2" +version = "4.8.0" description = "Pytest Snapshot Test Utility" optional = false python-versions = ">=3.8.1" files = [ - {file = "syrupy-4.7.2-py3-none-any.whl", hash = "sha256:eae7ba6be5aed190237caa93be288e97ca1eec5ca58760e4818972a10c4acc64"}, - {file = "syrupy-4.7.2.tar.gz", hash = "sha256:ea45e099f242de1bb53018c238f408a5bb6c82007bc687aefcbeaa0e1c2e935a"}, + {file = "syrupy-4.8.0-py3-none-any.whl", hash = "sha256:544f4ec6306f4b1c460fdab48fd60b2c7fe54a6c0a8243aeea15f9ad9c638c3f"}, + {file = "syrupy-4.8.0.tar.gz", hash = "sha256:648f0e9303aaa8387c8365d7314784c09a6bab0a407455c6a01d6a4f5c6a8ede"}, ] [package.dependencies] @@ -2762,33 +2803,63 @@ test = ["pytest", "ruff"] [[package]] name = "tomli" -version = "2.1.0" +version = "2.2.1" description = "A lil' TOML parser" optional = false python-versions = ">=3.8" files = [ - {file = "tomli-2.1.0-py3-none-any.whl", hash = "sha256:a5c57c3d1c56f5ccdf89f6523458f60ef716e210fc47c4cfb188c5ba473e0391"}, - {file = "tomli-2.1.0.tar.gz", hash = "sha256:3f646cae2aec94e17d04973e4249548320197cfabdf130015d023de4b74d8ab8"}, + {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, + {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"}, + {file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"}, + {file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"}, + {file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"}, + {file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"}, + {file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"}, + {file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"}, + {file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"}, + {file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"}, + {file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"}, + {file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"}, + {file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"}, + {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"}, ] [[package]] name = "tornado" -version = "6.4.1" +version = "6.4.2" description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." optional = false python-versions = ">=3.8" files = [ - {file = "tornado-6.4.1-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:163b0aafc8e23d8cdc3c9dfb24c5368af84a81e3364745ccb4427669bf84aec8"}, - {file = "tornado-6.4.1-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:6d5ce3437e18a2b66fbadb183c1d3364fb03f2be71299e7d10dbeeb69f4b2a14"}, - {file = "tornado-6.4.1-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2e20b9113cd7293f164dc46fffb13535266e713cdb87bd2d15ddb336e96cfc4"}, - {file = "tornado-6.4.1-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ae50a504a740365267b2a8d1a90c9fbc86b780a39170feca9bcc1787ff80842"}, - {file = "tornado-6.4.1-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:613bf4ddf5c7a95509218b149b555621497a6cc0d46ac341b30bd9ec19eac7f3"}, - {file = "tornado-6.4.1-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:25486eb223babe3eed4b8aecbac33b37e3dd6d776bc730ca14e1bf93888b979f"}, - {file = "tornado-6.4.1-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:454db8a7ecfcf2ff6042dde58404164d969b6f5d58b926da15e6b23817950fc4"}, - {file = "tornado-6.4.1-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a02a08cc7a9314b006f653ce40483b9b3c12cda222d6a46d4ac63bb6c9057698"}, - {file = "tornado-6.4.1-cp38-abi3-win32.whl", hash = "sha256:d9a566c40b89757c9aa8e6f032bcdb8ca8795d7c1a9762910c722b1635c9de4d"}, - {file = "tornado-6.4.1-cp38-abi3-win_amd64.whl", hash = "sha256:b24b8982ed444378d7f21d563f4180a2de31ced9d8d84443907a0a64da2072e7"}, - {file = "tornado-6.4.1.tar.gz", hash = "sha256:92d3ab53183d8c50f8204a51e6f91d18a15d5ef261e84d452800d4ff6fc504e9"}, + {file = "tornado-6.4.2-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e828cce1123e9e44ae2a50a9de3055497ab1d0aeb440c5ac23064d9e44880da1"}, + {file = "tornado-6.4.2-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:072ce12ada169c5b00b7d92a99ba089447ccc993ea2143c9ede887e0937aa803"}, + {file = "tornado-6.4.2-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a017d239bd1bb0919f72af256a970624241f070496635784d9bf0db640d3fec"}, + {file = "tornado-6.4.2-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c36e62ce8f63409301537222faffcef7dfc5284f27eec227389f2ad11b09d946"}, + {file = "tornado-6.4.2-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bca9eb02196e789c9cb5c3c7c0f04fb447dc2adffd95265b2c7223a8a615ccbf"}, + {file = "tornado-6.4.2-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:304463bd0772442ff4d0f5149c6f1c2135a1fae045adf070821c6cdc76980634"}, + {file = "tornado-6.4.2-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:c82c46813ba483a385ab2a99caeaedf92585a1f90defb5693351fa7e4ea0bf73"}, + {file = "tornado-6.4.2-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:932d195ca9015956fa502c6b56af9eb06106140d844a335590c1ec7f5277d10c"}, + {file = "tornado-6.4.2-cp38-abi3-win32.whl", hash = "sha256:2876cef82e6c5978fde1e0d5b1f919d756968d5b4282418f3146b79b58556482"}, + {file = "tornado-6.4.2-cp38-abi3-win_amd64.whl", hash = "sha256:908b71bf3ff37d81073356a5fadcc660eb10c1476ee6e2725588626ce7e5ca38"}, + {file = "tornado-6.4.2.tar.gz", hash = "sha256:92bad5b4746e9879fd7bf1eb21dce4e3fc5128d71601f80005afa39237ad620b"}, ] [[package]] @@ -2833,13 +2904,13 @@ files = [ [[package]] name = "types-python-dateutil" -version = "2.9.0.20241003" +version = "2.9.0.20241206" description = "Typing stubs for python-dateutil" optional = false python-versions = ">=3.8" files = [ - {file = "types-python-dateutil-2.9.0.20241003.tar.gz", hash = "sha256:58cb85449b2a56d6684e41aeefb4c4280631246a0da1a719bdbe6f3fb0317446"}, - {file = "types_python_dateutil-2.9.0.20241003-py3-none-any.whl", hash = "sha256:250e1d8e80e7bbc3a6c99b907762711d1a1cdd00e978ad39cb5940f6f0a87f3d"}, + {file = "types_python_dateutil-2.9.0.20241206-py3-none-any.whl", hash = "sha256:e248a4bc70a486d3e3ec84d0dc30eec3a5f979d6e7ee4123ae043eedbb987f53"}, + {file = "types_python_dateutil-2.9.0.20241206.tar.gz", hash = "sha256:18f493414c26ffba692a72369fea7a154c502646301ebfe3d56a04b3767284cb"}, ] [[package]] @@ -3033,4 +3104,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = ">=3.9,<4.0" -content-hash = "02bfef4d884d17f83dde79a4076a3d07f94a1b3dbf2414f07379dab64a78e183" +content-hash = "5accfdfd412486fbf7bb3ef18f00e75db40599034428651ef014b0bc3927ddfa" diff --git a/libs/core/pyproject.toml b/libs/core/pyproject.toml index 8ba3b07011ce5..c72f30980589c 100644 --- a/libs/core/pyproject.toml +++ b/libs/core/pyproject.toml @@ -1,10 +1,10 @@ [build-system] -requires = ["poetry-core>=1.0.0"] +requires = [ "poetry-core>=1.0.0",] build-backend = "poetry.core.masonry.api" [tool.poetry] name = "langchain-core" -version = "0.3.21" +version = "0.3.25" description = "Building applications with LLMs through composability" authors = [] license = "MIT" @@ -12,16 +12,10 @@ readme = "README.md" repository = "https://github.com/langchain-ai/langchain" [tool.mypy] -exclude = [ - "notebooks", - "examples", - "example_data", - "langchain_core/pydantic", - "tests/unit_tests/utils/test_function_calling.py", -] +exclude = [ "notebooks", "examples", "example_data", "langchain_core/pydantic", "tests/unit_tests/utils/test_function_calling.py",] disallow_untyped_defs = "True" [[tool.mypy.overrides]] -module = ["numpy", "pytest"] +module = [ "numpy", "pytest",] ignore_missing_imports = true [tool.ruff] @@ -33,7 +27,7 @@ target-version = "py39" [tool.poetry.dependencies] python = ">=3.9,<4.0" -langsmith = "^0.1.125" +langsmith = ">=0.1.125,<0.3" tenacity = ">=8.1.0,!=8.4.0,<10.0.0" jsonpatch = "^1.33" PyYAML = ">=5.3" @@ -50,53 +44,17 @@ python = ">=3.12.4" [tool.poetry.extras] [tool.ruff.lint] -select = [ - "ASYNC", - "B", - "C4", - "COM", - "DJ", - "E", - "EM", - "EXE", - "F", - "FLY", - "FURB", - "I", - "ICN", - "INT", - "LOG", - "N", - "NPY", - "PD", - "PIE", - "Q", - "RSE", - "S", - "SIM", - "SLOT", - "T10", - "T201", - "TID", - "UP", - "W", - "YTT", -] -ignore = ["COM812", "UP007", "W293", "S101", "S110", "S112"] +select = [ "ASYNC", "B", "C4", "COM", "DJ", "E", "EM", "EXE", "F", "FLY", "FURB", "I", "ICN", "INT", "LOG", "N", "NPY", "PD", "PIE", "Q", "RSE", "S", "SIM", "SLOT", "T10", "T201", "TID", "UP", "W", "YTT",] +ignore = [ "COM812", "UP007", "W293", "S101", "S110", "S112",] [tool.coverage.run] -omit = ["tests/*"] +omit = [ "tests/*",] [tool.pytest.ini_options] addopts = "--snapshot-warn-unused --strict-markers --strict-config --durations=5" -markers = [ - "requires: mark tests as requiring a specific library", - "compile: mark placeholder test used to compile integration tests without running them", -] +markers = [ "requires: mark tests as requiring a specific library", "compile: mark placeholder test used to compile integration tests without running them",] asyncio_mode = "auto" -filterwarnings = [ - "ignore::langchain_core._api.beta_decorator.LangChainBetaWarning", -] +filterwarnings = [ "ignore::langchain_core._api.beta_decorator.LangChainBetaWarning",] [tool.poetry.group.lint] optional = true @@ -114,19 +72,14 @@ optional = true optional = true [tool.ruff.lint.pep8-naming] -classmethod-decorators = [ - "classmethod", - "langchain_core.utils.pydantic.pre_init", - "pydantic.field_validator", - "pydantic.v1.root_validator", -] +classmethod-decorators = [ "classmethod", "langchain_core.utils.pydantic.pre_init", "pydantic.field_validator", "pydantic.v1.root_validator",] [tool.ruff.lint.per-file-ignores] -"tests/unit_tests/prompts/test_chat.py" = ["E501"] -"tests/unit_tests/runnables/test_runnable.py" = ["E501"] -"tests/unit_tests/runnables/test_graph.py" = ["E501"] -"tests/**" = ["S"] -"scripts/**" = ["S"] +"tests/unit_tests/prompts/test_chat.py" = [ "E501",] +"tests/unit_tests/runnables/test_runnable.py" = [ "E501",] +"tests/unit_tests/runnables/test_graph.py" = [ "E501",] +"tests/**" = [ "S",] +"scripts/**" = [ "S",] [tool.poetry.group.lint.dependencies] ruff = "^0.5" diff --git a/libs/core/tests/unit_tests/_api/test_path.py b/libs/core/tests/unit_tests/_api/test_path.py index 89428c7cfcb85..93896aab1158e 100644 --- a/libs/core/tests/unit_tests/_api/test_path.py +++ b/libs/core/tests/unit_tests/_api/test_path.py @@ -10,7 +10,11 @@ def test_as_import_path() -> None: """Test that the path is converted to a LangChain import path.""" # Verify that default paths are correct - assert path.PACKAGE_DIR == ROOT / "langchain_core" + + # if editable install, check directory structure + if path.PACKAGE_DIR == ROOT / "langchain_core": + assert path.PACKAGE_DIR == ROOT / "langchain_core" + # Verify that as import path works correctly assert path.as_import_path(HERE, relative_to=ROOT) == "tests.unit_tests._api" assert ( diff --git a/libs/core/tests/unit_tests/indexing/test_indexing.py b/libs/core/tests/unit_tests/indexing/test_indexing.py index 287b6b49f66a4..fbe59013e8355 100644 --- a/libs/core/tests/unit_tests/indexing/test_indexing.py +++ b/libs/core/tests/unit_tests/indexing/test_indexing.py @@ -13,7 +13,7 @@ from langchain_core.documents import Document from langchain_core.embeddings import DeterministicFakeEmbedding from langchain_core.indexing import InMemoryRecordManager, aindex, index -from langchain_core.indexing.api import _abatch, _HashedDocument +from langchain_core.indexing.api import IndexingException, _abatch, _HashedDocument from langchain_core.indexing.in_memory import InMemoryDocumentIndex from langchain_core.vectorstores import InMemoryVectorStore, VectorStore @@ -287,6 +287,164 @@ async def test_aindex_simple_delete_full( } +def test_index_delete_full_recovery_after_deletion_failure( + record_manager: InMemoryRecordManager, vector_store: InMemoryVectorStore +) -> None: + """Indexing some content to confirm it gets added only once.""" + loader = ToyLoader( + documents=[ + Document( + page_content="This is a test document.", + ), + Document( + page_content="This is another document.", + ), + ] + ) + + with patch.object( + record_manager, "get_time", return_value=datetime(2021, 1, 1).timestamp() + ): + assert index(loader, record_manager, vector_store, cleanup="full") == { + "num_added": 2, + "num_deleted": 0, + "num_skipped": 0, + "num_updated": 0, + } + + loader = ToyLoader( + documents=[ + Document( + page_content="mutated document 1", + ), + Document( + page_content="This is another document.", # <-- Same as original + ), + ] + ) + + with ( + patch.object( + record_manager, "get_time", return_value=datetime(2021, 1, 2).timestamp() + ), + patch.object(vector_store, "delete", return_value=False), + pytest.raises(IndexingException), + ): + indexing_result = index(loader, record_manager, vector_store, cleanup="full") + + # At this point, there should be 3 records in both the record manager + # and the vector store + doc_texts = { + # Ignoring type since doc should be in the store and not a None + vector_store.get_by_ids([uid])[0].page_content # type: ignore + for uid in vector_store.store + } + assert doc_texts == { + "This is a test document.", + "mutated document 1", + "This is another document.", + } + + with patch.object( + record_manager, "get_time", return_value=datetime(2021, 1, 3).timestamp() + ): + indexing_result = index(loader, record_manager, vector_store, cleanup="full") + doc_texts = { + # Ignoring type since doc should be in the store and not a None + vector_store.get_by_ids([uid])[0].page_content # type: ignore + for uid in vector_store.store + } + assert doc_texts == {"mutated document 1", "This is another document."} + + assert indexing_result == { + "num_added": 0, + "num_deleted": 1, + "num_skipped": 2, + "num_updated": 0, + } + + +async def test_aindex_delete_full_recovery_after_deletion_failure( + arecord_manager: InMemoryRecordManager, vector_store: InMemoryVectorStore +) -> None: + """Indexing some content to confirm it gets added only once.""" + loader = ToyLoader( + documents=[ + Document( + page_content="This is a test document.", + ), + Document( + page_content="This is another document.", + ), + ] + ) + + with patch.object( + arecord_manager, "get_time", return_value=datetime(2021, 1, 1).timestamp() + ): + assert await aindex(loader, arecord_manager, vector_store, cleanup="full") == { + "num_added": 2, + "num_deleted": 0, + "num_skipped": 0, + "num_updated": 0, + } + + loader = ToyLoader( + documents=[ + Document( + page_content="mutated document 1", + ), + Document( + page_content="This is another document.", # <-- Same as original + ), + ] + ) + + with ( + patch.object( + arecord_manager, "get_time", return_value=datetime(2021, 1, 2).timestamp() + ), + patch.object(vector_store, "adelete", return_value=False), + pytest.raises(IndexingException), + ): + indexing_result = await aindex( + loader, arecord_manager, vector_store, cleanup="full" + ) + + # At this point, there should be 3 records in both the record manager + # and the vector store + doc_texts = { + # Ignoring type since doc should be in the store and not a None + vector_store.get_by_ids([uid])[0].page_content # type: ignore + for uid in vector_store.store + } + assert doc_texts == { + "This is a test document.", + "mutated document 1", + "This is another document.", + } + + with patch.object( + arecord_manager, "get_time", return_value=datetime(2021, 1, 3).timestamp() + ): + indexing_result = await aindex( + loader, arecord_manager, vector_store, cleanup="full" + ) + doc_texts = { + # Ignoring type since doc should be in the store and not a None + vector_store.get_by_ids([uid])[0].page_content # type: ignore + for uid in vector_store.store + } + assert doc_texts == {"mutated document 1", "This is another document."} + + assert indexing_result == { + "num_added": 0, + "num_deleted": 1, + "num_skipped": 2, + "num_updated": 0, + } + + def test_incremental_fails_with_bad_source_ids( record_manager: InMemoryRecordManager, vector_store: InMemoryVectorStore ) -> None: @@ -364,6 +522,306 @@ async def test_aincremental_fails_with_bad_source_ids( ) +def test_index_simple_delete_scoped_full( + record_manager: InMemoryRecordManager, vector_store: InMemoryVectorStore +) -> None: + """Test Indexing with scoped_full strategy.""" + loader = ToyLoader( + documents=[ + Document( + page_content="This is a test document.", + metadata={"source": "1"}, + ), + Document( + page_content="This is another document.", + metadata={"source": "1"}, + ), + Document( + page_content="This is yet another document.", + metadata={"source": "1"}, + ), + Document( + page_content="This is a test document from another source.", + metadata={"source": "2"}, + ), + ] + ) + + with patch.object( + record_manager, "get_time", return_value=datetime(2021, 1, 1).timestamp() + ): + assert index( + loader, + record_manager, + vector_store, + cleanup="scoped_full", + source_id_key="source", + ) == { + "num_added": 4, + "num_deleted": 0, + "num_skipped": 0, + "num_updated": 0, + } + + with patch.object( + record_manager, "get_time", return_value=datetime(2021, 1, 2).timestamp() + ): + assert index( + loader, + record_manager, + vector_store, + cleanup="scoped_full", + source_id_key="source", + ) == { + "num_added": 0, + "num_deleted": 0, + "num_skipped": 4, + "num_updated": 0, + } + + loader = ToyLoader( + documents=[ + Document( + page_content="mutated document 1", + metadata={"source": "1"}, + ), + Document( + page_content="This is another document.", # <-- Same as original + metadata={"source": "1"}, + ), + ] + ) + + with patch.object( + record_manager, "get_time", return_value=datetime(2021, 1, 3).timestamp() + ): + assert index( + loader, + record_manager, + vector_store, + cleanup="scoped_full", + source_id_key="source", + ) == { + "num_added": 1, + "num_deleted": 2, + "num_skipped": 1, + "num_updated": 0, + } + doc_texts = { + # Ignoring type since doc should be in the store and not a None + vector_store.get_by_ids([uid])[0].page_content # type: ignore + for uid in vector_store.store + } + assert doc_texts == { + "mutated document 1", + "This is another document.", + "This is a test document from another source.", + } + + # Attempt to index again verify that nothing changes + with patch.object( + record_manager, "get_time", return_value=datetime(2021, 1, 4).timestamp() + ): + assert index( + loader, + record_manager, + vector_store, + cleanup="scoped_full", + source_id_key="source", + ) == { + "num_added": 0, + "num_deleted": 0, + "num_skipped": 2, + "num_updated": 0, + } + + +async def test_aindex_simple_delete_scoped_full( + arecord_manager: InMemoryRecordManager, vector_store: InMemoryVectorStore +) -> None: + """Test Indexing with scoped_full strategy.""" + loader = ToyLoader( + documents=[ + Document( + page_content="This is a test document.", + metadata={"source": "1"}, + ), + Document( + page_content="This is another document.", + metadata={"source": "1"}, + ), + Document( + page_content="This is yet another document.", + metadata={"source": "1"}, + ), + Document( + page_content="This is a test document from another source.", + metadata={"source": "2"}, + ), + ] + ) + + with patch.object( + arecord_manager, "get_time", return_value=datetime(2021, 1, 1).timestamp() + ): + assert await aindex( + loader, + arecord_manager, + vector_store, + cleanup="scoped_full", + source_id_key="source", + ) == { + "num_added": 4, + "num_deleted": 0, + "num_skipped": 0, + "num_updated": 0, + } + + with patch.object( + arecord_manager, "get_time", return_value=datetime(2021, 1, 2).timestamp() + ): + assert await aindex( + loader, + arecord_manager, + vector_store, + cleanup="scoped_full", + source_id_key="source", + ) == { + "num_added": 0, + "num_deleted": 0, + "num_skipped": 4, + "num_updated": 0, + } + + loader = ToyLoader( + documents=[ + Document( + page_content="mutated document 1", + metadata={"source": "1"}, + ), + Document( + page_content="This is another document.", # <-- Same as original + metadata={"source": "1"}, + ), + ] + ) + + with patch.object( + arecord_manager, "get_time", return_value=datetime(2021, 1, 3).timestamp() + ): + assert await aindex( + loader, + arecord_manager, + vector_store, + cleanup="scoped_full", + source_id_key="source", + ) == { + "num_added": 1, + "num_deleted": 2, + "num_skipped": 1, + "num_updated": 0, + } + doc_texts = { + # Ignoring type since doc should be in the store and not a None + vector_store.get_by_ids([uid])[0].page_content # type: ignore + for uid in vector_store.store + } + assert doc_texts == { + "mutated document 1", + "This is another document.", + "This is a test document from another source.", + } + + # Attempt to index again verify that nothing changes + with patch.object( + arecord_manager, "get_time", return_value=datetime(2021, 1, 4).timestamp() + ): + assert await aindex( + loader, + arecord_manager, + vector_store, + cleanup="scoped_full", + source_id_key="source", + ) == { + "num_added": 0, + "num_deleted": 0, + "num_skipped": 2, + "num_updated": 0, + } + + +def test_scoped_full_fails_with_bad_source_ids( + record_manager: InMemoryRecordManager, vector_store: InMemoryVectorStore +) -> None: + """Test Indexing with scoped_full strategy.""" + loader = ToyLoader( + documents=[ + Document( + page_content="This is a test document.", + metadata={"source": "1"}, + ), + Document( + page_content="This is another document.", + metadata={"source": "2"}, + ), + Document( + page_content="This is yet another document.", + metadata={"source": None}, + ), + ] + ) + + with pytest.raises(ValueError): + # Should raise an error because no source id function was specified + index(loader, record_manager, vector_store, cleanup="scoped_full") + + with pytest.raises(ValueError): + # Should raise an error because no source id function was specified + index( + loader, + record_manager, + vector_store, + cleanup="scoped_full", + source_id_key="source", + ) + + +async def test_ascoped_full_fails_with_bad_source_ids( + arecord_manager: InMemoryRecordManager, vector_store: InMemoryVectorStore +) -> None: + """Test Indexing with scoped_full strategy.""" + loader = ToyLoader( + documents=[ + Document( + page_content="This is a test document.", + metadata={"source": "1"}, + ), + Document( + page_content="This is another document.", + metadata={"source": "2"}, + ), + Document( + page_content="This is yet another document.", + metadata={"source": None}, + ), + ] + ) + + with pytest.raises(ValueError): + # Should raise an error because no source id function was specified + await aindex(loader, arecord_manager, vector_store, cleanup="scoped_full") + + with pytest.raises(ValueError): + # Should raise an error because no source id function was specified + await aindex( + loader, + arecord_manager, + vector_store, + cleanup="scoped_full", + source_id_key="source", + ) + + def test_no_delete( record_manager: InMemoryRecordManager, vector_store: InMemoryVectorStore ) -> None: @@ -544,7 +1002,7 @@ def test_incremental_delete( ) with patch.object( - record_manager, "get_time", return_value=datetime(2021, 1, 2).timestamp() + record_manager, "get_time", return_value=datetime(2021, 1, 1).timestamp() ): assert index( loader, @@ -630,26 +1088,18 @@ def test_incremental_delete( } -def test_incremental_indexing_with_batch_size( +def test_incremental_delete_with_same_source( record_manager: InMemoryRecordManager, vector_store: InMemoryVectorStore ) -> None: - """Test indexing with incremental indexing""" + """Test indexing with incremental deletion strategy.""" loader = ToyLoader( documents=[ Document( - page_content="1", - metadata={"source": "1"}, - ), - Document( - page_content="2", - metadata={"source": "1"}, - ), - Document( - page_content="3", + page_content="This is a test document.", metadata={"source": "1"}, ), Document( - page_content="4", + page_content="This is another document.", metadata={"source": "1"}, ), ] @@ -664,9 +1114,8 @@ def test_incremental_indexing_with_batch_size( vector_store, cleanup="incremental", source_id_key="source", - batch_size=2, ) == { - "num_added": 4, + "num_added": 2, "num_deleted": 0, "num_skipped": 0, "num_updated": 0, @@ -677,7 +1126,17 @@ def test_incremental_indexing_with_batch_size( vector_store.get_by_ids([uid])[0].page_content # type: ignore for uid in vector_store.store } - assert doc_texts == {"1", "2", "3", "4"} + assert doc_texts == {"This is another document.", "This is a test document."} + + # Delete 1 document and unchange 1 document + loader = ToyLoader( + documents=[ + Document( + page_content="This is another document.", # <-- Same as original + metadata={"source": "1"}, + ), + ] + ) with patch.object( record_manager, "get_time", return_value=datetime(2021, 1, 2).timestamp() @@ -688,11 +1147,10 @@ def test_incremental_indexing_with_batch_size( vector_store, cleanup="incremental", source_id_key="source", - batch_size=2, ) == { "num_added": 0, - "num_deleted": 0, - "num_skipped": 4, + "num_deleted": 1, + "num_skipped": 1, "num_updated": 0, } @@ -701,40 +1159,41 @@ def test_incremental_indexing_with_batch_size( vector_store.get_by_ids([uid])[0].page_content # type: ignore for uid in vector_store.store } - assert doc_texts == {"1", "2", "3", "4"} + assert doc_texts == { + "This is another document.", + } -def test_incremental_indexing_with_batch_size_with_optimization( +def test_incremental_indexing_with_batch_size( record_manager: InMemoryRecordManager, vector_store: InMemoryVectorStore ) -> None: - """Test case when batch_size < num of docs. - - Here, we'll verify that an indexing optimization works as expected. - """ - documents = [ - Document( - page_content="1", - metadata={"source": "1"}, - ), - Document( - page_content="2", - metadata={"source": "1"}, - ), - Document( - page_content="3", - metadata={"source": "1"}, - ), - Document( - page_content="4", - metadata={"source": "1"}, - ), - ] + """Test indexing with incremental indexing""" + loader = ToyLoader( + documents=[ + Document( + page_content="1", + metadata={"source": "1"}, + ), + Document( + page_content="2", + metadata={"source": "1"}, + ), + Document( + page_content="3", + metadata={"source": "1"}, + ), + Document( + page_content="4", + metadata={"source": "1"}, + ), + ] + ) with patch.object( record_manager, "get_time", return_value=datetime(2021, 1, 1).timestamp() ): assert index( - documents, + loader, record_manager, vector_store, cleanup="incremental", @@ -754,88 +1213,20 @@ def test_incremental_indexing_with_batch_size_with_optimization( } assert doc_texts == {"1", "2", "3", "4"} - # Mutate content in first batch - doc_first_batch_mutation = [ - Document( - page_content="updated 1", - metadata={"source": "1"}, - ), - Document( - page_content="2", - metadata={"source": "1"}, - ), - Document( - page_content="3", - metadata={"source": "1"}, - ), - Document( - page_content="4", - metadata={"source": "1"}, - ), - ] - with patch.object( record_manager, "get_time", return_value=datetime(2021, 1, 2).timestamp() ): assert index( - doc_first_batch_mutation, - record_manager, - vector_store, - cleanup="incremental", - source_id_key="source", - batch_size=2, - ) == { - # Cannot optimize here since the first batch was changed - # So only skpping indexing the document with content "2" - "num_added": 3, - "num_deleted": 3, - "num_skipped": 1, - "num_updated": 0, - } - - doc_texts = { - # Ignoring type since doc should be in the store and not a None - vector_store.get_by_ids([uid])[0].page_content # type: ignore - for uid in vector_store.store - } - assert doc_texts == {"updated 1", "2", "3", "4"} - - # Mutate content in second batch - doc_second_batch_mutation = [ - Document( - page_content="updated 1", # <-- This was already previously updated - metadata={"source": "1"}, - ), - Document( - page_content="2", - metadata={"source": "1"}, - ), - Document( - page_content="3", - metadata={"source": "1"}, - ), - Document( - page_content="updated 4", - metadata={"source": "1"}, - ), - ] - - with patch.object( - record_manager, "get_time", return_value=datetime(2021, 1, 3).timestamp() - ): - assert index( - doc_second_batch_mutation, + loader, record_manager, vector_store, cleanup="incremental", source_id_key="source", batch_size=2, ) == { - # Skips updating content from the first batch, only updates - # the `updated 4` content - "num_added": 1, - "num_deleted": 1, - "num_skipped": 3, + "num_added": 2, + "num_deleted": 2, + "num_skipped": 2, "num_updated": 0, } @@ -844,7 +1235,7 @@ def test_incremental_indexing_with_batch_size_with_optimization( vector_store.get_by_ids([uid])[0].page_content # type: ignore for uid in vector_store.store } - assert doc_texts == {"updated 1", "2", "3", "updated 4"} + assert doc_texts == {"1", "2", "3", "4"} def test_incremental_delete_with_batch_size( @@ -1588,149 +1979,6 @@ async def test_aindex_into_document_index( } -async def test_incremental_aindexing_with_batch_size_with_optimization( - arecord_manager: InMemoryRecordManager, vector_store: InMemoryVectorStore -) -> None: - """Test case when batch_size < num of docs. - - Here, we'll verify that an indexing optimization works as expected. - """ - documents = [ - Document( - page_content="1", - metadata={"source": "1"}, - ), - Document( - page_content="2", - metadata={"source": "1"}, - ), - Document( - page_content="3", - metadata={"source": "1"}, - ), - Document( - page_content="4", - metadata={"source": "1"}, - ), - ] - - with patch.object( - arecord_manager, "get_time", return_value=datetime(2021, 1, 1).timestamp() - ): - assert await aindex( - documents, - arecord_manager, - vector_store, - cleanup="incremental", - source_id_key="source", - batch_size=2, - ) == { - "num_added": 4, - "num_deleted": 0, - "num_skipped": 0, - "num_updated": 0, - } - - doc_texts = { - # Ignoring type since doc should be in the store and not a None - vector_store.get_by_ids([uid])[0].page_content # type: ignore - for uid in vector_store.store - } - assert doc_texts == {"1", "2", "3", "4"} - - # Mutate content in first batch - doc_first_batch_mutation = [ - Document( - page_content="updated 1", - metadata={"source": "1"}, - ), - Document( - page_content="2", - metadata={"source": "1"}, - ), - Document( - page_content="3", - metadata={"source": "1"}, - ), - Document( - page_content="4", - metadata={"source": "1"}, - ), - ] - - with patch.object( - arecord_manager, "get_time", return_value=datetime(2021, 1, 2).timestamp() - ): - assert await aindex( - doc_first_batch_mutation, - arecord_manager, - vector_store, - cleanup="incremental", - source_id_key="source", - batch_size=2, - ) == { - # Cannot optimize here since the first batch was changed - # So only skpping indexing the document with content "2" - "num_added": 3, - "num_deleted": 3, - "num_skipped": 1, - "num_updated": 0, - } - - doc_texts = { - # Ignoring type since doc should be in the store and not a None - vector_store.get_by_ids([uid])[0].page_content # type: ignore - for uid in vector_store.store - } - assert doc_texts == {"updated 1", "2", "3", "4"} - - # Mutate content in second batch - doc_second_batch_mutation = [ - Document( - page_content="updated 1", # <-- This was already previously updated - metadata={"source": "1"}, - ), - Document( - page_content="2", - metadata={"source": "1"}, - ), - Document( - page_content="3", - metadata={"source": "1"}, - ), - Document( - page_content="updated 4", - metadata={"source": "1"}, - ), - ] - - with patch.object( - arecord_manager, "get_time", return_value=datetime(2021, 1, 3).timestamp() - ): - assert await aindex( - doc_second_batch_mutation, - arecord_manager, - vector_store, - cleanup="incremental", - source_id_key="source", - batch_size=2, - ) == { - # Skips updating content from the first batch, only updates - # the `updated 4` content - "num_added": 1, - "num_deleted": 1, - "num_skipped": 3, - "num_updated": 0, - } - - doc_texts = { - # Ignoring type since doc should be in the store and not a None - vector_store.get_by_ids([uid])[0].page_content # type: ignore - for uid in vector_store.store - } - assert doc_texts == {"updated 1", "2", "3", "updated 4"} - - def test_index_with_upsert_kwargs( record_manager: InMemoryRecordManager, upserting_vector_store: InMemoryVectorStore ) -> None: diff --git a/libs/core/tests/unit_tests/runnables/test_graph.py b/libs/core/tests/unit_tests/runnables/test_graph.py index 789875db62362..e98a0a18a9c5c 100644 --- a/libs/core/tests/unit_tests/runnables/test_graph.py +++ b/libs/core/tests/unit_tests/runnables/test_graph.py @@ -69,6 +69,26 @@ class Schema(BaseModel): assert graph.last_node() is end +def test_trim_multi_edge() -> None: + class Scheme(BaseModel): + a: str + + graph = Graph() + start = graph.add_node(Scheme, id="__start__") + a = graph.add_node(Scheme, id="a") + last = graph.add_node(Scheme, id="__end__") + + graph.add_edge(start, a) + graph.add_edge(a, last) + graph.add_edge(start, last) + + graph.trim_first_node() # should not remove __start__ since it has 2 outgoing edges + assert graph.first_node() is start + + graph.trim_last_node() # should not remove the __end__ node since it has 2 incoming edges + assert graph.last_node() is last + + def test_graph_sequence(snapshot: SnapshotAssertion) -> None: fake_llm = FakeListLLM(responses=["a"]) prompt = PromptTemplate.from_template("Hello, {name}!") diff --git a/libs/core/tests/unit_tests/runnables/test_runnable.py b/libs/core/tests/unit_tests/runnables/test_runnable.py index 828f4cafcabf9..b06cb381e80e6 100644 --- a/libs/core/tests/unit_tests/runnables/test_runnable.py +++ b/libs/core/tests/unit_tests/runnables/test_runnable.py @@ -4600,18 +4600,18 @@ async def af(x: int) -> int: assert repr(RunnableLambda(func=f, afunc=af)) == "RunnableLambda(f)" assert repr( - RunnableLambda(lambda x: x + 2) + RunnableLambda(lambda x: x * 2) | { "a": RunnableLambda(lambda x: x * 2), "b": RunnableLambda(lambda x: x * 3), } ) == ( - "RunnableLambda(...)\n" + "RunnableLambda(lambda x: x * 2)\n" "| {\n" " a: RunnableLambda(...),\n" " b: RunnableLambda(...)\n" " }" - ), "repr where code string contains multiple lambdas gives up" + ) async def test_tool_from_runnable() -> None: diff --git a/libs/core/tests/unit_tests/test_retrievers.py b/libs/core/tests/unit_tests/test_retrievers.py new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/libs/core/tests/unit_tests/test_tools.py b/libs/core/tests/unit_tests/test_tools.py index ce7ea4894bb5a..164ecc508e76e 100644 --- a/libs/core/tests/unit_tests/test_tools.py +++ b/libs/core/tests/unit_tests/test_tools.py @@ -31,6 +31,7 @@ CallbackManagerForToolRun, ) from langchain_core.messages import ToolMessage +from langchain_core.messages.tool import ToolOutputMixin from langchain_core.runnables import ( Runnable, RunnableConfig, @@ -46,6 +47,7 @@ ) from langchain_core.tools.base import ( InjectedToolArg, + InjectedToolCallId, SchemaAnnotationError, _is_message_content_block, _is_message_content_type, @@ -856,6 +858,7 @@ class _RaiseNonValidationErrorTool(BaseTool): def _parse_input( self, tool_input: Union[str, dict], + tool_call_id: Optional[str], ) -> Union[str, dict[str, Any]]: raise NotImplementedError @@ -920,6 +923,7 @@ class _RaiseNonValidationErrorTool(BaseTool): def _parse_input( self, tool_input: Union[str, dict], + tool_call_id: Optional[str], ) -> Union[str, dict[str, Any]]: raise NotImplementedError @@ -2110,3 +2114,63 @@ def injected_tool(x: int, foo: Annotated[Foo, InjectedToolArg]) -> str: return foo.value assert injected_tool.invoke({"x": 5, "foo": Foo()}) == "bar" # type: ignore + + +def test_tool_injected_tool_call_id() -> None: + @tool + def foo(x: int, tool_call_id: Annotated[str, InjectedToolCallId]) -> ToolMessage: + """foo""" + return ToolMessage(x, tool_call_id=tool_call_id) # type: ignore + + assert foo.invoke( + {"type": "tool_call", "args": {"x": 0}, "name": "foo", "id": "bar"} + ) == ToolMessage(0, tool_call_id="bar") # type: ignore + + with pytest.raises(ValueError): + assert foo.invoke({"x": 0}) + + @tool + def foo2(x: int, tool_call_id: Annotated[str, InjectedToolCallId()]) -> ToolMessage: + """foo""" + return ToolMessage(x, tool_call_id=tool_call_id) # type: ignore + + assert foo2.invoke( + {"type": "tool_call", "args": {"x": 0}, "name": "foo", "id": "bar"} + ) == ToolMessage(0, tool_call_id="bar") # type: ignore + + +def test_tool_uninjected_tool_call_id() -> None: + @tool + def foo(x: int, tool_call_id: str) -> ToolMessage: + """foo""" + return ToolMessage(x, tool_call_id=tool_call_id) # type: ignore + + with pytest.raises(ValueError): + foo.invoke({"type": "tool_call", "args": {"x": 0}, "name": "foo", "id": "bar"}) + + assert foo.invoke( + { + "type": "tool_call", + "args": {"x": 0, "tool_call_id": "zap"}, + "name": "foo", + "id": "bar", + } + ) == ToolMessage(0, tool_call_id="zap") # type: ignore + + +def test_tool_return_output_mixin() -> None: + class Bar(ToolOutputMixin): + def __init__(self, x: int) -> None: + self.x = x + + def __eq__(self, other: Any) -> bool: + return isinstance(other, self.__class__) and self.x == other.x + + @tool + def foo(x: int) -> Bar: + """Foo.""" + return Bar(x=x) + + assert foo.invoke( + {"type": "tool_call", "args": {"x": 0}, "name": "foo", "id": "bar"} + ) == Bar(x=0) diff --git a/libs/core/tests/unit_tests/utils/test_function_calling.py b/libs/core/tests/unit_tests/utils/test_function_calling.py index ba4c50187f139..bf1a4f56337fe 100644 --- a/libs/core/tests/unit_tests/utils/test_function_calling.py +++ b/libs/core/tests/unit_tests/utils/test_function_calling.py @@ -71,6 +71,19 @@ def dummy_function(arg1: int, arg2: Literal["bar", "baz"]) -> None: return dummy_function +@pytest.fixture() +def function_docstring_annotations() -> Callable: + def dummy_function(arg1: int, arg2: Literal["bar", "baz"]) -> None: + """dummy function + + Args: + arg1 (int): foo + arg2: one of 'bar', 'baz' + """ + + return dummy_function + + @pytest.fixture() def runnable() -> Runnable: class Args(ExtensionsTypedDict): @@ -278,6 +291,7 @@ def dummy_function(cls, arg1: int, arg2: Literal["bar", "baz"]) -> None: def test_convert_to_openai_function( pydantic: type[BaseModel], function: Callable, + function_docstring_annotations: Callable, dummy_structured_tool: StructuredTool, dummy_tool: BaseTool, json_schema: dict, @@ -311,6 +325,7 @@ def test_convert_to_openai_function( for fn in ( pydantic, function, + function_docstring_annotations, dummy_structured_tool, dummy_tool, json_schema, diff --git a/libs/core/tests/unit_tests/vectorstores/test_in_memory.py b/libs/core/tests/unit_tests/vectorstores/test_in_memory.py index 34764f99c1016..e76f843616be8 100644 --- a/libs/core/tests/unit_tests/vectorstores/test_in_memory.py +++ b/libs/core/tests/unit_tests/vectorstores/test_in_memory.py @@ -2,10 +2,7 @@ from unittest.mock import AsyncMock, Mock import pytest -from langchain_tests.integration_tests.vectorstores import ( - AsyncReadWriteTestSuite, - ReadWriteTestSuite, -) +from langchain_tests.integration_tests.vectorstores import VectorStoreIntegrationTests from langchain_core.documents import Document from langchain_core.embeddings.fake import DeterministicFakeEmbedding @@ -13,18 +10,12 @@ from tests.unit_tests.stubs import _any_id_document -class TestInMemoryReadWriteTestSuite(ReadWriteTestSuite): +class TestInMemoryStandard(VectorStoreIntegrationTests): @pytest.fixture def vectorstore(self) -> InMemoryVectorStore: return InMemoryVectorStore(embedding=self.get_embeddings()) -class TestAsyncInMemoryReadWriteTestSuite(AsyncReadWriteTestSuite): - @pytest.fixture - async def vectorstore(self) -> InMemoryVectorStore: - return InMemoryVectorStore(embedding=self.get_embeddings()) - - async def test_inmemory_similarity_search() -> None: """Test end to end similarity search.""" store = await InMemoryVectorStore.afrom_texts( diff --git a/libs/langchain/langchain/chains/router/multi_prompt.py b/libs/langchain/langchain/chains/router/multi_prompt.py index 214b9a2b37208..0531cdb834db3 100644 --- a/libs/langchain/langchain/chains/router/multi_prompt.py +++ b/libs/langchain/langchain/chains/router/multi_prompt.py @@ -20,9 +20,8 @@ since="0.2.12", removal="1.0", message=( - "Use RunnableLambda to select from multiple prompt templates. See example " - "in API reference: " - "https://api.python.langchain.com/en/latest/chains/langchain.chains.router.multi_prompt.MultiPromptChain.html" # noqa: E501 + "Please see migration guide here for recommended implementation: " + "https://python.langchain.com/docs/versions/migrating_chains/multi_prompt_chain/" # noqa: E501 ), ) class MultiPromptChain(MultiRouteChain): @@ -37,60 +36,109 @@ class MultiPromptChain(MultiRouteChain): from operator import itemgetter from typing import Literal - from typing_extensions import TypedDict from langchain_core.output_parsers import StrOutputParser from langchain_core.prompts import ChatPromptTemplate - from langchain_core.runnables import RunnableLambda, RunnablePassthrough + from langchain_core.runnables import RunnableConfig from langchain_openai import ChatOpenAI + from langgraph.graph import END, START, StateGraph + from typing_extensions import TypedDict llm = ChatOpenAI(model="gpt-4o-mini") + # Define the prompts we will route to prompt_1 = ChatPromptTemplate.from_messages( [ ("system", "You are an expert on animals."), - ("human", "{query}"), + ("human", "{input}"), ] ) prompt_2 = ChatPromptTemplate.from_messages( [ ("system", "You are an expert on vegetables."), - ("human", "{query}"), + ("human", "{input}"), ] ) + # Construct the chains we will route to. These format the input query + # into the respective prompt, run it through a chat model, and cast + # the result to a string. chain_1 = prompt_1 | llm | StrOutputParser() chain_2 = prompt_2 | llm | StrOutputParser() + + # Next: define the chain that selects which branch to route to. + # Here we will take advantage of tool-calling features to force + # the output to select one of two desired branches. route_system = "Route the user's query to either the animal or vegetable expert." route_prompt = ChatPromptTemplate.from_messages( [ ("system", route_system), - ("human", "{query}"), + ("human", "{input}"), ] ) + # Define schema for output: class RouteQuery(TypedDict): - \"\"\"Route query to destination.\"\"\" + \"\"\"Route query to destination expert.\"\"\" + destination: Literal["animal", "vegetable"] - route_chain = ( - route_prompt - | llm.with_structured_output(RouteQuery) - | itemgetter("destination") - ) + route_chain = route_prompt | llm.with_structured_output(RouteQuery) - chain = { - "destination": route_chain, # "animal" or "vegetable" - "query": lambda x: x["query"], # pass through input query - } | RunnableLambda( - # if animal, chain_1. otherwise, chain_2. - lambda x: chain_1 if x["destination"] == "animal" else chain_2, - ) - chain.invoke({"query": "what color are carrots"}) + # For LangGraph, we will define the state of the graph to hold the query, + # destination, and final answer. + class State(TypedDict): + query: str + destination: RouteQuery + answer: str + + + # We define functions for each node, including routing the query: + async def route_query(state: State, config: RunnableConfig): + destination = await route_chain.ainvoke(state["query"], config) + return {"destination": destination} + + + # And one node for each prompt + async def prompt_1(state: State, config: RunnableConfig): + return {"answer": await chain_1.ainvoke(state["query"], config)} + + + async def prompt_2(state: State, config: RunnableConfig): + return {"answer": await chain_2.ainvoke(state["query"], config)} + + + # We then define logic that selects the prompt based on the classification + def select_node(state: State) -> Literal["prompt_1", "prompt_2"]: + if state["destination"] == "animal": + return "prompt_1" + else: + return "prompt_2" + + + # Finally, assemble the multi-prompt chain. This is a sequence of two steps: + # 1) Select "animal" or "vegetable" via the route_chain, and collect the answer + # alongside the input query. + # 2) Route the input query to chain_1 or chain_2, based on the + # selection. + graph = StateGraph(State) + graph.add_node("route_query", route_query) + graph.add_node("prompt_1", prompt_1) + graph.add_node("prompt_2", prompt_2) + + graph.add_edge(START, "route_query") + graph.add_conditional_edges("route_query", select_node) + graph.add_edge("prompt_1", END) + graph.add_edge("prompt_2", END) + app = graph.compile() + + result = await app.ainvoke({"query": "what color are carrots"}) + print(result["destination"]) + print(result["answer"]) """ # noqa: E501 @property diff --git a/libs/langchain/langchain/retrievers/document_compressors/embeddings_filter.py b/libs/langchain/langchain/retrievers/document_compressors/embeddings_filter.py index d71e7f1b3c293..f7acd800ac1d6 100644 --- a/libs/langchain/langchain/retrievers/document_compressors/embeddings_filter.py +++ b/libs/langchain/langchain/retrievers/document_compressors/embeddings_filter.py @@ -75,7 +75,7 @@ def compress_documents( ) embedded_query = self.embeddings.embed_query(query) similarity = self.similarity_fn([embedded_query], embedded_documents)[0] - included_idxs = np.arange(len(embedded_documents)) + included_idxs: np.ndarray = np.arange(len(embedded_documents)) if self.k is not None: included_idxs = np.argsort(similarity)[::-1][: self.k] if self.similarity_threshold is not None: @@ -110,7 +110,7 @@ async def acompress_documents( ) embedded_query = await self.embeddings.aembed_query(query) similarity = self.similarity_fn([embedded_query], embedded_documents)[0] - included_idxs = np.arange(len(embedded_documents)) + included_idxs: np.ndarray = np.arange(len(embedded_documents)) if self.k is not None: included_idxs = np.argsort(similarity)[::-1][: self.k] if self.similarity_threshold is not None: diff --git a/libs/langchain/langchain/retrievers/self_query/base.py b/libs/langchain/langchain/retrievers/self_query/base.py index 7a13362d55348..cae199a75d7a9 100644 --- a/libs/langchain/langchain/retrievers/self_query/base.py +++ b/libs/langchain/langchain/retrievers/self_query/base.py @@ -170,6 +170,7 @@ def _get_builtin_translator(vectorstore: VectorStore) -> Visitor: return Neo4jTranslator() try: + # Trying langchain_chroma import if exists from langchain_chroma import Chroma except ImportError: pass @@ -204,6 +205,16 @@ def _get_builtin_translator(vectorstore: VectorStore) -> Visitor: if isinstance(vectorstore, HanaDB): return HanaTranslator() + try: + # Trying langchain_weaviate (weaviate v4) import if exists + from langchain_weaviate.vectorstores import WeaviateVectorStore + + except ImportError: + pass + else: + if isinstance(vectorstore, WeaviateVectorStore): + return WeaviateTranslator() + raise ValueError( f"Self query retriever with Vector Store type {vectorstore.__class__}" f" not supported." diff --git a/libs/langchain/poetry.lock b/libs/langchain/poetry.lock index 5864ac56eeac0..e99e3301ae9f3 100644 --- a/libs/langchain/poetry.lock +++ b/libs/langchain/poetry.lock @@ -1,99 +1,99 @@ -# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. [[package]] name = "aiohappyeyeballs" -version = "2.4.3" +version = "2.4.4" description = "Happy Eyeballs for asyncio" optional = false python-versions = ">=3.8" files = [ - {file = "aiohappyeyeballs-2.4.3-py3-none-any.whl", hash = "sha256:8a7a83727b2756f394ab2895ea0765a0a8c475e3c71e98d43d76f22b4b435572"}, - {file = "aiohappyeyeballs-2.4.3.tar.gz", hash = "sha256:75cf88a15106a5002a8eb1dab212525c00d1f4c0fa96e551c9fbe6f09a621586"}, + {file = "aiohappyeyeballs-2.4.4-py3-none-any.whl", hash = "sha256:a980909d50efcd44795c4afeca523296716d50cd756ddca6af8c65b996e27de8"}, + {file = "aiohappyeyeballs-2.4.4.tar.gz", hash = "sha256:5fdd7d87889c63183afc18ce9271f9b0a7d32c2303e394468dd45d514a757745"}, ] [[package]] name = "aiohttp" -version = "3.11.7" +version = "3.11.10" description = "Async http client/server framework (asyncio)" optional = false python-versions = ">=3.9" files = [ - {file = "aiohttp-3.11.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8bedb1f6cb919af3b6353921c71281b1491f948ca64408871465d889b4ee1b66"}, - {file = "aiohttp-3.11.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f5022504adab881e2d801a88b748ea63f2a9d130e0b2c430824682a96f6534be"}, - {file = "aiohttp-3.11.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e22d1721c978a6494adc824e0916f9d187fa57baeda34b55140315fa2f740184"}, - {file = "aiohttp-3.11.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e993676c71288618eb07e20622572b1250d8713e7e00ab3aabae28cb70f3640d"}, - {file = "aiohttp-3.11.7-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e13a05db87d3b241c186d0936808d0e4e12decc267c617d54e9c643807e968b6"}, - {file = "aiohttp-3.11.7-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ba8d043fed7ffa117024d7ba66fdea011c0e7602327c6d73cacaea38abe4491"}, - {file = "aiohttp-3.11.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dda3ed0a7869d2fa16aa41f9961ade73aa2c2e3b2fcb0a352524e7b744881889"}, - {file = "aiohttp-3.11.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43bfd25113c1e98aec6c70e26d5f4331efbf4aa9037ba9ad88f090853bf64d7f"}, - {file = "aiohttp-3.11.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3dd3e7e7c9ef3e7214f014f1ae260892286647b3cf7c7f1b644a568fd410f8ca"}, - {file = "aiohttp-3.11.7-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:78c657ece7a73b976905ab9ec8be9ef2df12ed8984c24598a1791c58ce3b4ce4"}, - {file = "aiohttp-3.11.7-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:db70a47987e34494b451a334605bee57a126fe8d290511349e86810b4be53b01"}, - {file = "aiohttp-3.11.7-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:9e67531370a3b07e49b280c1f8c2df67985c790ad2834d1b288a2f13cd341c5f"}, - {file = "aiohttp-3.11.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9202f184cc0582b1db15056f2225ab4c1e3dac4d9ade50dd0613ac3c46352ac2"}, - {file = "aiohttp-3.11.7-cp310-cp310-win32.whl", hash = "sha256:2257bdd5cf54a4039a4337162cd8048f05a724380a2283df34620f55d4e29341"}, - {file = "aiohttp-3.11.7-cp310-cp310-win_amd64.whl", hash = "sha256:b7215bf2b53bc6cb35808149980c2ae80a4ae4e273890ac85459c014d5aa60ac"}, - {file = "aiohttp-3.11.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:cea52d11e02123f125f9055dfe0ccf1c3857225fb879e4a944fae12989e2aef2"}, - {file = "aiohttp-3.11.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3ce18f703b7298e7f7633efd6a90138d99a3f9a656cb52c1201e76cb5d79cf08"}, - {file = "aiohttp-3.11.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:670847ee6aeb3a569cd7cdfbe0c3bec1d44828bbfbe78c5d305f7f804870ef9e"}, - {file = "aiohttp-3.11.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4dda726f89bfa5c465ba45b76515135a3ece0088dfa2da49b8bb278f3bdeea12"}, - {file = "aiohttp-3.11.7-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c25b74a811dba37c7ea6a14d99eb9402d89c8d739d50748a75f3cf994cf19c43"}, - {file = "aiohttp-3.11.7-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5522ee72f95661e79db691310290c4618b86dff2d9b90baedf343fd7a08bf79"}, - {file = "aiohttp-3.11.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1fbf41a6bbc319a7816ae0f0177c265b62f2a59ad301a0e49b395746eb2a9884"}, - {file = "aiohttp-3.11.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:59ee1925b5a5efdf6c4e7be51deee93984d0ac14a6897bd521b498b9916f1544"}, - {file = "aiohttp-3.11.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:24054fce8c6d6f33a3e35d1c603ef1b91bbcba73e3f04a22b4f2f27dac59b347"}, - {file = "aiohttp-3.11.7-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:351849aca2c6f814575c1a485c01c17a4240413f960df1bf9f5deb0003c61a53"}, - {file = "aiohttp-3.11.7-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:12724f3a211fa243570e601f65a8831372caf1a149d2f1859f68479f07efec3d"}, - {file = "aiohttp-3.11.7-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:7ea4490360b605804bea8173d2d086b6c379d6bb22ac434de605a9cbce006e7d"}, - {file = "aiohttp-3.11.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e0bf378db07df0a713a1e32381a1b277e62ad106d0dbe17b5479e76ec706d720"}, - {file = "aiohttp-3.11.7-cp311-cp311-win32.whl", hash = "sha256:cd8d62cab363dfe713067027a5adb4907515861f1e4ce63e7be810b83668b847"}, - {file = "aiohttp-3.11.7-cp311-cp311-win_amd64.whl", hash = "sha256:bf0e6cce113596377cadda4e3ac5fb89f095bd492226e46d91b4baef1dd16f60"}, - {file = "aiohttp-3.11.7-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:4bb7493c3e3a36d3012b8564bd0e2783259ddd7ef3a81a74f0dbfa000fce48b7"}, - {file = "aiohttp-3.11.7-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e143b0ef9cb1a2b4f74f56d4fbe50caa7c2bb93390aff52f9398d21d89bc73ea"}, - {file = "aiohttp-3.11.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f7c58a240260822dc07f6ae32a0293dd5bccd618bb2d0f36d51c5dbd526f89c0"}, - {file = "aiohttp-3.11.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d20cfe63a1c135d26bde8c1d0ea46fd1200884afbc523466d2f1cf517d1fe33"}, - {file = "aiohttp-3.11.7-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12e4d45847a174f77b2b9919719203769f220058f642b08504cf8b1cf185dacf"}, - {file = "aiohttp-3.11.7-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cf4efa2d01f697a7dbd0509891a286a4af0d86902fc594e20e3b1712c28c0106"}, - {file = "aiohttp-3.11.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ee6a4cdcbf54b8083dc9723cdf5f41f722c00db40ccf9ec2616e27869151129"}, - {file = "aiohttp-3.11.7-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c6095aaf852c34f42e1bd0cf0dc32d1e4b48a90bfb5054abdbb9d64b36acadcb"}, - {file = "aiohttp-3.11.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1cf03d27885f8c5ebf3993a220cc84fc66375e1e6e812731f51aab2b2748f4a6"}, - {file = "aiohttp-3.11.7-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:1a17f6a230f81eb53282503823f59d61dff14fb2a93847bf0399dc8e87817307"}, - {file = "aiohttp-3.11.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:481f10a1a45c5f4c4a578bbd74cff22eb64460a6549819242a87a80788461fba"}, - {file = "aiohttp-3.11.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:db37248535d1ae40735d15bdf26ad43be19e3d93ab3f3dad8507eb0f85bb8124"}, - {file = "aiohttp-3.11.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9d18a8b44ec8502a7fde91446cd9c9b95ce7c49f1eacc1fb2358b8907d4369fd"}, - {file = "aiohttp-3.11.7-cp312-cp312-win32.whl", hash = "sha256:3d1c9c15d3999107cbb9b2d76ca6172e6710a12fda22434ee8bd3f432b7b17e8"}, - {file = "aiohttp-3.11.7-cp312-cp312-win_amd64.whl", hash = "sha256:018f1b04883a12e77e7fc161934c0f298865d3a484aea536a6a2ca8d909f0ba0"}, - {file = "aiohttp-3.11.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:241a6ca732d2766836d62c58c49ca7a93d08251daef0c1e3c850df1d1ca0cbc4"}, - {file = "aiohttp-3.11.7-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:aa3705a8d14de39898da0fbad920b2a37b7547c3afd2a18b9b81f0223b7d0f68"}, - {file = "aiohttp-3.11.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9acfc7f652b31853eed3b92095b0acf06fd5597eeea42e939bd23a17137679d5"}, - {file = "aiohttp-3.11.7-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcefcf2915a2dbdbce37e2fc1622129a1918abfe3d06721ce9f6cdac9b6d2eaa"}, - {file = "aiohttp-3.11.7-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c1f6490dd1862af5aae6cfcf2a274bffa9a5b32a8f5acb519a7ecf5a99a88866"}, - {file = "aiohttp-3.11.7-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1ac5462582d6561c1c1708853a9faf612ff4e5ea5e679e99be36143d6eabd8e"}, - {file = "aiohttp-3.11.7-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c1a6309005acc4b2bcc577ba3b9169fea52638709ffacbd071f3503264620da"}, - {file = "aiohttp-3.11.7-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f5b973cce96793725ef63eb449adfb74f99c043c718acb76e0d2a447ae369962"}, - {file = "aiohttp-3.11.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ce91a24aac80de6be8512fb1c4838a9881aa713f44f4e91dd7bb3b34061b497d"}, - {file = "aiohttp-3.11.7-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:875f7100ce0e74af51d4139495eec4025affa1a605280f23990b6434b81df1bd"}, - {file = "aiohttp-3.11.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c171fc35d3174bbf4787381716564042a4cbc008824d8195eede3d9b938e29a8"}, - {file = "aiohttp-3.11.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:ee9afa1b0d2293c46954f47f33e150798ad68b78925e3710044e0d67a9487791"}, - {file = "aiohttp-3.11.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8360c7cc620abb320e1b8d603c39095101391a82b1d0be05fb2225471c9c5c52"}, - {file = "aiohttp-3.11.7-cp313-cp313-win32.whl", hash = "sha256:7a9318da4b4ada9a67c1dd84d1c0834123081e746bee311a16bb449f363d965e"}, - {file = "aiohttp-3.11.7-cp313-cp313-win_amd64.whl", hash = "sha256:fc6da202068e0a268e298d7cd09b6e9f3997736cd9b060e2750963754552a0a9"}, - {file = "aiohttp-3.11.7-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:17829f37c0d31d89aa6b8b010475a10233774771f9b6dc2cc352ea4f8ce95d9a"}, - {file = "aiohttp-3.11.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d6177077a31b1aecfc3c9070bd2f11419dbb4a70f30f4c65b124714f525c2e48"}, - {file = "aiohttp-3.11.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:badda65ac99555791eed75e234afb94686ed2317670c68bff8a4498acdaee935"}, - {file = "aiohttp-3.11.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0de6466b9d742b4ee56fe1b2440706e225eb48c77c63152b1584864a236e7a50"}, - {file = "aiohttp-3.11.7-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:04b0cc74d5a882c9dacaeeccc1444f0233212b6f5be8bc90833feef1e1ce14b9"}, - {file = "aiohttp-3.11.7-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c7af3e50e5903d21d7b935aceed901cc2475463bc16ddd5587653548661fdb"}, - {file = "aiohttp-3.11.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c63f898f683d1379b9be5afc3dd139e20b30b0b1e0bf69a3fc3681f364cf1629"}, - {file = "aiohttp-3.11.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fdadc3f6a32d6eca45f9a900a254757fd7855dfb2d8f8dcf0e88f0fae3ff8eb1"}, - {file = "aiohttp-3.11.7-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:d329300fb23e14ed1f8c6d688dfd867d1dcc3b1d7cd49b7f8c5b44e797ce0932"}, - {file = "aiohttp-3.11.7-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:5578cf40440eafcb054cf859964bc120ab52ebe0e0562d2b898126d868749629"}, - {file = "aiohttp-3.11.7-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:7b2f8107a3c329789f3c00b2daad0e35f548d0a55cda6291579136622099a46e"}, - {file = "aiohttp-3.11.7-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:43dd89a6194f6ab02a3fe36b09e42e2df19c211fc2050ce37374d96f39604997"}, - {file = "aiohttp-3.11.7-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:d2fa6fc7cc865d26ff42480ac9b52b8c9b7da30a10a6442a9cdf429de840e949"}, - {file = "aiohttp-3.11.7-cp39-cp39-win32.whl", hash = "sha256:a7d9a606355655617fee25dd7e54d3af50804d002f1fd3118dd6312d26692d70"}, - {file = "aiohttp-3.11.7-cp39-cp39-win_amd64.whl", hash = "sha256:53c921b58fdc6485d6b2603e0132bb01cd59b8f0620ffc0907f525e0ba071687"}, - {file = "aiohttp-3.11.7.tar.gz", hash = "sha256:01a8aca4af3da85cea5c90141d23f4b0eee3cbecfd33b029a45a80f28c66c668"}, + {file = "aiohttp-3.11.10-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cbad88a61fa743c5d283ad501b01c153820734118b65aee2bd7dbb735475ce0d"}, + {file = "aiohttp-3.11.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:80886dac673ceaef499de2f393fc80bb4481a129e6cb29e624a12e3296cc088f"}, + {file = "aiohttp-3.11.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:61b9bae80ed1f338c42f57c16918853dc51775fb5cb61da70d590de14d8b5fb4"}, + {file = "aiohttp-3.11.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9e2e576caec5c6a6b93f41626c9c02fc87cd91538b81a3670b2e04452a63def6"}, + {file = "aiohttp-3.11.10-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:02c13415b5732fb6ee7ff64583a5e6ed1c57aa68f17d2bda79c04888dfdc2769"}, + {file = "aiohttp-3.11.10-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4cfce37f31f20800a6a6620ce2cdd6737b82e42e06e6e9bd1b36f546feb3c44f"}, + {file = "aiohttp-3.11.10-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3bbbfff4c679c64e6e23cb213f57cc2c9165c9a65d63717108a644eb5a7398df"}, + {file = "aiohttp-3.11.10-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49c7dbbc1a559ae14fc48387a115b7d4bbc84b4a2c3b9299c31696953c2a5219"}, + {file = "aiohttp-3.11.10-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:68386d78743e6570f054fe7949d6cb37ef2b672b4d3405ce91fafa996f7d9b4d"}, + {file = "aiohttp-3.11.10-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:9ef405356ba989fb57f84cac66f7b0260772836191ccefbb987f414bcd2979d9"}, + {file = "aiohttp-3.11.10-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:5d6958671b296febe7f5f859bea581a21c1d05430d1bbdcf2b393599b1cdce77"}, + {file = "aiohttp-3.11.10-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:99b7920e7165be5a9e9a3a7f1b680f06f68ff0d0328ff4079e5163990d046767"}, + {file = "aiohttp-3.11.10-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0dc49f42422163efb7e6f1df2636fe3db72713f6cd94688e339dbe33fe06d61d"}, + {file = "aiohttp-3.11.10-cp310-cp310-win32.whl", hash = "sha256:40d1c7a7f750b5648642586ba7206999650208dbe5afbcc5284bcec6579c9b91"}, + {file = "aiohttp-3.11.10-cp310-cp310-win_amd64.whl", hash = "sha256:68ff6f48b51bd78ea92b31079817aff539f6c8fc80b6b8d6ca347d7c02384e33"}, + {file = "aiohttp-3.11.10-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:77c4aa15a89847b9891abf97f3d4048f3c2d667e00f8a623c89ad2dccee6771b"}, + {file = "aiohttp-3.11.10-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:909af95a72cedbefe5596f0bdf3055740f96c1a4baa0dd11fd74ca4de0b4e3f1"}, + {file = "aiohttp-3.11.10-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:386fbe79863eb564e9f3615b959e28b222259da0c48fd1be5929ac838bc65683"}, + {file = "aiohttp-3.11.10-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3de34936eb1a647aa919655ff8d38b618e9f6b7f250cc19a57a4bf7fd2062b6d"}, + {file = "aiohttp-3.11.10-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0c9527819b29cd2b9f52033e7fb9ff08073df49b4799c89cb5754624ecd98299"}, + {file = "aiohttp-3.11.10-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65a96e3e03300b41f261bbfd40dfdbf1c301e87eab7cd61c054b1f2e7c89b9e8"}, + {file = "aiohttp-3.11.10-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98f5635f7b74bcd4f6f72fcd85bea2154b323a9f05226a80bc7398d0c90763b0"}, + {file = "aiohttp-3.11.10-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:03b6002e20938fc6ee0918c81d9e776bebccc84690e2b03ed132331cca065ee5"}, + {file = "aiohttp-3.11.10-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6362cc6c23c08d18ddbf0e8c4d5159b5df74fea1a5278ff4f2c79aed3f4e9f46"}, + {file = "aiohttp-3.11.10-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:3691ed7726fef54e928fe26344d930c0c8575bc968c3e239c2e1a04bd8cf7838"}, + {file = "aiohttp-3.11.10-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:31d5093d3acd02b31c649d3a69bb072d539d4c7659b87caa4f6d2bcf57c2fa2b"}, + {file = "aiohttp-3.11.10-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:8b3cf2dc0f0690a33f2d2b2cb15db87a65f1c609f53c37e226f84edb08d10f52"}, + {file = "aiohttp-3.11.10-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:fbbaea811a2bba171197b08eea288b9402faa2bab2ba0858eecdd0a4105753a3"}, + {file = "aiohttp-3.11.10-cp311-cp311-win32.whl", hash = "sha256:4b2c7ac59c5698a7a8207ba72d9e9c15b0fc484a560be0788b31312c2c5504e4"}, + {file = "aiohttp-3.11.10-cp311-cp311-win_amd64.whl", hash = "sha256:974d3a2cce5fcfa32f06b13ccc8f20c6ad9c51802bb7f829eae8a1845c4019ec"}, + {file = "aiohttp-3.11.10-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b78f053a7ecfc35f0451d961dacdc671f4bcbc2f58241a7c820e9d82559844cf"}, + {file = "aiohttp-3.11.10-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ab7485222db0959a87fbe8125e233b5a6f01f4400785b36e8a7878170d8c3138"}, + {file = "aiohttp-3.11.10-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cf14627232dfa8730453752e9cdc210966490992234d77ff90bc8dc0dce361d5"}, + {file = "aiohttp-3.11.10-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:076bc454a7e6fd646bc82ea7f98296be0b1219b5e3ef8a488afbdd8e81fbac50"}, + {file = "aiohttp-3.11.10-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:482cafb7dc886bebeb6c9ba7925e03591a62ab34298ee70d3dd47ba966370d2c"}, + {file = "aiohttp-3.11.10-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bf3d1a519a324af764a46da4115bdbd566b3c73fb793ffb97f9111dbc684fc4d"}, + {file = "aiohttp-3.11.10-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24213ba85a419103e641e55c27dc7ff03536c4873470c2478cce3311ba1eee7b"}, + {file = "aiohttp-3.11.10-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b99acd4730ad1b196bfb03ee0803e4adac371ae8efa7e1cbc820200fc5ded109"}, + {file = "aiohttp-3.11.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:14cdb5a9570be5a04eec2ace174a48ae85833c2aadc86de68f55541f66ce42ab"}, + {file = "aiohttp-3.11.10-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:7e97d622cb083e86f18317282084bc9fbf261801b0192c34fe4b1febd9f7ae69"}, + {file = "aiohttp-3.11.10-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:012f176945af138abc10c4a48743327a92b4ca9adc7a0e078077cdb5dbab7be0"}, + {file = "aiohttp-3.11.10-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:44224d815853962f48fe124748227773acd9686eba6dc102578defd6fc99e8d9"}, + {file = "aiohttp-3.11.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c87bf31b7fdab94ae3adbe4a48e711bfc5f89d21cf4c197e75561def39e223bc"}, + {file = "aiohttp-3.11.10-cp312-cp312-win32.whl", hash = "sha256:06a8e2ee1cbac16fe61e51e0b0c269400e781b13bcfc33f5425912391a542985"}, + {file = "aiohttp-3.11.10-cp312-cp312-win_amd64.whl", hash = "sha256:be2b516f56ea883a3e14dda17059716593526e10fb6303189aaf5503937db408"}, + {file = "aiohttp-3.11.10-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8cc5203b817b748adccb07f36390feb730b1bc5f56683445bfe924fc270b8816"}, + {file = "aiohttp-3.11.10-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5ef359ebc6949e3a34c65ce20230fae70920714367c63afd80ea0c2702902ccf"}, + {file = "aiohttp-3.11.10-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9bca390cb247dbfaec3c664326e034ef23882c3f3bfa5fbf0b56cad0320aaca5"}, + {file = "aiohttp-3.11.10-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:811f23b3351ca532af598405db1093f018edf81368e689d1b508c57dcc6b6a32"}, + {file = "aiohttp-3.11.10-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ddf5f7d877615f6a1e75971bfa5ac88609af3b74796ff3e06879e8422729fd01"}, + {file = "aiohttp-3.11.10-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6ab29b8a0beb6f8eaf1e5049252cfe74adbaafd39ba91e10f18caeb0e99ffb34"}, + {file = "aiohttp-3.11.10-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c49a76c1038c2dd116fa443eba26bbb8e6c37e924e2513574856de3b6516be99"}, + {file = "aiohttp-3.11.10-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7f3dc0e330575f5b134918976a645e79adf333c0a1439dcf6899a80776c9ab39"}, + {file = "aiohttp-3.11.10-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:efb15a17a12497685304b2d976cb4939e55137df7b09fa53f1b6a023f01fcb4e"}, + {file = "aiohttp-3.11.10-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:db1d0b28fcb7f1d35600150c3e4b490775251dea70f894bf15c678fdd84eda6a"}, + {file = "aiohttp-3.11.10-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:15fccaf62a4889527539ecb86834084ecf6e9ea70588efde86e8bc775e0e7542"}, + {file = "aiohttp-3.11.10-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:593c114a2221444f30749cc5e5f4012488f56bd14de2af44fe23e1e9894a9c60"}, + {file = "aiohttp-3.11.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7852bbcb4d0d2f0c4d583f40c3bc750ee033265d80598d0f9cb6f372baa6b836"}, + {file = "aiohttp-3.11.10-cp313-cp313-win32.whl", hash = "sha256:65e55ca7debae8faaffee0ebb4b47a51b4075f01e9b641c31e554fd376595c6c"}, + {file = "aiohttp-3.11.10-cp313-cp313-win_amd64.whl", hash = "sha256:beb39a6d60a709ae3fb3516a1581777e7e8b76933bb88c8f4420d875bb0267c6"}, + {file = "aiohttp-3.11.10-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0580f2e12de2138f34debcd5d88894786453a76e98febaf3e8fe5db62d01c9bf"}, + {file = "aiohttp-3.11.10-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a55d2ad345684e7c3dd2c20d2f9572e9e1d5446d57200ff630e6ede7612e307f"}, + {file = "aiohttp-3.11.10-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:04814571cb72d65a6899db6099e377ed00710bf2e3eafd2985166f2918beaf59"}, + {file = "aiohttp-3.11.10-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e44a9a3c053b90c6f09b1bb4edd880959f5328cf63052503f892c41ea786d99f"}, + {file = "aiohttp-3.11.10-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:502a1464ccbc800b4b1995b302efaf426e8763fadf185e933c2931df7db9a199"}, + {file = "aiohttp-3.11.10-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:613e5169f8ae77b1933e42e418a95931fb4867b2991fc311430b15901ed67079"}, + {file = "aiohttp-3.11.10-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cca22a61b7fe45da8fc73c3443150c3608750bbe27641fc7558ec5117b27fdf"}, + {file = "aiohttp-3.11.10-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:86a5dfcc39309470bd7b68c591d84056d195428d5d2e0b5ccadfbaf25b026ebc"}, + {file = "aiohttp-3.11.10-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:77ae58586930ee6b2b6f696c82cf8e78c8016ec4795c53e36718365f6959dc82"}, + {file = "aiohttp-3.11.10-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:78153314f26d5abef3239b4a9af20c229c6f3ecb97d4c1c01b22c4f87669820c"}, + {file = "aiohttp-3.11.10-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:98283b94cc0e11c73acaf1c9698dea80c830ca476492c0fe2622bd931f34b487"}, + {file = "aiohttp-3.11.10-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:53bf2097e05c2accc166c142a2090e4c6fd86581bde3fd9b2d3f9e93dda66ac1"}, + {file = "aiohttp-3.11.10-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c5532f0441fc09c119e1dca18fbc0687e64fbeb45aa4d6a87211ceaee50a74c4"}, + {file = "aiohttp-3.11.10-cp39-cp39-win32.whl", hash = "sha256:47ad15a65fb41c570cd0ad9a9ff8012489e68176e7207ec7b82a0940dddfd8be"}, + {file = "aiohttp-3.11.10-cp39-cp39-win_amd64.whl", hash = "sha256:c6b9e6d7e41656d78e37ce754813fa44b455c3d0d0dced2a047def7dc5570b74"}, + {file = "aiohttp-3.11.10.tar.gz", hash = "sha256:b1fc6b45010a8d0ff9e88f9f2418c6fd408c99c211257334aff41597ebece42e"}, ] [package.dependencies] @@ -111,13 +111,13 @@ speedups = ["Brotli", "aiodns (>=3.2.0)", "brotlicffi"] [[package]] name = "aiosignal" -version = "1.3.1" +version = "1.3.2" description = "aiosignal: a list of registered asynchronous callbacks" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ - {file = "aiosignal-1.3.1-py3-none-any.whl", hash = "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17"}, - {file = "aiosignal-1.3.1.tar.gz", hash = "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc"}, + {file = "aiosignal-1.3.2-py2.py3-none-any.whl", hash = "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5"}, + {file = "aiosignal-1.3.2.tar.gz", hash = "sha256:a8c255c66fafb1e499c9351d0bf32ff2d8a0321595ebac3b93713656d2436f54"}, ] [package.dependencies] @@ -136,24 +136,24 @@ files = [ [[package]] name = "anyio" -version = "4.6.2.post1" +version = "4.7.0" description = "High level compatibility layer for multiple asynchronous event loop implementations" optional = false python-versions = ">=3.9" files = [ - {file = "anyio-4.6.2.post1-py3-none-any.whl", hash = "sha256:6d170c36fba3bdd840c73d3868c1e777e33676a69c3a72cf0a0d5d6d8009b61d"}, - {file = "anyio-4.6.2.post1.tar.gz", hash = "sha256:4c8bc31ccdb51c7f7bd251f51c609e038d63e34219b44aa86e47576389880b4c"}, + {file = "anyio-4.7.0-py3-none-any.whl", hash = "sha256:ea60c3723ab42ba6fff7e8ccb0488c898ec538ff4df1f1d5e642c3601d07e352"}, + {file = "anyio-4.7.0.tar.gz", hash = "sha256:2f834749c602966b7d456a7567cafcb309f96482b5081d14ac93ccd457f9dd48"}, ] [package.dependencies] exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""} idna = ">=2.8" sniffio = ">=1.1" -typing-extensions = {version = ">=4.1", markers = "python_version < \"3.11\""} +typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""} [package.extras] -doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] -test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21.0b1)"] +doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx_rtd_theme"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21)"] trio = ["trio (>=0.26.1)"] [[package]] @@ -245,21 +245,18 @@ test = ["dateparser (==1.*)", "pre-commit", "pytest", "pytest-cov", "pytest-mock [[package]] name = "asttokens" -version = "2.4.1" +version = "3.0.0" description = "Annotate AST trees with source code positions" optional = false -python-versions = "*" +python-versions = ">=3.8" files = [ - {file = "asttokens-2.4.1-py2.py3-none-any.whl", hash = "sha256:051ed49c3dcae8913ea7cd08e46a606dba30b79993209636c4875bc1d637bc24"}, - {file = "asttokens-2.4.1.tar.gz", hash = "sha256:b03869718ba9a6eb027e134bfdf69f38a236d681c83c160d510768af11254ba0"}, + {file = "asttokens-3.0.0-py3-none-any.whl", hash = "sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2"}, + {file = "asttokens-3.0.0.tar.gz", hash = "sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7"}, ] -[package.dependencies] -six = ">=1.12.0" - [package.extras] -astroid = ["astroid (>=1,<2)", "astroid (>=2,<4)"] -test = ["astroid (>=1,<2)", "astroid (>=2,<4)", "pytest"] +astroid = ["astroid (>=2,<4)"] +test = ["astroid (>=2,<4)", "pytest", "pytest-cov", "pytest-xdist"] [[package]] name = "async-lru" @@ -422,13 +419,13 @@ requests = ">=2.31.0,<3.0.0" [[package]] name = "certifi" -version = "2024.8.30" +version = "2024.12.14" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"}, - {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, + {file = "certifi-2024.12.14-py3-none-any.whl", hash = "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56"}, + {file = "certifi-2024.12.14.tar.gz", hash = "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db"}, ] [[package]] @@ -764,73 +761,73 @@ test = ["pytest"] [[package]] name = "coverage" -version = "7.6.8" +version = "7.6.9" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.9" files = [ - {file = "coverage-7.6.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b39e6011cd06822eb964d038d5dff5da5d98652b81f5ecd439277b32361a3a50"}, - {file = "coverage-7.6.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:63c19702db10ad79151a059d2d6336fe0c470f2e18d0d4d1a57f7f9713875dcf"}, - {file = "coverage-7.6.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3985b9be361d8fb6b2d1adc9924d01dec575a1d7453a14cccd73225cb79243ee"}, - {file = "coverage-7.6.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:644ec81edec0f4ad17d51c838a7d01e42811054543b76d4ba2c5d6af741ce2a6"}, - {file = "coverage-7.6.8-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f188a2402f8359cf0c4b1fe89eea40dc13b52e7b4fd4812450da9fcd210181d"}, - {file = "coverage-7.6.8-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e19122296822deafce89a0c5e8685704c067ae65d45e79718c92df7b3ec3d331"}, - {file = "coverage-7.6.8-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:13618bed0c38acc418896005732e565b317aa9e98d855a0e9f211a7ffc2d6638"}, - {file = "coverage-7.6.8-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:193e3bffca48ad74b8c764fb4492dd875038a2f9925530cb094db92bb5e47bed"}, - {file = "coverage-7.6.8-cp310-cp310-win32.whl", hash = "sha256:3988665ee376abce49613701336544041f2117de7b7fbfe91b93d8ff8b151c8e"}, - {file = "coverage-7.6.8-cp310-cp310-win_amd64.whl", hash = "sha256:f56f49b2553d7dd85fd86e029515a221e5c1f8cb3d9c38b470bc38bde7b8445a"}, - {file = "coverage-7.6.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:86cffe9c6dfcfe22e28027069725c7f57f4b868a3f86e81d1c62462764dc46d4"}, - {file = "coverage-7.6.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d82ab6816c3277dc962cfcdc85b1efa0e5f50fb2c449432deaf2398a2928ab94"}, - {file = "coverage-7.6.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:13690e923a3932e4fad4c0ebfb9cb5988e03d9dcb4c5150b5fcbf58fd8bddfc4"}, - {file = "coverage-7.6.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4be32da0c3827ac9132bb488d331cb32e8d9638dd41a0557c5569d57cf22c9c1"}, - {file = "coverage-7.6.8-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:44e6c85bbdc809383b509d732b06419fb4544dca29ebe18480379633623baafb"}, - {file = "coverage-7.6.8-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:768939f7c4353c0fac2f7c37897e10b1414b571fd85dd9fc49e6a87e37a2e0d8"}, - {file = "coverage-7.6.8-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e44961e36cb13c495806d4cac67640ac2866cb99044e210895b506c26ee63d3a"}, - {file = "coverage-7.6.8-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3ea8bb1ab9558374c0ab591783808511d135a833c3ca64a18ec927f20c4030f0"}, - {file = "coverage-7.6.8-cp311-cp311-win32.whl", hash = "sha256:629a1ba2115dce8bf75a5cce9f2486ae483cb89c0145795603d6554bdc83e801"}, - {file = "coverage-7.6.8-cp311-cp311-win_amd64.whl", hash = "sha256:fb9fc32399dca861584d96eccd6c980b69bbcd7c228d06fb74fe53e007aa8ef9"}, - {file = "coverage-7.6.8-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e683e6ecc587643f8cde8f5da6768e9d165cd31edf39ee90ed7034f9ca0eefee"}, - {file = "coverage-7.6.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1defe91d41ce1bd44b40fabf071e6a01a5aa14de4a31b986aa9dfd1b3e3e414a"}, - {file = "coverage-7.6.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7ad66e8e50225ebf4236368cc43c37f59d5e6728f15f6e258c8639fa0dd8e6d"}, - {file = "coverage-7.6.8-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3fe47da3e4fda5f1abb5709c156eca207eacf8007304ce3019eb001e7a7204cb"}, - {file = "coverage-7.6.8-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:202a2d645c5a46b84992f55b0a3affe4f0ba6b4c611abec32ee88358db4bb649"}, - {file = "coverage-7.6.8-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4674f0daa1823c295845b6a740d98a840d7a1c11df00d1fd62614545c1583787"}, - {file = "coverage-7.6.8-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:74610105ebd6f33d7c10f8907afed696e79c59e3043c5f20eaa3a46fddf33b4c"}, - {file = "coverage-7.6.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:37cda8712145917105e07aab96388ae76e787270ec04bcb9d5cc786d7cbb8443"}, - {file = "coverage-7.6.8-cp312-cp312-win32.whl", hash = "sha256:9e89d5c8509fbd6c03d0dd1972925b22f50db0792ce06324ba069f10787429ad"}, - {file = "coverage-7.6.8-cp312-cp312-win_amd64.whl", hash = "sha256:379c111d3558272a2cae3d8e57e6b6e6f4fe652905692d54bad5ea0ca37c5ad4"}, - {file = "coverage-7.6.8-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0b0c69f4f724c64dfbfe79f5dfb503b42fe6127b8d479b2677f2b227478db2eb"}, - {file = "coverage-7.6.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c15b32a7aca8038ed7644f854bf17b663bc38e1671b5d6f43f9a2b2bd0c46f63"}, - {file = "coverage-7.6.8-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63068a11171e4276f6ece913bde059e77c713b48c3a848814a6537f35afb8365"}, - {file = "coverage-7.6.8-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f4548c5ead23ad13fb7a2c8ea541357474ec13c2b736feb02e19a3085fac002"}, - {file = "coverage-7.6.8-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b4b4299dd0d2c67caaaf286d58aef5e75b125b95615dda4542561a5a566a1e3"}, - {file = "coverage-7.6.8-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c9ebfb2507751f7196995142f057d1324afdab56db1d9743aab7f50289abd022"}, - {file = "coverage-7.6.8-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:c1b4474beee02ede1eef86c25ad4600a424fe36cff01a6103cb4533c6bf0169e"}, - {file = "coverage-7.6.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d9fd2547e6decdbf985d579cf3fc78e4c1d662b9b0ff7cc7862baaab71c9cc5b"}, - {file = "coverage-7.6.8-cp313-cp313-win32.whl", hash = "sha256:8aae5aea53cbfe024919715eca696b1a3201886ce83790537d1c3668459c7146"}, - {file = "coverage-7.6.8-cp313-cp313-win_amd64.whl", hash = "sha256:ae270e79f7e169ccfe23284ff5ea2d52a6f401dc01b337efb54b3783e2ce3f28"}, - {file = "coverage-7.6.8-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:de38add67a0af869b0d79c525d3e4588ac1ffa92f39116dbe0ed9753f26eba7d"}, - {file = "coverage-7.6.8-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b07c25d52b1c16ce5de088046cd2432b30f9ad5e224ff17c8f496d9cb7d1d451"}, - {file = "coverage-7.6.8-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62a66ff235e4c2e37ed3b6104d8b478d767ff73838d1222132a7a026aa548764"}, - {file = "coverage-7.6.8-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09b9f848b28081e7b975a3626e9081574a7b9196cde26604540582da60235fdf"}, - {file = "coverage-7.6.8-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:093896e530c38c8e9c996901858ac63f3d4171268db2c9c8b373a228f459bbc5"}, - {file = "coverage-7.6.8-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9a7b8ac36fd688c8361cbc7bf1cb5866977ece6e0b17c34aa0df58bda4fa18a4"}, - {file = "coverage-7.6.8-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:38c51297b35b3ed91670e1e4efb702b790002e3245a28c76e627478aa3c10d83"}, - {file = "coverage-7.6.8-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2e4e0f60cb4bd7396108823548e82fdab72d4d8a65e58e2c19bbbc2f1e2bfa4b"}, - {file = "coverage-7.6.8-cp313-cp313t-win32.whl", hash = "sha256:6535d996f6537ecb298b4e287a855f37deaf64ff007162ec0afb9ab8ba3b8b71"}, - {file = "coverage-7.6.8-cp313-cp313t-win_amd64.whl", hash = "sha256:c79c0685f142ca53256722a384540832420dff4ab15fec1863d7e5bc8691bdcc"}, - {file = "coverage-7.6.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3ac47fa29d8d41059ea3df65bd3ade92f97ee4910ed638e87075b8e8ce69599e"}, - {file = "coverage-7.6.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:24eda3a24a38157eee639ca9afe45eefa8d2420d49468819ac5f88b10de84f4c"}, - {file = "coverage-7.6.8-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4c81ed2820b9023a9a90717020315e63b17b18c274a332e3b6437d7ff70abe0"}, - {file = "coverage-7.6.8-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd55f8fc8fa494958772a2a7302b0354ab16e0b9272b3c3d83cdb5bec5bd1779"}, - {file = "coverage-7.6.8-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f39e2f3530ed1626c66e7493be7a8423b023ca852aacdc91fb30162c350d2a92"}, - {file = "coverage-7.6.8-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:716a78a342679cd1177bc8c2fe957e0ab91405bd43a17094324845200b2fddf4"}, - {file = "coverage-7.6.8-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:177f01eeaa3aee4a5ffb0d1439c5952b53d5010f86e9d2667963e632e30082cc"}, - {file = "coverage-7.6.8-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:912e95017ff51dc3d7b6e2be158dedc889d9a5cc3382445589ce554f1a34c0ea"}, - {file = "coverage-7.6.8-cp39-cp39-win32.whl", hash = "sha256:4db3ed6a907b555e57cc2e6f14dc3a4c2458cdad8919e40b5357ab9b6db6c43e"}, - {file = "coverage-7.6.8-cp39-cp39-win_amd64.whl", hash = "sha256:428ac484592f780e8cd7b6b14eb568f7c85460c92e2a37cb0c0e5186e1a0d076"}, - {file = "coverage-7.6.8-pp39.pp310-none-any.whl", hash = "sha256:5c52a036535d12590c32c49209e79cabaad9f9ad8aa4cbd875b68c4d67a9cbce"}, - {file = "coverage-7.6.8.tar.gz", hash = "sha256:8b2b8503edb06822c86d82fa64a4a5cb0760bb8f31f26e138ec743f422f37cfc"}, + {file = "coverage-7.6.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:85d9636f72e8991a1706b2b55b06c27545448baf9f6dbf51c4004609aacd7dcb"}, + {file = "coverage-7.6.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:608a7fd78c67bee8936378299a6cb9f5149bb80238c7a566fc3e6717a4e68710"}, + {file = "coverage-7.6.9-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:96d636c77af18b5cb664ddf12dab9b15a0cfe9c0bde715da38698c8cea748bfa"}, + {file = "coverage-7.6.9-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d75cded8a3cff93da9edc31446872d2997e327921d8eed86641efafd350e1df1"}, + {file = "coverage-7.6.9-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7b15f589593110ae767ce997775d645b47e5cbbf54fd322f8ebea6277466cec"}, + {file = "coverage-7.6.9-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:44349150f6811b44b25574839b39ae35291f6496eb795b7366fef3bd3cf112d3"}, + {file = "coverage-7.6.9-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:d891c136b5b310d0e702e186d70cd16d1119ea8927347045124cb286b29297e5"}, + {file = "coverage-7.6.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:db1dab894cc139f67822a92910466531de5ea6034ddfd2b11c0d4c6257168073"}, + {file = "coverage-7.6.9-cp310-cp310-win32.whl", hash = "sha256:41ff7b0da5af71a51b53f501a3bac65fb0ec311ebed1632e58fc6107f03b9198"}, + {file = "coverage-7.6.9-cp310-cp310-win_amd64.whl", hash = "sha256:35371f8438028fdccfaf3570b31d98e8d9eda8bb1d6ab9473f5a390969e98717"}, + {file = "coverage-7.6.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:932fc826442132dde42ee52cf66d941f581c685a6313feebed358411238f60f9"}, + {file = "coverage-7.6.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:085161be5f3b30fd9b3e7b9a8c301f935c8313dcf928a07b116324abea2c1c2c"}, + {file = "coverage-7.6.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ccc660a77e1c2bf24ddbce969af9447a9474790160cfb23de6be4fa88e3951c7"}, + {file = "coverage-7.6.9-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c69e42c892c018cd3c8d90da61d845f50a8243062b19d228189b0224150018a9"}, + {file = "coverage-7.6.9-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0824a28ec542a0be22f60c6ac36d679e0e262e5353203bea81d44ee81fe9c6d4"}, + {file = "coverage-7.6.9-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4401ae5fc52ad8d26d2a5d8a7428b0f0c72431683f8e63e42e70606374c311a1"}, + {file = "coverage-7.6.9-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:98caba4476a6c8d59ec1eb00c7dd862ba9beca34085642d46ed503cc2d440d4b"}, + {file = "coverage-7.6.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ee5defd1733fd6ec08b168bd4f5387d5b322f45ca9e0e6c817ea6c4cd36313e3"}, + {file = "coverage-7.6.9-cp311-cp311-win32.whl", hash = "sha256:f2d1ec60d6d256bdf298cb86b78dd715980828f50c46701abc3b0a2b3f8a0dc0"}, + {file = "coverage-7.6.9-cp311-cp311-win_amd64.whl", hash = "sha256:0d59fd927b1f04de57a2ba0137166d31c1a6dd9e764ad4af552912d70428c92b"}, + {file = "coverage-7.6.9-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:99e266ae0b5d15f1ca8d278a668df6f51cc4b854513daab5cae695ed7b721cf8"}, + {file = "coverage-7.6.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9901d36492009a0a9b94b20e52ebfc8453bf49bb2b27bca2c9706f8b4f5a554a"}, + {file = "coverage-7.6.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abd3e72dd5b97e3af4246cdada7738ef0e608168de952b837b8dd7e90341f015"}, + {file = "coverage-7.6.9-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff74026a461eb0660366fb01c650c1d00f833a086b336bdad7ab00cc952072b3"}, + {file = "coverage-7.6.9-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65dad5a248823a4996724a88eb51d4b31587aa7aa428562dbe459c684e5787ae"}, + {file = "coverage-7.6.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:22be16571504c9ccea919fcedb459d5ab20d41172056206eb2994e2ff06118a4"}, + {file = "coverage-7.6.9-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f957943bc718b87144ecaee70762bc2bc3f1a7a53c7b861103546d3a403f0a6"}, + {file = "coverage-7.6.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0ae1387db4aecb1f485fb70a6c0148c6cdaebb6038f1d40089b1fc84a5db556f"}, + {file = "coverage-7.6.9-cp312-cp312-win32.whl", hash = "sha256:1a330812d9cc7ac2182586f6d41b4d0fadf9be9049f350e0efb275c8ee8eb692"}, + {file = "coverage-7.6.9-cp312-cp312-win_amd64.whl", hash = "sha256:b12c6b18269ca471eedd41c1b6a1065b2f7827508edb9a7ed5555e9a56dcfc97"}, + {file = "coverage-7.6.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:899b8cd4781c400454f2f64f7776a5d87bbd7b3e7f7bda0cb18f857bb1334664"}, + {file = "coverage-7.6.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:61f70dc68bd36810972e55bbbe83674ea073dd1dcc121040a08cdf3416c5349c"}, + {file = "coverage-7.6.9-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a289d23d4c46f1a82d5db4abeb40b9b5be91731ee19a379d15790e53031c014"}, + {file = "coverage-7.6.9-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e216d8044a356fc0337c7a2a0536d6de07888d7bcda76febcb8adc50bdbbd00"}, + {file = "coverage-7.6.9-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c026eb44f744acaa2bda7493dad903aa5bf5fc4f2554293a798d5606710055d"}, + {file = "coverage-7.6.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e77363e8425325384f9d49272c54045bbed2f478e9dd698dbc65dbc37860eb0a"}, + {file = "coverage-7.6.9-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:777abfab476cf83b5177b84d7486497e034eb9eaea0d746ce0c1268c71652077"}, + {file = "coverage-7.6.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:447af20e25fdbe16f26e84eb714ba21d98868705cb138252d28bc400381f6ffb"}, + {file = "coverage-7.6.9-cp313-cp313-win32.whl", hash = "sha256:d872ec5aeb086cbea771c573600d47944eea2dcba8be5f3ee649bfe3cb8dc9ba"}, + {file = "coverage-7.6.9-cp313-cp313-win_amd64.whl", hash = "sha256:fd1213c86e48dfdc5a0cc676551db467495a95a662d2396ecd58e719191446e1"}, + {file = "coverage-7.6.9-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:ba9e7484d286cd5a43744e5f47b0b3fb457865baf07bafc6bee91896364e1419"}, + {file = "coverage-7.6.9-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e5ea1cf0872ee455c03e5674b5bca5e3e68e159379c1af0903e89f5eba9ccc3a"}, + {file = "coverage-7.6.9-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d10e07aa2b91835d6abec555ec8b2733347956991901eea6ffac295f83a30e4"}, + {file = "coverage-7.6.9-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:13a9e2d3ee855db3dd6ea1ba5203316a1b1fd8eaeffc37c5b54987e61e4194ae"}, + {file = "coverage-7.6.9-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c38bf15a40ccf5619fa2fe8f26106c7e8e080d7760aeccb3722664c8656b030"}, + {file = "coverage-7.6.9-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:d5275455b3e4627c8e7154feaf7ee0743c2e7af82f6e3b561967b1cca755a0be"}, + {file = "coverage-7.6.9-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8f8770dfc6e2c6a2d4569f411015c8d751c980d17a14b0530da2d7f27ffdd88e"}, + {file = "coverage-7.6.9-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8d2dfa71665a29b153a9681edb1c8d9c1ea50dfc2375fb4dac99ea7e21a0bcd9"}, + {file = "coverage-7.6.9-cp313-cp313t-win32.whl", hash = "sha256:5e6b86b5847a016d0fbd31ffe1001b63355ed309651851295315031ea7eb5a9b"}, + {file = "coverage-7.6.9-cp313-cp313t-win_amd64.whl", hash = "sha256:97ddc94d46088304772d21b060041c97fc16bdda13c6c7f9d8fcd8d5ae0d8611"}, + {file = "coverage-7.6.9-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:adb697c0bd35100dc690de83154627fbab1f4f3c0386df266dded865fc50a902"}, + {file = "coverage-7.6.9-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:be57b6d56e49c2739cdf776839a92330e933dd5e5d929966fbbd380c77f060be"}, + {file = "coverage-7.6.9-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1592791f8204ae9166de22ba7e6705fa4ebd02936c09436a1bb85aabca3e599"}, + {file = "coverage-7.6.9-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4e12ae8cc979cf83d258acb5e1f1cf2f3f83524d1564a49d20b8bec14b637f08"}, + {file = "coverage-7.6.9-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb5555cff66c4d3d6213a296b360f9e1a8e323e74e0426b6c10ed7f4d021e464"}, + {file = "coverage-7.6.9-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:b9389a429e0e5142e69d5bf4a435dd688c14478a19bb901735cdf75e57b13845"}, + {file = "coverage-7.6.9-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:592ac539812e9b46046620341498caf09ca21023c41c893e1eb9dbda00a70cbf"}, + {file = "coverage-7.6.9-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a27801adef24cc30871da98a105f77995e13a25a505a0161911f6aafbd66e678"}, + {file = "coverage-7.6.9-cp39-cp39-win32.whl", hash = "sha256:8e3c3e38930cfb729cb8137d7f055e5a473ddaf1217966aa6238c88bd9fd50e6"}, + {file = "coverage-7.6.9-cp39-cp39-win_amd64.whl", hash = "sha256:e28bf44afa2b187cc9f41749138a64435bf340adfcacb5b2290c070ce99839d4"}, + {file = "coverage-7.6.9-pp39.pp310-none-any.whl", hash = "sha256:f3ca78518bc6bc92828cd11867b121891d75cae4ea9e908d72030609b996db1b"}, + {file = "coverage-7.6.9.tar.gz", hash = "sha256:4a8d8977b0c6ef5aeadcb644da9e69ae0dcfe66ec7f368c89c72e058bd71164d"}, ] [package.dependencies] @@ -890,37 +887,37 @@ test-randomorder = ["pytest-randomly"] [[package]] name = "debugpy" -version = "1.8.9" +version = "1.8.11" description = "An implementation of the Debug Adapter Protocol for Python" optional = false python-versions = ">=3.8" files = [ - {file = "debugpy-1.8.9-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:cfe1e6c6ad7178265f74981edf1154ffce97b69005212fbc90ca22ddfe3d017e"}, - {file = "debugpy-1.8.9-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ada7fb65102a4d2c9ab62e8908e9e9f12aed9d76ef44880367bc9308ebe49a0f"}, - {file = "debugpy-1.8.9-cp310-cp310-win32.whl", hash = "sha256:c36856343cbaa448171cba62a721531e10e7ffb0abff838004701454149bc037"}, - {file = "debugpy-1.8.9-cp310-cp310-win_amd64.whl", hash = "sha256:17c5e0297678442511cf00a745c9709e928ea4ca263d764e90d233208889a19e"}, - {file = "debugpy-1.8.9-cp311-cp311-macosx_14_0_universal2.whl", hash = "sha256:b74a49753e21e33e7cf030883a92fa607bddc4ede1aa4145172debc637780040"}, - {file = "debugpy-1.8.9-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62d22dacdb0e296966d7d74a7141aaab4bec123fa43d1a35ddcb39bf9fd29d70"}, - {file = "debugpy-1.8.9-cp311-cp311-win32.whl", hash = "sha256:8138efff315cd09b8dcd14226a21afda4ca582284bf4215126d87342bba1cc66"}, - {file = "debugpy-1.8.9-cp311-cp311-win_amd64.whl", hash = "sha256:ff54ef77ad9f5c425398efb150239f6fe8e20c53ae2f68367eba7ece1e96226d"}, - {file = "debugpy-1.8.9-cp312-cp312-macosx_14_0_universal2.whl", hash = "sha256:957363d9a7a6612a37458d9a15e72d03a635047f946e5fceee74b50d52a9c8e2"}, - {file = "debugpy-1.8.9-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e565fc54b680292b418bb809f1386f17081d1346dca9a871bf69a8ac4071afe"}, - {file = "debugpy-1.8.9-cp312-cp312-win32.whl", hash = "sha256:3e59842d6c4569c65ceb3751075ff8d7e6a6ada209ceca6308c9bde932bcef11"}, - {file = "debugpy-1.8.9-cp312-cp312-win_amd64.whl", hash = "sha256:66eeae42f3137eb428ea3a86d4a55f28da9bd5a4a3d369ba95ecc3a92c1bba53"}, - {file = "debugpy-1.8.9-cp313-cp313-macosx_14_0_universal2.whl", hash = "sha256:957ecffff80d47cafa9b6545de9e016ae8c9547c98a538ee96ab5947115fb3dd"}, - {file = "debugpy-1.8.9-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1efbb3ff61487e2c16b3e033bc8595aea578222c08aaf3c4bf0f93fadbd662ee"}, - {file = "debugpy-1.8.9-cp313-cp313-win32.whl", hash = "sha256:7c4d65d03bee875bcb211c76c1d8f10f600c305dbd734beaed4077e902606fee"}, - {file = "debugpy-1.8.9-cp313-cp313-win_amd64.whl", hash = "sha256:e46b420dc1bea64e5bbedd678148be512442bc589b0111bd799367cde051e71a"}, - {file = "debugpy-1.8.9-cp38-cp38-macosx_14_0_x86_64.whl", hash = "sha256:472a3994999fe6c0756945ffa359e9e7e2d690fb55d251639d07208dbc37caea"}, - {file = "debugpy-1.8.9-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:365e556a4772d7d0d151d7eb0e77ec4db03bcd95f26b67b15742b88cacff88e9"}, - {file = "debugpy-1.8.9-cp38-cp38-win32.whl", hash = "sha256:54a7e6d3014c408eb37b0b06021366ee985f1539e12fe49ca2ee0d392d9ceca5"}, - {file = "debugpy-1.8.9-cp38-cp38-win_amd64.whl", hash = "sha256:8e99c0b1cc7bf86d83fb95d5ccdc4ad0586d4432d489d1f54e4055bcc795f693"}, - {file = "debugpy-1.8.9-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:7e8b079323a56f719977fde9d8115590cb5e7a1cba2fcee0986ef8817116e7c1"}, - {file = "debugpy-1.8.9-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6953b335b804a41f16a192fa2e7851bdcfd92173cbb2f9f777bb934f49baab65"}, - {file = "debugpy-1.8.9-cp39-cp39-win32.whl", hash = "sha256:7e646e62d4602bb8956db88b1e72fe63172148c1e25c041e03b103a25f36673c"}, - {file = "debugpy-1.8.9-cp39-cp39-win_amd64.whl", hash = "sha256:3d9755e77a2d680ce3d2c5394a444cf42be4a592caaf246dbfbdd100ffcf7ae5"}, - {file = "debugpy-1.8.9-py2.py3-none-any.whl", hash = "sha256:cc37a6c9987ad743d9c3a14fa1b1a14b7e4e6041f9dd0c8abf8895fe7a97b899"}, - {file = "debugpy-1.8.9.zip", hash = "sha256:1339e14c7d980407248f09824d1b25ff5c5616651689f1e0f0e51bdead3ea13e"}, + {file = "debugpy-1.8.11-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:2b26fefc4e31ff85593d68b9022e35e8925714a10ab4858fb1b577a8a48cb8cd"}, + {file = "debugpy-1.8.11-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61bc8b3b265e6949855300e84dc93d02d7a3a637f2aec6d382afd4ceb9120c9f"}, + {file = "debugpy-1.8.11-cp310-cp310-win32.whl", hash = "sha256:c928bbf47f65288574b78518449edaa46c82572d340e2750889bbf8cd92f3737"}, + {file = "debugpy-1.8.11-cp310-cp310-win_amd64.whl", hash = "sha256:8da1db4ca4f22583e834dcabdc7832e56fe16275253ee53ba66627b86e304da1"}, + {file = "debugpy-1.8.11-cp311-cp311-macosx_14_0_universal2.whl", hash = "sha256:85de8474ad53ad546ff1c7c7c89230db215b9b8a02754d41cb5a76f70d0be296"}, + {file = "debugpy-1.8.11-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ffc382e4afa4aee367bf413f55ed17bd91b191dcaf979890af239dda435f2a1"}, + {file = "debugpy-1.8.11-cp311-cp311-win32.whl", hash = "sha256:40499a9979c55f72f4eb2fc38695419546b62594f8af194b879d2a18439c97a9"}, + {file = "debugpy-1.8.11-cp311-cp311-win_amd64.whl", hash = "sha256:987bce16e86efa86f747d5151c54e91b3c1e36acc03ce1ddb50f9d09d16ded0e"}, + {file = "debugpy-1.8.11-cp312-cp312-macosx_14_0_universal2.whl", hash = "sha256:84e511a7545d11683d32cdb8f809ef63fc17ea2a00455cc62d0a4dbb4ed1c308"}, + {file = "debugpy-1.8.11-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce291a5aca4985d82875d6779f61375e959208cdf09fcec40001e65fb0a54768"}, + {file = "debugpy-1.8.11-cp312-cp312-win32.whl", hash = "sha256:28e45b3f827d3bf2592f3cf7ae63282e859f3259db44ed2b129093ca0ac7940b"}, + {file = "debugpy-1.8.11-cp312-cp312-win_amd64.whl", hash = "sha256:44b1b8e6253bceada11f714acf4309ffb98bfa9ac55e4fce14f9e5d4484287a1"}, + {file = "debugpy-1.8.11-cp313-cp313-macosx_14_0_universal2.whl", hash = "sha256:8988f7163e4381b0da7696f37eec7aca19deb02e500245df68a7159739bbd0d3"}, + {file = "debugpy-1.8.11-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c1f6a173d1140e557347419767d2b14ac1c9cd847e0b4c5444c7f3144697e4e"}, + {file = "debugpy-1.8.11-cp313-cp313-win32.whl", hash = "sha256:bb3b15e25891f38da3ca0740271e63ab9db61f41d4d8541745cfc1824252cb28"}, + {file = "debugpy-1.8.11-cp313-cp313-win_amd64.whl", hash = "sha256:d8768edcbeb34da9e11bcb8b5c2e0958d25218df7a6e56adf415ef262cd7b6d1"}, + {file = "debugpy-1.8.11-cp38-cp38-macosx_14_0_x86_64.whl", hash = "sha256:ad7efe588c8f5cf940f40c3de0cd683cc5b76819446abaa50dc0829a30c094db"}, + {file = "debugpy-1.8.11-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:189058d03a40103a57144752652b3ab08ff02b7595d0ce1f651b9acc3a3a35a0"}, + {file = "debugpy-1.8.11-cp38-cp38-win32.whl", hash = "sha256:32db46ba45849daed7ccf3f2e26f7a386867b077f39b2a974bb5c4c2c3b0a280"}, + {file = "debugpy-1.8.11-cp38-cp38-win_amd64.whl", hash = "sha256:116bf8342062246ca749013df4f6ea106f23bc159305843491f64672a55af2e5"}, + {file = "debugpy-1.8.11-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:654130ca6ad5de73d978057eaf9e582244ff72d4574b3e106fb8d3d2a0d32458"}, + {file = "debugpy-1.8.11-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23dc34c5e03b0212fa3c49a874df2b8b1b8fda95160bd79c01eb3ab51ea8d851"}, + {file = "debugpy-1.8.11-cp39-cp39-win32.whl", hash = "sha256:52d8a3166c9f2815bfae05f386114b0b2d274456980d41f320299a8d9a5615a7"}, + {file = "debugpy-1.8.11-cp39-cp39-win_amd64.whl", hash = "sha256:52c3cf9ecda273a19cc092961ee34eb9ba8687d67ba34cc7b79a521c1c64c4c0"}, + {file = "debugpy-1.8.11-py2.py3-none-any.whl", hash = "sha256:0e22f846f4211383e6a416d04b4c13ed174d24cc5d43f5fd52e7821d0ebc8920"}, + {file = "debugpy-1.8.11.tar.gz", hash = "sha256:6ad2688b69235c43b020e04fecccdf6a96c8943ca9c2fb340b8adc103c655e57"}, ] [[package]] @@ -1062,13 +1059,13 @@ tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipyth [[package]] name = "fastjsonschema" -version = "2.20.0" +version = "2.21.1" description = "Fastest Python implementation of JSON schema" optional = false python-versions = "*" files = [ - {file = "fastjsonschema-2.20.0-py3-none-any.whl", hash = "sha256:5875f0b0fa7a0043a91e93a9b8f793bcbbba9691e7fd83dca95c28ba26d21f0a"}, - {file = "fastjsonschema-2.20.0.tar.gz", hash = "sha256:3d48fc5300ee96f5d116f10fe6f28d938e6008f59a6a025c2649475b87f76a23"}, + {file = "fastjsonschema-2.21.1-py3-none-any.whl", hash = "sha256:c9e5b7e908310918cf494a434eeb31384dd84a98b57a30bcb1f535015b554667"}, + {file = "fastjsonschema-2.21.1.tar.gz", hash = "sha256:794d4f0a58f848961ba16af7b9c85a3e88cd360df008c59aac6fc5ae9323b5d4"}, ] [package.extras] @@ -1335,13 +1332,13 @@ trio = ["trio (>=0.22.0,<1.0)"] [[package]] name = "httpx" -version = "0.27.2" +version = "0.28.1" description = "The next generation HTTP client." optional = false python-versions = ">=3.8" files = [ - {file = "httpx-0.27.2-py3-none-any.whl", hash = "sha256:7bb2708e112d8fdd7829cd4243970f0c223274051cb35ee80c03301ee29a3df0"}, - {file = "httpx-0.27.2.tar.gz", hash = "sha256:f7c2be1d2f3c3c3160d441802406b206c2b76f5947b11115e6df10c6c65e66c2"}, + {file = "httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad"}, + {file = "httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc"}, ] [package.dependencies] @@ -1349,7 +1346,6 @@ anyio = "*" certifi = "*" httpcore = "==1.*" idna = "*" -sniffio = "*" [package.extras] brotli = ["brotli", "brotlicffi"] @@ -1549,95 +1545,98 @@ i18n = ["Babel (>=2.7)"] [[package]] name = "jiter" -version = "0.7.1" +version = "0.8.2" description = "Fast iterable JSON parser." optional = true python-versions = ">=3.8" files = [ - {file = "jiter-0.7.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:262e96d06696b673fad6f257e6a0abb6e873dc22818ca0e0600f4a1189eb334f"}, - {file = "jiter-0.7.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:be6de02939aac5be97eb437f45cfd279b1dc9de358b13ea6e040e63a3221c40d"}, - {file = "jiter-0.7.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:935f10b802bc1ce2b2f61843e498c7720aa7f4e4bb7797aa8121eab017293c3d"}, - {file = "jiter-0.7.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9cd3cccccabf5064e4bb3099c87bf67db94f805c1e62d1aefd2b7476e90e0ee2"}, - {file = "jiter-0.7.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4aa919ebfc5f7b027cc368fe3964c0015e1963b92e1db382419dadb098a05192"}, - {file = "jiter-0.7.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ae2d01e82c94491ce4d6f461a837f63b6c4e6dd5bb082553a70c509034ff3d4"}, - {file = "jiter-0.7.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f9568cd66dbbdab67ae1b4c99f3f7da1228c5682d65913e3f5f95586b3cb9a9"}, - {file = "jiter-0.7.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9ecbf4e20ec2c26512736284dc1a3f8ed79b6ca7188e3b99032757ad48db97dc"}, - {file = "jiter-0.7.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b1a0508fddc70ce00b872e463b387d49308ef02b0787992ca471c8d4ba1c0fa1"}, - {file = "jiter-0.7.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f84c9996664c460f24213ff1e5881530abd8fafd82058d39af3682d5fd2d6316"}, - {file = "jiter-0.7.1-cp310-none-win32.whl", hash = "sha256:c915e1a1960976ba4dfe06551ea87063b2d5b4d30759012210099e712a414d9f"}, - {file = "jiter-0.7.1-cp310-none-win_amd64.whl", hash = "sha256:75bf3b7fdc5c0faa6ffffcf8028a1f974d126bac86d96490d1b51b3210aa0f3f"}, - {file = "jiter-0.7.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:ad04a23a91f3d10d69d6c87a5f4471b61c2c5cd6e112e85136594a02043f462c"}, - {file = "jiter-0.7.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e47a554de88dff701226bb5722b7f1b6bccd0b98f1748459b7e56acac2707a5"}, - {file = "jiter-0.7.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e44fff69c814a2e96a20b4ecee3e2365e9b15cf5fe4e00869d18396daa91dab"}, - {file = "jiter-0.7.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:df0a1d05081541b45743c965436f8b5a1048d6fd726e4a030113a2699a6046ea"}, - {file = "jiter-0.7.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f22cf8f236a645cb6d8ffe2a64edb5d2b66fb148bf7c75eea0cb36d17014a7bc"}, - {file = "jiter-0.7.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:da8589f50b728ea4bf22e0632eefa125c8aa9c38ed202a5ee6ca371f05eeb3ff"}, - {file = "jiter-0.7.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f20de711224f2ca2dbb166a8d512f6ff48c9c38cc06b51f796520eb4722cc2ce"}, - {file = "jiter-0.7.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8a9803396032117b85ec8cbf008a54590644a062fedd0425cbdb95e4b2b60479"}, - {file = "jiter-0.7.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:3d8bae77c82741032e9d89a4026479061aba6e646de3bf5f2fc1ae2bbd9d06e0"}, - {file = "jiter-0.7.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3dc9939e576bbc68c813fc82f6620353ed68c194c7bcf3d58dc822591ec12490"}, - {file = "jiter-0.7.1-cp311-none-win32.whl", hash = "sha256:f7605d24cd6fab156ec89e7924578e21604feee9c4f1e9da34d8b67f63e54892"}, - {file = "jiter-0.7.1-cp311-none-win_amd64.whl", hash = "sha256:f3ea649e7751a1a29ea5ecc03c4ada0a833846c59c6da75d747899f9b48b7282"}, - {file = "jiter-0.7.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:ad36a1155cbd92e7a084a568f7dc6023497df781adf2390c345dd77a120905ca"}, - {file = "jiter-0.7.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7ba52e6aaed2dc5c81a3d9b5e4ab95b039c4592c66ac973879ba57c3506492bb"}, - {file = "jiter-0.7.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2b7de0b6f6728b678540c7927587e23f715284596724be203af952418acb8a2d"}, - {file = "jiter-0.7.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9463b62bd53c2fb85529c700c6a3beb2ee54fde8bef714b150601616dcb184a6"}, - {file = "jiter-0.7.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:627164ec01d28af56e1f549da84caf0fe06da3880ebc7b7ee1ca15df106ae172"}, - {file = "jiter-0.7.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:25d0e5bf64e368b0aa9e0a559c3ab2f9b67e35fe7269e8a0d81f48bbd10e8963"}, - {file = "jiter-0.7.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c244261306f08f8008b3087059601997016549cb8bb23cf4317a4827f07b7d74"}, - {file = "jiter-0.7.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7ded4e4b75b68b843b7cea5cd7c55f738c20e1394c68c2cb10adb655526c5f1b"}, - {file = "jiter-0.7.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:80dae4f1889b9d09e5f4de6b58c490d9c8ce7730e35e0b8643ab62b1538f095c"}, - {file = "jiter-0.7.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5970cf8ec943b51bce7f4b98d2e1ed3ada170c2a789e2db3cb484486591a176a"}, - {file = "jiter-0.7.1-cp312-none-win32.whl", hash = "sha256:701d90220d6ecb3125d46853c8ca8a5bc158de8c49af60fd706475a49fee157e"}, - {file = "jiter-0.7.1-cp312-none-win_amd64.whl", hash = "sha256:7824c3ecf9ecf3321c37f4e4d4411aad49c666ee5bc2a937071bdd80917e4533"}, - {file = "jiter-0.7.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:097676a37778ba3c80cb53f34abd6943ceb0848263c21bf423ae98b090f6c6ba"}, - {file = "jiter-0.7.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3298af506d4271257c0a8f48668b0f47048d69351675dd8500f22420d4eec378"}, - {file = "jiter-0.7.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:12fd88cfe6067e2199964839c19bd2b422ca3fd792949b8f44bb8a4e7d21946a"}, - {file = "jiter-0.7.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:dacca921efcd21939123c8ea8883a54b9fa7f6545c8019ffcf4f762985b6d0c8"}, - {file = "jiter-0.7.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de3674a5fe1f6713a746d25ad9c32cd32fadc824e64b9d6159b3b34fd9134143"}, - {file = "jiter-0.7.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65df9dbae6d67e0788a05b4bad5706ad40f6f911e0137eb416b9eead6ba6f044"}, - {file = "jiter-0.7.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ba9a358d59a0a55cccaa4957e6ae10b1a25ffdabda863c0343c51817610501d"}, - {file = "jiter-0.7.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:576eb0f0c6207e9ede2b11ec01d9c2182973986514f9c60bc3b3b5d5798c8f50"}, - {file = "jiter-0.7.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:e550e29cdf3577d2c970a18f3959e6b8646fd60ef1b0507e5947dc73703b5627"}, - {file = "jiter-0.7.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:81d968dbf3ce0db2e0e4dec6b0a0d5d94f846ee84caf779b07cab49f5325ae43"}, - {file = "jiter-0.7.1-cp313-none-win32.whl", hash = "sha256:f892e547e6e79a1506eb571a676cf2f480a4533675f834e9ae98de84f9b941ac"}, - {file = "jiter-0.7.1-cp313-none-win_amd64.whl", hash = "sha256:0302f0940b1455b2a7fb0409b8d5b31183db70d2b07fd177906d83bf941385d1"}, - {file = "jiter-0.7.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:c65a3ce72b679958b79d556473f192a4dfc5895e8cc1030c9f4e434690906076"}, - {file = "jiter-0.7.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e80052d3db39f9bb8eb86d207a1be3d9ecee5e05fdec31380817f9609ad38e60"}, - {file = "jiter-0.7.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70a497859c4f3f7acd71c8bd89a6f9cf753ebacacf5e3e799138b8e1843084e3"}, - {file = "jiter-0.7.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c1288bc22b9e36854a0536ba83666c3b1fb066b811019d7b682c9cf0269cdf9f"}, - {file = "jiter-0.7.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b096ca72dd38ef35675e1d3b01785874315182243ef7aea9752cb62266ad516f"}, - {file = "jiter-0.7.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8dbbd52c50b605af13dbee1a08373c520e6fcc6b5d32f17738875847fea4e2cd"}, - {file = "jiter-0.7.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af29c5c6eb2517e71ffa15c7ae9509fa5e833ec2a99319ac88cc271eca865519"}, - {file = "jiter-0.7.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f114a4df1e40c03c0efbf974b376ed57756a1141eb27d04baee0680c5af3d424"}, - {file = "jiter-0.7.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:191fbaee7cf46a9dd9b817547bf556facde50f83199d07fc48ebeff4082f9df4"}, - {file = "jiter-0.7.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0e2b445e5ee627fb4ee6bbceeb486251e60a0c881a8e12398dfdff47c56f0723"}, - {file = "jiter-0.7.1-cp38-none-win32.whl", hash = "sha256:47ac4c3cf8135c83e64755b7276339b26cd3c7ddadf9e67306ace4832b283edf"}, - {file = "jiter-0.7.1-cp38-none-win_amd64.whl", hash = "sha256:60b49c245cd90cde4794f5c30f123ee06ccf42fb8730a019a2870cd005653ebd"}, - {file = "jiter-0.7.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:8f212eeacc7203256f526f550d105d8efa24605828382cd7d296b703181ff11d"}, - {file = "jiter-0.7.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d9e247079d88c00e75e297e6cb3a18a039ebcd79fefc43be9ba4eb7fb43eb726"}, - {file = "jiter-0.7.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0aacaa56360139c53dcf352992b0331f4057a0373bbffd43f64ba0c32d2d155"}, - {file = "jiter-0.7.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bc1b55314ca97dbb6c48d9144323896e9c1a25d41c65bcb9550b3e0c270ca560"}, - {file = "jiter-0.7.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f281aae41b47e90deb70e7386558e877a8e62e1693e0086f37d015fa1c102289"}, - {file = "jiter-0.7.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:93c20d2730a84d43f7c0b6fb2579dc54335db742a59cf9776d0b80e99d587382"}, - {file = "jiter-0.7.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e81ccccd8069110e150613496deafa10da2f6ff322a707cbec2b0d52a87b9671"}, - {file = "jiter-0.7.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0a7d5e85766eff4c9be481d77e2226b4c259999cb6862ccac5ef6621d3c8dcce"}, - {file = "jiter-0.7.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f52ce5799df5b6975439ecb16b1e879d7655e1685b6e3758c9b1b97696313bfb"}, - {file = "jiter-0.7.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e0c91a0304373fdf97d56f88356a010bba442e6d995eb7773cbe32885b71cdd8"}, - {file = "jiter-0.7.1-cp39-none-win32.whl", hash = "sha256:5c08adf93e41ce2755970e8aa95262298afe2bf58897fb9653c47cd93c3c6cdc"}, - {file = "jiter-0.7.1-cp39-none-win_amd64.whl", hash = "sha256:6592f4067c74176e5f369228fb2995ed01400c9e8e1225fb73417183a5e635f0"}, - {file = "jiter-0.7.1.tar.gz", hash = "sha256:448cf4f74f7363c34cdef26214da527e8eeffd88ba06d0b80b485ad0667baf5d"}, + {file = "jiter-0.8.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:ca8577f6a413abe29b079bc30f907894d7eb07a865c4df69475e868d73e71c7b"}, + {file = "jiter-0.8.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b25bd626bde7fb51534190c7e3cb97cee89ee76b76d7585580e22f34f5e3f393"}, + {file = "jiter-0.8.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5c826a221851a8dc028eb6d7d6429ba03184fa3c7e83ae01cd6d3bd1d4bd17d"}, + {file = "jiter-0.8.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d35c864c2dff13dfd79fb070fc4fc6235d7b9b359efe340e1261deb21b9fcb66"}, + {file = "jiter-0.8.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f557c55bc2b7676e74d39d19bcb8775ca295c7a028246175d6a8b431e70835e5"}, + {file = "jiter-0.8.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:580ccf358539153db147e40751a0b41688a5ceb275e6f3e93d91c9467f42b2e3"}, + {file = "jiter-0.8.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af102d3372e917cffce49b521e4c32c497515119dc7bd8a75665e90a718bbf08"}, + {file = "jiter-0.8.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cadcc978f82397d515bb2683fc0d50103acff2a180552654bb92d6045dec2c49"}, + {file = "jiter-0.8.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ba5bdf56969cad2019d4e8ffd3f879b5fdc792624129741d3d83fc832fef8c7d"}, + {file = "jiter-0.8.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3b94a33a241bee9e34b8481cdcaa3d5c2116f575e0226e421bed3f7a6ea71cff"}, + {file = "jiter-0.8.2-cp310-cp310-win32.whl", hash = "sha256:6e5337bf454abddd91bd048ce0dca5134056fc99ca0205258766db35d0a2ea43"}, + {file = "jiter-0.8.2-cp310-cp310-win_amd64.whl", hash = "sha256:4a9220497ca0cb1fe94e3f334f65b9b5102a0b8147646118f020d8ce1de70105"}, + {file = "jiter-0.8.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:2dd61c5afc88a4fda7d8b2cf03ae5947c6ac7516d32b7a15bf4b49569a5c076b"}, + {file = "jiter-0.8.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a6c710d657c8d1d2adbbb5c0b0c6bfcec28fd35bd6b5f016395f9ac43e878a15"}, + {file = "jiter-0.8.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9584de0cd306072635fe4b89742bf26feae858a0683b399ad0c2509011b9dc0"}, + {file = "jiter-0.8.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5a90a923338531b7970abb063cfc087eebae6ef8ec8139762007188f6bc69a9f"}, + {file = "jiter-0.8.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d21974d246ed0181558087cd9f76e84e8321091ebfb3a93d4c341479a736f099"}, + {file = "jiter-0.8.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:32475a42b2ea7b344069dc1e81445cfc00b9d0e3ca837f0523072432332e9f74"}, + {file = "jiter-0.8.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b9931fd36ee513c26b5bf08c940b0ac875de175341cbdd4fa3be109f0492586"}, + {file = "jiter-0.8.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ce0820f4a3a59ddced7fce696d86a096d5cc48d32a4183483a17671a61edfddc"}, + {file = "jiter-0.8.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:8ffc86ae5e3e6a93765d49d1ab47b6075a9c978a2b3b80f0f32628f39caa0c88"}, + {file = "jiter-0.8.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5127dc1abd809431172bc3fbe8168d6b90556a30bb10acd5ded41c3cfd6f43b6"}, + {file = "jiter-0.8.2-cp311-cp311-win32.whl", hash = "sha256:66227a2c7b575720c1871c8800d3a0122bb8ee94edb43a5685aa9aceb2782d44"}, + {file = "jiter-0.8.2-cp311-cp311-win_amd64.whl", hash = "sha256:cde031d8413842a1e7501e9129b8e676e62a657f8ec8166e18a70d94d4682855"}, + {file = "jiter-0.8.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:e6ec2be506e7d6f9527dae9ff4b7f54e68ea44a0ef6b098256ddf895218a2f8f"}, + {file = "jiter-0.8.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:76e324da7b5da060287c54f2fabd3db5f76468006c811831f051942bf68c9d44"}, + {file = "jiter-0.8.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:180a8aea058f7535d1c84183c0362c710f4750bef66630c05f40c93c2b152a0f"}, + {file = "jiter-0.8.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:025337859077b41548bdcbabe38698bcd93cfe10b06ff66617a48ff92c9aec60"}, + {file = "jiter-0.8.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ecff0dc14f409599bbcafa7e470c00b80f17abc14d1405d38ab02e4b42e55b57"}, + {file = "jiter-0.8.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ffd9fee7d0775ebaba131f7ca2e2d83839a62ad65e8e02fe2bd8fc975cedeb9e"}, + {file = "jiter-0.8.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14601dcac4889e0a1c75ccf6a0e4baf70dbc75041e51bcf8d0e9274519df6887"}, + {file = "jiter-0.8.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:92249669925bc1c54fcd2ec73f70f2c1d6a817928480ee1c65af5f6b81cdf12d"}, + {file = "jiter-0.8.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e725edd0929fa79f8349ab4ec7f81c714df51dc4e991539a578e5018fa4a7152"}, + {file = "jiter-0.8.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bf55846c7b7a680eebaf9c3c48d630e1bf51bdf76c68a5f654b8524335b0ad29"}, + {file = "jiter-0.8.2-cp312-cp312-win32.whl", hash = "sha256:7efe4853ecd3d6110301665a5178b9856be7e2a9485f49d91aa4d737ad2ae49e"}, + {file = "jiter-0.8.2-cp312-cp312-win_amd64.whl", hash = "sha256:83c0efd80b29695058d0fd2fa8a556490dbce9804eac3e281f373bbc99045f6c"}, + {file = "jiter-0.8.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:ca1f08b8e43dc3bd0594c992fb1fd2f7ce87f7bf0d44358198d6da8034afdf84"}, + {file = "jiter-0.8.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5672a86d55416ccd214c778efccf3266b84f87b89063b582167d803246354be4"}, + {file = "jiter-0.8.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58dc9bc9767a1101f4e5e22db1b652161a225874d66f0e5cb8e2c7d1c438b587"}, + {file = "jiter-0.8.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:37b2998606d6dadbb5ccda959a33d6a5e853252d921fec1792fc902351bb4e2c"}, + {file = "jiter-0.8.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4ab9a87f3784eb0e098f84a32670cfe4a79cb6512fd8f42ae3d0709f06405d18"}, + {file = "jiter-0.8.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:79aec8172b9e3c6d05fd4b219d5de1ac616bd8da934107325a6c0d0e866a21b6"}, + {file = "jiter-0.8.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:711e408732d4e9a0208008e5892c2966b485c783cd2d9a681f3eb147cf36c7ef"}, + {file = "jiter-0.8.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:653cf462db4e8c41995e33d865965e79641ef45369d8a11f54cd30888b7e6ff1"}, + {file = "jiter-0.8.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:9c63eaef32b7bebac8ebebf4dabebdbc6769a09c127294db6babee38e9f405b9"}, + {file = "jiter-0.8.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:eb21aaa9a200d0a80dacc7a81038d2e476ffe473ffdd9c91eb745d623561de05"}, + {file = "jiter-0.8.2-cp313-cp313-win32.whl", hash = "sha256:789361ed945d8d42850f919342a8665d2dc79e7e44ca1c97cc786966a21f627a"}, + {file = "jiter-0.8.2-cp313-cp313-win_amd64.whl", hash = "sha256:ab7f43235d71e03b941c1630f4b6e3055d46b6cb8728a17663eaac9d8e83a865"}, + {file = "jiter-0.8.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b426f72cd77da3fec300ed3bc990895e2dd6b49e3bfe6c438592a3ba660e41ca"}, + {file = "jiter-0.8.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2dd880785088ff2ad21ffee205e58a8c1ddabc63612444ae41e5e4b321b39c0"}, + {file = "jiter-0.8.2-cp313-cp313t-win_amd64.whl", hash = "sha256:3ac9f578c46f22405ff7f8b1f5848fb753cc4b8377fbec8470a7dc3997ca7566"}, + {file = "jiter-0.8.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:9e1fa156ee9454642adb7e7234a383884452532bc9d53d5af2d18d98ada1d79c"}, + {file = "jiter-0.8.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0cf5dfa9956d96ff2efb0f8e9c7d055904012c952539a774305aaaf3abdf3d6c"}, + {file = "jiter-0.8.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e52bf98c7e727dd44f7c4acb980cb988448faeafed8433c867888268899b298b"}, + {file = "jiter-0.8.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a2ecaa3c23e7a7cf86d00eda3390c232f4d533cd9ddea4b04f5d0644faf642c5"}, + {file = "jiter-0.8.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:08d4c92bf480e19fc3f2717c9ce2aa31dceaa9163839a311424b6862252c943e"}, + {file = "jiter-0.8.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:99d9a1eded738299ba8e106c6779ce5c3893cffa0e32e4485d680588adae6db8"}, + {file = "jiter-0.8.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d20be8b7f606df096e08b0b1b4a3c6f0515e8dac296881fe7461dfa0fb5ec817"}, + {file = "jiter-0.8.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d33f94615fcaf872f7fd8cd98ac3b429e435c77619777e8a449d9d27e01134d1"}, + {file = "jiter-0.8.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:317b25e98a35ffec5c67efe56a4e9970852632c810d35b34ecdd70cc0e47b3b6"}, + {file = "jiter-0.8.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fc9043259ee430ecd71d178fccabd8c332a3bf1e81e50cae43cc2b28d19e4cb7"}, + {file = "jiter-0.8.2-cp38-cp38-win32.whl", hash = "sha256:fc5adda618205bd4678b146612ce44c3cbfdee9697951f2c0ffdef1f26d72b63"}, + {file = "jiter-0.8.2-cp38-cp38-win_amd64.whl", hash = "sha256:cd646c827b4f85ef4a78e4e58f4f5854fae0caf3db91b59f0d73731448a970c6"}, + {file = "jiter-0.8.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:e41e75344acef3fc59ba4765df29f107f309ca9e8eace5baacabd9217e52a5ee"}, + {file = "jiter-0.8.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7f22b16b35d5c1df9dfd58843ab2cd25e6bf15191f5a236bed177afade507bfc"}, + {file = "jiter-0.8.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7200b8f7619d36aa51c803fd52020a2dfbea36ffec1b5e22cab11fd34d95a6d"}, + {file = "jiter-0.8.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:70bf4c43652cc294040dbb62256c83c8718370c8b93dd93d934b9a7bf6c4f53c"}, + {file = "jiter-0.8.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f9d471356dc16f84ed48768b8ee79f29514295c7295cb41e1133ec0b2b8d637d"}, + {file = "jiter-0.8.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:859e8eb3507894093d01929e12e267f83b1d5f6221099d3ec976f0c995cb6bd9"}, + {file = "jiter-0.8.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eaa58399c01db555346647a907b4ef6d4f584b123943be6ed5588c3f2359c9f4"}, + {file = "jiter-0.8.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8f2d5ed877f089862f4c7aacf3a542627c1496f972a34d0474ce85ee7d939c27"}, + {file = "jiter-0.8.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:03c9df035d4f8d647f8c210ddc2ae0728387275340668fb30d2421e17d9a0841"}, + {file = "jiter-0.8.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8bd2a824d08d8977bb2794ea2682f898ad3d8837932e3a74937e93d62ecbb637"}, + {file = "jiter-0.8.2-cp39-cp39-win32.whl", hash = "sha256:ca29b6371ebc40e496995c94b988a101b9fbbed48a51190a4461fcb0a68b4a36"}, + {file = "jiter-0.8.2-cp39-cp39-win_amd64.whl", hash = "sha256:1c0dfbd1be3cbefc7510102370d86e35d1d53e5a93d48519688b1bf0f761160a"}, + {file = "jiter-0.8.2.tar.gz", hash = "sha256:cd73d3e740666d0e639f678adb176fad25c1bcbdae88d8d7b857e1783bb4212d"}, ] [[package]] name = "json5" -version = "0.9.28" +version = "0.10.0" description = "A Python implementation of the JSON5 data format." optional = false python-versions = ">=3.8.0" files = [ - {file = "json5-0.9.28-py3-none-any.whl", hash = "sha256:29c56f1accdd8bc2e037321237662034a7e07921e2b7223281a5ce2c46f0c4df"}, - {file = "json5-0.9.28.tar.gz", hash = "sha256:1f82f36e615bc5b42f1bbd49dbc94b12563c56408c6ffa06414ea310890e9a6e"}, + {file = "json5-0.10.0-py3-none-any.whl", hash = "sha256:19b23410220a7271e8377f81ba8aacba2fdd56947fbb137ee5977cbe1f5e8dfa"}, + {file = "json5-0.10.0.tar.gz", hash = "sha256:e66941c8f0a02026943c52c2eb34ebeb2a6f819a0be05920a6f5243cd30fd559"}, ] [package.extras] @@ -1894,13 +1893,13 @@ test = ["jupyter-server (>=2.0.0)", "pytest (>=7.0)", "pytest-jupyter[server] (> [[package]] name = "jupyterlab" -version = "4.3.1" +version = "4.3.3" description = "JupyterLab computational environment" optional = false python-versions = ">=3.8" files = [ - {file = "jupyterlab-4.3.1-py3-none-any.whl", hash = "sha256:2d9a1c305bc748e277819a17a5d5e22452e533e835f4237b2f30f3b0e491e01f"}, - {file = "jupyterlab-4.3.1.tar.gz", hash = "sha256:a4a338327556443521731d82f2a6ccf926df478914ca029616621704d47c3c65"}, + {file = "jupyterlab-4.3.3-py3-none-any.whl", hash = "sha256:32a8fd30677e734ffcc3916a4758b9dab21b02015b668c60eb36f84357b7d4b1"}, + {file = "jupyterlab-4.3.3.tar.gz", hash = "sha256:76fa39e548fdac94dc1204af5956c556f54c785f70ee26aa47ea08eda4d5bbcd"}, ] [package.dependencies] @@ -1915,7 +1914,7 @@ jupyter-server = ">=2.4.0,<3" jupyterlab-server = ">=2.27.1,<3" notebook-shim = ">=0.2" packaging = "*" -setuptools = ">=40.1.0" +setuptools = ">=40.8.0" tomli = {version = ">=1.2.2", markers = "python_version < \"3.11\""} tornado = ">=6.2.0" traitlets = "*" @@ -1977,7 +1976,7 @@ files = [ [[package]] name = "langchain-core" -version = "0.3.21" +version = "0.3.25" description = "Building applications with LLMs through composability" optional = false python-versions = ">=3.9,<4.0" @@ -1986,7 +1985,7 @@ develop = true [package.dependencies] jsonpatch = "^1.33" -langsmith = "^0.1.125" +langsmith = ">=0.1.125,<0.3" packaging = ">=23.2,<25" pydantic = [ {version = ">=2.5.2,<3.0.0", markers = "python_full_version < \"3.12.4\""}, @@ -2002,7 +2001,7 @@ url = "../core" [[package]] name = "langchain-openai" -version = "0.2.9" +version = "0.2.12" description = "An integration package connecting OpenAI and LangChain" optional = true python-versions = ">=3.9,<4.0" @@ -2010,8 +2009,8 @@ files = [] develop = true [package.dependencies] -langchain-core = "^0.3.17" -openai = "^1.54.0" +langchain-core = "^0.3.21" +openai = "^1.55.3" tiktoken = ">=0.7,<1" [package.source] @@ -2020,7 +2019,7 @@ url = "../partners/openai" [[package]] name = "langchain-tests" -version = "0.3.4" +version = "0.3.7" description = "Standard tests for LangChain implementations" optional = false python-versions = ">=3.9,<4.0" @@ -2028,9 +2027,15 @@ files = [] develop = true [package.dependencies] -httpx = "^0.27.0" -langchain-core = "^0.3.19" +httpx = ">=0.25.0,<1" +langchain-core = "^0.3.22" +numpy = [ + {version = ">=1.24.0,<2.0.0", markers = "python_version < \"3.12\""}, + {version = ">=1.26.2,<3", markers = "python_version >= \"3.12\""}, +] pytest = ">=7,<9" +pytest-asyncio = ">=0.20,<1" +pytest-socket = ">=0.6.0,<1" syrupy = "^4" [package.source] @@ -2039,7 +2044,7 @@ url = "../standard-tests" [[package]] name = "langchain-text-splitters" -version = "0.3.2" +version = "0.3.3" description = "LangChain text splitting utilities" optional = false python-versions = ">=3.9,<4.0" @@ -2047,7 +2052,7 @@ files = [] develop = true [package.dependencies] -langchain-core = "^0.3.15" +langchain-core = "^0.3.25" [package.source] type = "directory" @@ -2071,13 +2076,13 @@ types-requests = ">=2.31.0.2,<3.0.0.0" [[package]] name = "langsmith" -version = "0.1.146" +version = "0.2.3" description = "Client library to connect to the LangSmith LLM Tracing and Evaluation Platform." optional = false -python-versions = "<4.0,>=3.8.1" +python-versions = "<4.0,>=3.9" files = [ - {file = "langsmith-0.1.146-py3-none-any.whl", hash = "sha256:9d062222f1a32c9b047dab0149b24958f988989cd8d4a5f9139ff959a51e59d8"}, - {file = "langsmith-0.1.146.tar.gz", hash = "sha256:ead8b0b9d5b6cd3ac42937ec48bdf09d4afe7ca1bba22dc05eb65591a18106f8"}, + {file = "langsmith-0.2.3-py3-none-any.whl", hash = "sha256:4958b6e918f57fedba6ddc55b8534d1e06478bb44c779aa73713ce898ca6ae87"}, + {file = "langsmith-0.2.3.tar.gz", hash = "sha256:54c231b07fdeb0f8472925074a0ec0ed2cb654a0437d63c6ccf76a9da635900d"}, ] [package.dependencies] @@ -2090,6 +2095,9 @@ pydantic = [ requests = ">=2,<3" requests-toolbelt = ">=1.0.0,<2.0.0" +[package.extras] +langsmith-pyo3 = ["langsmith-pyo3 (>=0.1.0rc2,<0.2.0)"] + [[package]] name = "lark" version = "1.2.2" @@ -2387,13 +2395,13 @@ types-protobuf = ">=4.24" [[package]] name = "nbclient" -version = "0.10.0" +version = "0.10.1" description = "A client library for executing notebooks. Formerly nbconvert's ExecutePreprocessor." optional = false python-versions = ">=3.8.0" files = [ - {file = "nbclient-0.10.0-py3-none-any.whl", hash = "sha256:f13e3529332a1f1f81d82a53210322476a168bb7090a0289c795fe9cc11c9d3f"}, - {file = "nbclient-0.10.0.tar.gz", hash = "sha256:4b3f1b7dba531e498449c4db4f53da339c91d449dc11e9af3a43b4eb5c5abb09"}, + {file = "nbclient-0.10.1-py3-none-any.whl", hash = "sha256:949019b9240d66897e442888cfb618f69ef23dc71c01cb5fced8499c2cfc084d"}, + {file = "nbclient-0.10.1.tar.gz", hash = "sha256:3e93e348ab27e712acd46fccd809139e356eb9a31aab641d1a7991a6eb4e6f68"}, ] [package.dependencies] @@ -2404,7 +2412,7 @@ traitlets = ">=5.4" [package.extras] dev = ["pre-commit"] -docs = ["autodoc-traits", "mock", "moto", "myst-parser", "nbclient[test]", "sphinx (>=1.7)", "sphinx-book-theme", "sphinxcontrib-spelling"] +docs = ["autodoc-traits", "flaky", "ipykernel (>=6.19.3)", "ipython", "ipywidgets", "mock", "moto", "myst-parser", "nbconvert (>=7.0.0)", "pytest (>=7.0,<8)", "pytest-asyncio", "pytest-cov (>=4.0)", "sphinx (>=1.7)", "sphinx-book-theme", "sphinxcontrib-spelling", "testpath", "xmltodict"] test = ["flaky", "ipykernel (>=6.19.3)", "ipython", "ipywidgets", "nbconvert (>=7.0.0)", "pytest (>=7.0,<8)", "pytest-asyncio", "pytest-cov (>=4.0)", "testpath", "xmltodict"] [[package]] @@ -2479,26 +2487,26 @@ files = [ [[package]] name = "notebook" -version = "7.0.7" +version = "7.3.1" description = "Jupyter Notebook - A web-based notebook environment for interactive computing" optional = false python-versions = ">=3.8" files = [ - {file = "notebook-7.0.7-py3-none-any.whl", hash = "sha256:289b606d7e173f75a18beb1406ef411b43f97f7a9c55ba03efa3622905a62346"}, - {file = "notebook-7.0.7.tar.gz", hash = "sha256:3bcff00c17b3ac142ef5f436d50637d936b274cfa0b41f6ac0175363de9b4e09"}, + {file = "notebook-7.3.1-py3-none-any.whl", hash = "sha256:212e1486b2230fe22279043f33c7db5cf9a01d29feb063a85cb139747b7c9483"}, + {file = "notebook-7.3.1.tar.gz", hash = "sha256:84381c2a82d867517fd25b86e986dae1fe113a70b98f03edff9b94e499fec8fa"}, ] [package.dependencies] jupyter-server = ">=2.4.0,<3" -jupyterlab = ">=4.0.2,<5" -jupyterlab-server = ">=2.22.1,<3" +jupyterlab = ">=4.3.2,<4.4" +jupyterlab-server = ">=2.27.1,<3" notebook-shim = ">=0.2,<0.3" tornado = ">=6.2.0" [package.extras] dev = ["hatch", "pre-commit"] docs = ["myst-parser", "nbsphinx", "pydata-sphinx-theme", "sphinx (>=1.3.6)", "sphinxcontrib-github-alt", "sphinxcontrib-spelling"] -test = ["importlib-resources (>=5.0)", "ipykernel", "jupyter-server[test] (>=2.4.0,<3)", "jupyterlab-server[test] (>=2.22.1,<3)", "nbval", "pytest (>=7.0)", "pytest-console-scripts", "pytest-timeout", "pytest-tornasync", "requests"] +test = ["importlib-resources (>=5.0)", "ipykernel", "jupyter-server[test] (>=2.4.0,<3)", "jupyterlab-server[test] (>=2.27.1,<3)", "nbval", "pytest (>=7.0)", "pytest-console-scripts", "pytest-timeout", "pytest-tornasync", "requests"] [[package]] name = "notebook-shim" @@ -2564,77 +2572,77 @@ files = [ [[package]] name = "numpy" -version = "2.1.3" +version = "2.2.0" description = "Fundamental package for array computing in Python" optional = false python-versions = ">=3.10" files = [ - {file = "numpy-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c894b4305373b9c5576d7a12b473702afdf48ce5369c074ba304cc5ad8730dff"}, - {file = "numpy-2.1.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b47fbb433d3260adcd51eb54f92a2ffbc90a4595f8970ee00e064c644ac788f5"}, - {file = "numpy-2.1.3-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:825656d0743699c529c5943554d223c021ff0494ff1442152ce887ef4f7561a1"}, - {file = "numpy-2.1.3-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:6a4825252fcc430a182ac4dee5a505053d262c807f8a924603d411f6718b88fd"}, - {file = "numpy-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e711e02f49e176a01d0349d82cb5f05ba4db7d5e7e0defd026328e5cfb3226d3"}, - {file = "numpy-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78574ac2d1a4a02421f25da9559850d59457bac82f2b8d7a44fe83a64f770098"}, - {file = "numpy-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c7662f0e3673fe4e832fe07b65c50342ea27d989f92c80355658c7f888fcc83c"}, - {file = "numpy-2.1.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:fa2d1337dc61c8dc417fbccf20f6d1e139896a30721b7f1e832b2bb6ef4eb6c4"}, - {file = "numpy-2.1.3-cp310-cp310-win32.whl", hash = "sha256:72dcc4a35a8515d83e76b58fdf8113a5c969ccd505c8a946759b24e3182d1f23"}, - {file = "numpy-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:ecc76a9ba2911d8d37ac01de72834d8849e55473457558e12995f4cd53e778e0"}, - {file = "numpy-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4d1167c53b93f1f5d8a139a742b3c6f4d429b54e74e6b57d0eff40045187b15d"}, - {file = "numpy-2.1.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c80e4a09b3d95b4e1cac08643f1152fa71a0a821a2d4277334c88d54b2219a41"}, - {file = "numpy-2.1.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:576a1c1d25e9e02ed7fa5477f30a127fe56debd53b8d2c89d5578f9857d03ca9"}, - {file = "numpy-2.1.3-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:973faafebaae4c0aaa1a1ca1ce02434554d67e628b8d805e61f874b84e136b09"}, - {file = "numpy-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:762479be47a4863e261a840e8e01608d124ee1361e48b96916f38b119cfda04a"}, - {file = "numpy-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc6f24b3d1ecc1eebfbf5d6051faa49af40b03be1aaa781ebdadcbc090b4539b"}, - {file = "numpy-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:17ee83a1f4fef3c94d16dc1802b998668b5419362c8a4f4e8a491de1b41cc3ee"}, - {file = "numpy-2.1.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:15cb89f39fa6d0bdfb600ea24b250e5f1a3df23f901f51c8debaa6a5d122b2f0"}, - {file = "numpy-2.1.3-cp311-cp311-win32.whl", hash = "sha256:d9beb777a78c331580705326d2367488d5bc473b49a9bc3036c154832520aca9"}, - {file = "numpy-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:d89dd2b6da69c4fff5e39c28a382199ddedc3a5be5390115608345dec660b9e2"}, - {file = "numpy-2.1.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f55ba01150f52b1027829b50d70ef1dafd9821ea82905b63936668403c3b471e"}, - {file = "numpy-2.1.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:13138eadd4f4da03074851a698ffa7e405f41a0845a6b1ad135b81596e4e9958"}, - {file = "numpy-2.1.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:a6b46587b14b888e95e4a24d7b13ae91fa22386c199ee7b418f449032b2fa3b8"}, - {file = "numpy-2.1.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:0fa14563cc46422e99daef53d725d0c326e99e468a9320a240affffe87852564"}, - {file = "numpy-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8637dcd2caa676e475503d1f8fdb327bc495554e10838019651b76d17b98e512"}, - {file = "numpy-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2312b2aa89e1f43ecea6da6ea9a810d06aae08321609d8dc0d0eda6d946a541b"}, - {file = "numpy-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a38c19106902bb19351b83802531fea19dee18e5b37b36454f27f11ff956f7fc"}, - {file = "numpy-2.1.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:02135ade8b8a84011cbb67dc44e07c58f28575cf9ecf8ab304e51c05528c19f0"}, - {file = "numpy-2.1.3-cp312-cp312-win32.whl", hash = "sha256:e6988e90fcf617da2b5c78902fe8e668361b43b4fe26dbf2d7b0f8034d4cafb9"}, - {file = "numpy-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:0d30c543f02e84e92c4b1f415b7c6b5326cbe45ee7882b6b77db7195fb971e3a"}, - {file = "numpy-2.1.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96fe52fcdb9345b7cd82ecd34547fca4321f7656d500eca497eb7ea5a926692f"}, - {file = "numpy-2.1.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f653490b33e9c3a4c1c01d41bc2aef08f9475af51146e4a7710c450cf9761598"}, - {file = "numpy-2.1.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:dc258a761a16daa791081d026f0ed4399b582712e6fc887a95af09df10c5ca57"}, - {file = "numpy-2.1.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:016d0f6f5e77b0f0d45d77387ffa4bb89816b57c835580c3ce8e099ef830befe"}, - {file = "numpy-2.1.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c181ba05ce8299c7aa3125c27b9c2167bca4a4445b7ce73d5febc411ca692e43"}, - {file = "numpy-2.1.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5641516794ca9e5f8a4d17bb45446998c6554704d888f86df9b200e66bdcce56"}, - {file = "numpy-2.1.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ea4dedd6e394a9c180b33c2c872b92f7ce0f8e7ad93e9585312b0c5a04777a4a"}, - {file = "numpy-2.1.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b0df3635b9c8ef48bd3be5f862cf71b0a4716fa0e702155c45067c6b711ddcef"}, - {file = "numpy-2.1.3-cp313-cp313-win32.whl", hash = "sha256:50ca6aba6e163363f132b5c101ba078b8cbd3fa92c7865fd7d4d62d9779ac29f"}, - {file = "numpy-2.1.3-cp313-cp313-win_amd64.whl", hash = "sha256:747641635d3d44bcb380d950679462fae44f54b131be347d5ec2bce47d3df9ed"}, - {file = "numpy-2.1.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:996bb9399059c5b82f76b53ff8bb686069c05acc94656bb259b1d63d04a9506f"}, - {file = "numpy-2.1.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:45966d859916ad02b779706bb43b954281db43e185015df6eb3323120188f9e4"}, - {file = "numpy-2.1.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:baed7e8d7481bfe0874b566850cb0b85243e982388b7b23348c6db2ee2b2ae8e"}, - {file = "numpy-2.1.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:a9f7f672a3388133335589cfca93ed468509cb7b93ba3105fce780d04a6576a0"}, - {file = "numpy-2.1.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7aac50327da5d208db2eec22eb11e491e3fe13d22653dce51b0f4109101b408"}, - {file = "numpy-2.1.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4394bc0dbd074b7f9b52024832d16e019decebf86caf909d94f6b3f77a8ee3b6"}, - {file = "numpy-2.1.3-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:50d18c4358a0a8a53f12a8ba9d772ab2d460321e6a93d6064fc22443d189853f"}, - {file = "numpy-2.1.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:14e253bd43fc6b37af4921b10f6add6925878a42a0c5fe83daee390bca80bc17"}, - {file = "numpy-2.1.3-cp313-cp313t-win32.whl", hash = "sha256:08788d27a5fd867a663f6fc753fd7c3ad7e92747efc73c53bca2f19f8bc06f48"}, - {file = "numpy-2.1.3-cp313-cp313t-win_amd64.whl", hash = "sha256:2564fbdf2b99b3f815f2107c1bbc93e2de8ee655a69c261363a1172a79a257d4"}, - {file = "numpy-2.1.3-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:4f2015dfe437dfebbfce7c85c7b53d81ba49e71ba7eadbf1df40c915af75979f"}, - {file = "numpy-2.1.3-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:3522b0dfe983a575e6a9ab3a4a4dfe156c3e428468ff08ce582b9bb6bd1d71d4"}, - {file = "numpy-2.1.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c006b607a865b07cd981ccb218a04fc86b600411d83d6fc261357f1c0966755d"}, - {file = "numpy-2.1.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e14e26956e6f1696070788252dcdff11b4aca4c3e8bd166e0df1bb8f315a67cb"}, - {file = "numpy-2.1.3.tar.gz", hash = "sha256:aa08e04e08aaf974d4458def539dece0d28146d866a39da5639596f4921fd761"}, + {file = "numpy-2.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1e25507d85da11ff5066269d0bd25d06e0a0f2e908415534f3e603d2a78e4ffa"}, + {file = "numpy-2.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a62eb442011776e4036af5c8b1a00b706c5bc02dc15eb5344b0c750428c94219"}, + {file = "numpy-2.2.0-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:b606b1aaf802e6468c2608c65ff7ece53eae1a6874b3765f69b8ceb20c5fa78e"}, + {file = "numpy-2.2.0-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:36b2b43146f646642b425dd2027730f99bac962618ec2052932157e213a040e9"}, + {file = "numpy-2.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7fe8f3583e0607ad4e43a954e35c1748b553bfe9fdac8635c02058023277d1b3"}, + {file = "numpy-2.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:122fd2fcfafdefc889c64ad99c228d5a1f9692c3a83f56c292618a59aa60ae83"}, + {file = "numpy-2.2.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3f2f5cddeaa4424a0a118924b988746db6ffa8565e5829b1841a8a3bd73eb59a"}, + {file = "numpy-2.2.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7fe4bb0695fe986a9e4deec3b6857003b4cfe5c5e4aac0b95f6a658c14635e31"}, + {file = "numpy-2.2.0-cp310-cp310-win32.whl", hash = "sha256:b30042fe92dbd79f1ba7f6898fada10bdaad1847c44f2dff9a16147e00a93661"}, + {file = "numpy-2.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:54dc1d6d66f8d37843ed281773c7174f03bf7ad826523f73435deb88ba60d2d4"}, + {file = "numpy-2.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9874bc2ff574c40ab7a5cbb7464bf9b045d617e36754a7bc93f933d52bd9ffc6"}, + {file = "numpy-2.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0da8495970f6b101ddd0c38ace92edea30e7e12b9a926b57f5fabb1ecc25bb90"}, + {file = "numpy-2.2.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:0557eebc699c1c34cccdd8c3778c9294e8196df27d713706895edc6f57d29608"}, + {file = "numpy-2.2.0-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:3579eaeb5e07f3ded59298ce22b65f877a86ba8e9fe701f5576c99bb17c283da"}, + {file = "numpy-2.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40deb10198bbaa531509aad0cd2f9fadb26c8b94070831e2208e7df543562b74"}, + {file = "numpy-2.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c2aed8fcf8abc3020d6a9ccb31dbc9e7d7819c56a348cc88fd44be269b37427e"}, + {file = "numpy-2.2.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a222d764352c773aa5ebde02dd84dba3279c81c6db2e482d62a3fa54e5ece69b"}, + {file = "numpy-2.2.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4e58666988605e251d42c2818c7d3d8991555381be26399303053b58a5bbf30d"}, + {file = "numpy-2.2.0-cp311-cp311-win32.whl", hash = "sha256:4723a50e1523e1de4fccd1b9a6dcea750c2102461e9a02b2ac55ffeae09a4410"}, + {file = "numpy-2.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:16757cf28621e43e252c560d25b15f18a2f11da94fea344bf26c599b9cf54b73"}, + {file = "numpy-2.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cff210198bb4cae3f3c100444c5eaa573a823f05c253e7188e1362a5555235b3"}, + {file = "numpy-2.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:58b92a5828bd4d9aa0952492b7de803135038de47343b2aa3cc23f3b71a3dc4e"}, + {file = "numpy-2.2.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:ebe5e59545401fbb1b24da76f006ab19734ae71e703cdb4a8b347e84a0cece67"}, + {file = "numpy-2.2.0-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:e2b8cd48a9942ed3f85b95ca4105c45758438c7ed28fff1e4ce3e57c3b589d8e"}, + {file = "numpy-2.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57fcc997ffc0bef234b8875a54d4058afa92b0b0c4223fc1f62f24b3b5e86038"}, + {file = "numpy-2.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85ad7d11b309bd132d74397fcf2920933c9d1dc865487128f5c03d580f2c3d03"}, + {file = "numpy-2.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:cb24cca1968b21355cc6f3da1a20cd1cebd8a023e3c5b09b432444617949085a"}, + {file = "numpy-2.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0798b138c291d792f8ea40fe3768610f3c7dd2574389e37c3f26573757c8f7ef"}, + {file = "numpy-2.2.0-cp312-cp312-win32.whl", hash = "sha256:afe8fb968743d40435c3827632fd36c5fbde633b0423da7692e426529b1759b1"}, + {file = "numpy-2.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:3a4199f519e57d517ebd48cb76b36c82da0360781c6a0353e64c0cac30ecaad3"}, + {file = "numpy-2.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f8c8b141ef9699ae777c6278b52c706b653bf15d135d302754f6b2e90eb30367"}, + {file = "numpy-2.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0f0986e917aca18f7a567b812ef7ca9391288e2acb7a4308aa9d265bd724bdae"}, + {file = "numpy-2.2.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:1c92113619f7b272838b8d6702a7f8ebe5edea0df48166c47929611d0b4dea69"}, + {file = "numpy-2.2.0-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:5a145e956b374e72ad1dff82779177d4a3c62bc8248f41b80cb5122e68f22d13"}, + {file = "numpy-2.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18142b497d70a34b01642b9feabb70156311b326fdddd875a9981f34a369b671"}, + {file = "numpy-2.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a7d41d1612c1a82b64697e894b75db6758d4f21c3ec069d841e60ebe54b5b571"}, + {file = "numpy-2.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a98f6f20465e7618c83252c02041517bd2f7ea29be5378f09667a8f654a5918d"}, + {file = "numpy-2.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e09d40edfdb4e260cb1567d8ae770ccf3b8b7e9f0d9b5c2a9992696b30ce2742"}, + {file = "numpy-2.2.0-cp313-cp313-win32.whl", hash = "sha256:3905a5fffcc23e597ee4d9fb3fcd209bd658c352657548db7316e810ca80458e"}, + {file = "numpy-2.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:a184288538e6ad699cbe6b24859206e38ce5fba28f3bcfa51c90d0502c1582b2"}, + {file = "numpy-2.2.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:7832f9e8eb00be32f15fdfb9a981d6955ea9adc8574c521d48710171b6c55e95"}, + {file = "numpy-2.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f0dd071b95bbca244f4cb7f70b77d2ff3aaaba7fa16dc41f58d14854a6204e6c"}, + {file = "numpy-2.2.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:b0b227dcff8cdc3efbce66d4e50891f04d0a387cce282fe1e66199146a6a8fca"}, + {file = "numpy-2.2.0-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:6ab153263a7c5ccaf6dfe7e53447b74f77789f28ecb278c3b5d49db7ece10d6d"}, + {file = "numpy-2.2.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e500aba968a48e9019e42c0c199b7ec0696a97fa69037bea163b55398e390529"}, + {file = "numpy-2.2.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:440cfb3db4c5029775803794f8638fbdbf71ec702caf32735f53b008e1eaece3"}, + {file = "numpy-2.2.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a55dc7a7f0b6198b07ec0cd445fbb98b05234e8b00c5ac4874a63372ba98d4ab"}, + {file = "numpy-2.2.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4bddbaa30d78c86329b26bd6aaaea06b1e47444da99eddac7bf1e2fab717bd72"}, + {file = "numpy-2.2.0-cp313-cp313t-win32.whl", hash = "sha256:30bf971c12e4365153afb31fc73f441d4da157153f3400b82db32d04de1e4066"}, + {file = "numpy-2.2.0-cp313-cp313t-win_amd64.whl", hash = "sha256:d35717333b39d1b6bb8433fa758a55f1081543de527171543a2b710551d40881"}, + {file = "numpy-2.2.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e12c6c1ce84628c52d6367863773f7c8c8241be554e8b79686e91a43f1733773"}, + {file = "numpy-2.2.0-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:b6207dc8fb3c8cb5668e885cef9ec7f70189bec4e276f0ff70d5aa078d32c88e"}, + {file = "numpy-2.2.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a50aeff71d0f97b6450d33940c7181b08be1441c6c193e678211bff11aa725e7"}, + {file = "numpy-2.2.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:df12a1f99b99f569a7c2ae59aa2d31724e8d835fc7f33e14f4792e3071d11221"}, + {file = "numpy-2.2.0.tar.gz", hash = "sha256:140dd80ff8981a583a60980be1a655068f8adebf7a45a06a6858c873fcdcd4a0"}, ] [[package]] name = "openai" -version = "1.55.1" +version = "1.57.4" description = "The official Python library for the openai API" optional = true python-versions = ">=3.8" files = [ - {file = "openai-1.55.1-py3-none-any.whl", hash = "sha256:d10d96a4f9dc5f05d38dea389119ec8dcd24bc9698293c8357253c601b4a77a5"}, - {file = "openai-1.55.1.tar.gz", hash = "sha256:471324321e7739214f16a544e801947a046d3c5d516fae8719a317234e4968d3"}, + {file = "openai-1.57.4-py3-none-any.whl", hash = "sha256:7def1ab2d52f196357ce31b9cfcf4181529ce00838286426bb35be81c035dafb"}, + {file = "openai-1.57.4.tar.gz", hash = "sha256:a8f071a3e9198e2818f63aade68e759417b9f62c0971bdb83de82504b70b77f7"}, ] [package.dependencies] @@ -2767,30 +2775,41 @@ files = [ {file = "pandas-2.2.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:381175499d3802cde0eabbaf6324cce0c4f5d52ca6f8c377c29ad442f50f6348"}, {file = "pandas-2.2.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d9c45366def9a3dd85a6454c0e7908f2b3b8e9c138f5dc38fed7ce720d8453ed"}, {file = "pandas-2.2.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86976a1c5b25ae3f8ccae3a5306e443569ee3c3faf444dfd0f41cda24667ad57"}, + {file = "pandas-2.2.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b8661b0238a69d7aafe156b7fa86c44b881387509653fdf857bebc5e4008ad42"}, {file = "pandas-2.2.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:37e0aced3e8f539eccf2e099f65cdb9c8aa85109b0be6e93e2baff94264bdc6f"}, {file = "pandas-2.2.3-cp310-cp310-win_amd64.whl", hash = "sha256:56534ce0746a58afaf7942ba4863e0ef81c9c50d3f0ae93e9497d6a41a057645"}, {file = "pandas-2.2.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:66108071e1b935240e74525006034333f98bcdb87ea116de573a6a0dccb6c039"}, {file = "pandas-2.2.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7c2875855b0ff77b2a64a0365e24455d9990730d6431b9e0ee18ad8acee13dbd"}, + {file = "pandas-2.2.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd8d0c3be0515c12fed0bdbae072551c8b54b7192c7b1fda0ba56059a0179698"}, {file = "pandas-2.2.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c124333816c3a9b03fbeef3a9f230ba9a737e9e5bb4060aa2107a86cc0a497fc"}, + {file = "pandas-2.2.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:63cc132e40a2e084cf01adf0775b15ac515ba905d7dcca47e9a251819c575ef3"}, {file = "pandas-2.2.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:29401dbfa9ad77319367d36940cd8a0b3a11aba16063e39632d98b0e931ddf32"}, {file = "pandas-2.2.3-cp311-cp311-win_amd64.whl", hash = "sha256:3fc6873a41186404dad67245896a6e440baacc92f5b716ccd1bc9ed2995ab2c5"}, {file = "pandas-2.2.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b1d432e8d08679a40e2a6d8b2f9770a5c21793a6f9f47fdd52c5ce1948a5a8a9"}, {file = "pandas-2.2.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a5a1595fe639f5988ba6a8e5bc9649af3baf26df3998a0abe56c02609392e0a4"}, + {file = "pandas-2.2.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5de54125a92bb4d1c051c0659e6fcb75256bf799a732a87184e5ea503965bce3"}, {file = "pandas-2.2.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fffb8ae78d8af97f849404f21411c95062db1496aeb3e56f146f0355c9989319"}, + {file = "pandas-2.2.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6dfcb5ee8d4d50c06a51c2fffa6cff6272098ad6540aed1a76d15fb9318194d8"}, {file = "pandas-2.2.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:062309c1b9ea12a50e8ce661145c6aab431b1e99530d3cd60640e255778bd43a"}, {file = "pandas-2.2.3-cp312-cp312-win_amd64.whl", hash = "sha256:59ef3764d0fe818125a5097d2ae867ca3fa64df032331b7e0917cf5d7bf66b13"}, {file = "pandas-2.2.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f00d1345d84d8c86a63e476bb4955e46458b304b9575dcf71102b5c705320015"}, {file = "pandas-2.2.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3508d914817e153ad359d7e069d752cdd736a247c322d932eb89e6bc84217f28"}, + {file = "pandas-2.2.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22a9d949bfc9a502d320aa04e5d02feab689d61da4e7764b62c30b991c42c5f0"}, {file = "pandas-2.2.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3a255b2c19987fbbe62a9dfd6cff7ff2aa9ccab3fc75218fd4b7530f01efa24"}, + {file = "pandas-2.2.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:800250ecdadb6d9c78eae4990da62743b857b470883fa27f652db8bdde7f6659"}, {file = "pandas-2.2.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6374c452ff3ec675a8f46fd9ab25c4ad0ba590b71cf0656f8b6daa5202bca3fb"}, {file = "pandas-2.2.3-cp313-cp313-win_amd64.whl", hash = "sha256:61c5ad4043f791b61dd4752191d9f07f0ae412515d59ba8f005832a532f8736d"}, {file = "pandas-2.2.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3b71f27954685ee685317063bf13c7709a7ba74fc996b84fc6821c59b0f06468"}, {file = "pandas-2.2.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:38cf8125c40dae9d5acc10fa66af8ea6fdf760b2714ee482ca691fc66e6fcb18"}, + {file = "pandas-2.2.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ba96630bc17c875161df3818780af30e43be9b166ce51c9a18c1feae342906c2"}, {file = "pandas-2.2.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1db71525a1538b30142094edb9adc10be3f3e176748cd7acc2240c2f2e5aa3a4"}, + {file = "pandas-2.2.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:15c0e1e02e93116177d29ff83e8b1619c93ddc9c49083f237d4312337a61165d"}, {file = "pandas-2.2.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ad5b65698ab28ed8d7f18790a0dc58005c7629f227be9ecc1072aa74c0c1d43a"}, {file = "pandas-2.2.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bc6b93f9b966093cb0fd62ff1a7e4c09e6d546ad7c1de191767baffc57628f39"}, {file = "pandas-2.2.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5dbca4c1acd72e8eeef4753eeca07de9b1db4f398669d5994086f788a5d7cc30"}, + {file = "pandas-2.2.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8cd6d7cc958a3910f934ea8dbdf17b2364827bb4dafc38ce6eef6bb3d65ff09c"}, {file = "pandas-2.2.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99df71520d25fade9db7c1076ac94eb994f4d2673ef2aa2e86ee039b6746d20c"}, + {file = "pandas-2.2.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:31d0ced62d4ea3e231a9f228366919a5ea0b07440d9d4dac345376fd8e1477ea"}, {file = "pandas-2.2.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7eee9e7cea6adf3e3d24e304ac6b8300646e2a5d1cd3a3c2abed9101b0846761"}, {file = "pandas-2.2.3-cp39-cp39-win_amd64.whl", hash = "sha256:4850ba03528b6dd51d6c5d273c46f183f39a9baf3f0143e566b89450965b105e"}, {file = "pandas-2.2.3.tar.gz", hash = "sha256:4f18ba62b61d7e192368b84517265a99b4d7ee8912f8708660fb4a366cc82667"}, @@ -2889,18 +2908,18 @@ type = ["mypy (>=1.11.2)"] [[package]] name = "playwright" -version = "1.49.0" +version = "1.49.1" description = "A high-level API to automate web browsers" optional = false python-versions = ">=3.9" files = [ - {file = "playwright-1.49.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:704532a2d8ba580ec9e1895bfeafddce2e3d52320d4eb8aa38e80376acc5cbb0"}, - {file = "playwright-1.49.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:e453f02c4e5cc2db7e9759c47e7425f32e50ac76c76b7eb17c69eed72f01c4d8"}, - {file = "playwright-1.49.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:37ae985309184472946a6eb1a237e5d93c9e58a781fa73b75c8751325002a5d4"}, - {file = "playwright-1.49.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:68d94beffb3c9213e3ceaafa66171affd9a5d9162e0c8a3eed1b1132c2e57598"}, - {file = "playwright-1.49.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f12d2aecdb41fc25a624cb15f3e8391c252ebd81985e3d5c1c261fe93779345"}, - {file = "playwright-1.49.0-py3-none-win32.whl", hash = "sha256:91103de52d470594ad375b512d7143fa95d6039111ae11a93eb4fe2f2b4a4858"}, - {file = "playwright-1.49.0-py3-none-win_amd64.whl", hash = "sha256:34d28a2c2d46403368610be4339898dc9c34eb9f7c578207b4715c49743a072a"}, + {file = "playwright-1.49.1-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:1041ffb45a0d0bc44d698d3a5aa3ac4b67c9bd03540da43a0b70616ad52592b8"}, + {file = "playwright-1.49.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:9f38ed3d0c1f4e0a6d1c92e73dd9a61f8855133249d6f0cec28648d38a7137be"}, + {file = "playwright-1.49.1-py3-none-macosx_11_0_universal2.whl", hash = "sha256:3be48c6d26dc819ca0a26567c1ae36a980a0303dcd4249feb6f59e115aaddfb8"}, + {file = "playwright-1.49.1-py3-none-manylinux1_x86_64.whl", hash = "sha256:753ca90ee31b4b03d165cfd36e477309ebf2b4381953f2a982ff612d85b147d2"}, + {file = "playwright-1.49.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd9bc8dab37aa25198a01f555f0a2e2c3813fe200fef018ac34dfe86b34994b9"}, + {file = "playwright-1.49.1-py3-none-win32.whl", hash = "sha256:43b304be67f096058e587dac453ece550eff87b8fbed28de30f4f022cc1745bb"}, + {file = "playwright-1.49.1-py3-none-win_amd64.whl", hash = "sha256:47b23cb346283278f5b4d1e1990bcb6d6302f80c0aa0ca93dd0601a1400191df"}, ] [package.dependencies] @@ -2924,13 +2943,13 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "prometheus-client" -version = "0.21.0" +version = "0.21.1" description = "Python client for the Prometheus monitoring system." optional = false python-versions = ">=3.8" files = [ - {file = "prometheus_client-0.21.0-py3-none-any.whl", hash = "sha256:4fa6b4dd0ac16d58bb587c04b1caae65b8c5043e85f778f42f5f632f6af2e166"}, - {file = "prometheus_client-0.21.0.tar.gz", hash = "sha256:96c83c606b71ff2b0a433c98889d275f51ffec6c5e267de37c7a2b5c9aa9233e"}, + {file = "prometheus_client-0.21.1-py3-none-any.whl", hash = "sha256:594b45c410d6f4f8888940fe80b5cc2521b305a1fafe1c58609ef715a001f301"}, + {file = "prometheus_client-0.21.1.tar.gz", hash = "sha256:252505a722ac04b0456be05c05f75f45d760c2911ffc45f2a06bcaed9f3ae3fb"}, ] [package.extras] @@ -2952,129 +2971,113 @@ wcwidth = "*" [[package]] name = "propcache" -version = "0.2.0" +version = "0.2.1" description = "Accelerated property cache" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "propcache-0.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c5869b8fd70b81835a6f187c5fdbe67917a04d7e52b6e7cc4e5fe39d55c39d58"}, - {file = "propcache-0.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:952e0d9d07609d9c5be361f33b0d6d650cd2bae393aabb11d9b719364521984b"}, - {file = "propcache-0.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:33ac8f098df0585c0b53009f039dfd913b38c1d2edafed0cedcc0c32a05aa110"}, - {file = "propcache-0.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:97e48e8875e6c13909c800fa344cd54cc4b2b0db1d5f911f840458a500fde2c2"}, - {file = "propcache-0.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:388f3217649d6d59292b722d940d4d2e1e6a7003259eb835724092a1cca0203a"}, - {file = "propcache-0.2.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f571aea50ba5623c308aa146eb650eebf7dbe0fd8c5d946e28343cb3b5aad577"}, - {file = "propcache-0.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3dfafb44f7bb35c0c06eda6b2ab4bfd58f02729e7c4045e179f9a861b07c9850"}, - {file = "propcache-0.2.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a3ebe9a75be7ab0b7da2464a77bb27febcb4fab46a34f9288f39d74833db7f61"}, - {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d2f0d0f976985f85dfb5f3d685697ef769faa6b71993b46b295cdbbd6be8cc37"}, - {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:a3dc1a4b165283bd865e8f8cb5f0c64c05001e0718ed06250d8cac9bec115b48"}, - {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:9e0f07b42d2a50c7dd2d8675d50f7343d998c64008f1da5fef888396b7f84630"}, - {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e63e3e1e0271f374ed489ff5ee73d4b6e7c60710e1f76af5f0e1a6117cd26394"}, - {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:56bb5c98f058a41bb58eead194b4db8c05b088c93d94d5161728515bd52b052b"}, - {file = "propcache-0.2.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7665f04d0c7f26ff8bb534e1c65068409bf4687aa2534faf7104d7182debb336"}, - {file = "propcache-0.2.0-cp310-cp310-win32.whl", hash = "sha256:7cf18abf9764746b9c8704774d8b06714bcb0a63641518a3a89c7f85cc02c2ad"}, - {file = "propcache-0.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:cfac69017ef97db2438efb854edf24f5a29fd09a536ff3a992b75990720cdc99"}, - {file = "propcache-0.2.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:63f13bf09cc3336eb04a837490b8f332e0db41da66995c9fd1ba04552e516354"}, - {file = "propcache-0.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:608cce1da6f2672a56b24a015b42db4ac612ee709f3d29f27a00c943d9e851de"}, - {file = "propcache-0.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:466c219deee4536fbc83c08d09115249db301550625c7fef1c5563a584c9bc87"}, - {file = "propcache-0.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc2db02409338bf36590aa985a461b2c96fce91f8e7e0f14c50c5fcc4f229016"}, - {file = "propcache-0.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a6ed8db0a556343d566a5c124ee483ae113acc9a557a807d439bcecc44e7dfbb"}, - {file = "propcache-0.2.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:91997d9cb4a325b60d4e3f20967f8eb08dfcb32b22554d5ef78e6fd1dda743a2"}, - {file = "propcache-0.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c7dde9e533c0a49d802b4f3f218fa9ad0a1ce21f2c2eb80d5216565202acab4"}, - {file = "propcache-0.2.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffcad6c564fe6b9b8916c1aefbb37a362deebf9394bd2974e9d84232e3e08504"}, - {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:97a58a28bcf63284e8b4d7b460cbee1edaab24634e82059c7b8c09e65284f178"}, - {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:945db8ee295d3af9dbdbb698cce9bbc5c59b5c3fe328bbc4387f59a8a35f998d"}, - {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:39e104da444a34830751715f45ef9fc537475ba21b7f1f5b0f4d71a3b60d7fe2"}, - {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c5ecca8f9bab618340c8e848d340baf68bcd8ad90a8ecd7a4524a81c1764b3db"}, - {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:c436130cc779806bdf5d5fae0d848713105472b8566b75ff70048c47d3961c5b"}, - {file = "propcache-0.2.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:191db28dc6dcd29d1a3e063c3be0b40688ed76434622c53a284e5427565bbd9b"}, - {file = "propcache-0.2.0-cp311-cp311-win32.whl", hash = "sha256:5f2564ec89058ee7c7989a7b719115bdfe2a2fb8e7a4543b8d1c0cc4cf6478c1"}, - {file = "propcache-0.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:6e2e54267980349b723cff366d1e29b138b9a60fa376664a157a342689553f71"}, - {file = "propcache-0.2.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:2ee7606193fb267be4b2e3b32714f2d58cad27217638db98a60f9efb5efeccc2"}, - {file = "propcache-0.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:91ee8fc02ca52e24bcb77b234f22afc03288e1dafbb1f88fe24db308910c4ac7"}, - {file = "propcache-0.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2e900bad2a8456d00a113cad8c13343f3b1f327534e3589acc2219729237a2e8"}, - {file = "propcache-0.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f52a68c21363c45297aca15561812d542f8fc683c85201df0bebe209e349f793"}, - {file = "propcache-0.2.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1e41d67757ff4fbc8ef2af99b338bfb955010444b92929e9e55a6d4dcc3c4f09"}, - {file = "propcache-0.2.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a64e32f8bd94c105cc27f42d3b658902b5bcc947ece3c8fe7bc1b05982f60e89"}, - {file = "propcache-0.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:55346705687dbd7ef0d77883ab4f6fabc48232f587925bdaf95219bae072491e"}, - {file = "propcache-0.2.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00181262b17e517df2cd85656fcd6b4e70946fe62cd625b9d74ac9977b64d8d9"}, - {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6994984550eaf25dd7fc7bd1b700ff45c894149341725bb4edc67f0ffa94efa4"}, - {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:56295eb1e5f3aecd516d91b00cfd8bf3a13991de5a479df9e27dd569ea23959c"}, - {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:439e76255daa0f8151d3cb325f6dd4a3e93043e6403e6491813bcaaaa8733887"}, - {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f6475a1b2ecb310c98c28d271a30df74f9dd436ee46d09236a6b750a7599ce57"}, - {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3444cdba6628accf384e349014084b1cacd866fbb88433cd9d279d90a54e0b23"}, - {file = "propcache-0.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4a9d9b4d0a9b38d1c391bb4ad24aa65f306c6f01b512e10a8a34a2dc5675d348"}, - {file = "propcache-0.2.0-cp312-cp312-win32.whl", hash = "sha256:69d3a98eebae99a420d4b28756c8ce6ea5a29291baf2dc9ff9414b42676f61d5"}, - {file = "propcache-0.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:ad9c9b99b05f163109466638bd30ada1722abb01bbb85c739c50b6dc11f92dc3"}, - {file = "propcache-0.2.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ecddc221a077a8132cf7c747d5352a15ed763b674c0448d811f408bf803d9ad7"}, - {file = "propcache-0.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0e53cb83fdd61cbd67202735e6a6687a7b491c8742dfc39c9e01e80354956763"}, - {file = "propcache-0.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92fe151145a990c22cbccf9ae15cae8ae9eddabfc949a219c9f667877e40853d"}, - {file = "propcache-0.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d6a21ef516d36909931a2967621eecb256018aeb11fc48656e3257e73e2e247a"}, - {file = "propcache-0.2.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f88a4095e913f98988f5b338c1d4d5d07dbb0b6bad19892fd447484e483ba6b"}, - {file = "propcache-0.2.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a5b3bb545ead161be780ee85a2b54fdf7092815995661947812dde94a40f6fb"}, - {file = "propcache-0.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67aeb72e0f482709991aa91345a831d0b707d16b0257e8ef88a2ad246a7280bf"}, - {file = "propcache-0.2.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c997f8c44ec9b9b0bcbf2d422cc00a1d9b9c681f56efa6ca149a941e5560da2"}, - {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2a66df3d4992bc1d725b9aa803e8c5a66c010c65c741ad901e260ece77f58d2f"}, - {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:3ebbcf2a07621f29638799828b8d8668c421bfb94c6cb04269130d8de4fb7136"}, - {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1235c01ddaa80da8235741e80815ce381c5267f96cc49b1477fdcf8c047ef325"}, - {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3947483a381259c06921612550867b37d22e1df6d6d7e8361264b6d037595f44"}, - {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:d5bed7f9805cc29c780f3aee05de3262ee7ce1f47083cfe9f77471e9d6777e83"}, - {file = "propcache-0.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e4a91d44379f45f5e540971d41e4626dacd7f01004826a18cb048e7da7e96544"}, - {file = "propcache-0.2.0-cp313-cp313-win32.whl", hash = "sha256:f902804113e032e2cdf8c71015651c97af6418363bea8d78dc0911d56c335032"}, - {file = "propcache-0.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:8f188cfcc64fb1266f4684206c9de0e80f54622c3f22a910cbd200478aeae61e"}, - {file = "propcache-0.2.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:53d1bd3f979ed529f0805dd35ddaca330f80a9a6d90bc0121d2ff398f8ed8861"}, - {file = "propcache-0.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:83928404adf8fb3d26793665633ea79b7361efa0287dfbd372a7e74311d51ee6"}, - {file = "propcache-0.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:77a86c261679ea5f3896ec060be9dc8e365788248cc1e049632a1be682442063"}, - {file = "propcache-0.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:218db2a3c297a3768c11a34812e63b3ac1c3234c3a086def9c0fee50d35add1f"}, - {file = "propcache-0.2.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7735e82e3498c27bcb2d17cb65d62c14f1100b71723b68362872bca7d0913d90"}, - {file = "propcache-0.2.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:20a617c776f520c3875cf4511e0d1db847a076d720714ae35ffe0df3e440be68"}, - {file = "propcache-0.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67b69535c870670c9f9b14a75d28baa32221d06f6b6fa6f77a0a13c5a7b0a5b9"}, - {file = "propcache-0.2.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4569158070180c3855e9c0791c56be3ceeb192defa2cdf6a3f39e54319e56b89"}, - {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:db47514ffdbd91ccdc7e6f8407aac4ee94cc871b15b577c1c324236b013ddd04"}, - {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:2a60ad3e2553a74168d275a0ef35e8c0a965448ffbc3b300ab3a5bb9956c2162"}, - {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:662dd62358bdeaca0aee5761de8727cfd6861432e3bb828dc2a693aa0471a563"}, - {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:25a1f88b471b3bc911d18b935ecb7115dff3a192b6fef46f0bfaf71ff4f12418"}, - {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:f60f0ac7005b9f5a6091009b09a419ace1610e163fa5deaba5ce3484341840e7"}, - {file = "propcache-0.2.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:74acd6e291f885678631b7ebc85d2d4aec458dd849b8c841b57ef04047833bed"}, - {file = "propcache-0.2.0-cp38-cp38-win32.whl", hash = "sha256:d9b6ddac6408194e934002a69bcaadbc88c10b5f38fb9307779d1c629181815d"}, - {file = "propcache-0.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:676135dcf3262c9c5081cc8f19ad55c8a64e3f7282a21266d05544450bffc3a5"}, - {file = "propcache-0.2.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:25c8d773a62ce0451b020c7b29a35cfbc05de8b291163a7a0f3b7904f27253e6"}, - {file = "propcache-0.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:375a12d7556d462dc64d70475a9ee5982465fbb3d2b364f16b86ba9135793638"}, - {file = "propcache-0.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1ec43d76b9677637a89d6ab86e1fef70d739217fefa208c65352ecf0282be957"}, - {file = "propcache-0.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f45eec587dafd4b2d41ac189c2156461ebd0c1082d2fe7013571598abb8505d1"}, - {file = "propcache-0.2.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bc092ba439d91df90aea38168e11f75c655880c12782facf5cf9c00f3d42b562"}, - {file = "propcache-0.2.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fa1076244f54bb76e65e22cb6910365779d5c3d71d1f18b275f1dfc7b0d71b4d"}, - {file = "propcache-0.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:682a7c79a2fbf40f5dbb1eb6bfe2cd865376deeac65acf9beb607505dced9e12"}, - {file = "propcache-0.2.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8e40876731f99b6f3c897b66b803c9e1c07a989b366c6b5b475fafd1f7ba3fb8"}, - {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:363ea8cd3c5cb6679f1c2f5f1f9669587361c062e4899fce56758efa928728f8"}, - {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:140fbf08ab3588b3468932974a9331aff43c0ab8a2ec2c608b6d7d1756dbb6cb"}, - {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e70fac33e8b4ac63dfc4c956fd7d85a0b1139adcfc0d964ce288b7c527537fea"}, - {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:b33d7a286c0dc1a15f5fc864cc48ae92a846df287ceac2dd499926c3801054a6"}, - {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:f6d5749fdd33d90e34c2efb174c7e236829147a2713334d708746e94c4bde40d"}, - {file = "propcache-0.2.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:22aa8f2272d81d9317ff5756bb108021a056805ce63dd3630e27d042c8092798"}, - {file = "propcache-0.2.0-cp39-cp39-win32.whl", hash = "sha256:73e4b40ea0eda421b115248d7e79b59214411109a5bc47d0d48e4c73e3b8fcf9"}, - {file = "propcache-0.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:9517d5e9e0731957468c29dbfd0f976736a0e55afaea843726e887f36fe017df"}, - {file = "propcache-0.2.0-py3-none-any.whl", hash = "sha256:2ccc28197af5313706511fab3a8b66dcd6da067a1331372c82ea1cb74285e036"}, - {file = "propcache-0.2.0.tar.gz", hash = "sha256:df81779732feb9d01e5d513fad0122efb3d53bbc75f61b2a4f29a020bc985e70"}, + {file = "propcache-0.2.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6b3f39a85d671436ee3d12c017f8fdea38509e4f25b28eb25877293c98c243f6"}, + {file = "propcache-0.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d51fbe4285d5db5d92a929e3e21536ea3dd43732c5b177c7ef03f918dff9f2"}, + {file = "propcache-0.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6445804cf4ec763dc70de65a3b0d9954e868609e83850a47ca4f0cb64bd79fea"}, + {file = "propcache-0.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9479aa06a793c5aeba49ce5c5692ffb51fcd9a7016e017d555d5e2b0045d212"}, + {file = "propcache-0.2.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9631c5e8b5b3a0fda99cb0d29c18133bca1e18aea9effe55adb3da1adef80d3"}, + {file = "propcache-0.2.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3156628250f46a0895f1f36e1d4fbe062a1af8718ec3ebeb746f1d23f0c5dc4d"}, + {file = "propcache-0.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b6fb63ae352e13748289f04f37868099e69dba4c2b3e271c46061e82c745634"}, + {file = "propcache-0.2.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:887d9b0a65404929641a9fabb6452b07fe4572b269d901d622d8a34a4e9043b2"}, + {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a96dc1fa45bd8c407a0af03b2d5218392729e1822b0c32e62c5bf7eeb5fb3958"}, + {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:a7e65eb5c003a303b94aa2c3852ef130230ec79e349632d030e9571b87c4698c"}, + {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:999779addc413181912e984b942fbcc951be1f5b3663cd80b2687758f434c583"}, + {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:19a0f89a7bb9d8048d9c4370c9c543c396e894c76be5525f5e1ad287f1750ddf"}, + {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:1ac2f5fe02fa75f56e1ad473f1175e11f475606ec9bd0be2e78e4734ad575034"}, + {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:574faa3b79e8ebac7cb1d7930f51184ba1ccf69adfdec53a12f319a06030a68b"}, + {file = "propcache-0.2.1-cp310-cp310-win32.whl", hash = "sha256:03ff9d3f665769b2a85e6157ac8b439644f2d7fd17615a82fa55739bc97863f4"}, + {file = "propcache-0.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:2d3af2e79991102678f53e0dbf4c35de99b6b8b58f29a27ca0325816364caaba"}, + {file = "propcache-0.2.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1ffc3cca89bb438fb9c95c13fc874012f7b9466b89328c3c8b1aa93cdcfadd16"}, + {file = "propcache-0.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f174bbd484294ed9fdf09437f889f95807e5f229d5d93588d34e92106fbf6717"}, + {file = "propcache-0.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:70693319e0b8fd35dd863e3e29513875eb15c51945bf32519ef52927ca883bc3"}, + {file = "propcache-0.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b480c6a4e1138e1aa137c0079b9b6305ec6dcc1098a8ca5196283e8a49df95a9"}, + {file = "propcache-0.2.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d27b84d5880f6d8aa9ae3edb253c59d9f6642ffbb2c889b78b60361eed449787"}, + {file = "propcache-0.2.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:857112b22acd417c40fa4595db2fe28ab900c8c5fe4670c7989b1c0230955465"}, + {file = "propcache-0.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf6c4150f8c0e32d241436526f3c3f9cbd34429492abddbada2ffcff506c51af"}, + {file = "propcache-0.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:66d4cfda1d8ed687daa4bc0274fcfd5267873db9a5bc0418c2da19273040eeb7"}, + {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c2f992c07c0fca81655066705beae35fc95a2fa7366467366db627d9f2ee097f"}, + {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:4a571d97dbe66ef38e472703067021b1467025ec85707d57e78711c085984e54"}, + {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:bb6178c241278d5fe853b3de743087be7f5f4c6f7d6d22a3b524d323eecec505"}, + {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:ad1af54a62ffe39cf34db1aa6ed1a1873bd548f6401db39d8e7cd060b9211f82"}, + {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e7048abd75fe40712005bcfc06bb44b9dfcd8e101dda2ecf2f5aa46115ad07ca"}, + {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:160291c60081f23ee43d44b08a7e5fb76681221a8e10b3139618c5a9a291b84e"}, + {file = "propcache-0.2.1-cp311-cp311-win32.whl", hash = "sha256:819ce3b883b7576ca28da3861c7e1a88afd08cc8c96908e08a3f4dd64a228034"}, + {file = "propcache-0.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:edc9fc7051e3350643ad929df55c451899bb9ae6d24998a949d2e4c87fb596d3"}, + {file = "propcache-0.2.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:081a430aa8d5e8876c6909b67bd2d937bfd531b0382d3fdedb82612c618bc41a"}, + {file = "propcache-0.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d2ccec9ac47cf4e04897619c0e0c1a48c54a71bdf045117d3a26f80d38ab1fb0"}, + {file = "propcache-0.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:14d86fe14b7e04fa306e0c43cdbeebe6b2c2156a0c9ce56b815faacc193e320d"}, + {file = "propcache-0.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:049324ee97bb67285b49632132db351b41e77833678432be52bdd0289c0e05e4"}, + {file = "propcache-0.2.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1cd9a1d071158de1cc1c71a26014dcdfa7dd3d5f4f88c298c7f90ad6f27bb46d"}, + {file = "propcache-0.2.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98110aa363f1bb4c073e8dcfaefd3a5cea0f0834c2aab23dda657e4dab2f53b5"}, + {file = "propcache-0.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:647894f5ae99c4cf6bb82a1bb3a796f6e06af3caa3d32e26d2350d0e3e3faf24"}, + {file = "propcache-0.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bfd3223c15bebe26518d58ccf9a39b93948d3dcb3e57a20480dfdd315356baff"}, + {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d71264a80f3fcf512eb4f18f59423fe82d6e346ee97b90625f283df56aee103f"}, + {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:e73091191e4280403bde6c9a52a6999d69cdfde498f1fdf629105247599b57ec"}, + {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3935bfa5fede35fb202c4b569bb9c042f337ca4ff7bd540a0aa5e37131659348"}, + {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f508b0491767bb1f2b87fdfacaba5f7eddc2f867740ec69ece6d1946d29029a6"}, + {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:1672137af7c46662a1c2be1e8dc78cb6d224319aaa40271c9257d886be4363a6"}, + {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b74c261802d3d2b85c9df2dfb2fa81b6f90deeef63c2db9f0e029a3cac50b518"}, + {file = "propcache-0.2.1-cp312-cp312-win32.whl", hash = "sha256:d09c333d36c1409d56a9d29b3a1b800a42c76a57a5a8907eacdbce3f18768246"}, + {file = "propcache-0.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:c214999039d4f2a5b2073ac506bba279945233da8c786e490d411dfc30f855c1"}, + {file = "propcache-0.2.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aca405706e0b0a44cc6bfd41fbe89919a6a56999157f6de7e182a990c36e37bc"}, + {file = "propcache-0.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:12d1083f001ace206fe34b6bdc2cb94be66d57a850866f0b908972f90996b3e9"}, + {file = "propcache-0.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d93f3307ad32a27bda2e88ec81134b823c240aa3abb55821a8da553eed8d9439"}, + {file = "propcache-0.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba278acf14471d36316159c94a802933d10b6a1e117b8554fe0d0d9b75c9d536"}, + {file = "propcache-0.2.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4e6281aedfca15301c41f74d7005e6e3f4ca143584ba696ac69df4f02f40d629"}, + {file = "propcache-0.2.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5b750a8e5a1262434fb1517ddf64b5de58327f1adc3524a5e44c2ca43305eb0b"}, + {file = "propcache-0.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf72af5e0fb40e9babf594308911436c8efde3cb5e75b6f206c34ad18be5c052"}, + {file = "propcache-0.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b2d0a12018b04f4cb820781ec0dffb5f7c7c1d2a5cd22bff7fb055a2cb19ebce"}, + {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e800776a79a5aabdb17dcc2346a7d66d0777e942e4cd251defeb084762ecd17d"}, + {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:4160d9283bd382fa6c0c2b5e017acc95bc183570cd70968b9202ad6d8fc48dce"}, + {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:30b43e74f1359353341a7adb783c8f1b1c676367b011709f466f42fda2045e95"}, + {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:58791550b27d5488b1bb52bc96328456095d96206a250d28d874fafe11b3dfaf"}, + {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:0f022d381747f0dfe27e99d928e31bc51a18b65bb9e481ae0af1380a6725dd1f"}, + {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:297878dc9d0a334358f9b608b56d02e72899f3b8499fc6044133f0d319e2ec30"}, + {file = "propcache-0.2.1-cp313-cp313-win32.whl", hash = "sha256:ddfab44e4489bd79bda09d84c430677fc7f0a4939a73d2bba3073036f487a0a6"}, + {file = "propcache-0.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:556fc6c10989f19a179e4321e5d678db8eb2924131e64652a51fe83e4c3db0e1"}, + {file = "propcache-0.2.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:6a9a8c34fb7bb609419a211e59da8887eeca40d300b5ea8e56af98f6fbbb1541"}, + {file = "propcache-0.2.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ae1aa1cd222c6d205853b3013c69cd04515f9d6ab6de4b0603e2e1c33221303e"}, + {file = "propcache-0.2.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:accb6150ce61c9c4b7738d45550806aa2b71c7668c6942f17b0ac182b6142fd4"}, + {file = "propcache-0.2.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5eee736daafa7af6d0a2dc15cc75e05c64f37fc37bafef2e00d77c14171c2097"}, + {file = "propcache-0.2.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7a31fc1e1bd362874863fdeed71aed92d348f5336fd84f2197ba40c59f061bd"}, + {file = "propcache-0.2.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba4cfa1052819d16699e1d55d18c92b6e094d4517c41dd231a8b9f87b6fa681"}, + {file = "propcache-0.2.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f089118d584e859c62b3da0892b88a83d611c2033ac410e929cb6754eec0ed16"}, + {file = "propcache-0.2.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:781e65134efaf88feb447e8c97a51772aa75e48b794352f94cb7ea717dedda0d"}, + {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:31f5af773530fd3c658b32b6bdc2d0838543de70eb9a2156c03e410f7b0d3aae"}, + {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:a7a078f5d37bee6690959c813977da5291b24286e7b962e62a94cec31aa5188b"}, + {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:cea7daf9fc7ae6687cf1e2c049752f19f146fdc37c2cc376e7d0032cf4f25347"}, + {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:8b3489ff1ed1e8315674d0775dc7d2195fb13ca17b3808721b54dbe9fd020faf"}, + {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:9403db39be1393618dd80c746cb22ccda168efce239c73af13c3763ef56ffc04"}, + {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5d97151bc92d2b2578ff7ce779cdb9174337390a535953cbb9452fb65164c587"}, + {file = "propcache-0.2.1-cp39-cp39-win32.whl", hash = "sha256:9caac6b54914bdf41bcc91e7eb9147d331d29235a7c967c150ef5df6464fd1bb"}, + {file = "propcache-0.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:92fc4500fcb33899b05ba73276dfb684a20d31caa567b7cb5252d48f896a91b1"}, + {file = "propcache-0.2.1-py3-none-any.whl", hash = "sha256:52277518d6aae65536e9cea52d4e7fd2f7a66f4aa2d30ed3f2fcea620ace3c54"}, + {file = "propcache-0.2.1.tar.gz", hash = "sha256:3f77ce728b19cb537714499928fe800c3dda29e8d9428778fc7c186da4c09a64"}, ] [[package]] name = "protobuf" -version = "5.28.3" +version = "5.29.1" description = "" optional = false python-versions = ">=3.8" files = [ - {file = "protobuf-5.28.3-cp310-abi3-win32.whl", hash = "sha256:0c4eec6f987338617072592b97943fdbe30d019c56126493111cf24344c1cc24"}, - {file = "protobuf-5.28.3-cp310-abi3-win_amd64.whl", hash = "sha256:91fba8f445723fcf400fdbe9ca796b19d3b1242cd873907979b9ed71e4afe868"}, - {file = "protobuf-5.28.3-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a3f6857551e53ce35e60b403b8a27b0295f7d6eb63d10484f12bc6879c715687"}, - {file = "protobuf-5.28.3-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:3fa2de6b8b29d12c61911505d893afe7320ce7ccba4df913e2971461fa36d584"}, - {file = "protobuf-5.28.3-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:712319fbdddb46f21abb66cd33cb9e491a5763b2febd8f228251add221981135"}, - {file = "protobuf-5.28.3-cp38-cp38-win32.whl", hash = "sha256:3e6101d095dfd119513cde7259aa703d16c6bbdfae2554dfe5cfdbe94e32d548"}, - {file = "protobuf-5.28.3-cp38-cp38-win_amd64.whl", hash = "sha256:27b246b3723692bf1068d5734ddaf2fccc2cdd6e0c9b47fe099244d80200593b"}, - {file = "protobuf-5.28.3-cp39-cp39-win32.whl", hash = "sha256:135658402f71bbd49500322c0f736145731b16fc79dc8f367ab544a17eab4535"}, - {file = "protobuf-5.28.3-cp39-cp39-win_amd64.whl", hash = "sha256:70585a70fc2dd4818c51287ceef5bdba6387f88a578c86d47bb34669b5552c36"}, - {file = "protobuf-5.28.3-py3-none-any.whl", hash = "sha256:cee1757663fa32a1ee673434fcf3bf24dd54763c79690201208bafec62f19eed"}, - {file = "protobuf-5.28.3.tar.gz", hash = "sha256:64badbc49180a5e401f373f9ce7ab1d18b63f7dd4a9cdc43c92b9f0b481cef7b"}, + {file = "protobuf-5.29.1-cp310-abi3-win32.whl", hash = "sha256:22c1f539024241ee545cbcb00ee160ad1877975690b16656ff87dde107b5f110"}, + {file = "protobuf-5.29.1-cp310-abi3-win_amd64.whl", hash = "sha256:1fc55267f086dd4050d18ef839d7bd69300d0d08c2a53ca7df3920cc271a3c34"}, + {file = "protobuf-5.29.1-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:d473655e29c0c4bbf8b69e9a8fb54645bc289dead6d753b952e7aa660254ae18"}, + {file = "protobuf-5.29.1-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:b5ba1d0e4c8a40ae0496d0e2ecfdbb82e1776928a205106d14ad6985a09ec155"}, + {file = "protobuf-5.29.1-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:8ee1461b3af56145aca2800e6a3e2f928108c749ba8feccc6f5dd0062c410c0d"}, + {file = "protobuf-5.29.1-cp38-cp38-win32.whl", hash = "sha256:50879eb0eb1246e3a5eabbbe566b44b10348939b7cc1b267567e8c3d07213853"}, + {file = "protobuf-5.29.1-cp38-cp38-win_amd64.whl", hash = "sha256:027fbcc48cea65a6b17028510fdd054147057fa78f4772eb547b9274e5219331"}, + {file = "protobuf-5.29.1-cp39-cp39-win32.whl", hash = "sha256:5a41deccfa5e745cef5c65a560c76ec0ed8e70908a67cc8f4da5fce588b50d57"}, + {file = "protobuf-5.29.1-cp39-cp39-win_amd64.whl", hash = "sha256:012ce28d862ff417fd629285aca5d9772807f15ceb1a0dbd15b88f58c776c98c"}, + {file = "protobuf-5.29.1-py3-none-any.whl", hash = "sha256:32600ddb9c2a53dedc25b8581ea0f1fd8ea04956373c0c07577ce58d312522e0"}, + {file = "protobuf-5.29.1.tar.gz", hash = "sha256:683be02ca21a6ffe80db6dd02c0b5b2892322c59ca57fd6c872d652cb80549cb"}, ] [[package]] @@ -3145,13 +3148,13 @@ files = [ [[package]] name = "pydantic" -version = "2.10.2" +version = "2.10.3" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic-2.10.2-py3-none-any.whl", hash = "sha256:cfb96e45951117c3024e6b67b25cdc33a3cb7b2fa62e239f7af1378358a1d99e"}, - {file = "pydantic-2.10.2.tar.gz", hash = "sha256:2bc2d7f17232e0841cbba4641e65ba1eb6fafb3a08de3a091ff3ce14a197c4fa"}, + {file = "pydantic-2.10.3-py3-none-any.whl", hash = "sha256:be04d85bbc7b65651c5f8e6b9976ed9c6f41782a55524cef079a34a0bb82144d"}, + {file = "pydantic-2.10.3.tar.gz", hash = "sha256:cb5ac360ce894ceacd69c403187900a02c4b20b693a9dd1d643e1effab9eadf9"}, ] [package.dependencies] @@ -3308,13 +3311,13 @@ windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pytest" -version = "8.3.3" +version = "8.3.4" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2"}, - {file = "pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181"}, + {file = "pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6"}, + {file = "pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761"}, ] [package.dependencies] @@ -3469,15 +3472,21 @@ cli = ["click (>=5.0)"] [[package]] name = "python-json-logger" -version = "2.0.7" -description = "A python library adding a json log formatter" +version = "3.2.0" +description = "JSON Log Formatter for the Python Logging Package" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "python-json-logger-2.0.7.tar.gz", hash = "sha256:23e7ec02d34237c5aa1e29a070193a4ea87583bb4e7f8fd06d3de8264c4b2e1c"}, - {file = "python_json_logger-2.0.7-py3-none-any.whl", hash = "sha256:f380b826a991ebbe3de4d897aeec42760035ac760345e57b812938dc8b35e2bd"}, + {file = "python_json_logger-3.2.0-py3-none-any.whl", hash = "sha256:d73522ddcfc6d0461394120feaddea9025dc64bf804d96357dd42fa878cc5fe8"}, + {file = "python_json_logger-3.2.0.tar.gz", hash = "sha256:2c11056458d3f56614480b24e9cb28f7aba69cbfbebddbb77c92f0ec0d4947ab"}, ] +[package.dependencies] +typing_extensions = {version = "*", markers = "python_version < \"3.10\""} + +[package.extras] +dev = ["backports.zoneinfo", "black", "build", "freezegun", "mdx_truly_sane_lists", "mike", "mkdocs", "mkdocs-awesome-pages-plugin", "mkdocs-gen-files", "mkdocs-literate-nav", "mkdocs-material (>=8.5)", "mkdocstrings[python]", "msgspec", "msgspec-python313-pre", "mypy", "orjson", "pylint", "pytest", "tzdata", "validate-pyproject[all]"] + [[package]] name = "pytz" version = "2024.2" @@ -3931,101 +3940,114 @@ files = [ [[package]] name = "rpds-py" -version = "0.21.0" +version = "0.22.3" description = "Python bindings to Rust's persistent data structures (rpds)" optional = false python-versions = ">=3.9" files = [ - {file = "rpds_py-0.21.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:a017f813f24b9df929674d0332a374d40d7f0162b326562daae8066b502d0590"}, - {file = "rpds_py-0.21.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:20cc1ed0bcc86d8e1a7e968cce15be45178fd16e2ff656a243145e0b439bd250"}, - {file = "rpds_py-0.21.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad116dda078d0bc4886cb7840e19811562acdc7a8e296ea6ec37e70326c1b41c"}, - {file = "rpds_py-0.21.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:808f1ac7cf3b44f81c9475475ceb221f982ef548e44e024ad5f9e7060649540e"}, - {file = "rpds_py-0.21.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de552f4a1916e520f2703ec474d2b4d3f86d41f353e7680b597512ffe7eac5d0"}, - {file = "rpds_py-0.21.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:efec946f331349dfc4ae9d0e034c263ddde19414fe5128580f512619abed05f1"}, - {file = "rpds_py-0.21.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b80b4690bbff51a034bfde9c9f6bf9357f0a8c61f548942b80f7b66356508bf5"}, - {file = "rpds_py-0.21.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:085ed25baac88953d4283e5b5bd094b155075bb40d07c29c4f073e10623f9f2e"}, - {file = "rpds_py-0.21.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:daa8efac2a1273eed2354397a51216ae1e198ecbce9036fba4e7610b308b6153"}, - {file = "rpds_py-0.21.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:95a5bad1ac8a5c77b4e658671642e4af3707f095d2b78a1fdd08af0dfb647624"}, - {file = "rpds_py-0.21.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3e53861b29a13d5b70116ea4230b5f0f3547b2c222c5daa090eb7c9c82d7f664"}, - {file = "rpds_py-0.21.0-cp310-none-win32.whl", hash = "sha256:ea3a6ac4d74820c98fcc9da4a57847ad2cc36475a8bd9683f32ab6d47a2bd682"}, - {file = "rpds_py-0.21.0-cp310-none-win_amd64.whl", hash = "sha256:b8f107395f2f1d151181880b69a2869c69e87ec079c49c0016ab96860b6acbe5"}, - {file = "rpds_py-0.21.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:5555db3e618a77034954b9dc547eae94166391a98eb867905ec8fcbce1308d95"}, - {file = "rpds_py-0.21.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:97ef67d9bbc3e15584c2f3c74bcf064af36336c10d2e21a2131e123ce0f924c9"}, - {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ab2c2a26d2f69cdf833174f4d9d86118edc781ad9a8fa13970b527bf8236027"}, - {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4e8921a259f54bfbc755c5bbd60c82bb2339ae0324163f32868f63f0ebb873d9"}, - {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8a7ff941004d74d55a47f916afc38494bd1cfd4b53c482b77c03147c91ac0ac3"}, - {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5145282a7cd2ac16ea0dc46b82167754d5e103a05614b724457cffe614f25bd8"}, - {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de609a6f1b682f70bb7163da745ee815d8f230d97276db049ab447767466a09d"}, - {file = "rpds_py-0.21.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:40c91c6e34cf016fa8e6b59d75e3dbe354830777fcfd74c58b279dceb7975b75"}, - {file = "rpds_py-0.21.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d2132377f9deef0c4db89e65e8bb28644ff75a18df5293e132a8d67748397b9f"}, - {file = "rpds_py-0.21.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:0a9e0759e7be10109645a9fddaaad0619d58c9bf30a3f248a2ea57a7c417173a"}, - {file = "rpds_py-0.21.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9e20da3957bdf7824afdd4b6eeb29510e83e026473e04952dca565170cd1ecc8"}, - {file = "rpds_py-0.21.0-cp311-none-win32.whl", hash = "sha256:f71009b0d5e94c0e86533c0b27ed7cacc1239cb51c178fd239c3cfefefb0400a"}, - {file = "rpds_py-0.21.0-cp311-none-win_amd64.whl", hash = "sha256:e168afe6bf6ab7ab46c8c375606298784ecbe3ba31c0980b7dcbb9631dcba97e"}, - {file = "rpds_py-0.21.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:30b912c965b2aa76ba5168fd610087bad7fcde47f0a8367ee8f1876086ee6d1d"}, - {file = "rpds_py-0.21.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ca9989d5d9b1b300bc18e1801c67b9f6d2c66b8fd9621b36072ed1df2c977f72"}, - {file = "rpds_py-0.21.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f54e7106f0001244a5f4cf810ba8d3f9c542e2730821b16e969d6887b664266"}, - {file = "rpds_py-0.21.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fed5dfefdf384d6fe975cc026886aece4f292feaf69d0eeb716cfd3c5a4dd8be"}, - {file = "rpds_py-0.21.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:590ef88db231c9c1eece44dcfefd7515d8bf0d986d64d0caf06a81998a9e8cab"}, - {file = "rpds_py-0.21.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f983e4c2f603c95dde63df633eec42955508eefd8d0f0e6d236d31a044c882d7"}, - {file = "rpds_py-0.21.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b229ce052ddf1a01c67d68166c19cb004fb3612424921b81c46e7ea7ccf7c3bf"}, - {file = "rpds_py-0.21.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ebf64e281a06c904a7636781d2e973d1f0926a5b8b480ac658dc0f556e7779f4"}, - {file = "rpds_py-0.21.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:998a8080c4495e4f72132f3d66ff91f5997d799e86cec6ee05342f8f3cda7dca"}, - {file = "rpds_py-0.21.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:98486337f7b4f3c324ab402e83453e25bb844f44418c066623db88e4c56b7c7b"}, - {file = "rpds_py-0.21.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a78d8b634c9df7f8d175451cfeac3810a702ccb85f98ec95797fa98b942cea11"}, - {file = "rpds_py-0.21.0-cp312-none-win32.whl", hash = "sha256:a58ce66847711c4aa2ecfcfaff04cb0327f907fead8945ffc47d9407f41ff952"}, - {file = "rpds_py-0.21.0-cp312-none-win_amd64.whl", hash = "sha256:e860f065cc4ea6f256d6f411aba4b1251255366e48e972f8a347cf88077b24fd"}, - {file = "rpds_py-0.21.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:ee4eafd77cc98d355a0d02f263efc0d3ae3ce4a7c24740010a8b4012bbb24937"}, - {file = "rpds_py-0.21.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:688c93b77e468d72579351a84b95f976bd7b3e84aa6686be6497045ba84be560"}, - {file = "rpds_py-0.21.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c38dbf31c57032667dd5a2f0568ccde66e868e8f78d5a0d27dcc56d70f3fcd3b"}, - {file = "rpds_py-0.21.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2d6129137f43f7fa02d41542ffff4871d4aefa724a5fe38e2c31a4e0fd343fb0"}, - {file = "rpds_py-0.21.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:520ed8b99b0bf86a176271f6fe23024323862ac674b1ce5b02a72bfeff3fff44"}, - {file = "rpds_py-0.21.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aaeb25ccfb9b9014a10eaf70904ebf3f79faaa8e60e99e19eef9f478651b9b74"}, - {file = "rpds_py-0.21.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af04ac89c738e0f0f1b913918024c3eab6e3ace989518ea838807177d38a2e94"}, - {file = "rpds_py-0.21.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b9b76e2afd585803c53c5b29e992ecd183f68285b62fe2668383a18e74abe7a3"}, - {file = "rpds_py-0.21.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5afb5efde74c54724e1a01118c6e5c15e54e642c42a1ba588ab1f03544ac8c7a"}, - {file = "rpds_py-0.21.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:52c041802a6efa625ea18027a0723676a778869481d16803481ef6cc02ea8cb3"}, - {file = "rpds_py-0.21.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ee1e4fc267b437bb89990b2f2abf6c25765b89b72dd4a11e21934df449e0c976"}, - {file = "rpds_py-0.21.0-cp313-none-win32.whl", hash = "sha256:0c025820b78817db6a76413fff6866790786c38f95ea3f3d3c93dbb73b632202"}, - {file = "rpds_py-0.21.0-cp313-none-win_amd64.whl", hash = "sha256:320c808df533695326610a1b6a0a6e98f033e49de55d7dc36a13c8a30cfa756e"}, - {file = "rpds_py-0.21.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:2c51d99c30091f72a3c5d126fad26236c3f75716b8b5e5cf8effb18889ced928"}, - {file = "rpds_py-0.21.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cbd7504a10b0955ea287114f003b7ad62330c9e65ba012c6223dba646f6ffd05"}, - {file = "rpds_py-0.21.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6dcc4949be728ede49e6244eabd04064336012b37f5c2200e8ec8eb2988b209c"}, - {file = "rpds_py-0.21.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f414da5c51bf350e4b7960644617c130140423882305f7574b6cf65a3081cecb"}, - {file = "rpds_py-0.21.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9afe42102b40007f588666bc7de82451e10c6788f6f70984629db193849dced1"}, - {file = "rpds_py-0.21.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b929c2bb6e29ab31f12a1117c39f7e6d6450419ab7464a4ea9b0b417174f044"}, - {file = "rpds_py-0.21.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8404b3717da03cbf773a1d275d01fec84ea007754ed380f63dfc24fb76ce4592"}, - {file = "rpds_py-0.21.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e12bb09678f38b7597b8346983d2323a6482dcd59e423d9448108c1be37cac9d"}, - {file = "rpds_py-0.21.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:58a0e345be4b18e6b8501d3b0aa540dad90caeed814c515e5206bb2ec26736fd"}, - {file = "rpds_py-0.21.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:c3761f62fcfccf0864cc4665b6e7c3f0c626f0380b41b8bd1ce322103fa3ef87"}, - {file = "rpds_py-0.21.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c2b2f71c6ad6c2e4fc9ed9401080badd1469fa9889657ec3abea42a3d6b2e1ed"}, - {file = "rpds_py-0.21.0-cp39-none-win32.whl", hash = "sha256:b21747f79f360e790525e6f6438c7569ddbfb1b3197b9e65043f25c3c9b489d8"}, - {file = "rpds_py-0.21.0-cp39-none-win_amd64.whl", hash = "sha256:0626238a43152918f9e72ede9a3b6ccc9e299adc8ade0d67c5e142d564c9a83d"}, - {file = "rpds_py-0.21.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:6b4ef7725386dc0762857097f6b7266a6cdd62bfd209664da6712cb26acef035"}, - {file = "rpds_py-0.21.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:6bc0e697d4d79ab1aacbf20ee5f0df80359ecf55db33ff41481cf3e24f206919"}, - {file = "rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da52d62a96e61c1c444f3998c434e8b263c384f6d68aca8274d2e08d1906325c"}, - {file = "rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:98e4fe5db40db87ce1c65031463a760ec7906ab230ad2249b4572c2fc3ef1f9f"}, - {file = "rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:30bdc973f10d28e0337f71d202ff29345320f8bc49a31c90e6c257e1ccef4333"}, - {file = "rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:faa5e8496c530f9c71f2b4e1c49758b06e5f4055e17144906245c99fa6d45356"}, - {file = "rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32eb88c30b6a4f0605508023b7141d043a79b14acb3b969aa0b4f99b25bc7d4a"}, - {file = "rpds_py-0.21.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a89a8ce9e4e75aeb7fa5d8ad0f3fecdee813802592f4f46a15754dcb2fd6b061"}, - {file = "rpds_py-0.21.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:241e6c125568493f553c3d0fdbb38c74babf54b45cef86439d4cd97ff8feb34d"}, - {file = "rpds_py-0.21.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:3b766a9f57663396e4f34f5140b3595b233a7b146e94777b97a8413a1da1be18"}, - {file = "rpds_py-0.21.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:af4a644bf890f56e41e74be7d34e9511e4954894d544ec6b8efe1e21a1a8da6c"}, - {file = "rpds_py-0.21.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:3e30a69a706e8ea20444b98a49f386c17b26f860aa9245329bab0851ed100677"}, - {file = "rpds_py-0.21.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:031819f906bb146561af051c7cef4ba2003d28cff07efacef59da973ff7969ba"}, - {file = "rpds_py-0.21.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:b876f2bc27ab5954e2fd88890c071bd0ed18b9c50f6ec3de3c50a5ece612f7a6"}, - {file = "rpds_py-0.21.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc5695c321e518d9f03b7ea6abb5ea3af4567766f9852ad1560f501b17588c7b"}, - {file = "rpds_py-0.21.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b4de1da871b5c0fd5537b26a6fc6814c3cc05cabe0c941db6e9044ffbb12f04a"}, - {file = "rpds_py-0.21.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:878f6fea96621fda5303a2867887686d7a198d9e0f8a40be100a63f5d60c88c9"}, - {file = "rpds_py-0.21.0-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8eeec67590e94189f434c6d11c426892e396ae59e4801d17a93ac96b8c02a6c"}, - {file = "rpds_py-0.21.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ff2eba7f6c0cb523d7e9cff0903f2fe1feff8f0b2ceb6bd71c0e20a4dcee271"}, - {file = "rpds_py-0.21.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a429b99337062877d7875e4ff1a51fe788424d522bd64a8c0a20ef3021fdb6ed"}, - {file = "rpds_py-0.21.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:d167e4dbbdac48bd58893c7e446684ad5d425b407f9336e04ab52e8b9194e2ed"}, - {file = "rpds_py-0.21.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:4eb2de8a147ffe0626bfdc275fc6563aa7bf4b6db59cf0d44f0ccd6ca625a24e"}, - {file = "rpds_py-0.21.0-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:e78868e98f34f34a88e23ee9ccaeeec460e4eaf6db16d51d7a9b883e5e785a5e"}, - {file = "rpds_py-0.21.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:4991ca61656e3160cdaca4851151fd3f4a92e9eba5c7a530ab030d6aee96ec89"}, - {file = "rpds_py-0.21.0.tar.gz", hash = "sha256:ed6378c9d66d0de903763e7706383d60c33829581f0adff47b6535f1802fa6db"}, + {file = "rpds_py-0.22.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:6c7b99ca52c2c1752b544e310101b98a659b720b21db00e65edca34483259967"}, + {file = "rpds_py-0.22.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:be2eb3f2495ba669d2a985f9b426c1797b7d48d6963899276d22f23e33d47e37"}, + {file = "rpds_py-0.22.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70eb60b3ae9245ddea20f8a4190bd79c705a22f8028aaf8bbdebe4716c3fab24"}, + {file = "rpds_py-0.22.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4041711832360a9b75cfb11b25a6a97c8fb49c07b8bd43d0d02b45d0b499a4ff"}, + {file = "rpds_py-0.22.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:64607d4cbf1b7e3c3c8a14948b99345eda0e161b852e122c6bb71aab6d1d798c"}, + {file = "rpds_py-0.22.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e69b0a0e2537f26d73b4e43ad7bc8c8efb39621639b4434b76a3de50c6966e"}, + {file = "rpds_py-0.22.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc27863442d388870c1809a87507727b799c8460573cfbb6dc0eeaef5a11b5ec"}, + {file = "rpds_py-0.22.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e79dd39f1e8c3504be0607e5fc6e86bb60fe3584bec8b782578c3b0fde8d932c"}, + {file = "rpds_py-0.22.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e0fa2d4ec53dc51cf7d3bb22e0aa0143966119f42a0c3e4998293a3dd2856b09"}, + {file = "rpds_py-0.22.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fda7cb070f442bf80b642cd56483b5548e43d366fe3f39b98e67cce780cded00"}, + {file = "rpds_py-0.22.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cff63a0272fcd259dcc3be1657b07c929c466b067ceb1c20060e8d10af56f5bf"}, + {file = "rpds_py-0.22.3-cp310-cp310-win32.whl", hash = "sha256:9bd7228827ec7bb817089e2eb301d907c0d9827a9e558f22f762bb690b131652"}, + {file = "rpds_py-0.22.3-cp310-cp310-win_amd64.whl", hash = "sha256:9beeb01d8c190d7581a4d59522cd3d4b6887040dcfc744af99aa59fef3e041a8"}, + {file = "rpds_py-0.22.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d20cfb4e099748ea39e6f7b16c91ab057989712d31761d3300d43134e26e165f"}, + {file = "rpds_py-0.22.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:68049202f67380ff9aa52f12e92b1c30115f32e6895cd7198fa2a7961621fc5a"}, + {file = "rpds_py-0.22.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb4f868f712b2dd4bcc538b0a0c1f63a2b1d584c925e69a224d759e7070a12d5"}, + {file = "rpds_py-0.22.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bc51abd01f08117283c5ebf64844a35144a0843ff7b2983e0648e4d3d9f10dbb"}, + {file = "rpds_py-0.22.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0f3cec041684de9a4684b1572fe28c7267410e02450f4561700ca5a3bc6695a2"}, + {file = "rpds_py-0.22.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7ef9d9da710be50ff6809fed8f1963fecdfecc8b86656cadfca3bc24289414b0"}, + {file = "rpds_py-0.22.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59f4a79c19232a5774aee369a0c296712ad0e77f24e62cad53160312b1c1eaa1"}, + {file = "rpds_py-0.22.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a60bce91f81ddaac922a40bbb571a12c1070cb20ebd6d49c48e0b101d87300d"}, + {file = "rpds_py-0.22.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e89391e6d60251560f0a8f4bd32137b077a80d9b7dbe6d5cab1cd80d2746f648"}, + {file = "rpds_py-0.22.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e3fb866d9932a3d7d0c82da76d816996d1667c44891bd861a0f97ba27e84fc74"}, + {file = "rpds_py-0.22.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1352ae4f7c717ae8cba93421a63373e582d19d55d2ee2cbb184344c82d2ae55a"}, + {file = "rpds_py-0.22.3-cp311-cp311-win32.whl", hash = "sha256:b0b4136a252cadfa1adb705bb81524eee47d9f6aab4f2ee4fa1e9d3cd4581f64"}, + {file = "rpds_py-0.22.3-cp311-cp311-win_amd64.whl", hash = "sha256:8bd7c8cfc0b8247c8799080fbff54e0b9619e17cdfeb0478ba7295d43f635d7c"}, + {file = "rpds_py-0.22.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:27e98004595899949bd7a7b34e91fa7c44d7a97c40fcaf1d874168bb652ec67e"}, + {file = "rpds_py-0.22.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1978d0021e943aae58b9b0b196fb4895a25cc53d3956b8e35e0b7682eefb6d56"}, + {file = "rpds_py-0.22.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:655ca44a831ecb238d124e0402d98f6212ac527a0ba6c55ca26f616604e60a45"}, + {file = "rpds_py-0.22.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:feea821ee2a9273771bae61194004ee2fc33f8ec7db08117ef9147d4bbcbca8e"}, + {file = "rpds_py-0.22.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:22bebe05a9ffc70ebfa127efbc429bc26ec9e9b4ee4d15a740033efda515cf3d"}, + {file = "rpds_py-0.22.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3af6e48651c4e0d2d166dc1b033b7042ea3f871504b6805ba5f4fe31581d8d38"}, + {file = "rpds_py-0.22.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e67ba3c290821343c192f7eae1d8fd5999ca2dc99994114643e2f2d3e6138b15"}, + {file = "rpds_py-0.22.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:02fbb9c288ae08bcb34fb41d516d5eeb0455ac35b5512d03181d755d80810059"}, + {file = "rpds_py-0.22.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f56a6b404f74ab372da986d240e2e002769a7d7102cc73eb238a4f72eec5284e"}, + {file = "rpds_py-0.22.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0a0461200769ab3b9ab7e513f6013b7a97fdeee41c29b9db343f3c5a8e2b9e61"}, + {file = "rpds_py-0.22.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8633e471c6207a039eff6aa116e35f69f3156b3989ea3e2d755f7bc41754a4a7"}, + {file = "rpds_py-0.22.3-cp312-cp312-win32.whl", hash = "sha256:593eba61ba0c3baae5bc9be2f5232430453fb4432048de28399ca7376de9c627"}, + {file = "rpds_py-0.22.3-cp312-cp312-win_amd64.whl", hash = "sha256:d115bffdd417c6d806ea9069237a4ae02f513b778e3789a359bc5856e0404cc4"}, + {file = "rpds_py-0.22.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:ea7433ce7e4bfc3a85654aeb6747babe3f66eaf9a1d0c1e7a4435bbdf27fea84"}, + {file = "rpds_py-0.22.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6dd9412824c4ce1aca56c47b0991e65bebb7ac3f4edccfd3f156150c96a7bf25"}, + {file = "rpds_py-0.22.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20070c65396f7373f5df4005862fa162db5d25d56150bddd0b3e8214e8ef45b4"}, + {file = "rpds_py-0.22.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0b09865a9abc0ddff4e50b5ef65467cd94176bf1e0004184eb915cbc10fc05c5"}, + {file = "rpds_py-0.22.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3453e8d41fe5f17d1f8e9c383a7473cd46a63661628ec58e07777c2fff7196dc"}, + {file = "rpds_py-0.22.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f5d36399a1b96e1a5fdc91e0522544580dbebeb1f77f27b2b0ab25559e103b8b"}, + {file = "rpds_py-0.22.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:009de23c9c9ee54bf11303a966edf4d9087cd43a6003672e6aa7def643d06518"}, + {file = "rpds_py-0.22.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1aef18820ef3e4587ebe8b3bc9ba6e55892a6d7b93bac6d29d9f631a3b4befbd"}, + {file = "rpds_py-0.22.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f60bd8423be1d9d833f230fdbccf8f57af322d96bcad6599e5a771b151398eb2"}, + {file = "rpds_py-0.22.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:62d9cfcf4948683a18a9aff0ab7e1474d407b7bab2ca03116109f8464698ab16"}, + {file = "rpds_py-0.22.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9253fc214112405f0afa7db88739294295f0e08466987f1d70e29930262b4c8f"}, + {file = "rpds_py-0.22.3-cp313-cp313-win32.whl", hash = "sha256:fb0ba113b4983beac1a2eb16faffd76cb41e176bf58c4afe3e14b9c681f702de"}, + {file = "rpds_py-0.22.3-cp313-cp313-win_amd64.whl", hash = "sha256:c58e2339def52ef6b71b8f36d13c3688ea23fa093353f3a4fee2556e62086ec9"}, + {file = "rpds_py-0.22.3-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:f82a116a1d03628a8ace4859556fb39fd1424c933341a08ea3ed6de1edb0283b"}, + {file = "rpds_py-0.22.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3dfcbc95bd7992b16f3f7ba05af8a64ca694331bd24f9157b49dadeeb287493b"}, + {file = "rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59259dc58e57b10e7e18ce02c311804c10c5a793e6568f8af4dead03264584d1"}, + {file = "rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5725dd9cc02068996d4438d397e255dcb1df776b7ceea3b9cb972bdb11260a83"}, + {file = "rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99b37292234e61325e7a5bb9689e55e48c3f5f603af88b1642666277a81f1fbd"}, + {file = "rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:27b1d3b3915a99208fee9ab092b8184c420f2905b7d7feb4aeb5e4a9c509b8a1"}, + {file = "rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f612463ac081803f243ff13cccc648578e2279295048f2a8d5eb430af2bae6e3"}, + {file = "rpds_py-0.22.3-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f73d3fef726b3243a811121de45193c0ca75f6407fe66f3f4e183c983573e130"}, + {file = "rpds_py-0.22.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:3f21f0495edea7fdbaaa87e633a8689cd285f8f4af5c869f27bc8074638ad69c"}, + {file = "rpds_py-0.22.3-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:1e9663daaf7a63ceccbbb8e3808fe90415b0757e2abddbfc2e06c857bf8c5e2b"}, + {file = "rpds_py-0.22.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a76e42402542b1fae59798fab64432b2d015ab9d0c8c47ba7addddbaf7952333"}, + {file = "rpds_py-0.22.3-cp313-cp313t-win32.whl", hash = "sha256:69803198097467ee7282750acb507fba35ca22cc3b85f16cf45fb01cb9097730"}, + {file = "rpds_py-0.22.3-cp313-cp313t-win_amd64.whl", hash = "sha256:f5cf2a0c2bdadf3791b5c205d55a37a54025c6e18a71c71f82bb536cf9a454bf"}, + {file = "rpds_py-0.22.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:378753b4a4de2a7b34063d6f95ae81bfa7b15f2c1a04a9518e8644e81807ebea"}, + {file = "rpds_py-0.22.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3445e07bf2e8ecfeef6ef67ac83de670358abf2996916039b16a218e3d95e97e"}, + {file = "rpds_py-0.22.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b2513ba235829860b13faa931f3b6846548021846ac808455301c23a101689d"}, + {file = "rpds_py-0.22.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eaf16ae9ae519a0e237a0f528fd9f0197b9bb70f40263ee57ae53c2b8d48aeb3"}, + {file = "rpds_py-0.22.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:583f6a1993ca3369e0f80ba99d796d8e6b1a3a2a442dd4e1a79e652116413091"}, + {file = "rpds_py-0.22.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4617e1915a539a0d9a9567795023de41a87106522ff83fbfaf1f6baf8e85437e"}, + {file = "rpds_py-0.22.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c150c7a61ed4a4f4955a96626574e9baf1adf772c2fb61ef6a5027e52803543"}, + {file = "rpds_py-0.22.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2fa4331c200c2521512595253f5bb70858b90f750d39b8cbfd67465f8d1b596d"}, + {file = "rpds_py-0.22.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:214b7a953d73b5e87f0ebece4a32a5bd83c60a3ecc9d4ec8f1dca968a2d91e99"}, + {file = "rpds_py-0.22.3-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:f47ad3d5f3258bd7058d2d506852217865afefe6153a36eb4b6928758041d831"}, + {file = "rpds_py-0.22.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:f276b245347e6e36526cbd4a266a417796fc531ddf391e43574cf6466c492520"}, + {file = "rpds_py-0.22.3-cp39-cp39-win32.whl", hash = "sha256:bbb232860e3d03d544bc03ac57855cd82ddf19c7a07651a7c0fdb95e9efea8b9"}, + {file = "rpds_py-0.22.3-cp39-cp39-win_amd64.whl", hash = "sha256:cfbc454a2880389dbb9b5b398e50d439e2e58669160f27b60e5eca11f68ae17c"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:d48424e39c2611ee1b84ad0f44fb3b2b53d473e65de061e3f460fc0be5f1939d"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:24e8abb5878e250f2eb0d7859a8e561846f98910326d06c0d51381fed59357bd"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b232061ca880db21fa14defe219840ad9b74b6158adb52ddf0e87bead9e8493"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac0a03221cdb5058ce0167ecc92a8c89e8d0decdc9e99a2ec23380793c4dcb96"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eb0c341fa71df5a4595f9501df4ac5abfb5a09580081dffbd1ddd4654e6e9123"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bf9db5488121b596dbfc6718c76092fda77b703c1f7533a226a5a9f65248f8ad"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b8db6b5b2d4491ad5b6bdc2bc7c017eec108acbf4e6785f42a9eb0ba234f4c9"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b3d504047aba448d70cf6fa22e06cb09f7cbd761939fdd47604f5e007675c24e"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:e61b02c3f7a1e0b75e20c3978f7135fd13cb6cf551bf4a6d29b999a88830a338"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:e35ba67d65d49080e8e5a1dd40101fccdd9798adb9b050ff670b7d74fa41c566"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:26fd7cac7dd51011a245f29a2cc6489c4608b5a8ce8d75661bb4a1066c52dfbe"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:177c7c0fce2855833819c98e43c262007f42ce86651ffbb84f37883308cb0e7d"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:bb47271f60660803ad11f4c61b42242b8c1312a31c98c578f79ef9387bbde21c"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:70fb28128acbfd264eda9bf47015537ba3fe86e40d046eb2963d75024be4d055"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:44d61b4b7d0c2c9ac019c314e52d7cbda0ae31078aabd0f22e583af3e0d79723"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f0e260eaf54380380ac3808aa4ebe2d8ca28b9087cf411649f96bad6900c728"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b25bc607423935079e05619d7de556c91fb6adeae9d5f80868dde3468657994b"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fb6116dfb8d1925cbdb52595560584db42a7f664617a1f7d7f6e32f138cdf37d"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a63cbdd98acef6570c62b92a1e43266f9e8b21e699c363c0fef13bd530799c11"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2b8f60e1b739a74bab7e01fcbe3dddd4657ec685caa04681df9d562ef15b625f"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:2e8b55d8517a2fda8d95cb45d62a5a8bbf9dd0ad39c5b25c8833efea07b880ca"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:2de29005e11637e7a2361fa151f780ff8eb2543a0da1413bb951e9f14b699ef3"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:666ecce376999bf619756a24ce15bb14c5bfaf04bf00abc7e663ce17c3f34fe7"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:5246b14ca64a8675e0a7161f7af68fe3e910e6b90542b4bfb5439ba752191df6"}, + {file = "rpds_py-0.22.3.tar.gz", hash = "sha256:e32fee8ab45d3c2db6da19a5323bc3362237c8b653c70194414b892fd06a080d"}, ] [[package]] @@ -4089,13 +4111,13 @@ testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs ( [[package]] name = "six" -version = "1.16.0" +version = "1.17.0" description = "Python 2 and 3 compatibility utilities" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" files = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, + {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, + {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, ] [[package]] @@ -4362,13 +4384,43 @@ files = [ [[package]] name = "tomli" -version = "2.1.0" +version = "2.2.1" description = "A lil' TOML parser" optional = false python-versions = ">=3.8" files = [ - {file = "tomli-2.1.0-py3-none-any.whl", hash = "sha256:a5c57c3d1c56f5ccdf89f6523458f60ef716e210fc47c4cfb188c5ba473e0391"}, - {file = "tomli-2.1.0.tar.gz", hash = "sha256:3f646cae2aec94e17d04973e4249548320197cfabdf130015d023de4b74d8ab8"}, + {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, + {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"}, + {file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"}, + {file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"}, + {file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"}, + {file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"}, + {file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"}, + {file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"}, + {file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"}, + {file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"}, + {file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"}, + {file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"}, + {file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"}, + {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"}, ] [[package]] @@ -4454,13 +4506,13 @@ files = [ [[package]] name = "types-protobuf" -version = "5.28.3.20241030" +version = "5.29.1.20241207" description = "Typing stubs for protobuf" optional = false python-versions = ">=3.8" files = [ - {file = "types-protobuf-5.28.3.20241030.tar.gz", hash = "sha256:f7e6b45845d75393fb41c0b3ce82c46d775f9771fae2097414a1dbfe5b51a988"}, - {file = "types_protobuf-5.28.3.20241030-py3-none-any.whl", hash = "sha256:f3dae16adf342d4fb5bb3673cabb22549a6252e5dd66fc52d8310b1a39c64ba9"}, + {file = "types_protobuf-5.29.1.20241207-py3-none-any.whl", hash = "sha256:92893c42083e9b718c678badc0af7a9a1307b92afe1599e5cba5f3d35b668b2f"}, + {file = "types_protobuf-5.29.1.20241207.tar.gz", hash = "sha256:2ebcadb8ab3ef2e3e2f067e0882906d64ba0dc65fc5b0fd7a8b692315b4a0be9"}, ] [[package]] @@ -4480,13 +4532,13 @@ types-cffi = "*" [[package]] name = "types-python-dateutil" -version = "2.9.0.20241003" +version = "2.9.0.20241206" description = "Typing stubs for python-dateutil" optional = false python-versions = ">=3.8" files = [ - {file = "types-python-dateutil-2.9.0.20241003.tar.gz", hash = "sha256:58cb85449b2a56d6684e41aeefb4c4280631246a0da1a719bdbe6f3fb0317446"}, - {file = "types_python_dateutil-2.9.0.20241003-py3-none-any.whl", hash = "sha256:250e1d8e80e7bbc3a6c99b907762711d1a1cdd00e978ad39cb5940f6f0a87f3d"}, + {file = "types_python_dateutil-2.9.0.20241206-py3-none-any.whl", hash = "sha256:e248a4bc70a486d3e3ec84d0dc30eec3a5f979d6e7ee4123ae043eedbb987f53"}, + {file = "types_python_dateutil-2.9.0.20241206.tar.gz", hash = "sha256:18f493414c26ffba692a72369fea7a154c502646301ebfe3d56a04b3767284cb"}, ] [[package]] @@ -4857,93 +4909,93 @@ files = [ [[package]] name = "yarl" -version = "1.18.0" +version = "1.18.3" description = "Yet another URL library" optional = false python-versions = ">=3.9" files = [ - {file = "yarl-1.18.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:074fee89caab89a97e18ef5f29060ef61ba3cae6cd77673acc54bfdd3214b7b7"}, - {file = "yarl-1.18.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b026cf2c32daf48d90c0c4e406815c3f8f4cfe0c6dfccb094a9add1ff6a0e41a"}, - {file = "yarl-1.18.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ae38bd86eae3ba3d2ce5636cc9e23c80c9db2e9cb557e40b98153ed102b5a736"}, - {file = "yarl-1.18.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:685cc37f3f307c6a8e879986c6d85328f4c637f002e219f50e2ef66f7e062c1d"}, - {file = "yarl-1.18.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8254dbfce84ee5d1e81051ee7a0f1536c108ba294c0fdb5933476398df0654f3"}, - {file = "yarl-1.18.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:20de4a8b04de70c49698dc2390b7fd2d18d424d3b876371f9b775e2b462d4b41"}, - {file = "yarl-1.18.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b0a2074a37285570d54b55820687de3d2f2b9ecf1b714e482e48c9e7c0402038"}, - {file = "yarl-1.18.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3f576ed278860df2721a5d57da3381040176ef1d07def9688a385c8330db61a1"}, - {file = "yarl-1.18.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3a3709450a574d61be6ac53d582496014342ea34876af8dc17cc16da32826c9a"}, - {file = "yarl-1.18.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:bd80ed29761490c622edde5dd70537ca8c992c2952eb62ed46984f8eff66d6e8"}, - {file = "yarl-1.18.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:32141e13a1d5a48525e519c9197d3f4d9744d818d5c7d6547524cc9eccc8971e"}, - {file = "yarl-1.18.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8b8d3e4e014fb4274f1c5bf61511d2199e263909fb0b8bda2a7428b0894e8dc6"}, - {file = "yarl-1.18.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:701bb4a8f4de191c8c0cc9a1e6d5142f4df880e9d1210e333b829ca9425570ed"}, - {file = "yarl-1.18.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a45d94075ac0647621eaaf693c8751813a3eccac455d423f473ffed38c8ac5c9"}, - {file = "yarl-1.18.0-cp310-cp310-win32.whl", hash = "sha256:34176bfb082add67cb2a20abd85854165540891147f88b687a5ed0dc225750a0"}, - {file = "yarl-1.18.0-cp310-cp310-win_amd64.whl", hash = "sha256:73553bbeea7d6ec88c08ad8027f4e992798f0abc459361bf06641c71972794dc"}, - {file = "yarl-1.18.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b8e8c516dc4e1a51d86ac975b0350735007e554c962281c432eaa5822aa9765c"}, - {file = "yarl-1.18.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2e6b4466714a73f5251d84b471475850954f1fa6acce4d3f404da1d55d644c34"}, - {file = "yarl-1.18.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c893f8c1a6d48b25961e00922724732d00b39de8bb0b451307482dc87bddcd74"}, - {file = "yarl-1.18.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:13aaf2bdbc8c86ddce48626b15f4987f22e80d898818d735b20bd58f17292ee8"}, - {file = "yarl-1.18.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dd21c0128e301851de51bc607b0a6da50e82dc34e9601f4b508d08cc89ee7929"}, - {file = "yarl-1.18.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:205de377bd23365cd85562c9c6c33844050a93661640fda38e0567d2826b50df"}, - {file = "yarl-1.18.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed69af4fe2a0949b1ea1d012bf065c77b4c7822bad4737f17807af2adb15a73c"}, - {file = "yarl-1.18.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8e1c18890091aa3cc8a77967943476b729dc2016f4cfe11e45d89b12519d4a93"}, - {file = "yarl-1.18.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:91b8fb9427e33f83ca2ba9501221ffaac1ecf0407f758c4d2f283c523da185ee"}, - {file = "yarl-1.18.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:536a7a8a53b75b2e98ff96edb2dfb91a26b81c4fed82782035767db5a465be46"}, - {file = "yarl-1.18.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:a64619a9c47c25582190af38e9eb382279ad42e1f06034f14d794670796016c0"}, - {file = "yarl-1.18.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c73a6bbc97ba1b5a0c3c992ae93d721c395bdbb120492759b94cc1ac71bc6350"}, - {file = "yarl-1.18.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:a173401d7821a2a81c7b47d4e7d5c4021375a1441af0c58611c1957445055056"}, - {file = "yarl-1.18.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7520e799b1f84e095cce919bd6c23c9d49472deeef25fe1ef960b04cca51c3fc"}, - {file = "yarl-1.18.0-cp311-cp311-win32.whl", hash = "sha256:c4cb992d8090d5ae5f7afa6754d7211c578be0c45f54d3d94f7781c495d56716"}, - {file = "yarl-1.18.0-cp311-cp311-win_amd64.whl", hash = "sha256:52c136f348605974c9b1c878addd6b7a60e3bf2245833e370862009b86fa4689"}, - {file = "yarl-1.18.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1ece25e2251c28bab737bdf0519c88189b3dd9492dc086a1d77336d940c28ced"}, - {file = "yarl-1.18.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:454902dc1830d935c90b5b53c863ba2a98dcde0fbaa31ca2ed1ad33b2a7171c6"}, - {file = "yarl-1.18.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:01be8688fc211dc237e628fcc209dda412d35de7642453059a0553747018d075"}, - {file = "yarl-1.18.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d26f1fa9fa2167bb238f6f4b20218eb4e88dd3ef21bb8f97439fa6b5313e30d"}, - {file = "yarl-1.18.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b234a4a9248a9f000b7a5dfe84b8cb6210ee5120ae70eb72a4dcbdb4c528f72f"}, - {file = "yarl-1.18.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe94d1de77c4cd8caff1bd5480e22342dbd54c93929f5943495d9c1e8abe9f42"}, - {file = "yarl-1.18.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b4c90c5363c6b0a54188122b61edb919c2cd1119684999d08cd5e538813a28e"}, - {file = "yarl-1.18.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49a98ecadc5a241c9ba06de08127ee4796e1009555efd791bac514207862b43d"}, - {file = "yarl-1.18.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9106025c7f261f9f5144f9aa7681d43867eed06349a7cfb297a1bc804de2f0d1"}, - {file = "yarl-1.18.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:f275ede6199d0f1ed4ea5d55a7b7573ccd40d97aee7808559e1298fe6efc8dbd"}, - {file = "yarl-1.18.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:f7edeb1dcc7f50a2c8e08b9dc13a413903b7817e72273f00878cb70e766bdb3b"}, - {file = "yarl-1.18.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c083f6dd6951b86e484ebfc9c3524b49bcaa9c420cb4b2a78ef9f7a512bfcc85"}, - {file = "yarl-1.18.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:80741ec5b471fbdfb997821b2842c59660a1c930ceb42f8a84ba8ca0f25a66aa"}, - {file = "yarl-1.18.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b1a3297b9cad594e1ff0c040d2881d7d3a74124a3c73e00c3c71526a1234a9f7"}, - {file = "yarl-1.18.0-cp312-cp312-win32.whl", hash = "sha256:cd6ab7d6776c186f544f893b45ee0c883542b35e8a493db74665d2e594d3ca75"}, - {file = "yarl-1.18.0-cp312-cp312-win_amd64.whl", hash = "sha256:039c299a0864d1f43c3e31570045635034ea7021db41bf4842693a72aca8df3a"}, - {file = "yarl-1.18.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6fb64dd45453225f57d82c4764818d7a205ee31ce193e9f0086e493916bd4f72"}, - {file = "yarl-1.18.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3adaaf9c6b1b4fc258584f4443f24d775a2086aee82d1387e48a8b4f3d6aecf6"}, - {file = "yarl-1.18.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:da206d1ec78438a563c5429ab808a2b23ad7bc025c8adbf08540dde202be37d5"}, - {file = "yarl-1.18.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:576d258b21c1db4c6449b1c572c75d03f16a482eb380be8003682bdbe7db2f28"}, - {file = "yarl-1.18.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c60e547c0a375c4bfcdd60eef82e7e0e8698bf84c239d715f5c1278a73050393"}, - {file = "yarl-1.18.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e3818eabaefb90adeb5e0f62f047310079d426387991106d4fbf3519eec7d90a"}, - {file = "yarl-1.18.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5f72421246c21af6a92fbc8c13b6d4c5427dfd949049b937c3b731f2f9076bd"}, - {file = "yarl-1.18.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7fa7d37f2ada0f42e0723632993ed422f2a679af0e200874d9d861720a54f53e"}, - {file = "yarl-1.18.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:42ba84e2ac26a3f252715f8ec17e6fdc0cbf95b9617c5367579fafcd7fba50eb"}, - {file = "yarl-1.18.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:6a49ad0102c0f0ba839628d0bf45973c86ce7b590cdedf7540d5b1833ddc6f00"}, - {file = "yarl-1.18.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:96404e8d5e1bbe36bdaa84ef89dc36f0e75939e060ca5cd45451aba01db02902"}, - {file = "yarl-1.18.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:a0509475d714df8f6d498935b3f307cd122c4ca76f7d426c7e1bb791bcd87eda"}, - {file = "yarl-1.18.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:1ff116f0285b5c8b3b9a2680aeca29a858b3b9e0402fc79fd850b32c2bcb9f8b"}, - {file = "yarl-1.18.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e2580c1d7e66e6d29d6e11855e3b1c6381971e0edd9a5066e6c14d79bc8967af"}, - {file = "yarl-1.18.0-cp313-cp313-win32.whl", hash = "sha256:14408cc4d34e202caba7b5ac9cc84700e3421a9e2d1b157d744d101b061a4a88"}, - {file = "yarl-1.18.0-cp313-cp313-win_amd64.whl", hash = "sha256:1db1537e9cb846eb0ff206eac667f627794be8b71368c1ab3207ec7b6f8c5afc"}, - {file = "yarl-1.18.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:fa2c9cb607e0f660d48c54a63de7a9b36fef62f6b8bd50ff592ce1137e73ac7d"}, - {file = "yarl-1.18.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c0f4808644baf0a434a3442df5e0bedf8d05208f0719cedcd499e168b23bfdc4"}, - {file = "yarl-1.18.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7db9584235895a1dffca17e1c634b13870852094f6389b68dcc6338086aa7b08"}, - {file = "yarl-1.18.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:309f8d27d6f93ceeeb80aa6980e883aa57895270f7f41842b92247e65d7aeddf"}, - {file = "yarl-1.18.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:609ffd44fed2ed88d9b4ef62ee860cf86446cf066333ad4ce4123505b819e581"}, - {file = "yarl-1.18.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f172b8b2c72a13a06ea49225a9c47079549036ad1b34afa12d5491b881f5b993"}, - {file = "yarl-1.18.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d89ae7de94631b60d468412c18290d358a9d805182373d804ec839978b120422"}, - {file = "yarl-1.18.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:466d31fd043ef9af822ee3f1df8fdff4e8c199a7f4012c2642006af240eade17"}, - {file = "yarl-1.18.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:7609b8462351c4836b3edce4201acb6dd46187b207c589b30a87ffd1813b48dc"}, - {file = "yarl-1.18.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:d9d4f5e471e8dc49b593a80766c2328257e405f943c56a3dc985c125732bc4cf"}, - {file = "yarl-1.18.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:67b336c15e564d76869c9a21316f90edf546809a5796a083b8f57c845056bc01"}, - {file = "yarl-1.18.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:b212452b80cae26cb767aa045b051740e464c5129b7bd739c58fbb7deb339e7b"}, - {file = "yarl-1.18.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:38b39b7b3e692b6c92b986b00137a3891eddb66311b229d1940dcbd4f025083c"}, - {file = "yarl-1.18.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a7ee6884a8848792d58b854946b685521f41d8871afa65e0d4a774954e9c9e89"}, - {file = "yarl-1.18.0-cp39-cp39-win32.whl", hash = "sha256:b4095c5019bb889aa866bf12ed4c85c0daea5aafcb7c20d1519f02a1e738f07f"}, - {file = "yarl-1.18.0-cp39-cp39-win_amd64.whl", hash = "sha256:2d90f2e4d16a5b0915ee065218b435d2ef619dd228973b1b47d262a6f7cd8fa5"}, - {file = "yarl-1.18.0-py3-none-any.whl", hash = "sha256:dbf53db46f7cf176ee01d8d98c39381440776fcda13779d269a8ba664f69bec0"}, - {file = "yarl-1.18.0.tar.gz", hash = "sha256:20d95535e7d833889982bfe7cc321b7f63bf8879788fee982c76ae2b24cfb715"}, + {file = "yarl-1.18.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7df647e8edd71f000a5208fe6ff8c382a1de8edfbccdbbfe649d263de07d8c34"}, + {file = "yarl-1.18.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c69697d3adff5aa4f874b19c0e4ed65180ceed6318ec856ebc423aa5850d84f7"}, + {file = "yarl-1.18.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:602d98f2c2d929f8e697ed274fbadc09902c4025c5a9963bf4e9edfc3ab6f7ed"}, + {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c654d5207c78e0bd6d749f6dae1dcbbfde3403ad3a4b11f3c5544d9906969dde"}, + {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5094d9206c64181d0f6e76ebd8fb2f8fe274950a63890ee9e0ebfd58bf9d787b"}, + {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35098b24e0327fc4ebdc8ffe336cee0a87a700c24ffed13161af80124b7dc8e5"}, + {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3236da9272872443f81fedc389bace88408f64f89f75d1bdb2256069a8730ccc"}, + {file = "yarl-1.18.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2c08cc9b16f4f4bc522771d96734c7901e7ebef70c6c5c35dd0f10845270bcd"}, + {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:80316a8bd5109320d38eef8833ccf5f89608c9107d02d2a7f985f98ed6876990"}, + {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:c1e1cc06da1491e6734f0ea1e6294ce00792193c463350626571c287c9a704db"}, + {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fea09ca13323376a2fdfb353a5fa2e59f90cd18d7ca4eaa1fd31f0a8b4f91e62"}, + {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e3b9fd71836999aad54084906f8663dffcd2a7fb5cdafd6c37713b2e72be1760"}, + {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:757e81cae69244257d125ff31663249b3013b5dc0a8520d73694aed497fb195b"}, + {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b1771de9944d875f1b98a745bc547e684b863abf8f8287da8466cf470ef52690"}, + {file = "yarl-1.18.3-cp310-cp310-win32.whl", hash = "sha256:8874027a53e3aea659a6d62751800cf6e63314c160fd607489ba5c2edd753cf6"}, + {file = "yarl-1.18.3-cp310-cp310-win_amd64.whl", hash = "sha256:93b2e109287f93db79210f86deb6b9bbb81ac32fc97236b16f7433db7fc437d8"}, + {file = "yarl-1.18.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8503ad47387b8ebd39cbbbdf0bf113e17330ffd339ba1144074da24c545f0069"}, + {file = "yarl-1.18.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:02ddb6756f8f4517a2d5e99d8b2f272488e18dd0bfbc802f31c16c6c20f22193"}, + {file = "yarl-1.18.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:67a283dd2882ac98cc6318384f565bffc751ab564605959df4752d42483ad889"}, + {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d980e0325b6eddc81331d3f4551e2a333999fb176fd153e075c6d1c2530aa8a8"}, + {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b643562c12680b01e17239be267bc306bbc6aac1f34f6444d1bded0c5ce438ca"}, + {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c017a3b6df3a1bd45b9fa49a0f54005e53fbcad16633870104b66fa1a30a29d8"}, + {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75674776d96d7b851b6498f17824ba17849d790a44d282929c42dbb77d4f17ae"}, + {file = "yarl-1.18.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ccaa3a4b521b780a7e771cc336a2dba389a0861592bbce09a476190bb0c8b4b3"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2d06d3005e668744e11ed80812e61efd77d70bb7f03e33c1598c301eea20efbb"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:9d41beda9dc97ca9ab0b9888cb71f7539124bc05df02c0cff6e5acc5a19dcc6e"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ba23302c0c61a9999784e73809427c9dbedd79f66a13d84ad1b1943802eaaf59"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:6748dbf9bfa5ba1afcc7556b71cda0d7ce5f24768043a02a58846e4a443d808d"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0b0cad37311123211dc91eadcb322ef4d4a66008d3e1bdc404808992260e1a0e"}, + {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0fb2171a4486bb075316ee754c6d8382ea6eb8b399d4ec62fde2b591f879778a"}, + {file = "yarl-1.18.3-cp311-cp311-win32.whl", hash = "sha256:61b1a825a13bef4a5f10b1885245377d3cd0bf87cba068e1d9a88c2ae36880e1"}, + {file = "yarl-1.18.3-cp311-cp311-win_amd64.whl", hash = "sha256:b9d60031cf568c627d028239693fd718025719c02c9f55df0a53e587aab951b5"}, + {file = "yarl-1.18.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1dd4bdd05407ced96fed3d7f25dbbf88d2ffb045a0db60dbc247f5b3c5c25d50"}, + {file = "yarl-1.18.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7c33dd1931a95e5d9a772d0ac5e44cac8957eaf58e3c8da8c1414de7dd27c576"}, + {file = "yarl-1.18.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:25b411eddcfd56a2f0cd6a384e9f4f7aa3efee14b188de13048c25b5e91f1640"}, + {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:436c4fc0a4d66b2badc6c5fc5ef4e47bb10e4fd9bf0c79524ac719a01f3607c2"}, + {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e35ef8683211db69ffe129a25d5634319a677570ab6b2eba4afa860f54eeaf75"}, + {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:84b2deecba4a3f1a398df819151eb72d29bfeb3b69abb145a00ddc8d30094512"}, + {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00e5a1fea0fd4f5bfa7440a47eff01d9822a65b4488f7cff83155a0f31a2ecba"}, + {file = "yarl-1.18.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d0e883008013c0e4aef84dcfe2a0b172c4d23c2669412cf5b3371003941f72bb"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5a3f356548e34a70b0172d8890006c37be92995f62d95a07b4a42e90fba54272"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ccd17349166b1bee6e529b4add61727d3f55edb7babbe4069b5764c9587a8cc6"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b958ddd075ddba5b09bb0be8a6d9906d2ce933aee81100db289badbeb966f54e"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c7d79f7d9aabd6011004e33b22bc13056a3e3fb54794d138af57f5ee9d9032cb"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:4891ed92157e5430874dad17b15eb1fda57627710756c27422200c52d8a4e393"}, + {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ce1af883b94304f493698b00d0f006d56aea98aeb49d75ec7d98cd4a777e9285"}, + {file = "yarl-1.18.3-cp312-cp312-win32.whl", hash = "sha256:f91c4803173928a25e1a55b943c81f55b8872f0018be83e3ad4938adffb77dd2"}, + {file = "yarl-1.18.3-cp312-cp312-win_amd64.whl", hash = "sha256:7e2ee16578af3b52ac2f334c3b1f92262f47e02cc6193c598502bd46f5cd1477"}, + {file = "yarl-1.18.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:90adb47ad432332d4f0bc28f83a5963f426ce9a1a8809f5e584e704b82685dcb"}, + {file = "yarl-1.18.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:913829534200eb0f789d45349e55203a091f45c37a2674678744ae52fae23efa"}, + {file = "yarl-1.18.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ef9f7768395923c3039055c14334ba4d926f3baf7b776c923c93d80195624782"}, + {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88a19f62ff30117e706ebc9090b8ecc79aeb77d0b1f5ec10d2d27a12bc9f66d0"}, + {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e17c9361d46a4d5addf777c6dd5eab0715a7684c2f11b88c67ac37edfba6c482"}, + {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1a74a13a4c857a84a845505fd2d68e54826a2cd01935a96efb1e9d86c728e186"}, + {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41f7ce59d6ee7741af71d82020346af364949314ed3d87553763a2df1829cc58"}, + {file = "yarl-1.18.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f52a265001d830bc425f82ca9eabda94a64a4d753b07d623a9f2863fde532b53"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:82123d0c954dc58db301f5021a01854a85bf1f3bb7d12ae0c01afc414a882ca2"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:2ec9bbba33b2d00999af4631a3397d1fd78290c48e2a3e52d8dd72db3a067ac8"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:fbd6748e8ab9b41171bb95c6142faf068f5ef1511935a0aa07025438dd9a9bc1"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:877d209b6aebeb5b16c42cbb377f5f94d9e556626b1bfff66d7b0d115be88d0a"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b464c4ab4bfcb41e3bfd3f1c26600d038376c2de3297760dfe064d2cb7ea8e10"}, + {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8d39d351e7faf01483cc7ff7c0213c412e38e5a340238826be7e0e4da450fdc8"}, + {file = "yarl-1.18.3-cp313-cp313-win32.whl", hash = "sha256:61ee62ead9b68b9123ec24bc866cbef297dd266175d53296e2db5e7f797f902d"}, + {file = "yarl-1.18.3-cp313-cp313-win_amd64.whl", hash = "sha256:578e281c393af575879990861823ef19d66e2b1d0098414855dd367e234f5b3c"}, + {file = "yarl-1.18.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:61e5e68cb65ac8f547f6b5ef933f510134a6bf31bb178be428994b0cb46c2a04"}, + {file = "yarl-1.18.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fe57328fbc1bfd0bd0514470ac692630f3901c0ee39052ae47acd1d90a436719"}, + {file = "yarl-1.18.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a440a2a624683108a1b454705ecd7afc1c3438a08e890a1513d468671d90a04e"}, + {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09c7907c8548bcd6ab860e5f513e727c53b4a714f459b084f6580b49fa1b9cee"}, + {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b4f6450109834af88cb4cc5ecddfc5380ebb9c228695afc11915a0bf82116789"}, + {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9ca04806f3be0ac6d558fffc2fdf8fcef767e0489d2684a21912cc4ed0cd1b8"}, + {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77a6e85b90a7641d2e07184df5557132a337f136250caafc9ccaa4a2a998ca2c"}, + {file = "yarl-1.18.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6333c5a377c8e2f5fae35e7b8f145c617b02c939d04110c76f29ee3676b5f9a5"}, + {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0b3c92fa08759dbf12b3a59579a4096ba9af8dd344d9a813fc7f5070d86bbab1"}, + {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:4ac515b860c36becb81bb84b667466885096b5fc85596948548b667da3bf9f24"}, + {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:045b8482ce9483ada4f3f23b3774f4e1bf4f23a2d5c912ed5170f68efb053318"}, + {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:a4bb030cf46a434ec0225bddbebd4b89e6471814ca851abb8696170adb163985"}, + {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:54d6921f07555713b9300bee9c50fb46e57e2e639027089b1d795ecd9f7fa910"}, + {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1d407181cfa6e70077df3377938c08012d18893f9f20e92f7d2f314a437c30b1"}, + {file = "yarl-1.18.3-cp39-cp39-win32.whl", hash = "sha256:ac36703a585e0929b032fbaab0707b75dc12703766d0b53486eabd5139ebadd5"}, + {file = "yarl-1.18.3-cp39-cp39-win_amd64.whl", hash = "sha256:ba87babd629f8af77f557b61e49e7c7cac36f22f871156b91e10a6e9d4f829e9"}, + {file = "yarl-1.18.3-py3-none-any.whl", hash = "sha256:b57f4f58099328dfb26c6a771d09fb20dbbae81d20cfb66141251ea063bd101b"}, + {file = "yarl-1.18.3.tar.gz", hash = "sha256:ac1801c45cbf77b6c99242eeff4fffb5e4e73a800b5c4ad4fc0be5def634d2e1"}, ] [package.dependencies] @@ -4973,4 +5025,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = ">=3.9,<4.0" -content-hash = "49339ee21f898abb08e43b110a54e89823192e23fddbfcffdbed8bf27e8417b2" +content-hash = "1ef180fa6f7a16484046779550a760e94e8b2e005676c3cc6279c9470baed447" diff --git a/libs/langchain/pyproject.toml b/libs/langchain/pyproject.toml index e649ac585a155..6995b9794628a 100644 --- a/libs/langchain/pyproject.toml +++ b/libs/langchain/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api" [tool.poetry] name = "langchain" -version = "0.3.9" +version = "0.3.12" description = "Building applications with LLMs through composability" authors = [] license = "MIT" @@ -33,9 +33,9 @@ langchain-server = "langchain.server:main" [tool.poetry.dependencies] python = ">=3.9,<4.0" -langchain-core = "^0.3.21" -langchain-text-splitters = "^0.3.0" -langsmith = "^0.1.17" +langchain-core = "^0.3.25" +langchain-text-splitters = "^0.3.3" +langsmith = ">=0.1.17,<0.3" pydantic = "^2.7.4" SQLAlchemy = ">=1.4,<3" requests = "^2" diff --git a/libs/packages.yml b/libs/packages.yml index a0a41894075d7..77e6b15a7c5e3 100644 --- a/libs/packages.yml +++ b/libs/packages.yml @@ -42,8 +42,8 @@ packages: repo: langchain-ai/langchain path: libs/partners/huggingface - name: langchain-ibm - repo: langchain-ai/langchain - path: libs/partners/ibm + repo: langchain-ai/langchain-ibm + path: libs/ibm - name: langchain-milvus repo: langchain-ai/langchain-milvus path: libs/milvus @@ -68,6 +68,9 @@ packages: - name: langchain-qdrant repo: langchain-ai/langchain path: libs/partners/qdrant + - name: langchain-scrapegraph + repo: ScrapeGraphAI/langchain-scrapegraph + path: . - name: langchain-sema4 repo: langchain-ai/langchain-sema4 path: libs/sema4 @@ -150,3 +153,6 @@ packages: - name: langchain-neo4j repo: langchain-ai/langchain-neo4j path: libs/neo4j + - name: langchain-linkup + repo: LinkupPlatform/langchain-linkup + path: . diff --git a/libs/partners/anthropic/Makefile b/libs/partners/anthropic/Makefile index 7a32f31997ab5..256ab677b83de 100644 --- a/libs/partners/anthropic/Makefile +++ b/libs/partners/anthropic/Makefile @@ -8,7 +8,7 @@ TEST_FILE ?= tests/unit_tests/ integration_test integration_tests: TEST_FILE=tests/integration_tests/ test tests integration_test integration_tests: - poetry run pytest $(TEST_FILE) + poetry run pytest -vvv --timeout 10 $(TEST_FILE) test_watch: poetry run ptw --snapshot-update --now . -- -vv $(TEST_FILE) diff --git a/libs/partners/anthropic/poetry.lock b/libs/partners/anthropic/poetry.lock index 24666fc665745..0770706b18597 100644 --- a/libs/partners/anthropic/poetry.lock +++ b/libs/partners/anthropic/poetry.lock @@ -852,6 +852,20 @@ pytest = ">=6.2.5" [package.extras] dev = ["pre-commit", "pytest-asyncio", "tox"] +[[package]] +name = "pytest-timeout" +version = "2.3.1" +description = "pytest plugin to abort hanging tests" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-timeout-2.3.1.tar.gz", hash = "sha256:12397729125c6ecbdaca01035b9e5239d4db97352320af155b3f5de1ba5165d9"}, + {file = "pytest_timeout-2.3.1-py3-none-any.whl", hash = "sha256:68188cb703edfc6a18fad98dc25a3c61e9f24d644b0b70f33af545219fc7813e"}, +] + +[package.dependencies] +pytest = ">=7.0.0" + [[package]] name = "pytest-watcher" version = "0.3.5" @@ -1140,4 +1154,4 @@ watchmedo = ["PyYAML (>=3.10)"] [metadata] lock-version = "2.0" python-versions = ">=3.9,<4.0" -content-hash = "ee4aaa06307b4dc7f7913147bf58f3f36245193c9d4a79c43aba07641f7b6ab9" +content-hash = "7d24a5eb5b867fa9ad80cbc9fb80d630e7cb00a490b62502cdb57c2fe95cd125" diff --git a/libs/partners/anthropic/pyproject.toml b/libs/partners/anthropic/pyproject.toml index cda312230357f..b6ff159efccb9 100644 --- a/libs/partners/anthropic/pyproject.toml +++ b/libs/partners/anthropic/pyproject.toml @@ -65,6 +65,7 @@ syrupy = "^4.0.2" pytest-watcher = "^0.3.4" pytest-asyncio = "^0.21.1" defusedxml = "^0.7.1" +pytest-timeout = "^2.3.1" [tool.poetry.group.codespell.dependencies] codespell = "^2.2.0" diff --git a/libs/partners/anthropic/tests/integration_tests/test_chat_models.py b/libs/partners/anthropic/tests/integration_tests/test_chat_models.py index 5c2295492632d..8ff2396c9d1c9 100644 --- a/libs/partners/anthropic/tests/integration_tests/test_chat_models.py +++ b/libs/partners/anthropic/tests/integration_tests/test_chat_models.py @@ -22,7 +22,7 @@ from langchain_anthropic import ChatAnthropic, ChatAnthropicMessages from tests.unit_tests._utils import FakeCallbackHandler -MODEL_NAME = "claude-3-sonnet-20240229" +MODEL_NAME = "claude-3-5-sonnet-20240620" def test_stream() -> None: diff --git a/libs/partners/chroma/langchain_chroma/vectorstores.py b/libs/partners/chroma/langchain_chroma/vectorstores.py index 2d0537b57700a..bc87f9e088f43 100644 --- a/libs/partners/chroma/langchain_chroma/vectorstores.py +++ b/libs/partners/chroma/langchain_chroma/vectorstores.py @@ -16,6 +16,7 @@ Iterable, List, Optional, + Sequence, Tuple, Type, Union, @@ -432,6 +433,8 @@ def add_images( # Populate IDs if ids is None: ids = [str(uuid.uuid4()) for _ in uris] + else: + ids = [id if id is not None else str(uuid.uuid4()) for id in ids] embeddings = None # Set embeddings if self._embedding_function is not None and hasattr( @@ -517,6 +520,9 @@ def add_texts( """ if ids is None: ids = [str(uuid.uuid4()) for _ in texts] + else: + ids = [id if id is not None else str(uuid.uuid4()) for id in ids] + embeddings = None texts = list(texts) if self._embedding_function is not None: @@ -752,7 +758,7 @@ def _select_relevance_score_fn(self) -> Callable[[float], float]: The most similar documents will have the lowest relevance score. Default relevance score function is euclidean distance. Distance metric must be - provided in `collection_metadata` during initizalition of Chroma object. + provided in `collection_metadata` during initialization of Chroma object. Example: collection_metadata={"hnsw:space": "cosine"}. Available distance metrics are: 'cosine', 'l2' and 'ip'. @@ -1028,6 +1034,38 @@ def get( return self._collection.get(**kwargs) # type: ignore + def get_by_ids(self, ids: Sequence[str], /) -> list[Document]: + """Get documents by their IDs. + + The returned documents are expected to have the ID field set to the ID of the + document in the vector store. + + Fewer documents may be returned than requested if some IDs are not found or + if there are duplicated IDs. + + Users should not assume that the order of the returned documents matches + the order of the input IDs. Instead, users should rely on the ID field of the + returned documents. + + This method should **NOT** raise exceptions if no documents are found for + some IDs. + + Args: + ids: List of ids to retrieve. + + Returns: + List of Documents. + + .. versionadded:: 0.2.1 + """ + results = self.get(ids=list(ids)) + return [ + Document(page_content=doc, metadata=meta, id=doc_id) + for doc, meta, doc_id in zip( + results["documents"], results["metadatas"], results["ids"] + ) + ] + def update_document(self, document_id: str, document: Document) -> None: """Update a document in the collection. @@ -1131,6 +1169,8 @@ def from_texts( ) if ids is None: ids = [str(uuid.uuid4()) for _ in texts] + else: + ids = [id if id is not None else str(uuid.uuid4()) for id in ids] if hasattr( chroma_collection._client, "get_max_batch_size" ) or hasattr( # for Chroma 0.5.1 and above @@ -1190,7 +1230,7 @@ def from_documents( texts = [doc.page_content for doc in documents] metadatas = [doc.metadata for doc in documents] if ids is None: - ids = [doc.id if doc.id else "" for doc in documents] + ids = [doc.id if doc.id else str(uuid.uuid4()) for doc in documents] return cls.from_texts( texts=texts, embedding=embedding, diff --git a/libs/partners/chroma/poetry.lock b/libs/partners/chroma/poetry.lock index 812eefb494a0e..f4b4192f448a0 100644 --- a/libs/partners/chroma/poetry.lock +++ b/libs/partners/chroma/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. [[package]] name = "annotated-types" @@ -955,6 +955,25 @@ typing-extensions = ">=4.7" type = "directory" url = "../../core" +[[package]] +name = "langchain-tests" +version = "0.3.4" +description = "Standard tests for LangChain implementations" +optional = false +python-versions = ">=3.9,<4.0" +files = [] +develop = true + +[package.dependencies] +httpx = "^0.27.0" +langchain-core = "^0.3.19" +pytest = ">=7,<9" +syrupy = "^4" + +[package.source] +type = "directory" +url = "../../standard-tests" + [[package]] name = "langsmith" version = "0.1.139" @@ -2805,4 +2824,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = ">=3.9,<4" -content-hash = "4e3e3152fdc954723a33ffc5cc5c42b763e5aee74f39df8b54f16cdb753b2d13" +content-hash = "2d6bc4b9a18a322c326c3f7d5786c4b196a997458e6d2ca4043cb6b7a4a123b3" diff --git a/libs/partners/chroma/pyproject.toml b/libs/partners/chroma/pyproject.toml index ede3a9fad5ab1..8a7e18bdf1bf3 100644 --- a/libs/partners/chroma/pyproject.toml +++ b/libs/partners/chroma/pyproject.toml @@ -90,6 +90,10 @@ python = ">=3.9" version = ">=0.1.40,<0.3" python = "<3.9" +[[tool.poetry.group.test.dependencies.langchain-tests]] +path = "../../standard-tests" +develop = true + [tool.poetry.group.codespell.dependencies] codespell = "^2.2.0" diff --git a/libs/partners/chroma/tests/integration_tests/test_vectorstores.py b/libs/partners/chroma/tests/integration_tests/test_vectorstores.py index bea50c909748f..f7bed4cfa5588 100644 --- a/libs/partners/chroma/tests/integration_tests/test_vectorstores.py +++ b/libs/partners/chroma/tests/integration_tests/test_vectorstores.py @@ -51,6 +51,22 @@ def test_chroma() -> None: assert output[0].id is not None +def test_from_documents() -> None: + """Test init using .from_documents.""" + documents = [ + Document(page_content="foo"), + Document(page_content="bar"), + Document(page_content="baz"), + ] + docsearch = Chroma.from_documents(documents=documents, embedding=FakeEmbeddings()) + output = docsearch.similarity_search("foo", k=1) + + docsearch.delete_collection() + assert len(output) == 1 + assert output[0].page_content == "foo" + assert output[0].id is not None + + def test_chroma_with_ids() -> None: """Test end to end construction and search.""" texts = ["foo", "bar", "baz"] diff --git a/libs/partners/chroma/tests/unit_tests/test_standard.py b/libs/partners/chroma/tests/unit_tests/test_standard.py new file mode 100644 index 0000000000000..3e2945cb7fa39 --- /dev/null +++ b/libs/partners/chroma/tests/unit_tests/test_standard.py @@ -0,0 +1,19 @@ +from typing import Generator + +import pytest +from langchain_core.vectorstores import VectorStore +from langchain_tests.integration_tests.vectorstores import VectorStoreIntegrationTests + +from langchain_chroma import Chroma + + +class TestChromaStandard(VectorStoreIntegrationTests): + @pytest.fixture() + def vectorstore(self) -> Generator[VectorStore, None, None]: # type: ignore + """Get an empty vectorstore for unit tests.""" + store = Chroma(embedding_function=self.get_embeddings()) + try: + yield store + finally: + store.delete_collection() + pass diff --git a/libs/partners/chroma/tests/unit_tests/test_vectorstores.py b/libs/partners/chroma/tests/unit_tests/test_vectorstores.py index 84d8637879e29..66ac3b0622465 100644 --- a/libs/partners/chroma/tests/unit_tests/test_vectorstores.py +++ b/libs/partners/chroma/tests/unit_tests/test_vectorstores.py @@ -13,3 +13,18 @@ def test_initialization() -> None: texts=texts, embedding=FakeEmbeddings(size=10), ) + + +def test_similarity_search() -> None: + """Test similarity search by Chroma.""" + texts = ["foo", "bar", "baz"] + metadatas = [{"page": str(i)} for i in range(len(texts))] + docsearch = Chroma.from_texts( + collection_name="test_collection", + texts=texts, + embedding=FakeEmbeddings(size=10), + metadatas=metadatas, + ) + output = docsearch.similarity_search("foo", k=1) + docsearch.delete_collection() + assert len(output) == 1 diff --git a/libs/partners/fireworks/tests/integration_tests/test_standard.py b/libs/partners/fireworks/tests/integration_tests/test_standard.py index 692dcb40cf357..7a595e4c7da44 100644 --- a/libs/partners/fireworks/tests/integration_tests/test_standard.py +++ b/libs/partners/fireworks/tests/integration_tests/test_standard.py @@ -4,6 +4,7 @@ import pytest from langchain_core.language_models import BaseChatModel +from langchain_core.tools import BaseTool from langchain_tests.integration_tests import ( # type: ignore[import-not-found] ChatModelIntegrationTests, # type: ignore[import-not-found] ) @@ -24,5 +25,7 @@ def chat_model_params(self) -> dict: } @pytest.mark.xfail(reason="Not yet implemented.") - def test_tool_message_histories_list_content(self, model: BaseChatModel) -> None: - super().test_tool_message_histories_list_content(model) + def test_tool_message_histories_list_content( + self, model: BaseChatModel, my_adder_tool: BaseTool + ) -> None: + super().test_tool_message_histories_list_content(model, my_adder_tool) diff --git a/libs/partners/groq/tests/integration_tests/test_standard.py b/libs/partners/groq/tests/integration_tests/test_standard.py index 3870ae953f6ee..b97b8c10422ef 100644 --- a/libs/partners/groq/tests/integration_tests/test_standard.py +++ b/libs/partners/groq/tests/integration_tests/test_standard.py @@ -5,6 +5,7 @@ import pytest from langchain_core.language_models import BaseChatModel from langchain_core.rate_limiters import InMemoryRateLimiter +from langchain_core.tools import BaseTool from langchain_tests.integration_tests import ( ChatModelIntegrationTests, ) @@ -20,8 +21,10 @@ def chat_model_class(self) -> Type[BaseChatModel]: return ChatGroq @pytest.mark.xfail(reason="Not yet implemented.") - def test_tool_message_histories_list_content(self, model: BaseChatModel) -> None: - super().test_tool_message_histories_list_content(model) + def test_tool_message_histories_list_content( + self, model: BaseChatModel, my_adder_tool: BaseTool + ) -> None: + super().test_tool_message_histories_list_content(model, my_adder_tool) class TestGroqLlama(BaseTestGroq): @@ -47,8 +50,10 @@ def test_tool_calling_with_no_arguments(self, model: BaseChatModel) -> None: @pytest.mark.xfail( reason=("Fails with 'Failed to call a function. Please adjust your prompt.'") ) - def test_tool_message_histories_string_content(self, model: BaseChatModel) -> None: - super().test_tool_message_histories_string_content(model) + def test_tool_message_histories_string_content( + self, model: BaseChatModel, my_adder_tool: BaseTool + ) -> None: + super().test_tool_message_histories_string_content(model, my_adder_tool) @pytest.mark.xfail( reason=( diff --git a/libs/partners/huggingface/langchain_huggingface/chat_models/huggingface.py b/libs/partners/huggingface/langchain_huggingface/chat_models/huggingface.py index c09f0bcbb6045..593df16133bbe 100644 --- a/libs/partners/huggingface/langchain_huggingface/chat_models/huggingface.py +++ b/libs/partners/huggingface/langchain_huggingface/chat_models/huggingface.py @@ -1,5 +1,6 @@ """Hugging Face Chat Wrapper.""" +import json from dataclasses import dataclass from typing import ( Any, @@ -106,9 +107,10 @@ def _convert_TGI_message_to_LC_message( additional_kwargs: Dict = {} if tool_calls := _message.tool_calls: if "arguments" in tool_calls[0]["function"]: - functions_string = str(tool_calls[0]["function"].pop("arguments")) - corrected_functions = functions_string.replace("'", '"') - tool_calls[0]["function"]["arguments"] = corrected_functions + functions = tool_calls[0]["function"].pop("arguments") + tool_calls[0]["function"]["arguments"] = json.dumps( + functions, ensure_ascii=False + ) additional_kwargs["tool_calls"] = tool_calls return AIMessage(content=content, additional_kwargs=additional_kwargs) diff --git a/libs/partners/huggingface/langchain_huggingface/embeddings/huggingface.py b/libs/partners/huggingface/langchain_huggingface/embeddings/huggingface.py index 2bbc551f4e0b1..a6a70db231014 100644 --- a/libs/partners/huggingface/langchain_huggingface/embeddings/huggingface.py +++ b/libs/partners/huggingface/langchain_huggingface/embeddings/huggingface.py @@ -26,7 +26,7 @@ class HuggingFaceEmbeddings(BaseModel, Embeddings): ) """ - model_name: str = DEFAULT_MODEL_NAME + model_name: str = Field(default=DEFAULT_MODEL_NAME, alias="model") """Model name to use.""" cache_folder: Optional[str] = None """Path to store models. @@ -68,6 +68,7 @@ def __init__(self, **kwargs: Any): model_config = ConfigDict( extra="forbid", protected_namespaces=(), + populate_by_name=True, ) def _embed( diff --git a/libs/partners/huggingface/tests/integration_tests/test_standard.py b/libs/partners/huggingface/tests/integration_tests/test_standard.py index 682a4c625ee9f..ce9b5b6b44b9f 100644 --- a/libs/partners/huggingface/tests/integration_tests/test_standard.py +++ b/libs/partners/huggingface/tests/integration_tests/test_standard.py @@ -4,6 +4,7 @@ import pytest from langchain_core.language_models import BaseChatModel +from langchain_core.tools import BaseTool from langchain_tests.integration_tests import ChatModelIntegrationTests from langchain_huggingface import ChatHuggingFace, HuggingFaceEndpoint @@ -82,9 +83,15 @@ def test_structured_output_optional_param(self, model: BaseChatModel) -> None: super().test_structured_output_optional_param(model) @pytest.mark.xfail(reason=("Not implemented")) - def test_tool_message_histories_list_content(self, model: BaseChatModel) -> None: - super().test_tool_message_histories_list_content(model) + def test_tool_message_histories_list_content( + self, model: BaseChatModel, my_adder_tool: BaseTool + ) -> None: + super().test_tool_message_histories_list_content( + model, my_adder_tool=my_adder_tool + ) @pytest.mark.xfail(reason=("Not implemented")) - def test_structured_few_shot_examples(self, model: BaseChatModel) -> None: - super().test_structured_few_shot_examples(model) + def test_structured_few_shot_examples( + self, model: BaseChatModel, my_adder_tool: BaseTool + ) -> None: + super().test_structured_few_shot_examples(model, my_adder_tool=my_adder_tool) diff --git a/libs/partners/huggingface/tests/unit_tests/test_chat_models.py b/libs/partners/huggingface/tests/unit_tests/test_chat_models.py index 1b9bfad66dfc9..fb9d141d022be 100644 --- a/libs/partners/huggingface/tests/unit_tests/test_chat_models.py +++ b/libs/partners/huggingface/tests/unit_tests/test_chat_models.py @@ -66,7 +66,7 @@ def test_convert_message_to_chat_message( TGI_MESSAGE( role="assistant", content="", - tool_calls=[{"function": {"arguments": "'function string'"}}], + tool_calls=[{"function": {"arguments": "function string"}}], ), AIMessage( content="", @@ -75,6 +75,23 @@ def test_convert_message_to_chat_message( }, ), ), + ( + TGI_MESSAGE( + role="assistant", + content="", + tool_calls=[ + {"function": {"arguments": {"answer": "function's string"}}} + ], + ), + AIMessage( + content="", + additional_kwargs={ + "tool_calls": [ + {"function": {"arguments": '{"answer": "function\'s string"}'}} + ] + }, + ), + ), ], ) def test_convert_TGI_message_to_LC_message( diff --git a/libs/partners/mistralai/langchain_mistralai/chat_models.py b/libs/partners/mistralai/langchain_mistralai/chat_models.py index aed0f70542786..be973f3b9ec78 100644 --- a/libs/partners/mistralai/langchain_mistralai/chat_models.py +++ b/libs/partners/mistralai/langchain_mistralai/chat_models.py @@ -388,7 +388,7 @@ class ChatMistralAI(BaseChatModel): """Decode using nucleus sampling: consider the smallest set of tokens whose probability sum is at least top_p. Must be in the closed interval [0.0, 1.0].""" random_seed: Optional[int] = None - safe_mode: bool = False + safe_mode: Optional[bool] = None streaming: bool = False model_config = ConfigDict( @@ -595,7 +595,7 @@ def _stream( for chunk in self.completion_with_retry( messages=message_dicts, run_manager=run_manager, **params ): - if len(chunk["choices"]) == 0: + if len(chunk.get("choices", [])) == 0: continue new_chunk = _convert_chunk_to_message_chunk(chunk, default_chunk_class) # make future chunks same type as first chunk @@ -621,7 +621,7 @@ async def _astream( async for chunk in await acompletion_with_retry( self, messages=message_dicts, run_manager=run_manager, **params ): - if len(chunk["choices"]) == 0: + if len(chunk.get("choices", [])) == 0: continue new_chunk = _convert_chunk_to_message_chunk(chunk, default_chunk_class) # make future chunks same type as first chunk diff --git a/libs/partners/mistralai/langchain_mistralai/embeddings.py b/libs/partners/mistralai/langchain_mistralai/embeddings.py index bb792139fc8d8..7c14536a31503 100644 --- a/libs/partners/mistralai/langchain_mistralai/embeddings.py +++ b/libs/partners/mistralai/langchain_mistralai/embeddings.py @@ -4,6 +4,7 @@ from typing import Iterable, List import httpx +from httpx import Response from langchain_core.embeddings import Embeddings from langchain_core.utils import ( secret_from_env, @@ -15,6 +16,7 @@ SecretStr, model_validator, ) +from tenacity import retry, retry_if_exception_type, stop_after_attempt, wait_fixed from tokenizers import Tokenizer # type: ignore from typing_extensions import Self @@ -58,6 +60,8 @@ class MistralAIEmbeddings(BaseModel, Embeddings): The number of times to retry a request if it fails. timeout: int The number of seconds to wait for a response before timing out. + wait_time: int + The number of seconds to wait before retrying a request in case of 429 error. max_concurrent_requests: int The maximum number of concurrent requests to make to the Mistral API. @@ -128,6 +132,7 @@ class MistralAIEmbeddings(BaseModel, Embeddings): endpoint: str = "https://api.mistral.ai/v1/" max_retries: int = 5 timeout: int = 120 + wait_time: int = 30 max_concurrent_requests: int = 64 tokenizer: Tokenizer = Field(default=None) @@ -215,16 +220,26 @@ def embed_documents(self, texts: List[str]) -> List[List[float]]: List of embeddings, one for each text. """ try: - batch_responses = ( - self.client.post( + batch_responses = [] + + @retry( + retry=retry_if_exception_type(httpx.TimeoutException), + wait=wait_fixed(self.wait_time), + stop=stop_after_attempt(self.max_retries), + ) + def _embed_batch(batch: List[str]) -> Response: + response = self.client.post( url="/embeddings", json=dict( model=self.model, input=batch, ), ) - for batch in self._get_batches(texts) - ) + response.raise_for_status() + return response + + for batch in self._get_batches(texts): + batch_responses.append(_embed_batch(batch)) return [ list(map(float, embedding_obj["embedding"])) for response in batch_responses diff --git a/libs/partners/ollama/langchain_ollama/chat_models.py b/libs/partners/ollama/langchain_ollama/chat_models.py index 4f074206c8f00..b4f8c0f2d9aab 100644 --- a/libs/partners/ollama/langchain_ollama/chat_models.py +++ b/libs/partners/ollama/langchain_ollama/chat_models.py @@ -1,6 +1,7 @@ """Ollama chat models.""" import json +from operator import itemgetter from typing import ( Any, AsyncIterator, @@ -36,13 +37,24 @@ ) from langchain_core.messages.ai import UsageMetadata from langchain_core.messages.tool import tool_call +from langchain_core.output_parsers import ( + JsonOutputKeyToolsParser, + JsonOutputParser, + PydanticOutputParser, + PydanticToolsParser, +) from langchain_core.outputs import ChatGeneration, ChatGenerationChunk, ChatResult -from langchain_core.runnables import Runnable +from langchain_core.runnables import Runnable, RunnableMap, RunnablePassthrough from langchain_core.tools import BaseTool +from langchain_core.utils.function_calling import ( + _convert_any_typed_dicts_to_pydantic as convert_any_typed_dicts_to_pydantic, +) from langchain_core.utils.function_calling import convert_to_openai_tool +from langchain_core.utils.pydantic import TypeBaseModel, is_basemodel_subclass from ollama import AsyncClient, Client, Message, Options -from pydantic import PrivateAttr, model_validator -from typing_extensions import Self +from pydantic import BaseModel, PrivateAttr, model_validator +from pydantic.json_schema import JsonSchemaValue +from typing_extensions import Self, is_typeddict def _get_usage_metadata_from_generation_info( @@ -157,6 +169,10 @@ def _lc_tool_call_to_openai_tool_call(tool_call: ToolCall) -> dict: } +def _is_pydantic_class(obj: Any) -> bool: + return isinstance(obj, type) and is_basemodel_subclass(obj) + + class ChatOllama(BaseChatModel): r"""Ollama chat model integration. @@ -290,8 +306,6 @@ class ChatOllama(BaseChatModel): '{"location": "Pune, India", "time_of_day": "morning"}' Tool Calling: - .. warning:: - Ollama currently does not support streaming for tools .. code-block:: python @@ -385,8 +399,8 @@ class Multiply(BaseModel): to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)""" - format: Literal["", "json"] = "" - """Specify the format of the output (options: json)""" + format: Optional[Union[Literal["", "json"], JsonSchemaValue]] = None + """Specify the format of the output (options: "json", JSON schema).""" keep_alive: Optional[Union[int, str]] = None """How long the model will stay loaded into memory.""" @@ -443,12 +457,9 @@ def _chat_params( }, ) - tools = kwargs.get("tools") - default_stream = not bool(tools) - params = { "messages": ollama_messages, - "stream": kwargs.pop("stream", default_stream), + "stream": kwargs.pop("stream", True), "model": kwargs.pop("model", self.model), "format": kwargs.pop("format", self.format), "options": Options(**options_dict), @@ -456,7 +467,7 @@ def _chat_params( **kwargs, } - if tools: + if tools := kwargs.get("tools"): params["tools"] = tools return params @@ -815,3 +826,322 @@ def bind_tools( """ # noqa: E501 formatted_tools = [convert_to_openai_tool(tool) for tool in tools] return super().bind(tools=formatted_tools, **kwargs) + + def with_structured_output( + self, + schema: Union[Dict, type], + *, + method: Literal[ + "function_calling", "json_mode", "json_schema" + ] = "function_calling", + include_raw: bool = False, + **kwargs: Any, + ) -> Runnable[LanguageModelInput, Union[Dict, BaseModel]]: + """Model wrapper that returns outputs formatted to match the given schema. + + Args: + schema: + The output schema. Can be passed in as: + + - a Pydantic class, + - a JSON schema + - a TypedDict class + - an OpenAI function/tool schema. + + If ``schema`` is a Pydantic class then the model output will be a + Pydantic instance of that class, and the model-generated fields will be + validated by the Pydantic class. Otherwise the model output will be a + dict and will not be validated. See :meth:`langchain_core.utils.function_calling.convert_to_openai_tool` + for more on how to properly specify types and descriptions of + schema fields when specifying a Pydantic or TypedDict class. + + method: The method for steering model generation, one of: + + - "function_calling": + Uses Ollama's tool-calling API + - "json_schema": + Uses Ollama's structured output API: https://ollama.com/blog/structured-outputs + - "json_mode": + Specifies ``format="json"``. Note that if using JSON mode then you + must include instructions for formatting the output into the + desired schema into the model call. + + include_raw: + If False then only the parsed structured output is returned. If + an error occurs during model output parsing it will be raised. If True + then both the raw model response (a BaseMessage) and the parsed model + response will be returned. If an error occurs during output parsing it + will be caught and returned as well. The final output is always a dict + with keys "raw", "parsed", and "parsing_error". + + kwargs: Additional keyword args aren't supported. + + Returns: + A Runnable that takes same inputs as a :class:`langchain_core.language_models.chat.BaseChatModel`. + + | If ``include_raw`` is False and ``schema`` is a Pydantic class, Runnable outputs an instance of ``schema`` (i.e., a Pydantic object). Otherwise, if ``include_raw`` is False then Runnable outputs a dict. + + | If ``include_raw`` is True, then Runnable outputs a dict with keys: + + - "raw": BaseMessage + - "parsed": None if there was a parsing error, otherwise the type depends on the ``schema`` as described above. + - "parsing_error": Optional[BaseException] + + .. versionchanged:: 0.2.2 + + Added support for structured output API via ``format`` parameter. + + .. dropdown:: Example: schema=Pydantic class, method="function_calling", include_raw=False + + .. code-block:: python + + from typing import Optional + + from langchain_ollama import ChatOllama + from pydantic import BaseModel, Field + + + class AnswerWithJustification(BaseModel): + '''An answer to the user question along with justification for the answer.''' + + answer: str + justification: Optional[str] = Field( + default=..., description="A justification for the answer." + ) + + + llm = ChatOllama(model="llama3.1", temperature=0) + structured_llm = llm.with_structured_output( + AnswerWithJustification + ) + + structured_llm.invoke( + "What weighs more a pound of bricks or a pound of feathers" + ) + + # -> AnswerWithJustification( + # answer='They weigh the same', + # justification='Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ.' + # ) + + .. dropdown:: Example: schema=Pydantic class, method="function_calling", include_raw=True + + .. code-block:: python + + from langchain_ollama import ChatOllama + from pydantic import BaseModel + + + class AnswerWithJustification(BaseModel): + '''An answer to the user question along with justification for the answer.''' + + answer: str + justification: str + + + llm = ChatOllama(model="llama3.1", temperature=0) + structured_llm = llm.with_structured_output( + AnswerWithJustification, include_raw=True + ) + + structured_llm.invoke( + "What weighs more a pound of bricks or a pound of feathers" + ) + # -> { + # 'raw': AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_Ao02pnFYXD6GN1yzc0uXPsvF', 'function': {'arguments': '{"answer":"They weigh the same.","justification":"Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ."}', 'name': 'AnswerWithJustification'}, 'type': 'function'}]}), + # 'parsed': AnswerWithJustification(answer='They weigh the same.', justification='Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ.'), + # 'parsing_error': None + # } + + .. dropdown:: Example: schema=Pydantic class, method="json_schema", include_raw=False + + .. code-block:: python + + from typing import Optional + + from langchain_ollama import ChatOllama + from pydantic import BaseModel, Field + + + class AnswerWithJustification(BaseModel): + '''An answer to the user question along with justification for the answer.''' + + answer: str + justification: Optional[str] = Field( + default=..., description="A justification for the answer." + ) + + + llm = ChatOllama(model="llama3.1", temperature=0) + structured_llm = llm.with_structured_output( + AnswerWithJustification, method="json_schema" + ) + + structured_llm.invoke( + "What weighs more a pound of bricks or a pound of feathers" + ) + + # -> AnswerWithJustification( + # answer='They weigh the same', + # justification='Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ.' + # ) + + .. dropdown:: Example: schema=TypedDict class, method="function_calling", include_raw=False + + .. code-block:: python + + # IMPORTANT: If you are using Python <=3.8, you need to import Annotated + # from typing_extensions, not from typing. + from typing_extensions import Annotated, TypedDict + + from langchain_ollama import ChatOllama + + + class AnswerWithJustification(TypedDict): + '''An answer to the user question along with justification for the answer.''' + + answer: str + justification: Annotated[ + Optional[str], None, "A justification for the answer." + ] + + + llm = ChatOllama(model="llama3.1", temperature=0) + structured_llm = llm.with_structured_output(AnswerWithJustification) + + structured_llm.invoke( + "What weighs more a pound of bricks or a pound of feathers" + ) + # -> { + # 'answer': 'They weigh the same', + # 'justification': 'Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume and density of the two substances differ.' + # } + + .. dropdown:: Example: schema=OpenAI function schema, method="function_calling", include_raw=False + + .. code-block:: python + + from langchain_ollama import ChatOllama + + oai_schema = { + 'name': 'AnswerWithJustification', + 'description': 'An answer to the user question along with justification for the answer.', + 'parameters': { + 'type': 'object', + 'properties': { + 'answer': {'type': 'string'}, + 'justification': {'description': 'A justification for the answer.', 'type': 'string'} + }, + 'required': ['answer'] + } + } + + llm = ChatOllama(model="llama3.1", temperature=0) + structured_llm = llm.with_structured_output(oai_schema) + + structured_llm.invoke( + "What weighs more a pound of bricks or a pound of feathers" + ) + # -> { + # 'answer': 'They weigh the same', + # 'justification': 'Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume and density of the two substances differ.' + # } + + .. dropdown:: Example: schema=Pydantic class, method="json_mode", include_raw=True + + .. code-block:: + + from langchain_ollama import ChatOllama + from pydantic import BaseModel + + class AnswerWithJustification(BaseModel): + answer: str + justification: str + + llm = ChatOllama(model="llama3.1", temperature=0) + structured_llm = llm.with_structured_output( + AnswerWithJustification, + method="json_mode", + include_raw=True + ) + + structured_llm.invoke( + "Answer the following question. " + "Make sure to return a JSON blob with keys 'answer' and 'justification'.\\n\\n" + "What's heavier a pound of bricks or a pound of feathers?" + ) + # -> { + # 'raw': AIMessage(content='{\\n "answer": "They are both the same weight.",\\n "justification": "Both a pound of bricks and a pound of feathers weigh one pound. The difference lies in the volume and density of the materials, not the weight." \\n}'), + # 'parsed': AnswerWithJustification(answer='They are both the same weight.', justification='Both a pound of bricks and a pound of feathers weigh one pound. The difference lies in the volume and density of the materials, not the weight.'), + # 'parsing_error': None + # } + """ # noqa: E501, D301 + if kwargs: + raise ValueError(f"Received unsupported arguments {kwargs}") + is_pydantic_schema = _is_pydantic_class(schema) + if method == "function_calling": + if schema is None: + raise ValueError( + "schema must be specified when method is not 'json_mode'. " + "Received None." + ) + tool_name = convert_to_openai_tool(schema)["function"]["name"] + llm = self.bind_tools([schema], tool_choice=tool_name) + if is_pydantic_schema: + output_parser: Runnable = PydanticToolsParser( + tools=[schema], # type: ignore[list-item] + first_tool_only=True, + ) + else: + output_parser = JsonOutputKeyToolsParser( + key_name=tool_name, first_tool_only=True + ) + elif method == "json_mode": + llm = self.bind(format="json") + output_parser = ( + PydanticOutputParser(pydantic_object=schema) # type: ignore[arg-type] + if is_pydantic_schema + else JsonOutputParser() + ) + elif method == "json_schema": + if schema is None: + raise ValueError( + "schema must be specified when method is not 'json_mode'. " + "Received None." + ) + if is_pydantic_schema: + schema = cast(TypeBaseModel, schema) + llm = self.bind(format=schema.model_json_schema()) + output_parser = PydanticOutputParser(pydantic_object=schema) + else: + if is_typeddict(schema): + schema = cast(type, schema) + response_format = convert_any_typed_dicts_to_pydantic( + schema, visited={} + ).schema() # type: ignore[attr-defined] + if "required" not in response_format: + response_format["required"] = list( + response_format["properties"].keys() + ) + else: + # is JSON schema + response_format = schema + llm = self.bind(format=response_format) + output_parser = JsonOutputParser() + else: + raise ValueError( + f"Unrecognized method argument. Expected one of 'function_calling', " + f"'json_schema', or 'json_mode'. Received: '{method}'" + ) + + if include_raw: + parser_assign = RunnablePassthrough.assign( + parsed=itemgetter("raw") | output_parser, parsing_error=lambda _: None + ) + parser_none = RunnablePassthrough.assign(parsed=lambda _: None) + parser_with_fallback = parser_assign.with_fallbacks( + [parser_none], exception_key="parsing_error" + ) + return RunnableMap(raw=llm) | parser_with_fallback + else: + return llm | output_parser diff --git a/libs/partners/ollama/poetry.lock b/libs/partners/ollama/poetry.lock index 47e6066ced28b..7ad5d63438a28 100644 --- a/libs/partners/ollama/poetry.lock +++ b/libs/partners/ollama/poetry.lock @@ -13,24 +13,24 @@ files = [ [[package]] name = "anyio" -version = "4.6.2.post1" +version = "4.7.0" description = "High level compatibility layer for multiple asynchronous event loop implementations" optional = false python-versions = ">=3.9" files = [ - {file = "anyio-4.6.2.post1-py3-none-any.whl", hash = "sha256:6d170c36fba3bdd840c73d3868c1e777e33676a69c3a72cf0a0d5d6d8009b61d"}, - {file = "anyio-4.6.2.post1.tar.gz", hash = "sha256:4c8bc31ccdb51c7f7bd251f51c609e038d63e34219b44aa86e47576389880b4c"}, + {file = "anyio-4.7.0-py3-none-any.whl", hash = "sha256:ea60c3723ab42ba6fff7e8ccb0488c898ec538ff4df1f1d5e642c3601d07e352"}, + {file = "anyio-4.7.0.tar.gz", hash = "sha256:2f834749c602966b7d456a7567cafcb309f96482b5081d14ac93ccd457f9dd48"}, ] [package.dependencies] exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""} idna = ">=2.8" sniffio = ">=1.1" -typing-extensions = {version = ">=4.1", markers = "python_version < \"3.11\""} +typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""} [package.extras] -doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] -test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21.0b1)"] +doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx_rtd_theme"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21)"] trio = ["trio (>=0.26.1)"] [[package]] @@ -309,7 +309,7 @@ files = [ [[package]] name = "langchain-core" -version = "0.3.21" +version = "0.3.22" description = "Building applications with LLMs through composability" optional = false python-versions = ">=3.9,<4.0" @@ -334,7 +334,7 @@ url = "../../core" [[package]] name = "langchain-tests" -version = "0.3.4" +version = "0.3.6" description = "Standard tests for LangChain implementations" optional = false python-versions = ">=3.9,<4.0" @@ -342,9 +342,15 @@ files = [] develop = true [package.dependencies] -httpx = "^0.27.0" -langchain-core = "^0.3.19" +httpx = ">=0.25.0,<1" +langchain-core = "^0.3.22" +numpy = [ + {version = ">=1.24.0,<2.0.0", markers = "python_version < \"3.12\""}, + {version = ">=1.26.2,<3", markers = "python_version >= \"3.12\""}, +] pytest = ">=7,<9" +pytest-asyncio = ">=0.20,<1" +pytest-socket = ">=0.6.0,<1" syrupy = "^4" [package.source] @@ -353,13 +359,13 @@ url = "../../standard-tests" [[package]] name = "langsmith" -version = "0.1.144" +version = "0.1.147" description = "Client library to connect to the LangSmith LLM Tracing and Evaluation Platform." optional = false python-versions = "<4.0,>=3.8.1" files = [ - {file = "langsmith-0.1.144-py3-none-any.whl", hash = "sha256:08ffb975bff2e82fc6f5428837c64c074ea25102d08a25e256361a80812c6100"}, - {file = "langsmith-0.1.144.tar.gz", hash = "sha256:b621f358d5a33441d7b5e7264c376bf4ea82bfc62d7e41aafc0f8094e3bd6369"}, + {file = "langsmith-0.1.147-py3-none-any.whl", hash = "sha256:7166fc23b965ccf839d64945a78e9f1157757add228b086141eb03a60d699a15"}, + {file = "langsmith-0.1.147.tar.gz", hash = "sha256:2e933220318a4e73034657103b3b1a3a6109cc5db3566a7e8e03be8d6d7def7a"}, ] [package.dependencies] @@ -372,6 +378,9 @@ pydantic = [ requests = ">=2,<3" requests-toolbelt = ">=1.0.0,<2.0.0" +[package.extras] +langsmith-pyo3 = ["langsmith-pyo3 (>=0.1.0rc2,<0.2.0)"] + [[package]] name = "mypy" version = "1.13.0" @@ -436,15 +445,124 @@ files = [ {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, ] +[[package]] +name = "numpy" +version = "1.26.4" +description = "Fundamental package for array computing in Python" +optional = false +python-versions = ">=3.9" +files = [ + {file = "numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0"}, + {file = "numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a"}, + {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4"}, + {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f"}, + {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a"}, + {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2"}, + {file = "numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07"}, + {file = "numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5"}, + {file = "numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71"}, + {file = "numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef"}, + {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e"}, + {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5"}, + {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a"}, + {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a"}, + {file = "numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20"}, + {file = "numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2"}, + {file = "numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218"}, + {file = "numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b"}, + {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b"}, + {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed"}, + {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a"}, + {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0"}, + {file = "numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110"}, + {file = "numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818"}, + {file = "numpy-1.26.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7349ab0fa0c429c82442a27a9673fc802ffdb7c7775fad780226cb234965e53c"}, + {file = "numpy-1.26.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:52b8b60467cd7dd1e9ed082188b4e6bb35aa5cdd01777621a1658910745b90be"}, + {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5241e0a80d808d70546c697135da2c613f30e28251ff8307eb72ba696945764"}, + {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f870204a840a60da0b12273ef34f7051e98c3b5961b61b0c2c1be6dfd64fbcd3"}, + {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:679b0076f67ecc0138fd2ede3a8fd196dddc2ad3254069bcb9faf9a79b1cebcd"}, + {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:47711010ad8555514b434df65f7d7b076bb8261df1ca9bb78f53d3b2db02e95c"}, + {file = "numpy-1.26.4-cp39-cp39-win32.whl", hash = "sha256:a354325ee03388678242a4d7ebcd08b5c727033fcff3b2f536aea978e15ee9e6"}, + {file = "numpy-1.26.4-cp39-cp39-win_amd64.whl", hash = "sha256:3373d5d70a5fe74a2c1bb6d2cfd9609ecf686d47a2d7b1d37a8f3b6bf6003aea"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:afedb719a9dcfc7eaf2287b839d8198e06dcd4cb5d276a3df279231138e83d30"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95a7476c59002f2f6c590b9b7b998306fba6a5aa646b1e22ddfeaf8f78c3a29c"}, + {file = "numpy-1.26.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7e50d0a0cc3189f9cb0aeb3a6a6af18c16f59f004b866cd2be1c14b36134a4a0"}, + {file = "numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010"}, +] + +[[package]] +name = "numpy" +version = "2.2.0" +description = "Fundamental package for array computing in Python" +optional = false +python-versions = ">=3.10" +files = [ + {file = "numpy-2.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1e25507d85da11ff5066269d0bd25d06e0a0f2e908415534f3e603d2a78e4ffa"}, + {file = "numpy-2.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a62eb442011776e4036af5c8b1a00b706c5bc02dc15eb5344b0c750428c94219"}, + {file = "numpy-2.2.0-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:b606b1aaf802e6468c2608c65ff7ece53eae1a6874b3765f69b8ceb20c5fa78e"}, + {file = "numpy-2.2.0-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:36b2b43146f646642b425dd2027730f99bac962618ec2052932157e213a040e9"}, + {file = "numpy-2.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7fe8f3583e0607ad4e43a954e35c1748b553bfe9fdac8635c02058023277d1b3"}, + {file = "numpy-2.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:122fd2fcfafdefc889c64ad99c228d5a1f9692c3a83f56c292618a59aa60ae83"}, + {file = "numpy-2.2.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3f2f5cddeaa4424a0a118924b988746db6ffa8565e5829b1841a8a3bd73eb59a"}, + {file = "numpy-2.2.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7fe4bb0695fe986a9e4deec3b6857003b4cfe5c5e4aac0b95f6a658c14635e31"}, + {file = "numpy-2.2.0-cp310-cp310-win32.whl", hash = "sha256:b30042fe92dbd79f1ba7f6898fada10bdaad1847c44f2dff9a16147e00a93661"}, + {file = "numpy-2.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:54dc1d6d66f8d37843ed281773c7174f03bf7ad826523f73435deb88ba60d2d4"}, + {file = "numpy-2.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9874bc2ff574c40ab7a5cbb7464bf9b045d617e36754a7bc93f933d52bd9ffc6"}, + {file = "numpy-2.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0da8495970f6b101ddd0c38ace92edea30e7e12b9a926b57f5fabb1ecc25bb90"}, + {file = "numpy-2.2.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:0557eebc699c1c34cccdd8c3778c9294e8196df27d713706895edc6f57d29608"}, + {file = "numpy-2.2.0-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:3579eaeb5e07f3ded59298ce22b65f877a86ba8e9fe701f5576c99bb17c283da"}, + {file = "numpy-2.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40deb10198bbaa531509aad0cd2f9fadb26c8b94070831e2208e7df543562b74"}, + {file = "numpy-2.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c2aed8fcf8abc3020d6a9ccb31dbc9e7d7819c56a348cc88fd44be269b37427e"}, + {file = "numpy-2.2.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a222d764352c773aa5ebde02dd84dba3279c81c6db2e482d62a3fa54e5ece69b"}, + {file = "numpy-2.2.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4e58666988605e251d42c2818c7d3d8991555381be26399303053b58a5bbf30d"}, + {file = "numpy-2.2.0-cp311-cp311-win32.whl", hash = "sha256:4723a50e1523e1de4fccd1b9a6dcea750c2102461e9a02b2ac55ffeae09a4410"}, + {file = "numpy-2.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:16757cf28621e43e252c560d25b15f18a2f11da94fea344bf26c599b9cf54b73"}, + {file = "numpy-2.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cff210198bb4cae3f3c100444c5eaa573a823f05c253e7188e1362a5555235b3"}, + {file = "numpy-2.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:58b92a5828bd4d9aa0952492b7de803135038de47343b2aa3cc23f3b71a3dc4e"}, + {file = "numpy-2.2.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:ebe5e59545401fbb1b24da76f006ab19734ae71e703cdb4a8b347e84a0cece67"}, + {file = "numpy-2.2.0-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:e2b8cd48a9942ed3f85b95ca4105c45758438c7ed28fff1e4ce3e57c3b589d8e"}, + {file = "numpy-2.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57fcc997ffc0bef234b8875a54d4058afa92b0b0c4223fc1f62f24b3b5e86038"}, + {file = "numpy-2.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85ad7d11b309bd132d74397fcf2920933c9d1dc865487128f5c03d580f2c3d03"}, + {file = "numpy-2.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:cb24cca1968b21355cc6f3da1a20cd1cebd8a023e3c5b09b432444617949085a"}, + {file = "numpy-2.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0798b138c291d792f8ea40fe3768610f3c7dd2574389e37c3f26573757c8f7ef"}, + {file = "numpy-2.2.0-cp312-cp312-win32.whl", hash = "sha256:afe8fb968743d40435c3827632fd36c5fbde633b0423da7692e426529b1759b1"}, + {file = "numpy-2.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:3a4199f519e57d517ebd48cb76b36c82da0360781c6a0353e64c0cac30ecaad3"}, + {file = "numpy-2.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f8c8b141ef9699ae777c6278b52c706b653bf15d135d302754f6b2e90eb30367"}, + {file = "numpy-2.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0f0986e917aca18f7a567b812ef7ca9391288e2acb7a4308aa9d265bd724bdae"}, + {file = "numpy-2.2.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:1c92113619f7b272838b8d6702a7f8ebe5edea0df48166c47929611d0b4dea69"}, + {file = "numpy-2.2.0-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:5a145e956b374e72ad1dff82779177d4a3c62bc8248f41b80cb5122e68f22d13"}, + {file = "numpy-2.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18142b497d70a34b01642b9feabb70156311b326fdddd875a9981f34a369b671"}, + {file = "numpy-2.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a7d41d1612c1a82b64697e894b75db6758d4f21c3ec069d841e60ebe54b5b571"}, + {file = "numpy-2.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a98f6f20465e7618c83252c02041517bd2f7ea29be5378f09667a8f654a5918d"}, + {file = "numpy-2.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e09d40edfdb4e260cb1567d8ae770ccf3b8b7e9f0d9b5c2a9992696b30ce2742"}, + {file = "numpy-2.2.0-cp313-cp313-win32.whl", hash = "sha256:3905a5fffcc23e597ee4d9fb3fcd209bd658c352657548db7316e810ca80458e"}, + {file = "numpy-2.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:a184288538e6ad699cbe6b24859206e38ce5fba28f3bcfa51c90d0502c1582b2"}, + {file = "numpy-2.2.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:7832f9e8eb00be32f15fdfb9a981d6955ea9adc8574c521d48710171b6c55e95"}, + {file = "numpy-2.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f0dd071b95bbca244f4cb7f70b77d2ff3aaaba7fa16dc41f58d14854a6204e6c"}, + {file = "numpy-2.2.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:b0b227dcff8cdc3efbce66d4e50891f04d0a387cce282fe1e66199146a6a8fca"}, + {file = "numpy-2.2.0-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:6ab153263a7c5ccaf6dfe7e53447b74f77789f28ecb278c3b5d49db7ece10d6d"}, + {file = "numpy-2.2.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e500aba968a48e9019e42c0c199b7ec0696a97fa69037bea163b55398e390529"}, + {file = "numpy-2.2.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:440cfb3db4c5029775803794f8638fbdbf71ec702caf32735f53b008e1eaece3"}, + {file = "numpy-2.2.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a55dc7a7f0b6198b07ec0cd445fbb98b05234e8b00c5ac4874a63372ba98d4ab"}, + {file = "numpy-2.2.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4bddbaa30d78c86329b26bd6aaaea06b1e47444da99eddac7bf1e2fab717bd72"}, + {file = "numpy-2.2.0-cp313-cp313t-win32.whl", hash = "sha256:30bf971c12e4365153afb31fc73f441d4da157153f3400b82db32d04de1e4066"}, + {file = "numpy-2.2.0-cp313-cp313t-win_amd64.whl", hash = "sha256:d35717333b39d1b6bb8433fa758a55f1081543de527171543a2b710551d40881"}, + {file = "numpy-2.2.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e12c6c1ce84628c52d6367863773f7c8c8241be554e8b79686e91a43f1733773"}, + {file = "numpy-2.2.0-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:b6207dc8fb3c8cb5668e885cef9ec7f70189bec4e276f0ff70d5aa078d32c88e"}, + {file = "numpy-2.2.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a50aeff71d0f97b6450d33940c7181b08be1441c6c193e678211bff11aa725e7"}, + {file = "numpy-2.2.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:df12a1f99b99f569a7c2ae59aa2d31724e8d835fc7f33e14f4792e3071d11221"}, + {file = "numpy-2.2.0.tar.gz", hash = "sha256:140dd80ff8981a583a60980be1a655068f8adebf7a45a06a6858c873fcdcd4a0"}, +] + [[package]] name = "ollama" -version = "0.4.1" +version = "0.4.4" description = "The official Python client for Ollama." optional = false python-versions = "<4.0,>=3.8" files = [ - {file = "ollama-0.4.1-py3-none-any.whl", hash = "sha256:b6fb16aa5a3652633e1716acb12cf2f44aa18beb229329e46a0302734822dfad"}, - {file = "ollama-0.4.1.tar.gz", hash = "sha256:8c6b5e7ff80dd0b8692150b03359f60bac7ca162b088c604069409142a684ad3"}, + {file = "ollama-0.4.4-py3-none-any.whl", hash = "sha256:0f466e845e2205a1cbf5a2fef4640027b90beaa3b06c574426d8b6b17fd6e139"}, + {file = "ollama-0.4.4.tar.gz", hash = "sha256:e1db064273c739babc2dde9ea84029c4a43415354741b6c50939ddd3dd0f7ffb"}, ] [package.dependencies] @@ -453,69 +571,86 @@ pydantic = ">=2.9.0,<3.0.0" [[package]] name = "orjson" -version = "3.10.11" +version = "3.10.12" description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" optional = false python-versions = ">=3.8" files = [ - {file = "orjson-3.10.11-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:6dade64687f2bd7c090281652fe18f1151292d567a9302b34c2dbb92a3872f1f"}, - {file = "orjson-3.10.11-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82f07c550a6ccd2b9290849b22316a609023ed851a87ea888c0456485a7d196a"}, - {file = "orjson-3.10.11-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bd9a187742d3ead9df2e49240234d728c67c356516cf4db018833a86f20ec18c"}, - {file = "orjson-3.10.11-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:77b0fed6f209d76c1c39f032a70df2d7acf24b1812ca3e6078fd04e8972685a3"}, - {file = "orjson-3.10.11-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:63fc9d5fe1d4e8868f6aae547a7b8ba0a2e592929245fff61d633f4caccdcdd6"}, - {file = "orjson-3.10.11-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65cd3e3bb4fbb4eddc3c1e8dce10dc0b73e808fcb875f9fab40c81903dd9323e"}, - {file = "orjson-3.10.11-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6f67c570602300c4befbda12d153113b8974a3340fdcf3d6de095ede86c06d92"}, - {file = "orjson-3.10.11-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1f39728c7f7d766f1f5a769ce4d54b5aaa4c3f92d5b84817053cc9995b977acc"}, - {file = "orjson-3.10.11-cp310-none-win32.whl", hash = "sha256:1789d9db7968d805f3d94aae2c25d04014aae3a2fa65b1443117cd462c6da647"}, - {file = "orjson-3.10.11-cp310-none-win_amd64.whl", hash = "sha256:5576b1e5a53a5ba8f8df81872bb0878a112b3ebb1d392155f00f54dd86c83ff6"}, - {file = "orjson-3.10.11-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:1444f9cb7c14055d595de1036f74ecd6ce15f04a715e73f33bb6326c9cef01b6"}, - {file = "orjson-3.10.11-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdec57fe3b4bdebcc08a946db3365630332dbe575125ff3d80a3272ebd0ddafe"}, - {file = "orjson-3.10.11-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4eed32f33a0ea6ef36ccc1d37f8d17f28a1d6e8eefae5928f76aff8f1df85e67"}, - {file = "orjson-3.10.11-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80df27dd8697242b904f4ea54820e2d98d3f51f91e97e358fc13359721233e4b"}, - {file = "orjson-3.10.11-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:705f03cee0cb797256d54de6695ef219e5bc8c8120b6654dd460848d57a9af3d"}, - {file = "orjson-3.10.11-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03246774131701de8e7059b2e382597da43144a9a7400f178b2a32feafc54bd5"}, - {file = "orjson-3.10.11-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8b5759063a6c940a69c728ea70d7c33583991c6982915a839c8da5f957e0103a"}, - {file = "orjson-3.10.11-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:677f23e32491520eebb19c99bb34675daf5410c449c13416f7f0d93e2cf5f981"}, - {file = "orjson-3.10.11-cp311-none-win32.whl", hash = "sha256:a11225d7b30468dcb099498296ffac36b4673a8398ca30fdaec1e6c20df6aa55"}, - {file = "orjson-3.10.11-cp311-none-win_amd64.whl", hash = "sha256:df8c677df2f9f385fcc85ab859704045fa88d4668bc9991a527c86e710392bec"}, - {file = "orjson-3.10.11-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:360a4e2c0943da7c21505e47cf6bd725588962ff1d739b99b14e2f7f3545ba51"}, - {file = "orjson-3.10.11-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:496e2cb45de21c369079ef2d662670a4892c81573bcc143c4205cae98282ba97"}, - {file = "orjson-3.10.11-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7dfa8db55c9792d53c5952900c6a919cfa377b4f4534c7a786484a6a4a350c19"}, - {file = "orjson-3.10.11-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:51f3382415747e0dbda9dade6f1e1a01a9d37f630d8c9049a8ed0e385b7a90c0"}, - {file = "orjson-3.10.11-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f35a1b9f50a219f470e0e497ca30b285c9f34948d3c8160d5ad3a755d9299433"}, - {file = "orjson-3.10.11-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2f3b7c5803138e67028dde33450e054c87e0703afbe730c105f1fcd873496d5"}, - {file = "orjson-3.10.11-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f91d9eb554310472bd09f5347950b24442600594c2edc1421403d7610a0998fd"}, - {file = "orjson-3.10.11-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dfbb2d460a855c9744bbc8e36f9c3a997c4b27d842f3d5559ed54326e6911f9b"}, - {file = "orjson-3.10.11-cp312-none-win32.whl", hash = "sha256:d4a62c49c506d4d73f59514986cadebb7e8d186ad510c518f439176cf8d5359d"}, - {file = "orjson-3.10.11-cp312-none-win_amd64.whl", hash = "sha256:f1eec3421a558ff7a9b010a6c7effcfa0ade65327a71bb9b02a1c3b77a247284"}, - {file = "orjson-3.10.11-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:c46294faa4e4d0eb73ab68f1a794d2cbf7bab33b1dda2ac2959ffb7c61591899"}, - {file = "orjson-3.10.11-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52e5834d7d6e58a36846e059d00559cb9ed20410664f3ad156cd2cc239a11230"}, - {file = "orjson-3.10.11-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2fc947e5350fdce548bfc94f434e8760d5cafa97fb9c495d2fef6757aa02ec0"}, - {file = "orjson-3.10.11-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0efabbf839388a1dab5b72b5d3baedbd6039ac83f3b55736eb9934ea5494d258"}, - {file = "orjson-3.10.11-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a3f29634260708c200c4fe148e42b4aae97d7b9fee417fbdd74f8cfc265f15b0"}, - {file = "orjson-3.10.11-cp313-none-win32.whl", hash = "sha256:1a1222ffcee8a09476bbdd5d4f6f33d06d0d6642df2a3d78b7a195ca880d669b"}, - {file = "orjson-3.10.11-cp313-none-win_amd64.whl", hash = "sha256:bc274ac261cc69260913b2d1610760e55d3c0801bb3457ba7b9004420b6b4270"}, - {file = "orjson-3.10.11-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:19b3763e8bbf8ad797df6b6b5e0fc7c843ec2e2fc0621398534e0c6400098f87"}, - {file = "orjson-3.10.11-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1be83a13312e5e58d633580c5eb8d0495ae61f180da2722f20562974188af205"}, - {file = "orjson-3.10.11-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:afacfd1ab81f46dedd7f6001b6d4e8de23396e4884cd3c3436bd05defb1a6446"}, - {file = "orjson-3.10.11-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cb4d0bea56bba596723d73f074c420aec3b2e5d7d30698bc56e6048066bd560c"}, - {file = "orjson-3.10.11-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96ed1de70fcb15d5fed529a656df29f768187628727ee2788344e8a51e1c1350"}, - {file = "orjson-3.10.11-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4bfb30c891b530f3f80e801e3ad82ef150b964e5c38e1fb8482441c69c35c61c"}, - {file = "orjson-3.10.11-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d496c74fc2b61341e3cefda7eec21b7854c5f672ee350bc55d9a4997a8a95204"}, - {file = "orjson-3.10.11-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:655a493bac606655db9a47fe94d3d84fc7f3ad766d894197c94ccf0c5408e7d3"}, - {file = "orjson-3.10.11-cp38-none-win32.whl", hash = "sha256:b9546b278c9fb5d45380f4809e11b4dd9844ca7aaf1134024503e134ed226161"}, - {file = "orjson-3.10.11-cp38-none-win_amd64.whl", hash = "sha256:b592597fe551d518f42c5a2eb07422eb475aa8cfdc8c51e6da7054b836b26782"}, - {file = "orjson-3.10.11-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:c95f2ecafe709b4e5c733b5e2768ac569bed308623c85806c395d9cca00e08af"}, - {file = "orjson-3.10.11-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:80c00d4acded0c51c98754fe8218cb49cb854f0f7eb39ea4641b7f71732d2cb7"}, - {file = "orjson-3.10.11-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:461311b693d3d0a060439aa669c74f3603264d4e7a08faa68c47ae5a863f352d"}, - {file = "orjson-3.10.11-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:52ca832f17d86a78cbab86cdc25f8c13756ebe182b6fc1a97d534051c18a08de"}, - {file = "orjson-3.10.11-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4c57ea78a753812f528178aa2f1c57da633754c91d2124cb28991dab4c79a54"}, - {file = "orjson-3.10.11-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7fcfc6f7ca046383fb954ba528587e0f9336828b568282b27579c49f8e16aad"}, - {file = "orjson-3.10.11-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:86b9dd983857970c29e4c71bb3e95ff085c07d3e83e7c46ebe959bac07ebd80b"}, - {file = "orjson-3.10.11-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:4d83f87582d223e54efb2242a79547611ba4ebae3af8bae1e80fa9a0af83bb7f"}, - {file = "orjson-3.10.11-cp39-none-win32.whl", hash = "sha256:9fd0ad1c129bc9beb1154c2655f177620b5beaf9a11e0d10bac63ef3fce96950"}, - {file = "orjson-3.10.11-cp39-none-win_amd64.whl", hash = "sha256:10f416b2a017c8bd17f325fb9dee1fb5cdd7a54e814284896b7c3f2763faa017"}, - {file = "orjson-3.10.11.tar.gz", hash = "sha256:e35b6d730de6384d5b2dab5fd23f0d76fae8bbc8c353c2f78210aa5fa4beb3ef"}, + {file = "orjson-3.10.12-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:ece01a7ec71d9940cc654c482907a6b65df27251255097629d0dea781f255c6d"}, + {file = "orjson-3.10.12-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c34ec9aebc04f11f4b978dd6caf697a2df2dd9b47d35aa4cc606cabcb9df69d7"}, + {file = "orjson-3.10.12-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fd6ec8658da3480939c79b9e9e27e0db31dffcd4ba69c334e98c9976ac29140e"}, + {file = "orjson-3.10.12-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f17e6baf4cf01534c9de8a16c0c611f3d94925d1701bf5f4aff17003677d8ced"}, + {file = "orjson-3.10.12-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6402ebb74a14ef96f94a868569f5dccf70d791de49feb73180eb3c6fda2ade56"}, + {file = "orjson-3.10.12-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0000758ae7c7853e0a4a6063f534c61656ebff644391e1f81698c1b2d2fc8cd2"}, + {file = "orjson-3.10.12-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:888442dcee99fd1e5bd37a4abb94930915ca6af4db50e23e746cdf4d1e63db13"}, + {file = "orjson-3.10.12-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c1f7a3ce79246aa0e92f5458d86c54f257fb5dfdc14a192651ba7ec2c00f8a05"}, + {file = "orjson-3.10.12-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:802a3935f45605c66fb4a586488a38af63cb37aaad1c1d94c982c40dcc452e85"}, + {file = "orjson-3.10.12-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:1da1ef0113a2be19bb6c557fb0ec2d79c92ebd2fed4cfb1b26bab93f021fb885"}, + {file = "orjson-3.10.12-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7a3273e99f367f137d5b3fecb5e9f45bcdbfac2a8b2f32fbc72129bbd48789c2"}, + {file = "orjson-3.10.12-cp310-none-win32.whl", hash = "sha256:475661bf249fd7907d9b0a2a2421b4e684355a77ceef85b8352439a9163418c3"}, + {file = "orjson-3.10.12-cp310-none-win_amd64.whl", hash = "sha256:87251dc1fb2b9e5ab91ce65d8f4caf21910d99ba8fb24b49fd0c118b2362d509"}, + {file = "orjson-3.10.12-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a734c62efa42e7df94926d70fe7d37621c783dea9f707a98cdea796964d4cf74"}, + {file = "orjson-3.10.12-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:750f8b27259d3409eda8350c2919a58b0cfcd2054ddc1bd317a643afc646ef23"}, + {file = "orjson-3.10.12-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bb52c22bfffe2857e7aa13b4622afd0dd9d16ea7cc65fd2bf318d3223b1b6252"}, + {file = "orjson-3.10.12-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:440d9a337ac8c199ff8251e100c62e9488924c92852362cd27af0e67308c16ef"}, + {file = "orjson-3.10.12-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9e15c06491c69997dfa067369baab3bf094ecb74be9912bdc4339972323f252"}, + {file = "orjson-3.10.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:362d204ad4b0b8724cf370d0cd917bb2dc913c394030da748a3bb632445ce7c4"}, + {file = "orjson-3.10.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2b57cbb4031153db37b41622eac67329c7810e5f480fda4cfd30542186f006ae"}, + {file = "orjson-3.10.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:165c89b53ef03ce0d7c59ca5c82fa65fe13ddf52eeb22e859e58c237d4e33b9b"}, + {file = "orjson-3.10.12-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:5dee91b8dfd54557c1a1596eb90bcd47dbcd26b0baaed919e6861f076583e9da"}, + {file = "orjson-3.10.12-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:77a4e1cfb72de6f905bdff061172adfb3caf7a4578ebf481d8f0530879476c07"}, + {file = "orjson-3.10.12-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:038d42c7bc0606443459b8fe2d1f121db474c49067d8d14c6a075bbea8bf14dd"}, + {file = "orjson-3.10.12-cp311-none-win32.whl", hash = "sha256:03b553c02ab39bed249bedd4abe37b2118324d1674e639b33fab3d1dafdf4d79"}, + {file = "orjson-3.10.12-cp311-none-win_amd64.whl", hash = "sha256:8b8713b9e46a45b2af6b96f559bfb13b1e02006f4242c156cbadef27800a55a8"}, + {file = "orjson-3.10.12-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:53206d72eb656ca5ac7d3a7141e83c5bbd3ac30d5eccfe019409177a57634b0d"}, + {file = "orjson-3.10.12-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac8010afc2150d417ebda810e8df08dd3f544e0dd2acab5370cfa6bcc0662f8f"}, + {file = "orjson-3.10.12-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed459b46012ae950dd2e17150e838ab08215421487371fa79d0eced8d1461d70"}, + {file = "orjson-3.10.12-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8dcb9673f108a93c1b52bfc51b0af422c2d08d4fc710ce9c839faad25020bb69"}, + {file = "orjson-3.10.12-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:22a51ae77680c5c4652ebc63a83d5255ac7d65582891d9424b566fb3b5375ee9"}, + {file = "orjson-3.10.12-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:910fdf2ac0637b9a77d1aad65f803bac414f0b06f720073438a7bd8906298192"}, + {file = "orjson-3.10.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:24ce85f7100160936bc2116c09d1a8492639418633119a2224114f67f63a4559"}, + {file = "orjson-3.10.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8a76ba5fc8dd9c913640292df27bff80a685bed3a3c990d59aa6ce24c352f8fc"}, + {file = "orjson-3.10.12-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ff70ef093895fd53f4055ca75f93f047e088d1430888ca1229393a7c0521100f"}, + {file = "orjson-3.10.12-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:f4244b7018b5753ecd10a6d324ec1f347da130c953a9c88432c7fbc8875d13be"}, + {file = "orjson-3.10.12-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:16135ccca03445f37921fa4b585cff9a58aa8d81ebcb27622e69bfadd220b32c"}, + {file = "orjson-3.10.12-cp312-none-win32.whl", hash = "sha256:2d879c81172d583e34153d524fcba5d4adafbab8349a7b9f16ae511c2cee8708"}, + {file = "orjson-3.10.12-cp312-none-win_amd64.whl", hash = "sha256:fc23f691fa0f5c140576b8c365bc942d577d861a9ee1142e4db468e4e17094fb"}, + {file = "orjson-3.10.12-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:47962841b2a8aa9a258b377f5188db31ba49af47d4003a32f55d6f8b19006543"}, + {file = "orjson-3.10.12-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6334730e2532e77b6054e87ca84f3072bee308a45a452ea0bffbbbc40a67e296"}, + {file = "orjson-3.10.12-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:accfe93f42713c899fdac2747e8d0d5c659592df2792888c6c5f829472e4f85e"}, + {file = "orjson-3.10.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a7974c490c014c48810d1dede6c754c3cc46598da758c25ca3b4001ac45b703f"}, + {file = "orjson-3.10.12-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:3f250ce7727b0b2682f834a3facff88e310f52f07a5dcfd852d99637d386e79e"}, + {file = "orjson-3.10.12-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f31422ff9486ae484f10ffc51b5ab2a60359e92d0716fcce1b3593d7bb8a9af6"}, + {file = "orjson-3.10.12-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5f29c5d282bb2d577c2a6bbde88d8fdcc4919c593f806aac50133f01b733846e"}, + {file = "orjson-3.10.12-cp313-none-win32.whl", hash = "sha256:f45653775f38f63dc0e6cd4f14323984c3149c05d6007b58cb154dd080ddc0dc"}, + {file = "orjson-3.10.12-cp313-none-win_amd64.whl", hash = "sha256:229994d0c376d5bdc91d92b3c9e6be2f1fbabd4cc1b59daae1443a46ee5e9825"}, + {file = "orjson-3.10.12-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:7d69af5b54617a5fac5c8e5ed0859eb798e2ce8913262eb522590239db6c6763"}, + {file = "orjson-3.10.12-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ed119ea7d2953365724a7059231a44830eb6bbb0cfead33fcbc562f5fd8f935"}, + {file = "orjson-3.10.12-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9c5fc1238ef197e7cad5c91415f524aaa51e004be5a9b35a1b8a84ade196f73f"}, + {file = "orjson-3.10.12-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:43509843990439b05f848539d6f6198d4ac86ff01dd024b2f9a795c0daeeab60"}, + {file = "orjson-3.10.12-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f72e27a62041cfb37a3de512247ece9f240a561e6c8662276beaf4d53d406db4"}, + {file = "orjson-3.10.12-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a904f9572092bb6742ab7c16c623f0cdccbad9eeb2d14d4aa06284867bddd31"}, + {file = "orjson-3.10.12-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:855c0833999ed5dc62f64552db26f9be767434917d8348d77bacaab84f787d7b"}, + {file = "orjson-3.10.12-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:897830244e2320f6184699f598df7fb9db9f5087d6f3f03666ae89d607e4f8ed"}, + {file = "orjson-3.10.12-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:0b32652eaa4a7539f6f04abc6243619c56f8530c53bf9b023e1269df5f7816dd"}, + {file = "orjson-3.10.12-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:36b4aa31e0f6a1aeeb6f8377769ca5d125db000f05c20e54163aef1d3fe8e833"}, + {file = "orjson-3.10.12-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:5535163054d6cbf2796f93e4f0dbc800f61914c0e3c4ed8499cf6ece22b4a3da"}, + {file = "orjson-3.10.12-cp38-none-win32.whl", hash = "sha256:90a5551f6f5a5fa07010bf3d0b4ca2de21adafbbc0af6cb700b63cd767266cb9"}, + {file = "orjson-3.10.12-cp38-none-win_amd64.whl", hash = "sha256:703a2fb35a06cdd45adf5d733cf613cbc0cb3ae57643472b16bc22d325b5fb6c"}, + {file = "orjson-3.10.12-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:f29de3ef71a42a5822765def1febfb36e0859d33abf5c2ad240acad5c6a1b78d"}, + {file = "orjson-3.10.12-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:de365a42acc65d74953f05e4772c974dad6c51cfc13c3240899f534d611be967"}, + {file = "orjson-3.10.12-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:91a5a0158648a67ff0004cb0df5df7dcc55bfc9ca154d9c01597a23ad54c8d0c"}, + {file = "orjson-3.10.12-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c47ce6b8d90fe9646a25b6fb52284a14ff215c9595914af63a5933a49972ce36"}, + {file = "orjson-3.10.12-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0eee4c2c5bfb5c1b47a5db80d2ac7aaa7e938956ae88089f098aff2c0f35d5d8"}, + {file = "orjson-3.10.12-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:35d3081bbe8b86587eb5c98a73b97f13d8f9fea685cf91a579beddacc0d10566"}, + {file = "orjson-3.10.12-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:73c23a6e90383884068bc2dba83d5222c9fcc3b99a0ed2411d38150734236755"}, + {file = "orjson-3.10.12-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5472be7dc3269b4b52acba1433dac239215366f89dc1d8d0e64029abac4e714e"}, + {file = "orjson-3.10.12-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:7319cda750fca96ae5973efb31b17d97a5c5225ae0bc79bf5bf84df9e1ec2ab6"}, + {file = "orjson-3.10.12-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:74d5ca5a255bf20b8def6a2b96b1e18ad37b4a122d59b154c458ee9494377f80"}, + {file = "orjson-3.10.12-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:ff31d22ecc5fb85ef62c7d4afe8301d10c558d00dd24274d4bbe464380d3cd69"}, + {file = "orjson-3.10.12-cp39-none-win32.whl", hash = "sha256:c22c3ea6fba91d84fcb4cda30e64aff548fcf0c44c876e681f47d61d24b12e6b"}, + {file = "orjson-3.10.12-cp39-none-win_amd64.whl", hash = "sha256:be604f60d45ace6b0b33dd990a66b4526f1a7a186ac411c942674625456ca548"}, + {file = "orjson-3.10.12.tar.gz", hash = "sha256:0a78bbda3aea0f9f079057ee1ee8a1ecf790d4f1af88dd67493c6b8ee52506ff"}, ] [[package]] @@ -546,13 +681,13 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "pydantic" -version = "2.10.1" +version = "2.10.3" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic-2.10.1-py3-none-any.whl", hash = "sha256:a8d20db84de64cf4a7d59e899c2caf0fe9d660c7cfc482528e7020d7dd189a7e"}, - {file = "pydantic-2.10.1.tar.gz", hash = "sha256:a4daca2dc0aa429555e0656d6bf94873a7dc5f54ee42b1f5873d666fb3f35560"}, + {file = "pydantic-2.10.3-py3-none-any.whl", hash = "sha256:be04d85bbc7b65651c5f8e6b9976ed9c6f41782a55524cef079a34a0bb82144d"}, + {file = "pydantic-2.10.3.tar.gz", hash = "sha256:cb5ac360ce894ceacd69c403187900a02c4b20b693a9dd1d643e1effab9eadf9"}, ] [package.dependencies] @@ -881,13 +1016,13 @@ files = [ [[package]] name = "syrupy" -version = "4.7.2" +version = "4.8.0" description = "Pytest Snapshot Test Utility" optional = false python-versions = ">=3.8.1" files = [ - {file = "syrupy-4.7.2-py3-none-any.whl", hash = "sha256:eae7ba6be5aed190237caa93be288e97ca1eec5ca58760e4818972a10c4acc64"}, - {file = "syrupy-4.7.2.tar.gz", hash = "sha256:ea45e099f242de1bb53018c238f408a5bb6c82007bc687aefcbeaa0e1c2e935a"}, + {file = "syrupy-4.8.0-py3-none-any.whl", hash = "sha256:544f4ec6306f4b1c460fdab48fd60b2c7fe54a6c0a8243aeea15f9ad9c638c3f"}, + {file = "syrupy-4.8.0.tar.gz", hash = "sha256:648f0e9303aaa8387c8365d7314784c09a6bab0a407455c6a01d6a4f5c6a8ede"}, ] [package.dependencies] @@ -910,13 +1045,43 @@ test = ["pytest", "tornado (>=4.5)", "typeguard"] [[package]] name = "tomli" -version = "2.1.0" +version = "2.2.1" description = "A lil' TOML parser" optional = false python-versions = ">=3.8" files = [ - {file = "tomli-2.1.0-py3-none-any.whl", hash = "sha256:a5c57c3d1c56f5ccdf89f6523458f60ef716e210fc47c4cfb188c5ba473e0391"}, - {file = "tomli-2.1.0.tar.gz", hash = "sha256:3f646cae2aec94e17d04973e4249548320197cfabdf130015d023de4b74d8ab8"}, + {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, + {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"}, + {file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"}, + {file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"}, + {file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"}, + {file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"}, + {file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"}, + {file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"}, + {file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"}, + {file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"}, + {file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"}, + {file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"}, + {file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"}, + {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"}, ] [[package]] @@ -992,4 +1157,4 @@ watchmedo = ["PyYAML (>=3.10)"] [metadata] lock-version = "2.0" python-versions = ">=3.9,<4.0" -content-hash = "078fb5da0212681a4fc4d02e4e4112fe7a3ebda0923411e72ad706a097a989ae" +content-hash = "6cdc5d8dc48cecb900f751e3e8f0be9ed5a52fb8e873a9ce2bcf3fd7db36ef79" diff --git a/libs/partners/ollama/pyproject.toml b/libs/partners/ollama/pyproject.toml index bab24cf619887..d0df93c840fd7 100644 --- a/libs/partners/ollama/pyproject.toml +++ b/libs/partners/ollama/pyproject.toml @@ -20,7 +20,7 @@ disallow_untyped_defs = "True" [tool.poetry.dependencies] python = ">=3.9,<4.0" -ollama = ">=0.3.0,<1" +ollama = ">=0.4.4,<1" langchain-core = "^0.3.20" [tool.ruff.lint] diff --git a/libs/partners/ollama/tests/integration_tests/chat_models/test_chat_models.py b/libs/partners/ollama/tests/integration_tests/chat_models/test_chat_models.py index 837df8c5164eb..61190f73f800d 100644 --- a/libs/partners/ollama/tests/integration_tests/chat_models/test_chat_models.py +++ b/libs/partners/ollama/tests/integration_tests/chat_models/test_chat_models.py @@ -4,10 +4,61 @@ import pytest from pydantic import BaseModel, Field +from typing_extensions import Annotated, TypedDict from langchain_ollama import ChatOllama +@pytest.mark.parametrize(("method"), [("function_calling"), ("json_schema")]) +def test_structured_output(method: str) -> None: + """Test to verify structured output via tool calling and ``format`` parameter.""" + + class Joke(BaseModel): + """Joke to tell user.""" + + setup: str = Field(description="question to set up a joke") + punchline: str = Field(description="answer to resolve the joke") + + llm = ChatOllama(model="llama3.1", temperature=0) + query = "Tell me a joke about cats." + + # Pydantic + structured_llm = llm.with_structured_output(Joke, method=method) # type: ignore[arg-type] + result = structured_llm.invoke(query) + assert isinstance(result, Joke) + + for chunk in structured_llm.stream(query): + assert isinstance(chunk, Joke) + + # JSON Schema + structured_llm = llm.with_structured_output(Joke.model_json_schema(), method=method) # type: ignore[arg-type] + result = structured_llm.invoke(query) + assert isinstance(result, dict) + assert set(result.keys()) == {"setup", "punchline"} + + for chunk in structured_llm.stream(query): + assert isinstance(chunk, dict) + assert isinstance(chunk, dict) # for mypy + assert set(chunk.keys()) == {"setup", "punchline"} + + # Typed Dict + class JokeSchema(TypedDict): + """Joke to tell user.""" + + setup: Annotated[str, "question to set up a joke"] + punchline: Annotated[str, "answer to resolve the joke"] + + structured_llm = llm.with_structured_output(JokeSchema, method=method) # type: ignore[arg-type] + result = structured_llm.invoke(query) + assert isinstance(result, dict) + assert set(result.keys()) == {"setup", "punchline"} + + for chunk in structured_llm.stream(query): + assert isinstance(chunk, dict) + assert isinstance(chunk, dict) # for mypy + assert set(chunk.keys()) == {"setup", "punchline"} + + @pytest.mark.parametrize(("model"), [("llama3.1")]) def test_structured_output_deeply_nested(model: str) -> None: """Test to verify structured output with a nested objects.""" diff --git a/libs/partners/openai/poetry.lock b/libs/partners/openai/poetry.lock index a3de06c40eb1c..913cacca821f6 100644 --- a/libs/partners/openai/poetry.lock +++ b/libs/partners/openai/poetry.lock @@ -495,7 +495,7 @@ files = [ [[package]] name = "langchain-core" -version = "0.3.21" +version = "0.3.22" description = "Building applications with LLMs through composability" optional = false python-versions = ">=3.9,<4.0" @@ -520,7 +520,7 @@ url = "../../core" [[package]] name = "langchain-tests" -version = "0.3.4" +version = "0.3.6" description = "Standard tests for LangChain implementations" optional = false python-versions = ">=3.9,<4.0" @@ -528,9 +528,15 @@ files = [] develop = true [package.dependencies] -httpx = "^0.27.0" -langchain-core = "^0.3.19" +httpx = ">=0.25.0,<1" +langchain-core = "^0.3.22" +numpy = [ + {version = ">=1.24.0,<2.0.0", markers = "python_version < \"3.12\""}, + {version = ">=1.26.2,<3", markers = "python_version >= \"3.12\""}, +] pytest = ">=7,<9" +pytest-asyncio = ">=0.20,<1" +pytest-socket = ">=0.6.0,<1" syrupy = "^4" [package.source] @@ -1639,4 +1645,4 @@ watchmedo = ["PyYAML (>=3.10)"] [metadata] lock-version = "2.0" python-versions = ">=3.9,<4.0" -content-hash = "ded25b72c77fad9a869f3308c1bba084b58f54eb13df2785f061bc340d6ec748" +content-hash = "6fb8c9f98c76ba402d53234ac2ac78bcebafbe818e64cd849e0ae26cafcd5ba4" diff --git a/libs/partners/openai/pyproject.toml b/libs/partners/openai/pyproject.toml index 4fee814b9ede5..a85ab72b05f51 100644 --- a/libs/partners/openai/pyproject.toml +++ b/libs/partners/openai/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api" [tool.poetry] name = "langchain-openai" -version = "0.2.11" +version = "0.2.12" description = "An integration package connecting OpenAI and LangChain" authors = [] readme = "README.md" @@ -24,7 +24,7 @@ ignore_missing_imports = true [tool.poetry.dependencies] python = ">=3.9,<4.0" langchain-core = "^0.3.21" -openai = "^1.54.0" +openai = "^1.55.3" tiktoken = ">=0.7,<1" [tool.ruff.lint] diff --git a/libs/partners/openai/tests/integration_tests/embeddings/test_base_standard.py b/libs/partners/openai/tests/integration_tests/embeddings/test_base_standard.py new file mode 100644 index 0000000000000..66f74f1687fd5 --- /dev/null +++ b/libs/partners/openai/tests/integration_tests/embeddings/test_base_standard.py @@ -0,0 +1,18 @@ +"""Standard LangChain interface tests""" + +from typing import Type + +from langchain_core.embeddings import Embeddings +from langchain_tests.integration_tests.embeddings import EmbeddingsIntegrationTests + +from langchain_openai import OpenAIEmbeddings + + +class TestOpenAIStandard(EmbeddingsIntegrationTests): + @property + def embeddings_class(self) -> Type[Embeddings]: + return OpenAIEmbeddings + + @property + def embedding_model_params(self) -> dict: + return {"model": "text-embedding-3-small", "dimensions": 128} diff --git a/libs/partners/openai/tests/unit_tests/chat_models/test_azure_standard.py b/libs/partners/openai/tests/unit_tests/chat_models/test_azure_standard.py index 3d1faa97db485..f74f1c6804f3a 100644 --- a/libs/partners/openai/tests/unit_tests/chat_models/test_azure_standard.py +++ b/libs/partners/openai/tests/unit_tests/chat_models/test_azure_standard.py @@ -4,6 +4,7 @@ import pytest from langchain_core.language_models import BaseChatModel +from langchain_core.tools import BaseTool from langchain_tests.unit_tests import ChatModelUnitTests from langchain_openai import AzureChatOpenAI @@ -23,8 +24,10 @@ def chat_model_params(self) -> dict: } @pytest.mark.xfail(reason="AzureOpenAI does not support tool_choice='any'") - def test_bind_tool_pydantic(self, model: BaseChatModel) -> None: - super().test_bind_tool_pydantic(model) + def test_bind_tool_pydantic( + self, model: BaseChatModel, my_adder_tool: BaseTool + ) -> None: + super().test_bind_tool_pydantic(model, my_adder_tool) @property def init_from_env_params(self) -> Tuple[dict, dict, dict]: diff --git a/libs/partners/xai/tests/integration_tests/test_chat_models_standard.py b/libs/partners/xai/tests/integration_tests/test_chat_models_standard.py index 1152fe44946f7..edcaf93eebf02 100644 --- a/libs/partners/xai/tests/integration_tests/test_chat_models_standard.py +++ b/libs/partners/xai/tests/integration_tests/test_chat_models_standard.py @@ -5,6 +5,7 @@ import pytest # type: ignore[import-not-found] from langchain_core.language_models import BaseChatModel from langchain_core.rate_limiters import InMemoryRateLimiter +from langchain_core.tools import BaseTool from langchain_tests.integration_tests import ( # type: ignore[import-not-found] ChatModelIntegrationTests, # type: ignore[import-not-found] ) @@ -40,13 +41,19 @@ def test_usage_metadata_streaming(self, model: BaseChatModel) -> None: super().test_usage_metadata_streaming(model) @pytest.mark.xfail(reason="Can't handle AIMessage with empty content.") - def test_tool_message_error_status(self, model: BaseChatModel) -> None: - super().test_tool_message_error_status(model) + def test_tool_message_error_status( + self, model: BaseChatModel, my_adder_tool: BaseTool + ) -> None: + super().test_tool_message_error_status(model, my_adder_tool) @pytest.mark.xfail(reason="Can't handle AIMessage with empty content.") - def test_structured_few_shot_examples(self, model: BaseChatModel) -> None: - super().test_structured_few_shot_examples(model) + def test_structured_few_shot_examples( + self, model: BaseChatModel, my_adder_tool: BaseTool + ) -> None: + super().test_structured_few_shot_examples(model, my_adder_tool) @pytest.mark.xfail(reason="Can't handle AIMessage with empty content.") - def test_tool_message_histories_string_content(self, model: BaseChatModel) -> None: - super().test_tool_message_histories_string_content(model) + def test_tool_message_histories_string_content( + self, model: BaseChatModel, my_adder_tool: BaseTool + ) -> None: + super().test_tool_message_histories_string_content(model, my_adder_tool) diff --git a/libs/standard-tests/langchain_tests/__init__.py b/libs/standard-tests/langchain_tests/__init__.py index e69de29bb2d1d..0f41b67980ccc 100644 --- a/libs/standard-tests/langchain_tests/__init__.py +++ b/libs/standard-tests/langchain_tests/__init__.py @@ -0,0 +1,7 @@ +""" +Base Test classes for standard testing. + +To learn how to use these classes, see the +`Integration standard testing `_ +guide. +""" diff --git a/libs/standard-tests/langchain_tests/base.py b/libs/standard-tests/langchain_tests/base.py index f2b7ca1f7e92d..df99a39b71d62 100644 --- a/libs/standard-tests/langchain_tests/base.py +++ b/libs/standard-tests/langchain_tests/base.py @@ -3,9 +3,15 @@ class BaseStandardTests(ABC): + """ + :private: + """ + def test_no_overrides_DO_NOT_OVERRIDE(self) -> None: """ Test that no standard tests are overridden. + + :private: """ # find path to standard test implementations comparison_class = None diff --git a/libs/standard-tests/langchain_tests/integration_tests/__init__.py b/libs/standard-tests/langchain_tests/integration_tests/__init__.py index ed7e5f111c9f4..b6f0a1b0f2a23 100644 --- a/libs/standard-tests/langchain_tests/integration_tests/__init__.py +++ b/libs/standard-tests/langchain_tests/integration_tests/__init__.py @@ -11,6 +11,7 @@ "vectorstores", "embeddings", "tools", + "retrievers", ] for module in modules: @@ -22,7 +23,7 @@ from .embeddings import EmbeddingsIntegrationTests from .retrievers import RetrieversIntegrationTests from .tools import ToolsIntegrationTests -from .vectorstores import AsyncReadWriteTestSuite, ReadWriteTestSuite +from .vectorstores import VectorStoreIntegrationTests __all__ = [ "ChatModelIntegrationTests", @@ -32,7 +33,6 @@ "BaseStoreSyncTests", "AsyncCacheTestSuite", "SyncCacheTestSuite", - "AsyncReadWriteTestSuite", - "ReadWriteTestSuite", + "VectorStoreIntegrationTests", "RetrieversIntegrationTests", ] diff --git a/libs/standard-tests/langchain_tests/integration_tests/base_store.py b/libs/standard-tests/langchain_tests/integration_tests/base_store.py index cc5fab8bcf7a4..ff9e178aea903 100644 --- a/libs/standard-tests/langchain_tests/integration_tests/base_store.py +++ b/libs/standard-tests/langchain_tests/integration_tests/base_store.py @@ -1,3 +1,11 @@ +""" +Standard tests for the BaseStore abstraction + +We don't recommend implementing externally managed BaseStore abstractions at this time. + +:private: +""" + from abc import abstractmethod from typing import AsyncGenerator, Generator, Generic, Tuple, TypeVar diff --git a/libs/standard-tests/langchain_tests/integration_tests/cache.py b/libs/standard-tests/langchain_tests/integration_tests/cache.py index 7087da8ea07b3..3d04731d16a74 100644 --- a/libs/standard-tests/langchain_tests/integration_tests/cache.py +++ b/libs/standard-tests/langchain_tests/integration_tests/cache.py @@ -1,3 +1,11 @@ +""" +Standard tests for the BaseCache abstraction + +We don't recommend implementing externally managed BaseCache abstractions at this time. + +:private: +""" + from abc import abstractmethod import pytest diff --git a/libs/standard-tests/langchain_tests/integration_tests/chat_models.py b/libs/standard-tests/langchain_tests/integration_tests/chat_models.py index 1cbec02ad0dac..7a787563b267b 100644 --- a/libs/standard-tests/langchain_tests/integration_tests/chat_models.py +++ b/libs/standard-tests/langchain_tests/integration_tests/chat_models.py @@ -16,7 +16,7 @@ ) from langchain_core.output_parsers import StrOutputParser from langchain_core.prompts import ChatPromptTemplate -from langchain_core.tools import tool +from langchain_core.tools import BaseTool, tool from langchain_core.utils.function_calling import tool_example_to_messages from pydantic import BaseModel, Field from pydantic.v1 import BaseModel as BaseModelV1 @@ -24,16 +24,29 @@ from langchain_tests.unit_tests.chat_models import ( ChatModelTests, - my_adder_tool, ) from langchain_tests.utils.pydantic import PYDANTIC_MAJOR_VERSION -class MagicFunctionSchema(BaseModel): +def _get_joke_class() -> type[BaseModel]: + """ + :private: + """ + + class Joke(BaseModel): + """Joke to tell user.""" + + setup: str = Field(description="question to set up a joke") + punchline: str = Field(description="answer to resolve the joke") + + return Joke + + +class _MagicFunctionSchema(BaseModel): input: int = Field(..., gt=-1000, lt=1000) -@tool(args_schema=MagicFunctionSchema) +@tool(args_schema=_MagicFunctionSchema) def magic_function(input: int) -> int: """Applies a magic function to an input.""" return input + 2 @@ -45,13 +58,6 @@ def magic_function_no_args() -> int: return 5 -class Joke(BaseModel): - """Joke to tell user.""" - - setup: str = Field(description="question to set up a joke") - punchline: str = Field(description="answer to resolve the joke") - - def _validate_tool_call_message(message: BaseMessage) -> None: assert isinstance(message, AIMessage) assert len(message.tool_calls) == 1 @@ -75,6 +81,35 @@ def _validate_tool_call_message_no_args(message: BaseMessage) -> None: class ChatModelIntegrationTests(ChatModelTests): """Base class for chat model integration tests. + Test subclasses must implement the ``chat_model_class`` and + ``chat_model_params`` properties to specify what model to test and its + initialization parameters. + + Example: + + .. code-block:: python + + from typing import Type + + from langchain_tests.integration_tests import ChatModelIntegrationTests + from my_package.chat_models import MyChatModel + + + class TestMyChatModelIntegration(ChatModelIntegrationTests): + @property + def chat_model_class(self) -> Type[MyChatModel]: + # Return the chat model class to test here + return MyChatModel + + @property + def chat_model_params(self) -> dict: + # Return initialization parameters for the model. + return {"model": "model-001", "temperature": 0} + + .. note:: + API references for individual test methods include troubleshooting tips. + + Test subclasses must implement the following two properties: chat_model_class @@ -99,20 +134,189 @@ def chat_model_class(self) -> Type[ChatParrotLink]: def chat_model_params(self) -> dict: return {"model": "bird-brain-001", "temperature": 0} - .. note:: - API references for individual test methods include troubleshooting tips. + In addition, test subclasses can control what features are tested (such as tool + calling or multi-modality) by selectively overriding the following properties. + Expand to see details: - .. note:: - Test subclasses can control what features are tested (such as tool - calling or multi-modality) by selectively overriding the properties on the - class. Relevant properties are mentioned in the references for each method. - See this page for detail on all properties: - https://python.langchain.com/api_reference/standard_tests/unit_tests/langchain_tests.unit_tests.chat_models.ChatModelTests.html + .. dropdown:: has_tool_calling + + Boolean property indicating whether the chat model supports tool calling. + + By default, this is determined by whether the chat model's `bind_tools` method + is overridden. It typically does not need to be overridden on the test class. + + Example override: + + .. code-block:: python + + @property + def has_tool_calling(self) -> bool: + return True + + .. dropdown:: tool_choice_value + + Value to use for tool choice when used in tests. + + Some tests for tool calling features attempt to force tool calling via a + `tool_choice` parameter. A common value for this parameter is "any". Defaults + to `None`. + + Note: if the value is set to "tool_name", the name of the tool used in each + test will be set as the value for `tool_choice`. + + Example: + + .. code-block:: python + + @property + def tool_choice_value(self) -> Optional[str]: + return "any" + + .. dropdown:: has_structured_output + + Boolean property indicating whether the chat model supports structured + output. + + By default, this is determined by whether the chat model's + `with_structured_output` method is overridden. If the base implementation is + intended to be used, this method should be overridden. + + See: https://python.langchain.com/docs/concepts/structured_outputs/ + + Example: + + .. code-block:: python + + @property + def has_structured_output(self) -> bool: + return True + + .. dropdown:: supports_image_inputs + + Boolean property indicating whether the chat model supports image inputs. + Defaults to ``False``. + + If set to ``True``, the chat model will be tested using content blocks of the + form + + .. code-block:: python + + [ + {"type": "text", "text": "describe the weather in this image"}, + { + "type": "image_url", + "image_url": {"url": f"data:image/jpeg;base64,{image_data}"}, + }, + ] + + See https://python.langchain.com/docs/concepts/multimodality/ + + Example: + + .. code-block:: python + + @property + def supports_image_inputs(self) -> bool: + return True + + .. dropdown:: supports_video_inputs + + Boolean property indicating whether the chat model supports image inputs. + Defaults to ``False``. No current tests are written for this feature. + + .. dropdown:: returns_usage_metadata + + Boolean property indicating whether the chat model returns usage metadata + on invoke and streaming responses. + + ``usage_metadata`` is an optional dict attribute on AIMessages that track input + and output tokens: https://python.langchain.com/api_reference/core/messages/langchain_core.messages.ai.UsageMetadata.html + + Example: + + .. code-block:: python + + @property + def returns_usage_metadata(self) -> bool: + return False + + .. dropdown:: supports_anthropic_inputs + + Boolean property indicating whether the chat model supports Anthropic-style + inputs. + + These inputs might feature "tool use" and "tool result" content blocks, e.g., + + .. code-block:: python + + [ + {"type": "text", "text": "Hmm let me think about that"}, + { + "type": "tool_use", + "input": {"fav_color": "green"}, + "id": "foo", + "name": "color_picker", + }, + ] + + If set to ``True``, the chat model will be tested using content blocks of this + form. + + Example: + + .. code-block:: python + + @property + def supports_anthropic_inputs(self) -> bool: + return False + + .. dropdown:: supports_image_tool_message + + Boolean property indicating whether the chat model supports ToolMessages + that include image content, e.g., + + .. code-block:: python + + ToolMessage( + content=[ + { + "type": "image_url", + "image_url": {"url": f"data:image/jpeg;base64,{image_data}"}, + }, + ], + tool_call_id="1", + name="random_image", + ) + + If set to ``True``, the chat model will be tested with message sequences that + include ToolMessages of this form. + + Example: + + .. code-block:: python + + @property + def supports_image_tool_message(self) -> bool: + return False + + .. dropdown:: supported_usage_metadata_details + + Property controlling what usage metadata details are emitted in both invoke + and stream. + + ``usage_metadata`` is an optional dict attribute on AIMessages that track input + and output tokens: https://python.langchain.com/api_reference/core/messages/langchain_core.messages.ai.UsageMetadata.html + + It includes optional keys ``input_token_details`` and ``output_token_details`` + that can track usage details associated with special types of tokens, such as + cached, audio, or reasoning. + + Only needs to be overridden if these details are supplied. """ @property def standard_chat_model_params(self) -> dict: - """:meta private:""" + """:private:""" return {} def test_invoke(self, model: BaseChatModel) -> None: @@ -907,6 +1111,7 @@ def has_tool_calling(self) -> bool: if not self.has_tool_calling: pytest.skip("Test requires tool calling.") + Joke = _get_joke_class() # Pydantic class # Type ignoring since the interface only officially supports pydantic 1 # or pydantic.v1.BaseModel but not pydantic.BaseModel from pydantic 2. @@ -959,6 +1164,8 @@ def has_tool_calling(self) -> bool: if not self.has_tool_calling: pytest.skip("Test requires tool calling.") + Joke = _get_joke_class() + # Pydantic class # Type ignoring since the interface only officially supports pydantic 1 # or pydantic.v1.BaseModel but not pydantic.BaseModel from pydantic 2. @@ -1088,7 +1295,9 @@ class Joke(BaseModel): joke_result = chat.invoke("Give me a joke about cats, include the punchline.") assert isinstance(joke_result, Joke) - def test_tool_message_histories_string_content(self, model: BaseChatModel) -> None: + def test_tool_message_histories_string_content( + self, model: BaseChatModel, my_adder_tool: BaseTool + ) -> None: """Test that message histories are compatible with string tool contents (e.g. OpenAI format). If a model passes this test, it should be compatible with messages generated from providers following OpenAI format. @@ -1122,8 +1331,8 @@ def has_tool_calling(self) -> bool: .. code-block:: python @pytest.mark.xfail(reason=("Not implemented.")) - def test_tool_message_histories_string_content(self, model: BaseChatModel) -> None: - super().test_tool_message_histories_string_content(model) + def test_tool_message_histories_string_content(self, *args: Any) -> None: + super().test_tool_message_histories_string_content(*args) """ # noqa: E501 if not self.has_tool_calling: pytest.skip("Test requires tool calling.") @@ -1157,6 +1366,7 @@ def test_tool_message_histories_string_content(self, model: BaseChatModel) -> No def test_tool_message_histories_list_content( self, model: BaseChatModel, + my_adder_tool: BaseTool, ) -> None: """Test that message histories are compatible with list tool contents (e.g. Anthropic format). @@ -1205,8 +1415,8 @@ def has_tool_calling(self) -> bool: .. code-block:: python @pytest.mark.xfail(reason=("Not implemented.")) - def test_tool_message_histories_list_content(self, model: BaseChatModel) -> None: - super().test_tool_message_histories_list_content(model) + def test_tool_message_histories_list_content(self, *args: Any) -> None: + super().test_tool_message_histories_list_content(*args) """ # noqa: E501 if not self.has_tool_calling: pytest.skip("Test requires tool calling.") @@ -1245,7 +1455,9 @@ def test_tool_message_histories_list_content(self, model: BaseChatModel) -> None result_list_content = model_with_tools.invoke(messages_list_content) assert isinstance(result_list_content, AIMessage) - def test_structured_few_shot_examples(self, model: BaseChatModel) -> None: + def test_structured_few_shot_examples( + self, model: BaseChatModel, my_adder_tool: BaseTool + ) -> None: """Test that the model can process few-shot examples with tool calls. These are represented as a sequence of messages of the following form: @@ -1285,8 +1497,8 @@ def has_tool_calling(self) -> bool: .. code-block:: python @pytest.mark.xfail(reason=("Not implemented.")) - def test_structured_few_shot_examples(self, model: BaseChatModel) -> None: - super().test_structured_few_shot_examples(model) + def test_structured_few_shot_examples(self, *args: Any) -> None: + super().test_structured_few_shot_examples(*args) """ # noqa: E501 if not self.has_tool_calling: pytest.skip("Test requires tool calling.") @@ -1556,7 +1768,9 @@ class color_picker(BaseModelV1): ] model.bind_tools([color_picker]).invoke(messages) - def test_tool_message_error_status(self, model: BaseChatModel) -> None: + def test_tool_message_error_status( + self, model: BaseChatModel, my_adder_tool: BaseTool + ) -> None: """Test that ToolMessage with ``status="error"`` can be handled. These messages may take the form: @@ -1646,16 +1860,21 @@ def test_message_with_name(self, model: BaseChatModel) -> None: assert len(result.content) > 0 def invoke_with_audio_input(self, *, stream: bool = False) -> AIMessage: + """:private:""" raise NotImplementedError() def invoke_with_audio_output(self, *, stream: bool = False) -> AIMessage: + """:private:""" raise NotImplementedError() def invoke_with_reasoning_output(self, *, stream: bool = False) -> AIMessage: + """:private:""" raise NotImplementedError() def invoke_with_cache_read_input(self, *, stream: bool = False) -> AIMessage: + """:private:""" raise NotImplementedError() def invoke_with_cache_creation_input(self, *, stream: bool = False) -> AIMessage: + """:private:""" raise NotImplementedError() diff --git a/libs/standard-tests/langchain_tests/integration_tests/embeddings.py b/libs/standard-tests/langchain_tests/integration_tests/embeddings.py index 7e3689d0f5429..8b4f20bb4a5e7 100644 --- a/libs/standard-tests/langchain_tests/integration_tests/embeddings.py +++ b/libs/standard-tests/langchain_tests/integration_tests/embeddings.py @@ -6,7 +6,47 @@ class EmbeddingsIntegrationTests(EmbeddingsTests): + """Base class for embeddings integration tests. + + Test subclasses must implement the ``embeddings_class`` property to specify the + embeddings model to be tested. You can also override the + ``embedding_model_params`` property to specify initialization parameters. + + Example: + + .. code-block:: python + + from typing import Type + + from langchain_tests.integration_tests import EmbeddingsIntegrationTests + from my_package.embeddings import MyEmbeddingsModel + + + class TestMyEmbeddingsModelIntegration(EmbeddingsIntegrationTests): + @property + def embeddings_class(self) -> Type[MyEmbeddingsModel]: + # Return the embeddings model class to test here + return MyEmbeddingsModel + + @property + def embedding_model_params(self) -> dict: + # Return initialization parameters for the model. + return {"model": "model-001"} + + .. note:: + API references for individual test methods include troubleshooting tips. + """ + def test_embed_query(self, model: Embeddings) -> None: + """Test embedding a string query. + + .. dropdown:: Troubleshooting + + If this test fails, check that: + + 1. The model will generate a list of floats when calling ``.embed_query`` on a string. + 2. The length of the list is consistent across different inputs. + """ # noqa: E501 embedding_1 = model.embed_query("foo") assert isinstance(embedding_1, List) @@ -18,6 +58,15 @@ def test_embed_query(self, model: Embeddings) -> None: assert len(embedding_1) == len(embedding_2) def test_embed_documents(self, model: Embeddings) -> None: + """Test embedding a list of strings. + + .. dropdown:: Troubleshooting + + If this test fails, check that: + + 1. The model will generate a list of lists of floats when calling ``.embed_documents`` on a list of strings. + 2. The length of each list is the same. + """ # noqa: E501 documents = ["foo", "bar", "baz"] embeddings = model.embed_documents(documents) @@ -28,6 +77,15 @@ def test_embed_documents(self, model: Embeddings) -> None: assert all(len(embedding) == len(embeddings[0]) for embedding in embeddings) async def test_aembed_query(self, model: Embeddings) -> None: + """Test embedding a string query async. + + .. dropdown:: Troubleshooting + + If this test fails, check that: + + 1. The model will generate a list of floats when calling ``.aembed_query`` on a string. + 2. The length of the list is consistent across different inputs. + """ # noqa: E501 embedding_1 = await model.aembed_query("foo") assert isinstance(embedding_1, List) @@ -39,6 +97,15 @@ async def test_aembed_query(self, model: Embeddings) -> None: assert len(embedding_1) == len(embedding_2) async def test_aembed_documents(self, model: Embeddings) -> None: + """Test embedding a list of strings async. + + .. dropdown:: Troubleshooting + + If this test fails, check that: + + 1. The model will generate a list of lists of floats when calling ``.aembed_documents`` on a list of strings. + 2. The length of each list is the same. + """ # noqa: E501 documents = ["foo", "bar", "baz"] embeddings = await model.aembed_documents(documents) diff --git a/libs/standard-tests/langchain_tests/integration_tests/indexer.py b/libs/standard-tests/langchain_tests/integration_tests/indexer.py index f1e5d9eee0a43..bdc0fc2e6b408 100644 --- a/libs/standard-tests/langchain_tests/integration_tests/indexer.py +++ b/libs/standard-tests/langchain_tests/integration_tests/indexer.py @@ -1,4 +1,12 @@ -"""Test suite to check index implementations.""" +"""Test suite to check index implementations. + +Standard tests for the DocumentIndex abstraction + +We don't recommend implementing externally managed DocumentIndex abstractions at this +time. + +:private: +""" import inspect import uuid diff --git a/libs/standard-tests/langchain_tests/integration_tests/retrievers.py b/libs/standard-tests/langchain_tests/integration_tests/retrievers.py index cf55e62f468ae..72425f30c7cab 100644 --- a/libs/standard-tests/langchain_tests/integration_tests/retrievers.py +++ b/libs/standard-tests/langchain_tests/integration_tests/retrievers.py @@ -9,29 +9,58 @@ class RetrieversIntegrationTests(BaseStandardTests): + """ + Base class for retrievers integration tests. + """ + @property @abstractmethod - def retriever_constructor(self) -> Type[BaseRetriever]: ... + def retriever_constructor(self) -> Type[BaseRetriever]: + """ + A BaseRetriever subclass to be tested. + """ + ... @property def retriever_constructor_params(self) -> dict: + """ + Returns a dictionary of parameters to pass to the retriever constructor. + """ return {} @property @abstractmethod def retriever_query_example(self) -> str: """ - Returns a dictionary representing the "args" of an example retriever call. + Returns a str representing the "query" of an example retriever call. """ ... @pytest.fixture def retriever(self) -> BaseRetriever: + """ + :private: + """ return self.retriever_constructor(**self.retriever_constructor_params) def test_k_constructor_param(self) -> None: """ - Test that the retriever constructor accepts a k parameter. + Test that the retriever constructor accepts a k parameter, representing + the number of documents to return. + + .. dropdown:: Troubleshooting + + If this test fails, either the retriever constructor does not accept a k + parameter, or the retriever does not return the correct number of documents + (`k`) when it is set. + + For example, a retriever like + + .. code-block:: python + + MyRetriever(k=3).invoke("query") + + should return 3 documents when invoked with a query. """ params = { k: v for k, v in self.retriever_constructor_params.items() if k != "k" @@ -49,6 +78,24 @@ def test_k_constructor_param(self) -> None: assert all(isinstance(doc, Document) for doc in result_1) def test_invoke_with_k_kwarg(self, retriever: BaseRetriever) -> None: + """ + Test that the invoke method accepts a k parameter, representing the number of + documents to return. + + .. dropdown:: Troubleshooting + + If this test fails, the retriever's invoke method does not accept a k + parameter, or the retriever does not return the correct number of documents + (`k`) when it is set. + + For example, a retriever like + + .. code-block:: python + + MyRetriever().invoke("query", k=3) + + should return 3 documents when invoked with a query. + """ result_1 = retriever.invoke(self.retriever_query_example, k=1) assert len(result_1) == 1 assert all(isinstance(doc, Document) for doc in result_1) @@ -61,6 +108,12 @@ def test_invoke_returns_documents(self, retriever: BaseRetriever) -> None: """ If invoked with the example params, the retriever should return a list of Documents. + + .. dropdown:: Troubleshooting + + If this test fails, the retriever's invoke method does not return a list of + `langchain_core.document.Document` objects. Please confirm that your + `_get_relevant_documents` method returns a list of `Document` objects. """ result = retriever.invoke(self.retriever_query_example) @@ -71,6 +124,9 @@ async def test_ainvoke_returns_documents(self, retriever: BaseRetriever) -> None """ If ainvoked with the example params, the retriever should return a list of Documents. + + See :meth:`test_invoke_returns_documents` for more information on + troubleshooting. """ result = await retriever.ainvoke(self.retriever_query_example) diff --git a/libs/standard-tests/langchain_tests/integration_tests/tools.py b/libs/standard-tests/langchain_tests/integration_tests/tools.py index 4987626037863..2fcd610ccc052 100644 --- a/libs/standard-tests/langchain_tests/integration_tests/tools.py +++ b/libs/standard-tests/langchain_tests/integration_tests/tools.py @@ -5,9 +5,21 @@ class ToolsIntegrationTests(ToolsTests): + """ + Base class for tools integration tests. + """ + def test_invoke_matches_output_schema(self, tool: BaseTool) -> None: """ If invoked with a ToolCall, the tool should return a valid ToolMessage content. + + If you have followed the `custom tool guide `_, + this test should always pass because ToolCall inputs are handled by the + :class:`langchain_core.tools.BaseTool` class. + + If you have not followed this guide, you should ensure that your tool's + `invoke` method returns a valid ToolMessage content when it receives + a dict representing a ToolCall as input (as opposed to distinct args). """ tool_call = ToolCall( name=tool.name, @@ -36,6 +48,8 @@ def test_invoke_matches_output_schema(self, tool: BaseTool) -> None: async def test_async_invoke_matches_output_schema(self, tool: BaseTool) -> None: """ If ainvoked with a ToolCall, the tool should return a valid ToolMessage content. + + For debugging tips, see :meth:`test_invoke_matches_output_schema`. """ tool_call = ToolCall( name=tool.name, @@ -65,12 +79,20 @@ def test_invoke_no_tool_call(self, tool: BaseTool) -> None: """ If invoked without a ToolCall, the tool can return anything but it shouldn't throw an error + + If this test fails, your tool may not be handling the input you defined + in `tool_invoke_params_example` correctly, and it's throwing an error. + + This test doesn't have any checks. It's just to ensure that the tool + doesn't throw an error when invoked with a dictionary of kwargs. """ tool.invoke(self.tool_invoke_params_example) async def test_async_invoke_no_tool_call(self, tool: BaseTool) -> None: """ - If invoked without a ToolCall, the tool can return anything + If ainvoked without a ToolCall, the tool can return anything but it shouldn't throw an error + + For debugging tips, see :meth:`test_invoke_no_tool_call`. """ await tool.ainvoke(self.tool_invoke_params_example) diff --git a/libs/standard-tests/langchain_tests/integration_tests/vectorstores.py b/libs/standard-tests/langchain_tests/integration_tests/vectorstores.py index 08b0358dcfb9e..6b8d8d1d565d6 100644 --- a/libs/standard-tests/langchain_tests/integration_tests/vectorstores.py +++ b/libs/standard-tests/langchain_tests/integration_tests/vectorstores.py @@ -14,19 +14,86 @@ EMBEDDING_SIZE = 6 -class ReadWriteTestSuite(BaseStandardTests): - """Test suite for checking the read-write API of a vectorstore. - - This test suite verifies the basic read-write API of a vectorstore. - - The test suite is designed for synchronous vectorstores. +class VectorStoreIntegrationTests(BaseStandardTests): + """Base class for vector store integration tests. Implementers should subclass this test suite and provide a fixture - that returns an empty vectorstore for each test. + that returns an empty vector store for each test. - The fixture should use the `get_embeddings` method to get a pre-defined + The fixture should use the ``get_embeddings`` method to get a pre-defined embeddings model that should be used for this test suite. - """ + + Here is a template: + + .. code-block:: python + + from typing import Generator + + import pytest + from langchain_core.vectorstores import VectorStore + from langchain_parrot_link.vectorstores import ParrotVectorStore + from langchain_tests.integration_tests.vectorstores import VectorStoreIntegrationTests + + + class TestParrotVectorStore(VectorStoreIntegrationTests): + @pytest.fixture() + def vectorstore(self) -> Generator[VectorStore, None, None]: # type: ignore + \"\"\"Get an empty vectorstore.\"\"\" + store = ParrotVectorStore(self.get_embeddings()) + # note: store should be EMPTY at this point + # if you need to delete data, you may do so here + try: + yield store + finally: + # cleanup operations, or deleting data + pass + + In the fixture, before the ``yield`` we instantiate an empty vector store. In the + ``finally`` block, we call whatever logic is necessary to bring the vector store + to a clean state. + + Example: + + .. code-block:: python + + from typing import Generator + + import pytest + from langchain_core.vectorstores import VectorStore + from langchain_tests.integration_tests.vectorstores import VectorStoreIntegrationTests + + from langchain_chroma import Chroma + + + class TestChromaStandard(VectorStoreIntegrationTests): + @pytest.fixture() + def vectorstore(self) -> Generator[VectorStore, None, None]: # type: ignore + \"\"\"Get an empty vectorstore for unit tests.\"\"\" + store = Chroma(embedding_function=self.get_embeddings()) + try: + yield store + finally: + store.delete_collection() + pass + + Note that by default we enable both sync and async tests. To disable either, + override the ``has_sync`` or ``has_async`` properties to ``False`` in the + subclass. For example: + + .. code-block:: python + + class TestParrotVectorStore(VectorStoreIntegrationTests): + @pytest.fixture() + def vectorstore(self) -> Generator[VectorStore, None, None]: # type: ignore + ... + + @property + def has_async(self) -> bool: + return False + + .. note:: + API references for individual test methods include troubleshooting tips. + """ # noqa: E501 @abstractmethod @pytest.fixture @@ -36,19 +103,61 @@ def vectorstore(self) -> VectorStore: The returned vectorstore should be EMPTY. """ + @property + def has_sync(self) -> bool: + """ + Configurable property to enable or disable sync tests. + """ + return True + + @property + def has_async(self) -> bool: + """ + Configurable property to enable or disable async tests. + """ + return True + @staticmethod def get_embeddings() -> Embeddings: - """A pre-defined embeddings model that should be used for this test.""" + """A pre-defined embeddings model that should be used for this test. + + This currently uses ``DeterministicFakeEmbedding`` from ``langchain-core``, + which uses numpy to generate random numbers based on a hash of the input text. + + The resulting embeddings are not meaningful, but they are deterministic. + """ return DeterministicFakeEmbedding( size=EMBEDDING_SIZE, ) def test_vectorstore_is_empty(self, vectorstore: VectorStore) -> None: - """Test that the vectorstore is empty.""" + """Test that the vectorstore is empty. + + .. dropdown:: Troubleshooting + + If this test fails, check that the test class (i.e., sub class of + ``VectorStoreIntegrationTests``) initializes an empty vector store in the + ``vectorestore`` fixture. + """ + if not self.has_sync: + pytest.skip("Sync tests not supported.") + assert vectorstore.similarity_search("foo", k=1) == [] def test_add_documents(self, vectorstore: VectorStore) -> None: - """Test adding documents into the vectorstore.""" + """Test adding documents into the vectorstore. + + .. dropdown:: Troubleshooting + + If this test fails, check that: + + 1. We correctly initialize an empty vector store in the ``vectorestore`` fixture. + 2. Calling ``.similarity_search`` for the top ``k`` similar documents does not threshold by score. + 3. We do not mutate the original document object when adding it to the vector store (e.g., by adding an ID). + """ # noqa: E501 + if not self.has_sync: + pytest.skip("Sync tests not supported.") + original_documents = [ Document(page_content="foo", metadata={"id": 1}), Document(page_content="bar", metadata={"id": 2}), @@ -71,11 +180,30 @@ def test_vectorstore_still_empty(self, vectorstore: VectorStore) -> None: This just verifies that the fixture is set up properly to be empty after each test. + + .. dropdown:: Troubleshooting + + If this test fails, check that the test class (i.e., sub class of + ``VectorStoreIntegrationTests``) correctly clears the vector store in the + ``finally`` block. """ + if not self.has_sync: + pytest.skip("Sync tests not supported.") + assert vectorstore.similarity_search("foo", k=1) == [] def test_deleting_documents(self, vectorstore: VectorStore) -> None: - """Test deleting documents from the vectorstore.""" + """Test deleting documents from the vectorstore. + + .. dropdown:: Troubleshooting + + If this test fails, check that ``add_documents`` preserves identifiers + passed in through ``ids``, and that ``delete`` correctly removes + documents. + """ + if not self.has_sync: + pytest.skip("Sync tests not supported.") + documents = [ Document(page_content="foo", metadata={"id": 1}), Document(page_content="bar", metadata={"id": 2}), @@ -87,7 +215,16 @@ def test_deleting_documents(self, vectorstore: VectorStore) -> None: assert documents == [Document(page_content="bar", metadata={"id": 2}, id="2")] def test_deleting_bulk_documents(self, vectorstore: VectorStore) -> None: - """Test that we can delete several documents at once.""" + """Test that we can delete several documents at once. + + .. dropdown:: Troubleshooting + + If this test fails, check that ``delete`` correctly removes multiple + documents when given a list of IDs. + """ + if not self.has_sync: + pytest.skip("Sync tests not supported.") + documents = [ Document(page_content="foo", metadata={"id": 1}), Document(page_content="bar", metadata={"id": 2}), @@ -100,14 +237,33 @@ def test_deleting_bulk_documents(self, vectorstore: VectorStore) -> None: assert documents == [Document(page_content="baz", metadata={"id": 3}, id="3")] def test_delete_missing_content(self, vectorstore: VectorStore) -> None: - """Deleting missing content should not raise an exception.""" + """Deleting missing content should not raise an exception. + + .. dropdown:: Troubleshooting + + If this test fails, check that ``delete`` does not raise an exception + when deleting IDs that do not exist. + """ + if not self.has_sync: + pytest.skip("Sync tests not supported.") + vectorstore.delete(["1"]) vectorstore.delete(["1", "2", "3"]) def test_add_documents_with_ids_is_idempotent( self, vectorstore: VectorStore ) -> None: - """Adding by ID should be idempotent.""" + """Adding by ID should be idempotent. + + .. dropdown:: Troubleshooting + + If this test fails, check that adding the same document twice with the + same IDs has the same effect as adding it once (i.e., it does not + duplicate the documents). + """ + if not self.has_sync: + pytest.skip("Sync tests not supported.") + documents = [ Document(page_content="foo", metadata={"id": 1}), Document(page_content="bar", metadata={"id": 2}), @@ -121,7 +277,17 @@ def test_add_documents_with_ids_is_idempotent( ] def test_add_documents_by_id_with_mutation(self, vectorstore: VectorStore) -> None: - """Test that we can overwrite by ID using add_documents.""" + """Test that we can overwrite by ID using add_documents. + + .. dropdown:: Troubleshooting + + If this test fails, check that when ``add_documents`` is called with an + ID that already exists in the vector store, the content is updated + rather than duplicated. + """ + if not self.has_sync: + pytest.skip("Sync tests not supported.") + documents = [ Document(page_content="foo", metadata={"id": 1}), Document(page_content="bar", metadata={"id": 2}), @@ -150,7 +316,29 @@ def test_add_documents_by_id_with_mutation(self, vectorstore: VectorStore) -> No ] def test_get_by_ids(self, vectorstore: VectorStore) -> None: - """Test get by IDs.""" + """Test get by IDs. + + This test requires that ``get_by_ids`` be implemented on the vector store. + + .. dropdown:: Troubleshooting + + If this test fails, check that ``get_by_ids`` is implemented and returns + documents in the same order as the IDs passed in. + + .. note:: + ``get_by_ids`` was added to the ``VectorStore`` interface in + ``langchain-core`` version 0.2.11. If difficult to implement, this + test can be skipped using a pytest ``xfail`` on the test class: + + .. code-block:: python + + @pytest.mark.xfail(reason=("get_by_ids not implemented.")) + def test_get_by_ids(self, vectorstore: VectorStore) -> None: + super().test_get_by_ids(vectorstore) + """ + if not self.has_sync: + pytest.skip("Sync tests not supported.") + documents = [ Document(page_content="foo", metadata={"id": 1}), Document(page_content="bar", metadata={"id": 2}), @@ -163,13 +351,56 @@ def test_get_by_ids(self, vectorstore: VectorStore) -> None: ] def test_get_by_ids_missing(self, vectorstore: VectorStore) -> None: - """Test get by IDs with missing IDs.""" + """Test get by IDs with missing IDs. + + .. dropdown:: Troubleshooting + + If this test fails, check that ``get_by_ids`` is implemented and does not + raise an exception when given IDs that do not exist. + + .. note:: + ``get_by_ids`` was added to the ``VectorStore`` interface in + ``langchain-core`` version 0.2.11. If difficult to implement, this + test can be skipped using a pytest ``xfail`` on the test class: + + .. code-block:: python + + @pytest.mark.xfail(reason=("get_by_ids not implemented.")) + def test_get_by_ids_missing(self, vectorstore: VectorStore) -> None: + super().test_get_by_ids_missing(vectorstore) + """ # noqa: E501 + if not self.has_sync: + pytest.skip("Sync tests not supported.") + # This should not raise an exception documents = vectorstore.get_by_ids(["1", "2", "3"]) assert documents == [] def test_add_documents_documents(self, vectorstore: VectorStore) -> None: - """Run add_documents tests.""" + """Run add_documents tests. + + .. dropdown:: Troubleshooting + + If this test fails, check that ``get_by_ids`` is implemented and returns + documents in the same order as the IDs passed in. + + Check also that ``add_documents`` will correctly generate string IDs if + none are provided. + + .. note:: + ``get_by_ids`` was added to the ``VectorStore`` interface in + ``langchain-core`` version 0.2.11. If difficult to implement, this + test can be skipped using a pytest ``xfail`` on the test class: + + .. code-block:: python + + @pytest.mark.xfail(reason=("get_by_ids not implemented.")) + def test_add_documents_documents(self, vectorstore: VectorStore) -> None: + super().test_add_documents_documents(vectorstore) + """ # noqa: E501 + if not self.has_sync: + pytest.skip("Sync tests not supported.") + documents = [ Document(page_content="foo", metadata={"id": 1}), Document(page_content="bar", metadata={"id": 2}), @@ -181,7 +412,32 @@ def test_add_documents_documents(self, vectorstore: VectorStore) -> None: ] def test_add_documents_with_existing_ids(self, vectorstore: VectorStore) -> None: - """Test that add_documentsing with existing IDs is idempotent.""" + """Test that add_documents with existing IDs is idempotent. + + .. dropdown:: Troubleshooting + + If this test fails, check that ``get_by_ids`` is implemented and returns + documents in the same order as the IDs passed in. + + This test also verifies that: + + 1. IDs specified in the ``Document.id`` field are assigned when adding documents. + 2. If some documents include IDs and others don't string IDs are generated for the latter. + + .. note:: + ``get_by_ids`` was added to the ``VectorStore`` interface in + ``langchain-core`` version 0.2.11. If difficult to implement, this + test can be skipped using a pytest ``xfail`` on the test class: + + .. code-block:: python + + @pytest.mark.xfail(reason=("get_by_ids not implemented.")) + def test_add_documents_with_existing_ids(self, vectorstore: VectorStore) -> None: + super().test_add_documents_with_existing_ids(vectorstore) + """ # noqa: E501 + if not self.has_sync: + pytest.skip("Sync tests not supported.") + documents = [ Document(id="foo", page_content="foo", metadata={"id": 1}), Document(page_content="bar", metadata={"id": 2}), @@ -193,42 +449,34 @@ def test_add_documents_with_existing_ids(self, vectorstore: VectorStore) -> None Document(page_content="bar", metadata={"id": 2}, id=ids[1]), ] + async def test_vectorstore_is_empty_async(self, vectorstore: VectorStore) -> None: + """Test that the vectorstore is empty. -class AsyncReadWriteTestSuite(BaseStandardTests): - """Test suite for checking the **async** read-write API of a vectorstore. + .. dropdown:: Troubleshooting - This test suite verifies the basic read-write API of a vectorstore. + If this test fails, check that the test class (i.e., sub class of + ``VectorStoreIntegrationTests``) initializes an empty vector store in the + ``vectorestore`` fixture. + """ + if not self.has_async: + pytest.skip("Async tests not supported.") - The test suite is designed for asynchronous vectorstores. + assert await vectorstore.asimilarity_search("foo", k=1) == [] - Implementers should subclass this test suite and provide a fixture - that returns an empty vectorstore for each test. + async def test_add_documents_async(self, vectorstore: VectorStore) -> None: + """Test adding documents into the vectorstore. - The fixture should use the `get_embeddings` method to get a pre-defined - embeddings model that should be used for this test suite. - """ + .. dropdown:: Troubleshooting - @abstractmethod - @pytest.fixture - async def vectorstore(self) -> VectorStore: - """Get the vectorstore class to test. + If this test fails, check that: - The returned vectorstore should be EMPTY. - """ + 1. We correctly initialize an empty vector store in the ``vectorestore`` fixture. + 2. Calling ``.asimilarity_search`` for the top ``k`` similar documents does not threshold by score. + 3. We do not mutate the original document object when adding it to the vector store (e.g., by adding an ID). + """ # noqa: E501 + if not self.has_async: + pytest.skip("Async tests not supported.") - @staticmethod - def get_embeddings() -> Embeddings: - """A pre-defined embeddings model that should be used for this test.""" - return DeterministicFakeEmbedding( - size=EMBEDDING_SIZE, - ) - - async def test_vectorstore_is_empty(self, vectorstore: VectorStore) -> None: - """Test that the vectorstore is empty.""" - assert await vectorstore.asimilarity_search("foo", k=1) == [] - - async def test_add_documents(self, vectorstore: VectorStore) -> None: - """Test adding documents into the vectorstore.""" original_documents = [ Document(page_content="foo", metadata={"id": 1}), Document(page_content="bar", metadata={"id": 2}), @@ -247,16 +495,37 @@ async def test_add_documents(self, vectorstore: VectorStore) -> None: Document(page_content="bar", metadata={"id": 2}), ] - async def test_vectorstore_still_empty(self, vectorstore: VectorStore) -> None: + async def test_vectorstore_still_empty_async( + self, vectorstore: VectorStore + ) -> None: """This test should follow a test that adds documents. This just verifies that the fixture is set up properly to be empty after each test. + + .. dropdown:: Troubleshooting + + If this test fails, check that the test class (i.e., sub class of + ``VectorStoreIntegrationTests``) correctly clears the vector store in the + ``finally`` block. """ + if not self.has_async: + pytest.skip("Async tests not supported.") + assert await vectorstore.asimilarity_search("foo", k=1) == [] - async def test_deleting_documents(self, vectorstore: VectorStore) -> None: - """Test deleting documents from the vectorstore.""" + async def test_deleting_documents_async(self, vectorstore: VectorStore) -> None: + """Test deleting documents from the vectorstore. + + .. dropdown:: Troubleshooting + + If this test fails, check that ``aadd_documents`` preserves identifiers + passed in through ``ids``, and that ``delete`` correctly removes + documents. + """ + if not self.has_async: + pytest.skip("Async tests not supported.") + documents = [ Document(page_content="foo", metadata={"id": 1}), Document(page_content="bar", metadata={"id": 2}), @@ -267,8 +536,19 @@ async def test_deleting_documents(self, vectorstore: VectorStore) -> None: documents = await vectorstore.asimilarity_search("foo", k=1) assert documents == [Document(page_content="bar", metadata={"id": 2}, id="2")] - async def test_deleting_bulk_documents(self, vectorstore: VectorStore) -> None: - """Test that we can delete several documents at once.""" + async def test_deleting_bulk_documents_async( + self, vectorstore: VectorStore + ) -> None: + """Test that we can delete several documents at once. + + .. dropdown:: Troubleshooting + + If this test fails, check that ``adelete`` correctly removes multiple + documents when given a list of IDs. + """ + if not self.has_async: + pytest.skip("Async tests not supported.") + documents = [ Document(page_content="foo", metadata={"id": 1}), Document(page_content="bar", metadata={"id": 2}), @@ -280,15 +560,34 @@ async def test_deleting_bulk_documents(self, vectorstore: VectorStore) -> None: documents = await vectorstore.asimilarity_search("foo", k=1) assert documents == [Document(page_content="baz", metadata={"id": 3}, id="3")] - async def test_delete_missing_content(self, vectorstore: VectorStore) -> None: - """Deleting missing content should not raise an exception.""" + async def test_delete_missing_content_async(self, vectorstore: VectorStore) -> None: + """Deleting missing content should not raise an exception. + + .. dropdown:: Troubleshooting + + If this test fails, check that ``adelete`` does not raise an exception + when deleting IDs that do not exist. + """ + if not self.has_async: + pytest.skip("Async tests not supported.") + await vectorstore.adelete(["1"]) await vectorstore.adelete(["1", "2", "3"]) - async def test_add_documents_with_ids_is_idempotent( + async def test_add_documents_with_ids_is_idempotent_async( self, vectorstore: VectorStore ) -> None: - """Adding by ID should be idempotent.""" + """Adding by ID should be idempotent. + + .. dropdown:: Troubleshooting + + If this test fails, check that adding the same document twice with the + same IDs has the same effect as adding it once (i.e., it does not + duplicate the documents). + """ + if not self.has_async: + pytest.skip("Async tests not supported.") + documents = [ Document(page_content="foo", metadata={"id": 1}), Document(page_content="bar", metadata={"id": 2}), @@ -301,10 +600,20 @@ async def test_add_documents_with_ids_is_idempotent( Document(page_content="foo", metadata={"id": 1}, id="1"), ] - async def test_add_documents_by_id_with_mutation( + async def test_add_documents_by_id_with_mutation_async( self, vectorstore: VectorStore ) -> None: - """Test that we can overwrite by ID using add_documents.""" + """Test that we can overwrite by ID using add_documents. + + .. dropdown:: Troubleshooting + + If this test fails, check that when ``aadd_documents`` is called with an + ID that already exists in the vector store, the content is updated + rather than duplicated. + """ + if not self.has_async: + pytest.skip("Async tests not supported.") + documents = [ Document(page_content="foo", metadata={"id": 1}), Document(page_content="bar", metadata={"id": 2}), @@ -332,8 +641,30 @@ async def test_add_documents_by_id_with_mutation( Document(id="2", page_content="bar", metadata={"id": 2}), ] - async def test_get_by_ids(self, vectorstore: VectorStore) -> None: - """Test get by IDs.""" + async def test_get_by_ids_async(self, vectorstore: VectorStore) -> None: + """Test get by IDs. + + This test requires that ``get_by_ids`` be implemented on the vector store. + + .. dropdown:: Troubleshooting + + If this test fails, check that ``get_by_ids`` is implemented and returns + documents in the same order as the IDs passed in. + + .. note:: + ``get_by_ids`` was added to the ``VectorStore`` interface in + ``langchain-core`` version 0.2.11. If difficult to implement, this + test can be skipped using a pytest ``xfail`` on the test class: + + .. code-block:: python + + @pytest.mark.xfail(reason=("get_by_ids not implemented.")) + async def test_get_by_ids(self, vectorstore: VectorStore) -> None: + await super().test_get_by_ids(vectorstore) + """ + if not self.has_async: + pytest.skip("Async tests not supported.") + documents = [ Document(page_content="foo", metadata={"id": 1}), Document(page_content="bar", metadata={"id": 2}), @@ -345,13 +676,58 @@ async def test_get_by_ids(self, vectorstore: VectorStore) -> None: Document(page_content="bar", metadata={"id": 2}, id=ids[1]), ] - async def test_get_by_ids_missing(self, vectorstore: VectorStore) -> None: - """Test get by IDs with missing IDs.""" + async def test_get_by_ids_missing_async(self, vectorstore: VectorStore) -> None: + """Test get by IDs with missing IDs. + + .. dropdown:: Troubleshooting + + If this test fails, check that ``get_by_ids`` is implemented and does not + raise an exception when given IDs that do not exist. + + .. note:: + ``get_by_ids`` was added to the ``VectorStore`` interface in + ``langchain-core`` version 0.2.11. If difficult to implement, this + test can be skipped using a pytest ``xfail`` on the test class: + + .. code-block:: python + + @pytest.mark.xfail(reason=("get_by_ids not implemented.")) + async def test_get_by_ids_missing(self, vectorstore: VectorStore) -> None: + await super().test_get_by_ids_missing(vectorstore) + """ # noqa: E501 + if not self.has_async: + pytest.skip("Async tests not supported.") + # This should not raise an exception assert await vectorstore.aget_by_ids(["1", "2", "3"]) == [] - async def test_add_documents_documents(self, vectorstore: VectorStore) -> None: - """Run add_documents tests.""" + async def test_add_documents_documents_async( + self, vectorstore: VectorStore + ) -> None: + """Run add_documents tests. + + .. dropdown:: Troubleshooting + + If this test fails, check that ``get_by_ids`` is implemented and returns + documents in the same order as the IDs passed in. + + Check also that ``aadd_documents`` will correctly generate string IDs if + none are provided. + + .. note:: + ``get_by_ids`` was added to the ``VectorStore`` interface in + ``langchain-core`` version 0.2.11. If difficult to implement, this + test can be skipped using a pytest ``xfail`` on the test class: + + .. code-block:: python + + @pytest.mark.xfail(reason=("get_by_ids not implemented.")) + async def test_add_documents_documents(self, vectorstore: VectorStore) -> None: + await super().test_add_documents_documents(vectorstore) + """ # noqa: E501 + if not self.has_async: + pytest.skip("Async tests not supported.") + documents = [ Document(page_content="foo", metadata={"id": 1}), Document(page_content="bar", metadata={"id": 2}), @@ -362,10 +738,35 @@ async def test_add_documents_documents(self, vectorstore: VectorStore) -> None: Document(page_content="bar", metadata={"id": 2}, id=ids[1]), ] - async def test_add_documents_with_existing_ids( + async def test_add_documents_with_existing_ids_async( self, vectorstore: VectorStore ) -> None: - """Test that add_documentsing with existing IDs is idempotent.""" + """Test that add_documents with existing IDs is idempotent. + + .. dropdown:: Troubleshooting + + If this test fails, check that ``get_by_ids`` is implemented and returns + documents in the same order as the IDs passed in. + + This test also verifies that: + + 1. IDs specified in the ``Document.id`` field are assigned when adding documents. + 2. If some documents include IDs and others don't string IDs are generated for the latter. + + .. note:: + ``get_by_ids`` was added to the ``VectorStore`` interface in + ``langchain-core`` version 0.2.11. If difficult to implement, this + test can be skipped using a pytest ``xfail`` on the test class: + + .. code-block:: python + + @pytest.mark.xfail(reason=("get_by_ids not implemented.")) + async def test_add_documents_with_existing_ids(self, vectorstore: VectorStore) -> None: + await super().test_add_documents_with_existing_ids(vectorstore) + """ # noqa: E501 + if not self.has_async: + pytest.skip("Async tests not supported.") + documents = [ Document(id="foo", page_content="foo", metadata={"id": 1}), Document(page_content="bar", metadata={"id": 2}), diff --git a/libs/standard-tests/langchain_tests/unit_tests/chat_models.py b/libs/standard-tests/langchain_tests/unit_tests/chat_models.py index 2de0776f5e310..9d3b20de4ceb9 100644 --- a/libs/standard-tests/langchain_tests/unit_tests/chat_models.py +++ b/libs/standard-tests/langchain_tests/unit_tests/chat_models.py @@ -11,7 +11,7 @@ from langchain_core.language_models import BaseChatModel from langchain_core.load import dumpd, load from langchain_core.runnables import RunnableBinding -from langchain_core.tools import tool +from langchain_core.tools import BaseTool, tool from pydantic import BaseModel, Field, SecretStr from pydantic.v1 import ( BaseModel as BaseModelV1, @@ -28,15 +28,12 @@ from langchain_tests.utils.pydantic import PYDANTIC_MAJOR_VERSION -class Person(BaseModel): # Used by some dependent tests. Should be deprecated. - """Record attributes of a person.""" - - name: str = Field(..., description="The name of the person.") - age: int = Field(..., description="The age of the person.") - - def generate_schema_pydantic_v1_from_2() -> Any: - """Use to generate a schema from v1 namespace in pydantic 2.""" + """ + Use to generate a schema from v1 namespace in pydantic 2. + + :private: + """ if PYDANTIC_MAJOR_VERSION != 2: raise AssertionError("This function is only compatible with Pydantic v2.") @@ -50,7 +47,11 @@ class PersonB(BaseModelV1): def generate_schema_pydantic() -> Any: - """Works with either pydantic 1 or 2""" + """ + Works with either pydantic 1 or 2 + + :private: + """ class PersonA(BaseModel): """Record attributes of a person.""" @@ -67,19 +68,150 @@ class PersonA(BaseModel): TEST_PYDANTIC_MODELS.append(generate_schema_pydantic_v1_from_2()) -@tool -def my_adder_tool(a: int, b: int) -> int: - """Takes two integers, a and b, and returns their sum.""" - return a + b +class ChatModelTests(BaseStandardTests): + """Base class for chat model tests. + :private: + """ # noqa: E501 -def my_adder(a: int, b: int) -> int: - """Takes two integers, a and b, and returns their sum.""" - return a + b + @property + @abstractmethod + def chat_model_class(self) -> Type[BaseChatModel]: + """The chat model class to test, e.g., ``ChatParrotLink``.""" + ... + @property + def chat_model_params(self) -> dict: + """Initialization parameters for the chat model.""" + return {} + + @property + def standard_chat_model_params(self) -> dict: + """:private:""" + return { + "temperature": 0, + "max_tokens": 100, + "timeout": 60, + "stop": [], + "max_retries": 2, + } + + @pytest.fixture + def model(self) -> BaseChatModel: + """:private:""" + return self.chat_model_class( + **{**self.standard_chat_model_params, **self.chat_model_params} + ) + + @pytest.fixture + def my_adder_tool(self) -> BaseTool: + """:private:""" + + @tool + def my_adder_tool(a: int, b: int) -> int: + """Takes two integers, a and b, and returns their sum.""" + return a + b + + return my_adder_tool + + @property + def has_tool_calling(self) -> bool: + """(bool) whether the model supports tool calling.""" + return self.chat_model_class.bind_tools is not BaseChatModel.bind_tools + + @property + def tool_choice_value(self) -> Optional[str]: + """(None or str) to use for tool choice when used in tests.""" + return None + + @property + def has_structured_output(self) -> bool: + """(bool) whether the chat model supports structured output.""" + return ( + self.chat_model_class.with_structured_output + is not BaseChatModel.with_structured_output + ) + + @property + def supports_image_inputs(self) -> bool: + """(bool) whether the chat model supports image inputs, defaults to + ``False``.""" + return False + + @property + def supports_video_inputs(self) -> bool: + """(bool) whether the chat model supports video inputs, efaults to ``False``. + No current tests are written for this feature.""" + return False + + @property + def returns_usage_metadata(self) -> bool: + """(bool) whether the chat model returns usage metadata on invoke and streaming + responses.""" + return True + + @property + def supports_anthropic_inputs(self) -> bool: + """(bool) whether the chat model supports Anthropic-style inputs.""" + return False + + @property + def supports_image_tool_message(self) -> bool: + """(bool) whether the chat model supports ToolMessages that include image + content.""" + return False + + @property + def supported_usage_metadata_details( + self, + ) -> Dict[ + Literal["invoke", "stream"], + List[ + Literal[ + "audio_input", + "audio_output", + "reasoning_output", + "cache_read_input", + "cache_creation_input", + ] + ], + ]: + """(dict) what usage metadata details are emitted in invoke and stream. Only + needs to be overridden if these details are returned by the model.""" + return {"invoke": [], "stream": []} + + +class ChatModelUnitTests(ChatModelTests): + """Base class for chat model unit tests. + + Test subclasses must implement the ``chat_model_class`` and + ``chat_model_params`` properties to specify what model to test and its + initialization parameters. + + Example: + + .. code-block:: python + + from typing import Type + + from langchain_tests.unit_tests import ChatModelUnitTests + from my_package.chat_models import MyChatModel + + + class TestMyChatModelUnit(ChatModelUnitTests): + @property + def chat_model_class(self) -> Type[MyChatModel]: + # Return the chat model class to test here + return MyChatModel + + @property + def chat_model_params(self) -> dict: + # Return initialization parameters for the model. + return {"model": "model-001", "temperature": 0} + + .. note:: + API references for individual test methods include troubleshooting tips. -class ChatModelTests(BaseStandardTests): - """Base class for chat model tests. Test subclasses must implement the following two properties: @@ -116,6 +248,14 @@ def chat_model_params(self) -> dict: By default, this is determined by whether the chat model's `bind_tools` method is overridden. It typically does not need to be overridden on the test class. + Example override: + + .. code-block:: python + + @property + def has_tool_calling(self) -> bool: + return True + .. dropdown:: tool_choice_value Value to use for tool choice when used in tests. @@ -275,145 +415,6 @@ def supports_image_tool_message(self) -> bool: cached, audio, or reasoning. Only needs to be overridden if these details are supplied. - """ # noqa: E501 - - @property - @abstractmethod - def chat_model_class(self) -> Type[BaseChatModel]: - """The chat model class to test, e.g., `ChatParrotLink`.""" - ... - - @property - def chat_model_params(self) -> dict: - """Initialization parameters for the chat mobdel.""" - return {} - - @property - def standard_chat_model_params(self) -> dict: - """:meta private:""" - return { - "temperature": 0, - "max_tokens": 100, - "timeout": 60, - "stop": [], - "max_retries": 2, - } - - @pytest.fixture - def model(self) -> BaseChatModel: - """Fixture that returns an instance of the chat model. Should not be - overridden.""" - return self.chat_model_class( - **{**self.standard_chat_model_params, **self.chat_model_params} - ) - - @property - def has_tool_calling(self) -> bool: - """Boolean property indicating whether the model supports tool calling.""" - return self.chat_model_class.bind_tools is not BaseChatModel.bind_tools - - @property - def tool_choice_value(self) -> Optional[str]: - """Value to use for tool choice when used in tests.""" - return None - - @property - def has_structured_output(self) -> bool: - """Boolean property indicating whether the chat model supports structured - output.""" - return ( - self.chat_model_class.with_structured_output - is not BaseChatModel.with_structured_output - ) - - @property - def supports_image_inputs(self) -> bool: - """Boolean property indicating whether the chat model supports image inputs. - Defaults to ``False``.""" - return False - - @property - def supports_video_inputs(self) -> bool: - """Boolean property indicating whether the chat model supports image inputs. - Defaults to ``False``. No current tests are written for this feature.""" - return False - - @property - def returns_usage_metadata(self) -> bool: - """Boolean property indicating whether the chat model returns usage metadata - on invoke and streaming responses.""" - return True - - @property - def supports_anthropic_inputs(self) -> bool: - """Boolean property indicating whether the chat model supports Anthropic-style - inputs.""" - return False - - @property - def supports_image_tool_message(self) -> bool: - """Boolean property indicating whether the chat model supports ToolMessages - that include image content.""" - return False - - @property - def supported_usage_metadata_details( - self, - ) -> Dict[ - Literal["invoke", "stream"], - List[ - Literal[ - "audio_input", - "audio_output", - "reasoning_output", - "cache_read_input", - "cache_creation_input", - ] - ], - ]: - """Property controlling what usage metadata details are emitted in both invoke - and stream. Only needs to be overridden if these details are returned by the - model.""" - return {"invoke": [], "stream": []} - - -class ChatModelUnitTests(ChatModelTests): - """Base class for chat model unit tests. - - Test subclasses must implement the following two properties: - - chat_model_class - The chat model class to test, e.g., ``ChatParrotLink``. - - Example: - - .. code-block:: python - - @property - def chat_model_class(self) -> Type[ChatParrotLink]: - return ChatParrotLink - - chat_model_params - Initialization parameters for the chat model. - - Example: - - .. code-block:: python - - @property - def chat_model_params(self) -> dict: - return {"model": "bird-brain-001", "temperature": 0} - - .. note:: - API references for individual test methods include troubleshooting tips. - - .. note:: - Test subclasses can control what features are tested (such as tool - calling or multi-modality) by selectively overriding the properties on the - class. Relevant properties are mentioned in the references for each method. - See this page for detail on all properties: - https://python.langchain.com/api_reference/standard_tests/unit_tests/langchain_tests.unit_tests.chat_models.ChatModelTests.html - Testing initialization from environment variables Some unit tests may require testing initialization from environment variables. @@ -450,17 +451,15 @@ def init_from_env_params(self) -> Tuple[dict, dict, dict]: @property def standard_chat_model_params(self) -> dict: - """:meta private:""" + """:private:""" params = super().standard_chat_model_params params["api_key"] = "test" return params @property def init_from_env_params(self) -> Tuple[dict, dict, dict]: - """This property is used in unit tests to test initialization from environment - variables. It should return a tuple of three dictionaries that specify the - environment variables, additional initialization args, and expected instance - attributes to check.""" + """(tuple) environment variables, additional initialization args, and expected + instance attributes for testing initialization from environment variables.""" return {}, {}, {} def test_init(self) -> None: @@ -486,7 +485,8 @@ def test_init_from_env(self) -> None: .. dropdown:: Troubleshooting If this test fails, ensure that ``init_from_env_params`` is specified - correctly. + correctly and that model parameters are properly set from environment + variables during initialization. """ env_params, model_params, expected_attrs = self.init_from_env_params if not env_params: @@ -524,6 +524,7 @@ def test_init_streaming( def test_bind_tool_pydantic( self, model: BaseChatModel, + my_adder_tool: BaseTool, ) -> None: """Test that chat model correctly handles Pydantic models that are passed into ``bind_tools``. Test is skipped if the ``has_tool_calling`` property @@ -540,6 +541,10 @@ def test_bind_tool_pydantic( if not self.has_tool_calling: return + def my_adder(a: int, b: int) -> int: + """Takes two integers, a and b, and returns their sum.""" + return a + b + tools = [my_adder_tool, my_adder] for pydantic_model in TEST_PYDANTIC_MODELS: diff --git a/libs/standard-tests/langchain_tests/unit_tests/embeddings.py b/libs/standard-tests/langchain_tests/unit_tests/embeddings.py index da7b78513844b..527be61e8be98 100644 --- a/libs/standard-tests/langchain_tests/unit_tests/embeddings.py +++ b/libs/standard-tests/langchain_tests/unit_tests/embeddings.py @@ -11,6 +11,10 @@ class EmbeddingsTests(BaseStandardTests): + """ + :private: + """ + @property @abstractmethod def embeddings_class(self) -> Type[Embeddings]: ... @@ -25,17 +29,98 @@ def model(self) -> Embeddings: class EmbeddingsUnitTests(EmbeddingsTests): + """Base class for embeddings unit tests. + + Test subclasses must implement the ``embeddings_class`` property to specify the + embeddings model to be tested. You can also override the + ``embedding_model_params`` property to specify initialization parameters. + + Example: + + .. code-block:: python + + from typing import Type + + from langchain_tests.unit_tests import EmbeddingsUnitTests + from my_package.embeddings import MyEmbeddingsModel + + + class TestMyEmbeddingsModelUnit(EmbeddingsUnitTests): + @property + def embeddings_class(self) -> Type[MyEmbeddingsModel]: + # Return the embeddings model class to test here + return MyEmbeddingsModel + + @property + def embedding_model_params(self) -> dict: + # Return initialization parameters for the model. + return {"model": "model-001"} + + .. note:: + API references for individual test methods include troubleshooting tips. + + Testing initialization from environment variables + Overriding the ``init_from_env_params`` property will enable additional tests + for initialization from environment variables. See below for details. + + .. dropdown:: init_from_env_params + + This property is used in unit tests to test initialization from + environment variables. It should return a tuple of three dictionaries + that specify the environment variables, additional initialization args, + and expected instance attributes to check. + + Defaults to empty dicts. If not overridden, the test is skipped. + + Example: + + .. code-block:: python + + @property + def init_from_env_params(self) -> Tuple[dict, dict, dict]: + return ( + { + "MY_API_KEY": "api_key", + }, + { + "model": "model-001", + }, + { + "my_api_key": "api_key", + }, + ) + """ # noqa: E501 + def test_init(self) -> None: + """Test model initialization. + + .. dropdown:: Troubleshooting + + If this test fails, ensure that ``embedding_model_params`` is specified + and the model can be initialized from those params. + """ model = self.embeddings_class(**self.embedding_model_params) assert model is not None @property def init_from_env_params(self) -> Tuple[dict, dict, dict]: - """Return env vars, init args, and expected instance attrs for initializing - from env vars.""" + """This property is used in unit tests to test initialization from environment + variables. It should return a tuple of three dictionaries that specify the + environment variables, additional initialization args, and expected instance + attributes to check.""" return {}, {}, {} def test_init_from_env(self) -> None: + """Test initialization from environment variables. Relies on the + ``init_from_env_params`` property. Test is skipped if that property is not + set. + + .. dropdown:: Troubleshooting + + If this test fails, ensure that ``init_from_env_params`` is specified + correctly and that model parameters are properly set from environment + variables during initialization. + """ env_params, embeddings_params, expected_attrs = self.init_from_env_params if env_params: with mock.patch.dict(os.environ, env_params): diff --git a/libs/standard-tests/langchain_tests/unit_tests/tools.py b/libs/standard-tests/langchain_tests/unit_tests/tools.py index 93701437da340..bdc2df1c9fb62 100644 --- a/libs/standard-tests/langchain_tests/unit_tests/tools.py +++ b/libs/standard-tests/langchain_tests/unit_tests/tools.py @@ -11,12 +11,25 @@ class ToolsTests(BaseStandardTests): + """ + :private: + Base class for testing tools. This won't show in the documentation, but + the docstrings will be inherited by subclasses. + """ + @property @abstractmethod - def tool_constructor(self) -> Union[Type[BaseTool], BaseTool]: ... + def tool_constructor(self) -> Union[Type[BaseTool], BaseTool]: + """ + Returns a class or instance of a tool to be tested. + """ + ... @property def tool_constructor_params(self) -> dict: + """ + Returns a dictionary of parameters to pass to the tool constructor. + """ return {} @property @@ -24,13 +37,16 @@ def tool_invoke_params_example(self) -> dict: """ Returns a dictionary representing the "args" of an example tool call. - This should NOT be a ToolCall dict - i.e. it should not + This should NOT be a ToolCall dict - it should not have {"name", "id", "args"} keys. """ return {} @pytest.fixture def tool(self) -> BaseTool: + """ + :private: + """ if isinstance(self.tool_constructor, BaseTool): if self.tool_constructor_params != {}: msg = ( @@ -43,12 +59,9 @@ def tool(self) -> BaseTool: class ToolsUnitTests(ToolsTests): - def test_init(self) -> None: - if isinstance(self.tool_constructor, BaseTool): - tool = self.tool_constructor - else: - tool = self.tool_constructor(**self.tool_constructor_params) - assert tool is not None + """ + Base class for tools unit tests. + """ @property def init_from_env_params(self) -> Tuple[dict, dict, dict]: @@ -56,6 +69,18 @@ def init_from_env_params(self) -> Tuple[dict, dict, dict]: from env vars.""" return {}, {}, {} + def test_init(self) -> None: + """ + Test that the tool can be initialized with :attr:`tool_constructor` and + :attr:`tool_constructor_params`. If this fails, check that the + keyword args defined in :attr:`tool_constructor_params` are valid. + """ + if isinstance(self.tool_constructor, BaseTool): + tool = self.tool_constructor + else: + tool = self.tool_constructor(**self.tool_constructor_params) + assert tool is not None + def test_init_from_env(self) -> None: env_params, tools_params, expected_attrs = self.init_from_env_params if env_params: @@ -69,14 +94,32 @@ def test_init_from_env(self) -> None: assert actual == expected def test_has_name(self, tool: BaseTool) -> None: + """ + Tests that the tool has a name attribute to pass to chat models. + + If this fails, add a `name` parameter to your tool. + """ assert tool.name def test_has_input_schema(self, tool: BaseTool) -> None: + """ + Tests that the tool has an input schema. + + If this fails, add an `args_schema` to your tool. + + See + `this guide `_ + and see how `CalculatorInput` is configured in the + `CustomCalculatorTool.args_schema` attribute + """ assert tool.get_input_schema() def test_input_schema_matches_invoke_params(self, tool: BaseTool) -> None: """ - Tests that the provided example params match the declared input schema + Tests that the provided example params match the declared input schema. + + If this fails, update the `tool_invoke_params_example` attribute to match + the input schema (`args_schema`) of the tool. """ # this will be a pydantic object input_schema = tool.get_input_schema() diff --git a/libs/standard-tests/langchain_tests/utils/pydantic.py b/libs/standard-tests/langchain_tests/utils/pydantic.py index 6aa0408a092d7..52720bbd081b2 100644 --- a/libs/standard-tests/langchain_tests/utils/pydantic.py +++ b/libs/standard-tests/langchain_tests/utils/pydantic.py @@ -1,4 +1,8 @@ -"""Utilities for working with pydantic models.""" +""" +Utilities for working with pydantic models. + +:private: +""" def get_pydantic_major_version() -> int: diff --git a/libs/standard-tests/poetry.lock b/libs/standard-tests/poetry.lock index a10861100dffc..316059246f762 100644 --- a/libs/standard-tests/poetry.lock +++ b/libs/standard-tests/poetry.lock @@ -13,24 +13,24 @@ files = [ [[package]] name = "anyio" -version = "4.6.2.post1" +version = "4.7.0" description = "High level compatibility layer for multiple asynchronous event loop implementations" optional = false python-versions = ">=3.9" files = [ - {file = "anyio-4.6.2.post1-py3-none-any.whl", hash = "sha256:6d170c36fba3bdd840c73d3868c1e777e33676a69c3a72cf0a0d5d6d8009b61d"}, - {file = "anyio-4.6.2.post1.tar.gz", hash = "sha256:4c8bc31ccdb51c7f7bd251f51c609e038d63e34219b44aa86e47576389880b4c"}, + {file = "anyio-4.7.0-py3-none-any.whl", hash = "sha256:ea60c3723ab42ba6fff7e8ccb0488c898ec538ff4df1f1d5e642c3601d07e352"}, + {file = "anyio-4.7.0.tar.gz", hash = "sha256:2f834749c602966b7d456a7567cafcb309f96482b5081d14ac93ccd457f9dd48"}, ] [package.dependencies] exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""} idna = ">=2.8" sniffio = ">=1.1" -typing-extensions = {version = ">=4.1", markers = "python_version < \"3.11\""} +typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""} [package.extras] -doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] -test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21.0b1)"] +doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx_rtd_theme"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21)"] trio = ["trio (>=0.26.1)"] [[package]] @@ -234,13 +234,13 @@ trio = ["trio (>=0.22.0,<1.0)"] [[package]] name = "httpx" -version = "0.27.2" +version = "0.28.1" description = "The next generation HTTP client." optional = false python-versions = ">=3.8" files = [ - {file = "httpx-0.27.2-py3-none-any.whl", hash = "sha256:7bb2708e112d8fdd7829cd4243970f0c223274051cb35ee80c03301ee29a3df0"}, - {file = "httpx-0.27.2.tar.gz", hash = "sha256:f7c2be1d2f3c3c3160d441802406b206c2b76f5947b11115e6df10c6c65e66c2"}, + {file = "httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad"}, + {file = "httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc"}, ] [package.dependencies] @@ -248,7 +248,6 @@ anyio = "*" certifi = "*" httpcore = "==1.*" idna = "*" -sniffio = "*" [package.extras] brotli = ["brotli", "brotlicffi"] @@ -309,7 +308,7 @@ files = [ [[package]] name = "langchain-core" -version = "0.3.19" +version = "0.3.22" description = "Building applications with LLMs through composability" optional = false python-versions = ">=3.9,<4.0" @@ -334,13 +333,13 @@ url = "../core" [[package]] name = "langsmith" -version = "0.1.144" +version = "0.1.147" description = "Client library to connect to the LangSmith LLM Tracing and Evaluation Platform." optional = false python-versions = "<4.0,>=3.8.1" files = [ - {file = "langsmith-0.1.144-py3-none-any.whl", hash = "sha256:08ffb975bff2e82fc6f5428837c64c074ea25102d08a25e256361a80812c6100"}, - {file = "langsmith-0.1.144.tar.gz", hash = "sha256:b621f358d5a33441d7b5e7264c376bf4ea82bfc62d7e41aafc0f8094e3bd6369"}, + {file = "langsmith-0.1.147-py3-none-any.whl", hash = "sha256:7166fc23b965ccf839d64945a78e9f1157757add228b086141eb03a60d699a15"}, + {file = "langsmith-0.1.147.tar.gz", hash = "sha256:2e933220318a4e73034657103b3b1a3a6109cc5db3566a7e8e03be8d6d7def7a"}, ] [package.dependencies] @@ -353,6 +352,9 @@ pydantic = [ requests = ">=2,<3" requests-toolbelt = ">=1.0.0,<2.0.0" +[package.extras] +langsmith-pyo3 = ["langsmith-pyo3 (>=0.1.0rc2,<0.2.0)"] + [[package]] name = "mypy" version = "1.13.0" @@ -462,71 +464,152 @@ files = [ {file = "numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010"}, ] +[[package]] +name = "numpy" +version = "2.2.0" +description = "Fundamental package for array computing in Python" +optional = false +python-versions = ">=3.10" +files = [ + {file = "numpy-2.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1e25507d85da11ff5066269d0bd25d06e0a0f2e908415534f3e603d2a78e4ffa"}, + {file = "numpy-2.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a62eb442011776e4036af5c8b1a00b706c5bc02dc15eb5344b0c750428c94219"}, + {file = "numpy-2.2.0-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:b606b1aaf802e6468c2608c65ff7ece53eae1a6874b3765f69b8ceb20c5fa78e"}, + {file = "numpy-2.2.0-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:36b2b43146f646642b425dd2027730f99bac962618ec2052932157e213a040e9"}, + {file = "numpy-2.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7fe8f3583e0607ad4e43a954e35c1748b553bfe9fdac8635c02058023277d1b3"}, + {file = "numpy-2.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:122fd2fcfafdefc889c64ad99c228d5a1f9692c3a83f56c292618a59aa60ae83"}, + {file = "numpy-2.2.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3f2f5cddeaa4424a0a118924b988746db6ffa8565e5829b1841a8a3bd73eb59a"}, + {file = "numpy-2.2.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7fe4bb0695fe986a9e4deec3b6857003b4cfe5c5e4aac0b95f6a658c14635e31"}, + {file = "numpy-2.2.0-cp310-cp310-win32.whl", hash = "sha256:b30042fe92dbd79f1ba7f6898fada10bdaad1847c44f2dff9a16147e00a93661"}, + {file = "numpy-2.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:54dc1d6d66f8d37843ed281773c7174f03bf7ad826523f73435deb88ba60d2d4"}, + {file = "numpy-2.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9874bc2ff574c40ab7a5cbb7464bf9b045d617e36754a7bc93f933d52bd9ffc6"}, + {file = "numpy-2.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0da8495970f6b101ddd0c38ace92edea30e7e12b9a926b57f5fabb1ecc25bb90"}, + {file = "numpy-2.2.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:0557eebc699c1c34cccdd8c3778c9294e8196df27d713706895edc6f57d29608"}, + {file = "numpy-2.2.0-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:3579eaeb5e07f3ded59298ce22b65f877a86ba8e9fe701f5576c99bb17c283da"}, + {file = "numpy-2.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40deb10198bbaa531509aad0cd2f9fadb26c8b94070831e2208e7df543562b74"}, + {file = "numpy-2.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c2aed8fcf8abc3020d6a9ccb31dbc9e7d7819c56a348cc88fd44be269b37427e"}, + {file = "numpy-2.2.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a222d764352c773aa5ebde02dd84dba3279c81c6db2e482d62a3fa54e5ece69b"}, + {file = "numpy-2.2.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4e58666988605e251d42c2818c7d3d8991555381be26399303053b58a5bbf30d"}, + {file = "numpy-2.2.0-cp311-cp311-win32.whl", hash = "sha256:4723a50e1523e1de4fccd1b9a6dcea750c2102461e9a02b2ac55ffeae09a4410"}, + {file = "numpy-2.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:16757cf28621e43e252c560d25b15f18a2f11da94fea344bf26c599b9cf54b73"}, + {file = "numpy-2.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cff210198bb4cae3f3c100444c5eaa573a823f05c253e7188e1362a5555235b3"}, + {file = "numpy-2.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:58b92a5828bd4d9aa0952492b7de803135038de47343b2aa3cc23f3b71a3dc4e"}, + {file = "numpy-2.2.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:ebe5e59545401fbb1b24da76f006ab19734ae71e703cdb4a8b347e84a0cece67"}, + {file = "numpy-2.2.0-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:e2b8cd48a9942ed3f85b95ca4105c45758438c7ed28fff1e4ce3e57c3b589d8e"}, + {file = "numpy-2.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57fcc997ffc0bef234b8875a54d4058afa92b0b0c4223fc1f62f24b3b5e86038"}, + {file = "numpy-2.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85ad7d11b309bd132d74397fcf2920933c9d1dc865487128f5c03d580f2c3d03"}, + {file = "numpy-2.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:cb24cca1968b21355cc6f3da1a20cd1cebd8a023e3c5b09b432444617949085a"}, + {file = "numpy-2.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0798b138c291d792f8ea40fe3768610f3c7dd2574389e37c3f26573757c8f7ef"}, + {file = "numpy-2.2.0-cp312-cp312-win32.whl", hash = "sha256:afe8fb968743d40435c3827632fd36c5fbde633b0423da7692e426529b1759b1"}, + {file = "numpy-2.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:3a4199f519e57d517ebd48cb76b36c82da0360781c6a0353e64c0cac30ecaad3"}, + {file = "numpy-2.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f8c8b141ef9699ae777c6278b52c706b653bf15d135d302754f6b2e90eb30367"}, + {file = "numpy-2.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0f0986e917aca18f7a567b812ef7ca9391288e2acb7a4308aa9d265bd724bdae"}, + {file = "numpy-2.2.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:1c92113619f7b272838b8d6702a7f8ebe5edea0df48166c47929611d0b4dea69"}, + {file = "numpy-2.2.0-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:5a145e956b374e72ad1dff82779177d4a3c62bc8248f41b80cb5122e68f22d13"}, + {file = "numpy-2.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18142b497d70a34b01642b9feabb70156311b326fdddd875a9981f34a369b671"}, + {file = "numpy-2.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a7d41d1612c1a82b64697e894b75db6758d4f21c3ec069d841e60ebe54b5b571"}, + {file = "numpy-2.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a98f6f20465e7618c83252c02041517bd2f7ea29be5378f09667a8f654a5918d"}, + {file = "numpy-2.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e09d40edfdb4e260cb1567d8ae770ccf3b8b7e9f0d9b5c2a9992696b30ce2742"}, + {file = "numpy-2.2.0-cp313-cp313-win32.whl", hash = "sha256:3905a5fffcc23e597ee4d9fb3fcd209bd658c352657548db7316e810ca80458e"}, + {file = "numpy-2.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:a184288538e6ad699cbe6b24859206e38ce5fba28f3bcfa51c90d0502c1582b2"}, + {file = "numpy-2.2.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:7832f9e8eb00be32f15fdfb9a981d6955ea9adc8574c521d48710171b6c55e95"}, + {file = "numpy-2.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f0dd071b95bbca244f4cb7f70b77d2ff3aaaba7fa16dc41f58d14854a6204e6c"}, + {file = "numpy-2.2.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:b0b227dcff8cdc3efbce66d4e50891f04d0a387cce282fe1e66199146a6a8fca"}, + {file = "numpy-2.2.0-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:6ab153263a7c5ccaf6dfe7e53447b74f77789f28ecb278c3b5d49db7ece10d6d"}, + {file = "numpy-2.2.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e500aba968a48e9019e42c0c199b7ec0696a97fa69037bea163b55398e390529"}, + {file = "numpy-2.2.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:440cfb3db4c5029775803794f8638fbdbf71ec702caf32735f53b008e1eaece3"}, + {file = "numpy-2.2.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a55dc7a7f0b6198b07ec0cd445fbb98b05234e8b00c5ac4874a63372ba98d4ab"}, + {file = "numpy-2.2.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4bddbaa30d78c86329b26bd6aaaea06b1e47444da99eddac7bf1e2fab717bd72"}, + {file = "numpy-2.2.0-cp313-cp313t-win32.whl", hash = "sha256:30bf971c12e4365153afb31fc73f441d4da157153f3400b82db32d04de1e4066"}, + {file = "numpy-2.2.0-cp313-cp313t-win_amd64.whl", hash = "sha256:d35717333b39d1b6bb8433fa758a55f1081543de527171543a2b710551d40881"}, + {file = "numpy-2.2.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e12c6c1ce84628c52d6367863773f7c8c8241be554e8b79686e91a43f1733773"}, + {file = "numpy-2.2.0-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:b6207dc8fb3c8cb5668e885cef9ec7f70189bec4e276f0ff70d5aa078d32c88e"}, + {file = "numpy-2.2.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a50aeff71d0f97b6450d33940c7181b08be1441c6c193e678211bff11aa725e7"}, + {file = "numpy-2.2.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:df12a1f99b99f569a7c2ae59aa2d31724e8d835fc7f33e14f4792e3071d11221"}, + {file = "numpy-2.2.0.tar.gz", hash = "sha256:140dd80ff8981a583a60980be1a655068f8adebf7a45a06a6858c873fcdcd4a0"}, +] + [[package]] name = "orjson" -version = "3.10.11" +version = "3.10.12" description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" optional = false python-versions = ">=3.8" files = [ - {file = "orjson-3.10.11-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:6dade64687f2bd7c090281652fe18f1151292d567a9302b34c2dbb92a3872f1f"}, - {file = "orjson-3.10.11-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82f07c550a6ccd2b9290849b22316a609023ed851a87ea888c0456485a7d196a"}, - {file = "orjson-3.10.11-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bd9a187742d3ead9df2e49240234d728c67c356516cf4db018833a86f20ec18c"}, - {file = "orjson-3.10.11-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:77b0fed6f209d76c1c39f032a70df2d7acf24b1812ca3e6078fd04e8972685a3"}, - {file = "orjson-3.10.11-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:63fc9d5fe1d4e8868f6aae547a7b8ba0a2e592929245fff61d633f4caccdcdd6"}, - {file = "orjson-3.10.11-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65cd3e3bb4fbb4eddc3c1e8dce10dc0b73e808fcb875f9fab40c81903dd9323e"}, - {file = "orjson-3.10.11-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6f67c570602300c4befbda12d153113b8974a3340fdcf3d6de095ede86c06d92"}, - {file = "orjson-3.10.11-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1f39728c7f7d766f1f5a769ce4d54b5aaa4c3f92d5b84817053cc9995b977acc"}, - {file = "orjson-3.10.11-cp310-none-win32.whl", hash = "sha256:1789d9db7968d805f3d94aae2c25d04014aae3a2fa65b1443117cd462c6da647"}, - {file = "orjson-3.10.11-cp310-none-win_amd64.whl", hash = "sha256:5576b1e5a53a5ba8f8df81872bb0878a112b3ebb1d392155f00f54dd86c83ff6"}, - {file = "orjson-3.10.11-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:1444f9cb7c14055d595de1036f74ecd6ce15f04a715e73f33bb6326c9cef01b6"}, - {file = "orjson-3.10.11-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdec57fe3b4bdebcc08a946db3365630332dbe575125ff3d80a3272ebd0ddafe"}, - {file = "orjson-3.10.11-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4eed32f33a0ea6ef36ccc1d37f8d17f28a1d6e8eefae5928f76aff8f1df85e67"}, - {file = "orjson-3.10.11-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80df27dd8697242b904f4ea54820e2d98d3f51f91e97e358fc13359721233e4b"}, - {file = "orjson-3.10.11-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:705f03cee0cb797256d54de6695ef219e5bc8c8120b6654dd460848d57a9af3d"}, - {file = "orjson-3.10.11-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03246774131701de8e7059b2e382597da43144a9a7400f178b2a32feafc54bd5"}, - {file = "orjson-3.10.11-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8b5759063a6c940a69c728ea70d7c33583991c6982915a839c8da5f957e0103a"}, - {file = "orjson-3.10.11-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:677f23e32491520eebb19c99bb34675daf5410c449c13416f7f0d93e2cf5f981"}, - {file = "orjson-3.10.11-cp311-none-win32.whl", hash = "sha256:a11225d7b30468dcb099498296ffac36b4673a8398ca30fdaec1e6c20df6aa55"}, - {file = "orjson-3.10.11-cp311-none-win_amd64.whl", hash = "sha256:df8c677df2f9f385fcc85ab859704045fa88d4668bc9991a527c86e710392bec"}, - {file = "orjson-3.10.11-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:360a4e2c0943da7c21505e47cf6bd725588962ff1d739b99b14e2f7f3545ba51"}, - {file = "orjson-3.10.11-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:496e2cb45de21c369079ef2d662670a4892c81573bcc143c4205cae98282ba97"}, - {file = "orjson-3.10.11-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7dfa8db55c9792d53c5952900c6a919cfa377b4f4534c7a786484a6a4a350c19"}, - {file = "orjson-3.10.11-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:51f3382415747e0dbda9dade6f1e1a01a9d37f630d8c9049a8ed0e385b7a90c0"}, - {file = "orjson-3.10.11-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f35a1b9f50a219f470e0e497ca30b285c9f34948d3c8160d5ad3a755d9299433"}, - {file = "orjson-3.10.11-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2f3b7c5803138e67028dde33450e054c87e0703afbe730c105f1fcd873496d5"}, - {file = "orjson-3.10.11-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f91d9eb554310472bd09f5347950b24442600594c2edc1421403d7610a0998fd"}, - {file = "orjson-3.10.11-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dfbb2d460a855c9744bbc8e36f9c3a997c4b27d842f3d5559ed54326e6911f9b"}, - {file = "orjson-3.10.11-cp312-none-win32.whl", hash = "sha256:d4a62c49c506d4d73f59514986cadebb7e8d186ad510c518f439176cf8d5359d"}, - {file = "orjson-3.10.11-cp312-none-win_amd64.whl", hash = "sha256:f1eec3421a558ff7a9b010a6c7effcfa0ade65327a71bb9b02a1c3b77a247284"}, - {file = "orjson-3.10.11-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:c46294faa4e4d0eb73ab68f1a794d2cbf7bab33b1dda2ac2959ffb7c61591899"}, - {file = "orjson-3.10.11-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52e5834d7d6e58a36846e059d00559cb9ed20410664f3ad156cd2cc239a11230"}, - {file = "orjson-3.10.11-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2fc947e5350fdce548bfc94f434e8760d5cafa97fb9c495d2fef6757aa02ec0"}, - {file = "orjson-3.10.11-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0efabbf839388a1dab5b72b5d3baedbd6039ac83f3b55736eb9934ea5494d258"}, - {file = "orjson-3.10.11-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a3f29634260708c200c4fe148e42b4aae97d7b9fee417fbdd74f8cfc265f15b0"}, - {file = "orjson-3.10.11-cp313-none-win32.whl", hash = "sha256:1a1222ffcee8a09476bbdd5d4f6f33d06d0d6642df2a3d78b7a195ca880d669b"}, - {file = "orjson-3.10.11-cp313-none-win_amd64.whl", hash = "sha256:bc274ac261cc69260913b2d1610760e55d3c0801bb3457ba7b9004420b6b4270"}, - {file = "orjson-3.10.11-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:19b3763e8bbf8ad797df6b6b5e0fc7c843ec2e2fc0621398534e0c6400098f87"}, - {file = "orjson-3.10.11-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1be83a13312e5e58d633580c5eb8d0495ae61f180da2722f20562974188af205"}, - {file = "orjson-3.10.11-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:afacfd1ab81f46dedd7f6001b6d4e8de23396e4884cd3c3436bd05defb1a6446"}, - {file = "orjson-3.10.11-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cb4d0bea56bba596723d73f074c420aec3b2e5d7d30698bc56e6048066bd560c"}, - {file = "orjson-3.10.11-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96ed1de70fcb15d5fed529a656df29f768187628727ee2788344e8a51e1c1350"}, - {file = "orjson-3.10.11-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4bfb30c891b530f3f80e801e3ad82ef150b964e5c38e1fb8482441c69c35c61c"}, - {file = "orjson-3.10.11-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d496c74fc2b61341e3cefda7eec21b7854c5f672ee350bc55d9a4997a8a95204"}, - {file = "orjson-3.10.11-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:655a493bac606655db9a47fe94d3d84fc7f3ad766d894197c94ccf0c5408e7d3"}, - {file = "orjson-3.10.11-cp38-none-win32.whl", hash = "sha256:b9546b278c9fb5d45380f4809e11b4dd9844ca7aaf1134024503e134ed226161"}, - {file = "orjson-3.10.11-cp38-none-win_amd64.whl", hash = "sha256:b592597fe551d518f42c5a2eb07422eb475aa8cfdc8c51e6da7054b836b26782"}, - {file = "orjson-3.10.11-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:c95f2ecafe709b4e5c733b5e2768ac569bed308623c85806c395d9cca00e08af"}, - {file = "orjson-3.10.11-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:80c00d4acded0c51c98754fe8218cb49cb854f0f7eb39ea4641b7f71732d2cb7"}, - {file = "orjson-3.10.11-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:461311b693d3d0a060439aa669c74f3603264d4e7a08faa68c47ae5a863f352d"}, - {file = "orjson-3.10.11-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:52ca832f17d86a78cbab86cdc25f8c13756ebe182b6fc1a97d534051c18a08de"}, - {file = "orjson-3.10.11-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4c57ea78a753812f528178aa2f1c57da633754c91d2124cb28991dab4c79a54"}, - {file = "orjson-3.10.11-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7fcfc6f7ca046383fb954ba528587e0f9336828b568282b27579c49f8e16aad"}, - {file = "orjson-3.10.11-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:86b9dd983857970c29e4c71bb3e95ff085c07d3e83e7c46ebe959bac07ebd80b"}, - {file = "orjson-3.10.11-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:4d83f87582d223e54efb2242a79547611ba4ebae3af8bae1e80fa9a0af83bb7f"}, - {file = "orjson-3.10.11-cp39-none-win32.whl", hash = "sha256:9fd0ad1c129bc9beb1154c2655f177620b5beaf9a11e0d10bac63ef3fce96950"}, - {file = "orjson-3.10.11-cp39-none-win_amd64.whl", hash = "sha256:10f416b2a017c8bd17f325fb9dee1fb5cdd7a54e814284896b7c3f2763faa017"}, - {file = "orjson-3.10.11.tar.gz", hash = "sha256:e35b6d730de6384d5b2dab5fd23f0d76fae8bbc8c353c2f78210aa5fa4beb3ef"}, + {file = "orjson-3.10.12-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:ece01a7ec71d9940cc654c482907a6b65df27251255097629d0dea781f255c6d"}, + {file = "orjson-3.10.12-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c34ec9aebc04f11f4b978dd6caf697a2df2dd9b47d35aa4cc606cabcb9df69d7"}, + {file = "orjson-3.10.12-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fd6ec8658da3480939c79b9e9e27e0db31dffcd4ba69c334e98c9976ac29140e"}, + {file = "orjson-3.10.12-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f17e6baf4cf01534c9de8a16c0c611f3d94925d1701bf5f4aff17003677d8ced"}, + {file = "orjson-3.10.12-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6402ebb74a14ef96f94a868569f5dccf70d791de49feb73180eb3c6fda2ade56"}, + {file = "orjson-3.10.12-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0000758ae7c7853e0a4a6063f534c61656ebff644391e1f81698c1b2d2fc8cd2"}, + {file = "orjson-3.10.12-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:888442dcee99fd1e5bd37a4abb94930915ca6af4db50e23e746cdf4d1e63db13"}, + {file = "orjson-3.10.12-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c1f7a3ce79246aa0e92f5458d86c54f257fb5dfdc14a192651ba7ec2c00f8a05"}, + {file = "orjson-3.10.12-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:802a3935f45605c66fb4a586488a38af63cb37aaad1c1d94c982c40dcc452e85"}, + {file = "orjson-3.10.12-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:1da1ef0113a2be19bb6c557fb0ec2d79c92ebd2fed4cfb1b26bab93f021fb885"}, + {file = "orjson-3.10.12-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7a3273e99f367f137d5b3fecb5e9f45bcdbfac2a8b2f32fbc72129bbd48789c2"}, + {file = "orjson-3.10.12-cp310-none-win32.whl", hash = "sha256:475661bf249fd7907d9b0a2a2421b4e684355a77ceef85b8352439a9163418c3"}, + {file = "orjson-3.10.12-cp310-none-win_amd64.whl", hash = "sha256:87251dc1fb2b9e5ab91ce65d8f4caf21910d99ba8fb24b49fd0c118b2362d509"}, + {file = "orjson-3.10.12-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a734c62efa42e7df94926d70fe7d37621c783dea9f707a98cdea796964d4cf74"}, + {file = "orjson-3.10.12-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:750f8b27259d3409eda8350c2919a58b0cfcd2054ddc1bd317a643afc646ef23"}, + {file = "orjson-3.10.12-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bb52c22bfffe2857e7aa13b4622afd0dd9d16ea7cc65fd2bf318d3223b1b6252"}, + {file = "orjson-3.10.12-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:440d9a337ac8c199ff8251e100c62e9488924c92852362cd27af0e67308c16ef"}, + {file = "orjson-3.10.12-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9e15c06491c69997dfa067369baab3bf094ecb74be9912bdc4339972323f252"}, + {file = "orjson-3.10.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:362d204ad4b0b8724cf370d0cd917bb2dc913c394030da748a3bb632445ce7c4"}, + {file = "orjson-3.10.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2b57cbb4031153db37b41622eac67329c7810e5f480fda4cfd30542186f006ae"}, + {file = "orjson-3.10.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:165c89b53ef03ce0d7c59ca5c82fa65fe13ddf52eeb22e859e58c237d4e33b9b"}, + {file = "orjson-3.10.12-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:5dee91b8dfd54557c1a1596eb90bcd47dbcd26b0baaed919e6861f076583e9da"}, + {file = "orjson-3.10.12-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:77a4e1cfb72de6f905bdff061172adfb3caf7a4578ebf481d8f0530879476c07"}, + {file = "orjson-3.10.12-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:038d42c7bc0606443459b8fe2d1f121db474c49067d8d14c6a075bbea8bf14dd"}, + {file = "orjson-3.10.12-cp311-none-win32.whl", hash = "sha256:03b553c02ab39bed249bedd4abe37b2118324d1674e639b33fab3d1dafdf4d79"}, + {file = "orjson-3.10.12-cp311-none-win_amd64.whl", hash = "sha256:8b8713b9e46a45b2af6b96f559bfb13b1e02006f4242c156cbadef27800a55a8"}, + {file = "orjson-3.10.12-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:53206d72eb656ca5ac7d3a7141e83c5bbd3ac30d5eccfe019409177a57634b0d"}, + {file = "orjson-3.10.12-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac8010afc2150d417ebda810e8df08dd3f544e0dd2acab5370cfa6bcc0662f8f"}, + {file = "orjson-3.10.12-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed459b46012ae950dd2e17150e838ab08215421487371fa79d0eced8d1461d70"}, + {file = "orjson-3.10.12-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8dcb9673f108a93c1b52bfc51b0af422c2d08d4fc710ce9c839faad25020bb69"}, + {file = "orjson-3.10.12-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:22a51ae77680c5c4652ebc63a83d5255ac7d65582891d9424b566fb3b5375ee9"}, + {file = "orjson-3.10.12-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:910fdf2ac0637b9a77d1aad65f803bac414f0b06f720073438a7bd8906298192"}, + {file = "orjson-3.10.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:24ce85f7100160936bc2116c09d1a8492639418633119a2224114f67f63a4559"}, + {file = "orjson-3.10.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8a76ba5fc8dd9c913640292df27bff80a685bed3a3c990d59aa6ce24c352f8fc"}, + {file = "orjson-3.10.12-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ff70ef093895fd53f4055ca75f93f047e088d1430888ca1229393a7c0521100f"}, + {file = "orjson-3.10.12-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:f4244b7018b5753ecd10a6d324ec1f347da130c953a9c88432c7fbc8875d13be"}, + {file = "orjson-3.10.12-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:16135ccca03445f37921fa4b585cff9a58aa8d81ebcb27622e69bfadd220b32c"}, + {file = "orjson-3.10.12-cp312-none-win32.whl", hash = "sha256:2d879c81172d583e34153d524fcba5d4adafbab8349a7b9f16ae511c2cee8708"}, + {file = "orjson-3.10.12-cp312-none-win_amd64.whl", hash = "sha256:fc23f691fa0f5c140576b8c365bc942d577d861a9ee1142e4db468e4e17094fb"}, + {file = "orjson-3.10.12-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:47962841b2a8aa9a258b377f5188db31ba49af47d4003a32f55d6f8b19006543"}, + {file = "orjson-3.10.12-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6334730e2532e77b6054e87ca84f3072bee308a45a452ea0bffbbbc40a67e296"}, + {file = "orjson-3.10.12-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:accfe93f42713c899fdac2747e8d0d5c659592df2792888c6c5f829472e4f85e"}, + {file = "orjson-3.10.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a7974c490c014c48810d1dede6c754c3cc46598da758c25ca3b4001ac45b703f"}, + {file = "orjson-3.10.12-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:3f250ce7727b0b2682f834a3facff88e310f52f07a5dcfd852d99637d386e79e"}, + {file = "orjson-3.10.12-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f31422ff9486ae484f10ffc51b5ab2a60359e92d0716fcce1b3593d7bb8a9af6"}, + {file = "orjson-3.10.12-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5f29c5d282bb2d577c2a6bbde88d8fdcc4919c593f806aac50133f01b733846e"}, + {file = "orjson-3.10.12-cp313-none-win32.whl", hash = "sha256:f45653775f38f63dc0e6cd4f14323984c3149c05d6007b58cb154dd080ddc0dc"}, + {file = "orjson-3.10.12-cp313-none-win_amd64.whl", hash = "sha256:229994d0c376d5bdc91d92b3c9e6be2f1fbabd4cc1b59daae1443a46ee5e9825"}, + {file = "orjson-3.10.12-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:7d69af5b54617a5fac5c8e5ed0859eb798e2ce8913262eb522590239db6c6763"}, + {file = "orjson-3.10.12-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ed119ea7d2953365724a7059231a44830eb6bbb0cfead33fcbc562f5fd8f935"}, + {file = "orjson-3.10.12-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9c5fc1238ef197e7cad5c91415f524aaa51e004be5a9b35a1b8a84ade196f73f"}, + {file = "orjson-3.10.12-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:43509843990439b05f848539d6f6198d4ac86ff01dd024b2f9a795c0daeeab60"}, + {file = "orjson-3.10.12-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f72e27a62041cfb37a3de512247ece9f240a561e6c8662276beaf4d53d406db4"}, + {file = "orjson-3.10.12-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a904f9572092bb6742ab7c16c623f0cdccbad9eeb2d14d4aa06284867bddd31"}, + {file = "orjson-3.10.12-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:855c0833999ed5dc62f64552db26f9be767434917d8348d77bacaab84f787d7b"}, + {file = "orjson-3.10.12-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:897830244e2320f6184699f598df7fb9db9f5087d6f3f03666ae89d607e4f8ed"}, + {file = "orjson-3.10.12-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:0b32652eaa4a7539f6f04abc6243619c56f8530c53bf9b023e1269df5f7816dd"}, + {file = "orjson-3.10.12-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:36b4aa31e0f6a1aeeb6f8377769ca5d125db000f05c20e54163aef1d3fe8e833"}, + {file = "orjson-3.10.12-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:5535163054d6cbf2796f93e4f0dbc800f61914c0e3c4ed8499cf6ece22b4a3da"}, + {file = "orjson-3.10.12-cp38-none-win32.whl", hash = "sha256:90a5551f6f5a5fa07010bf3d0b4ca2de21adafbbc0af6cb700b63cd767266cb9"}, + {file = "orjson-3.10.12-cp38-none-win_amd64.whl", hash = "sha256:703a2fb35a06cdd45adf5d733cf613cbc0cb3ae57643472b16bc22d325b5fb6c"}, + {file = "orjson-3.10.12-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:f29de3ef71a42a5822765def1febfb36e0859d33abf5c2ad240acad5c6a1b78d"}, + {file = "orjson-3.10.12-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:de365a42acc65d74953f05e4772c974dad6c51cfc13c3240899f534d611be967"}, + {file = "orjson-3.10.12-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:91a5a0158648a67ff0004cb0df5df7dcc55bfc9ca154d9c01597a23ad54c8d0c"}, + {file = "orjson-3.10.12-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c47ce6b8d90fe9646a25b6fb52284a14ff215c9595914af63a5933a49972ce36"}, + {file = "orjson-3.10.12-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0eee4c2c5bfb5c1b47a5db80d2ac7aaa7e938956ae88089f098aff2c0f35d5d8"}, + {file = "orjson-3.10.12-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:35d3081bbe8b86587eb5c98a73b97f13d8f9fea685cf91a579beddacc0d10566"}, + {file = "orjson-3.10.12-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:73c23a6e90383884068bc2dba83d5222c9fcc3b99a0ed2411d38150734236755"}, + {file = "orjson-3.10.12-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5472be7dc3269b4b52acba1433dac239215366f89dc1d8d0e64029abac4e714e"}, + {file = "orjson-3.10.12-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:7319cda750fca96ae5973efb31b17d97a5c5225ae0bc79bf5bf84df9e1ec2ab6"}, + {file = "orjson-3.10.12-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:74d5ca5a255bf20b8def6a2b96b1e18ad37b4a122d59b154c458ee9494377f80"}, + {file = "orjson-3.10.12-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:ff31d22ecc5fb85ef62c7d4afe8301d10c558d00dd24274d4bbe464380d3cd69"}, + {file = "orjson-3.10.12-cp39-none-win32.whl", hash = "sha256:c22c3ea6fba91d84fcb4cda30e64aff548fcf0c44c876e681f47d61d24b12e6b"}, + {file = "orjson-3.10.12-cp39-none-win_amd64.whl", hash = "sha256:be604f60d45ace6b0b33dd990a66b4526f1a7a186ac411c942674625456ca548"}, + {file = "orjson-3.10.12.tar.gz", hash = "sha256:0a78bbda3aea0f9f079057ee1ee8a1ecf790d4f1af88dd67493c6b8ee52506ff"}, ] [[package]] @@ -557,18 +640,18 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "pydantic" -version = "2.10.0" +version = "2.10.3" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic-2.10.0-py3-none-any.whl", hash = "sha256:5e7807ba9201bdf61b1b58aa6eb690916c40a47acfb114b1b4fef3e7fd5b30fc"}, - {file = "pydantic-2.10.0.tar.gz", hash = "sha256:0aca0f045ff6e2f097f1fe89521115335f15049eeb8a7bef3dafe4b19a74e289"}, + {file = "pydantic-2.10.3-py3-none-any.whl", hash = "sha256:be04d85bbc7b65651c5f8e6b9976ed9c6f41782a55524cef079a34a0bb82144d"}, + {file = "pydantic-2.10.3.tar.gz", hash = "sha256:cb5ac360ce894ceacd69c403187900a02c4b20b693a9dd1d643e1effab9eadf9"}, ] [package.dependencies] annotated-types = ">=0.6.0" -pydantic-core = "2.27.0" +pydantic-core = "2.27.1" typing-extensions = ">=4.12.2" [package.extras] @@ -577,111 +660,111 @@ timezone = ["tzdata"] [[package]] name = "pydantic-core" -version = "2.27.0" +version = "2.27.1" description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic_core-2.27.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:cd2ac6b919f7fed71b17fe0b4603c092a4c9b5bae414817c9c81d3c22d1e1bcc"}, - {file = "pydantic_core-2.27.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e015833384ca3e1a0565a79f5d953b0629d9138021c27ad37c92a9fa1af7623c"}, - {file = "pydantic_core-2.27.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:db72e40628967f6dc572020d04b5f800d71264e0531c6da35097e73bdf38b003"}, - {file = "pydantic_core-2.27.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:df45c4073bed486ea2f18757057953afed8dd77add7276ff01bccb79982cf46c"}, - {file = "pydantic_core-2.27.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:836a4bfe0cc6d36dc9a9cc1a7b391265bf6ce9d1eb1eac62ac5139f5d8d9a6fa"}, - {file = "pydantic_core-2.27.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4bf1340ae507f6da6360b24179c2083857c8ca7644aab65807023cf35404ea8d"}, - {file = "pydantic_core-2.27.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ab325fc86fbc077284c8d7f996d904d30e97904a87d6fb303dce6b3de7ebba9"}, - {file = "pydantic_core-2.27.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1da0c98a85a6c6ed702d5556db3b09c91f9b0b78de37b7593e2de8d03238807a"}, - {file = "pydantic_core-2.27.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7b0202ebf2268954090209a84f9897345719e46a57c5f2c9b7b250ca0a9d3e63"}, - {file = "pydantic_core-2.27.0-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:35380671c3c921fe8adf31ad349dc6f7588b7e928dbe44e1093789734f607399"}, - {file = "pydantic_core-2.27.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b4c19525c3538fbc0bbda6229f9682fb8199ce9ac37395880e6952798e00373"}, - {file = "pydantic_core-2.27.0-cp310-none-win32.whl", hash = "sha256:333c840a1303d1474f491e7be0b718226c730a39ead0f7dab2c7e6a2f3855555"}, - {file = "pydantic_core-2.27.0-cp310-none-win_amd64.whl", hash = "sha256:99b2863c1365f43f74199c980a3d40f18a218fbe683dd64e470199db426c4d6a"}, - {file = "pydantic_core-2.27.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4523c4009c3f39d948e01962223c9f5538602e7087a628479b723c939fab262d"}, - {file = "pydantic_core-2.27.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:84af1cf7bfdcbc6fcf5a5f70cc9896205e0350306e4dd73d54b6a18894f79386"}, - {file = "pydantic_core-2.27.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e65466b31be1070b4a5b7dbfbd14b247884cb8e8b79c64fb0f36b472912dbaea"}, - {file = "pydantic_core-2.27.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a5c022bb0d453192426221605efc865373dde43b17822a264671c53b068ac20c"}, - {file = "pydantic_core-2.27.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6bb69bf3b6500f195c3deb69c1205ba8fc3cb21d1915f1f158a10d6b1ef29b6a"}, - {file = "pydantic_core-2.27.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0aa4d1b2eba9a325897308b3124014a142cdccb9f3e016f31d3ebee6b5ea5e75"}, - {file = "pydantic_core-2.27.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e96ca781e0c01e32115912ebdf7b3fb0780ce748b80d7d28a0802fa9fbaf44e"}, - {file = "pydantic_core-2.27.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b872c86d8d71827235c7077461c502feb2db3f87d9d6d5a9daa64287d75e4fa0"}, - {file = "pydantic_core-2.27.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:82e1ad4ca170e8af4c928b67cff731b6296e6a0a0981b97b2eb7c275cc4e15bd"}, - {file = "pydantic_core-2.27.0-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:eb40f828bc2f73f777d1eb8fee2e86cd9692a4518b63b6b5aa8af915dfd3207b"}, - {file = "pydantic_core-2.27.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9a8fbf506fde1529a1e3698198fe64bfbe2e0c09557bc6a7dcf872e7c01fec40"}, - {file = "pydantic_core-2.27.0-cp311-none-win32.whl", hash = "sha256:24f984fc7762ed5f806d9e8c4c77ea69fdb2afd987b4fd319ef06c87595a8c55"}, - {file = "pydantic_core-2.27.0-cp311-none-win_amd64.whl", hash = "sha256:68950bc08f9735306322bfc16a18391fcaac99ded2509e1cc41d03ccb6013cfe"}, - {file = "pydantic_core-2.27.0-cp311-none-win_arm64.whl", hash = "sha256:3eb8849445c26b41c5a474061032c53e14fe92a11a5db969f722a2716cd12206"}, - {file = "pydantic_core-2.27.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:8117839a9bdbba86e7f9df57018fe3b96cec934c3940b591b0fd3fbfb485864a"}, - {file = "pydantic_core-2.27.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a291d0b4243a259c8ea7e2b84eb9ccb76370e569298875a7c5e3e71baf49057a"}, - {file = "pydantic_core-2.27.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84e35afd9e10b2698e6f2f32256678cb23ca6c1568d02628033a837638b3ed12"}, - {file = "pydantic_core-2.27.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:58ab0d979c969983cdb97374698d847a4acffb217d543e172838864636ef10d9"}, - {file = "pydantic_core-2.27.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0d06b667e53320332be2bf6f9461f4a9b78092a079b8ce8634c9afaa7e10cd9f"}, - {file = "pydantic_core-2.27.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:78f841523729e43e3928a364ec46e2e3f80e6625a4f62aca5c345f3f626c6e8a"}, - {file = "pydantic_core-2.27.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:400bf470e4327e920883b51e255617dfe4496d4e80c3fea0b5a5d0bf2c404dd4"}, - {file = "pydantic_core-2.27.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:951e71da6c89d354572098bada5ba5b5dc3a9390c933af8a614e37755d3d1840"}, - {file = "pydantic_core-2.27.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:2a51ce96224eadd1845150b204389623c8e129fde5a67a84b972bd83a85c6c40"}, - {file = "pydantic_core-2.27.0-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:483c2213a609e7db2c592bbc015da58b6c75af7360ca3c981f178110d9787bcf"}, - {file = "pydantic_core-2.27.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:359e7951f04ad35111b5ddce184db3391442345d0ab073aa63a95eb8af25a5ef"}, - {file = "pydantic_core-2.27.0-cp312-none-win32.whl", hash = "sha256:ee7d9d5537daf6d5c74a83b38a638cc001b648096c1cae8ef695b0c919d9d379"}, - {file = "pydantic_core-2.27.0-cp312-none-win_amd64.whl", hash = "sha256:2be0ad541bb9f059954ccf8877a49ed73877f862529575ff3d54bf4223e4dd61"}, - {file = "pydantic_core-2.27.0-cp312-none-win_arm64.whl", hash = "sha256:6e19401742ed7b69e51d8e4df3c03ad5ec65a83b36244479fd70edde2828a5d9"}, - {file = "pydantic_core-2.27.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:5f2b19b8d6fca432cb3acf48cf5243a7bf512988029b6e6fd27e9e8c0a204d85"}, - {file = "pydantic_core-2.27.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c86679f443e7085ea55a7376462553996c688395d18ef3f0d3dbad7838f857a2"}, - {file = "pydantic_core-2.27.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:510b11e9c3b1a852876d1ccd8d5903684336d635214148637ceb27366c75a467"}, - {file = "pydantic_core-2.27.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb704155e73b833801c247f39d562229c0303f54770ca14fb1c053acb376cf10"}, - {file = "pydantic_core-2.27.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9ce048deb1e033e7a865ca384770bccc11d44179cf09e5193a535c4c2f497bdc"}, - {file = "pydantic_core-2.27.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:58560828ee0951bb125c6f2862fbc37f039996d19ceb6d8ff1905abf7da0bf3d"}, - {file = "pydantic_core-2.27.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:abb4785894936d7682635726613c44578c420a096729f1978cd061a7e72d5275"}, - {file = "pydantic_core-2.27.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2883b260f7a93235488699d39cbbd94fa7b175d3a8063fbfddd3e81ad9988cb2"}, - {file = "pydantic_core-2.27.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c6fcb3fa3855d583aa57b94cf146f7781d5d5bc06cb95cb3afece33d31aac39b"}, - {file = "pydantic_core-2.27.0-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:e851a051f7260e6d688267eb039c81f05f23a19431bd7dfa4bf5e3cb34c108cd"}, - {file = "pydantic_core-2.27.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:edb1bfd45227dec8d50bc7c7d86463cd8728bcc574f9b07de7369880de4626a3"}, - {file = "pydantic_core-2.27.0-cp313-none-win32.whl", hash = "sha256:678f66462058dd978702db17eb6a3633d634f7aa0deaea61e0a674152766d3fc"}, - {file = "pydantic_core-2.27.0-cp313-none-win_amd64.whl", hash = "sha256:d28ca7066d6cdd347a50d8b725dc10d9a1d6a1cce09836cf071ea6a2d4908be0"}, - {file = "pydantic_core-2.27.0-cp313-none-win_arm64.whl", hash = "sha256:6f4a53af9e81d757756508b57cae1cf28293f0f31b9fa2bfcb416cc7fb230f9d"}, - {file = "pydantic_core-2.27.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:e9f9feee7f334b72ceae46313333d002b56f325b5f04271b4ae2aadd9e993ae4"}, - {file = "pydantic_core-2.27.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:225bfff5d425c34e1fd562cef52d673579d59b967d9de06178850c4802af9039"}, - {file = "pydantic_core-2.27.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c921ad596ff1a82f9c692b0758c944355abc9f0de97a4c13ca60ffc6d8dc15d4"}, - {file = "pydantic_core-2.27.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6354e18a9be37bfa124d6b288a87fb30c673745806c92956f1a25e3ae6e76b96"}, - {file = "pydantic_core-2.27.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8ee4c2a75af9fe21269a4a0898c5425afb01af1f5d276063f57e2ae1bc64e191"}, - {file = "pydantic_core-2.27.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c91e3c04f5191fd3fb68764bddeaf02025492d5d9f23343b283870f6ace69708"}, - {file = "pydantic_core-2.27.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a6ebfac28fd51890a61df36ef202adbd77d00ee5aca4a3dadb3d9ed49cfb929"}, - {file = "pydantic_core-2.27.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:36aa167f69d8807ba7e341d67ea93e50fcaaf6bc433bb04939430fa3dab06f31"}, - {file = "pydantic_core-2.27.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:3e8d89c276234579cd3d095d5fa2a44eb10db9a218664a17b56363cddf226ff3"}, - {file = "pydantic_core-2.27.0-cp38-cp38-musllinux_1_1_armv7l.whl", hash = "sha256:5cc822ab90a70ea3a91e6aed3afac570b276b1278c6909b1d384f745bd09c714"}, - {file = "pydantic_core-2.27.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e15315691fe2253eb447503153acef4d7223dfe7e7702f9ed66539fcd0c43801"}, - {file = "pydantic_core-2.27.0-cp38-none-win32.whl", hash = "sha256:dfa5f5c0a4c8fced1422dc2ca7eefd872d5d13eb33cf324361dbf1dbfba0a9fe"}, - {file = "pydantic_core-2.27.0-cp38-none-win_amd64.whl", hash = "sha256:513cb14c0cc31a4dfd849a4674b20c46d87b364f997bbcb02282306f5e187abf"}, - {file = "pydantic_core-2.27.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:4148dc9184ab79e356dc00a4199dc0ee8647973332cb385fc29a7cced49b9f9c"}, - {file = "pydantic_core-2.27.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5fc72fbfebbf42c0856a824b8b0dc2b5cd2e4a896050281a21cfa6fed8879cb1"}, - {file = "pydantic_core-2.27.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:185ef205256cd8b38431205698531026979db89a79587725c1e55c59101d64e9"}, - {file = "pydantic_core-2.27.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:395e3e1148fa7809016231f8065f30bb0dc285a97b4dc4360cd86e17bab58af7"}, - {file = "pydantic_core-2.27.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:33d14369739c5d07e2e7102cdb0081a1fa46ed03215e07f097b34e020b83b1ae"}, - {file = "pydantic_core-2.27.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e7820bb0d65e3ce1e3e70b6708c2f66143f55912fa02f4b618d0f08b61575f12"}, - {file = "pydantic_core-2.27.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:43b61989068de9ce62296cde02beffabcadb65672207fc51e7af76dca75e6636"}, - {file = "pydantic_core-2.27.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:15e350efb67b855cd014c218716feea4986a149ed1f42a539edd271ee074a196"}, - {file = "pydantic_core-2.27.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:433689845288f9a1ee5714444e65957be26d30915f7745091ede4a83cfb2d7bb"}, - {file = "pydantic_core-2.27.0-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:3fd8bc2690e7c39eecdf9071b6a889ce7b22b72073863940edc2a0a23750ca90"}, - {file = "pydantic_core-2.27.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:884f1806609c2c66564082540cffc96868c5571c7c3cf3a783f63f2fb49bd3cd"}, - {file = "pydantic_core-2.27.0-cp39-none-win32.whl", hash = "sha256:bf37b72834e7239cf84d4a0b2c050e7f9e48bced97bad9bdf98d26b8eb72e846"}, - {file = "pydantic_core-2.27.0-cp39-none-win_amd64.whl", hash = "sha256:31a2cae5f059329f9cfe3d8d266d3da1543b60b60130d186d9b6a3c20a346361"}, - {file = "pydantic_core-2.27.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:4fb49cfdb53af5041aba909be00cccfb2c0d0a2e09281bf542371c5fd36ad04c"}, - {file = "pydantic_core-2.27.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:49633583eb7dc5cba61aaf7cdb2e9e662323ad394e543ee77af265736bcd3eaa"}, - {file = "pydantic_core-2.27.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:153017e3d6cd3ce979de06d84343ca424bb6092727375eba1968c8b4693c6ecb"}, - {file = "pydantic_core-2.27.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff63a92f6e249514ef35bc795de10745be0226eaea06eb48b4bbeaa0c8850a4a"}, - {file = "pydantic_core-2.27.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5982048129f40b082c2654de10c0f37c67a14f5ff9d37cf35be028ae982f26df"}, - {file = "pydantic_core-2.27.0-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:91bc66f878557313c2a6bcf396e7befcffe5ab4354cfe4427318968af31143c3"}, - {file = "pydantic_core-2.27.0-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:68ef5377eb582fa4343c9d0b57a5b094046d447b4c73dd9fbd9ffb216f829e7d"}, - {file = "pydantic_core-2.27.0-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:c5726eec789ee38f2c53b10b1821457b82274f81f4f746bb1e666d8741fcfadb"}, - {file = "pydantic_core-2.27.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c0c431e4be5c1a0c6654e0c31c661cd89e0ca956ef65305c3c3fd96f4e72ca39"}, - {file = "pydantic_core-2.27.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:8e21d927469d04b39386255bf00d0feedead16f6253dcc85e9e10ddebc334084"}, - {file = "pydantic_core-2.27.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:4b51f964fcbb02949fc546022e56cdb16cda457af485e9a3e8b78ac2ecf5d77e"}, - {file = "pydantic_core-2.27.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25a7fd4de38f7ff99a37e18fa0098c3140286451bc823d1746ba80cec5b433a1"}, - {file = "pydantic_core-2.27.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fda87808429c520a002a85d6e7cdadbf58231d60e96260976c5b8f9a12a8e13"}, - {file = "pydantic_core-2.27.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8a150392102c402c538190730fda06f3bce654fc498865579a9f2c1d2b425833"}, - {file = "pydantic_core-2.27.0-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:c9ed88b398ba7e3bad7bd64d66cc01dcde9cfcb7ec629a6fd78a82fa0b559d78"}, - {file = "pydantic_core-2.27.0-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:9fe94d9d2a2b4edd7a4b22adcd45814b1b59b03feb00e56deb2e89747aec7bfe"}, - {file = "pydantic_core-2.27.0-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:d8b5ee4ae9170e2775d495b81f414cc20268041c42571530513496ba61e94ba3"}, - {file = "pydantic_core-2.27.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:d29e235ce13c91902ef3efc3d883a677655b3908b1cbc73dee816e5e1f8f7739"}, - {file = "pydantic_core-2.27.0.tar.gz", hash = "sha256:f57783fbaf648205ac50ae7d646f27582fc706be3977e87c3c124e7a92407b10"}, + {file = "pydantic_core-2.27.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:71a5e35c75c021aaf400ac048dacc855f000bdfed91614b4a726f7432f1f3d6a"}, + {file = "pydantic_core-2.27.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f82d068a2d6ecfc6e054726080af69a6764a10015467d7d7b9f66d6ed5afa23b"}, + {file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:121ceb0e822f79163dd4699e4c54f5ad38b157084d97b34de8b232bcaad70278"}, + {file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4603137322c18eaf2e06a4495f426aa8d8388940f3c457e7548145011bb68e05"}, + {file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a33cd6ad9017bbeaa9ed78a2e0752c5e250eafb9534f308e7a5f7849b0b1bfb4"}, + {file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15cc53a3179ba0fcefe1e3ae50beb2784dede4003ad2dfd24f81bba4b23a454f"}, + {file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45d9c5eb9273aa50999ad6adc6be5e0ecea7e09dbd0d31bd0c65a55a2592ca08"}, + {file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8bf7b66ce12a2ac52d16f776b31d16d91033150266eb796967a7e4621707e4f6"}, + {file = "pydantic_core-2.27.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:655d7dd86f26cb15ce8a431036f66ce0318648f8853d709b4167786ec2fa4807"}, + {file = "pydantic_core-2.27.1-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:5556470f1a2157031e676f776c2bc20acd34c1990ca5f7e56f1ebf938b9ab57c"}, + {file = "pydantic_core-2.27.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f69ed81ab24d5a3bd93861c8c4436f54afdf8e8cc421562b0c7504cf3be58206"}, + {file = "pydantic_core-2.27.1-cp310-none-win32.whl", hash = "sha256:f5a823165e6d04ccea61a9f0576f345f8ce40ed533013580e087bd4d7442b52c"}, + {file = "pydantic_core-2.27.1-cp310-none-win_amd64.whl", hash = "sha256:57866a76e0b3823e0b56692d1a0bf722bffb324839bb5b7226a7dbd6c9a40b17"}, + {file = "pydantic_core-2.27.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:ac3b20653bdbe160febbea8aa6c079d3df19310d50ac314911ed8cc4eb7f8cb8"}, + {file = "pydantic_core-2.27.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a5a8e19d7c707c4cadb8c18f5f60c843052ae83c20fa7d44f41594c644a1d330"}, + {file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f7059ca8d64fea7f238994c97d91f75965216bcbe5f695bb44f354893f11d52"}, + {file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bed0f8a0eeea9fb72937ba118f9db0cb7e90773462af7962d382445f3005e5a4"}, + {file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a3cb37038123447cf0f3ea4c74751f6a9d7afef0eb71aa07bf5f652b5e6a132c"}, + {file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:84286494f6c5d05243456e04223d5a9417d7f443c3b76065e75001beb26f88de"}, + {file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acc07b2cfc5b835444b44a9956846b578d27beeacd4b52e45489e93276241025"}, + {file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4fefee876e07a6e9aad7a8c8c9f85b0cdbe7df52b8a9552307b09050f7512c7e"}, + {file = "pydantic_core-2.27.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:258c57abf1188926c774a4c94dd29237e77eda19462e5bb901d88adcab6af919"}, + {file = "pydantic_core-2.27.1-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:35c14ac45fcfdf7167ca76cc80b2001205a8d5d16d80524e13508371fb8cdd9c"}, + {file = "pydantic_core-2.27.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d1b26e1dff225c31897696cab7d4f0a315d4c0d9e8666dbffdb28216f3b17fdc"}, + {file = "pydantic_core-2.27.1-cp311-none-win32.whl", hash = "sha256:2cdf7d86886bc6982354862204ae3b2f7f96f21a3eb0ba5ca0ac42c7b38598b9"}, + {file = "pydantic_core-2.27.1-cp311-none-win_amd64.whl", hash = "sha256:3af385b0cee8df3746c3f406f38bcbfdc9041b5c2d5ce3e5fc6637256e60bbc5"}, + {file = "pydantic_core-2.27.1-cp311-none-win_arm64.whl", hash = "sha256:81f2ec23ddc1b476ff96563f2e8d723830b06dceae348ce02914a37cb4e74b89"}, + {file = "pydantic_core-2.27.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9cbd94fc661d2bab2bc702cddd2d3370bbdcc4cd0f8f57488a81bcce90c7a54f"}, + {file = "pydantic_core-2.27.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5f8c4718cd44ec1580e180cb739713ecda2bdee1341084c1467802a417fe0f02"}, + {file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:15aae984e46de8d376df515f00450d1522077254ef6b7ce189b38ecee7c9677c"}, + {file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1ba5e3963344ff25fc8c40da90f44b0afca8cfd89d12964feb79ac1411a260ac"}, + {file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:992cea5f4f3b29d6b4f7f1726ed8ee46c8331c6b4eed6db5b40134c6fe1768bb"}, + {file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0325336f348dbee6550d129b1627cb8f5351a9dc91aad141ffb96d4937bd9529"}, + {file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7597c07fbd11515f654d6ece3d0e4e5093edc30a436c63142d9a4b8e22f19c35"}, + {file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3bbd5d8cc692616d5ef6fbbbd50dbec142c7e6ad9beb66b78a96e9c16729b089"}, + {file = "pydantic_core-2.27.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:dc61505e73298a84a2f317255fcc72b710b72980f3a1f670447a21efc88f8381"}, + {file = "pydantic_core-2.27.1-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:e1f735dc43da318cad19b4173dd1ffce1d84aafd6c9b782b3abc04a0d5a6f5bb"}, + {file = "pydantic_core-2.27.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:f4e5658dbffe8843a0f12366a4c2d1c316dbe09bb4dfbdc9d2d9cd6031de8aae"}, + {file = "pydantic_core-2.27.1-cp312-none-win32.whl", hash = "sha256:672ebbe820bb37988c4d136eca2652ee114992d5d41c7e4858cdd90ea94ffe5c"}, + {file = "pydantic_core-2.27.1-cp312-none-win_amd64.whl", hash = "sha256:66ff044fd0bb1768688aecbe28b6190f6e799349221fb0de0e6f4048eca14c16"}, + {file = "pydantic_core-2.27.1-cp312-none-win_arm64.whl", hash = "sha256:9a3b0793b1bbfd4146304e23d90045f2a9b5fd5823aa682665fbdaf2a6c28f3e"}, + {file = "pydantic_core-2.27.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f216dbce0e60e4d03e0c4353c7023b202d95cbaeff12e5fd2e82ea0a66905073"}, + {file = "pydantic_core-2.27.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a2e02889071850bbfd36b56fd6bc98945e23670773bc7a76657e90e6b6603c08"}, + {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42b0e23f119b2b456d07ca91b307ae167cc3f6c846a7b169fca5326e32fdc6cf"}, + {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:764be71193f87d460a03f1f7385a82e226639732214b402f9aa61f0d025f0737"}, + {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1c00666a3bd2f84920a4e94434f5974d7bbc57e461318d6bb34ce9cdbbc1f6b2"}, + {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ccaa88b24eebc0f849ce0a4d09e8a408ec5a94afff395eb69baf868f5183107"}, + {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c65af9088ac534313e1963443d0ec360bb2b9cba6c2909478d22c2e363d98a51"}, + {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:206b5cf6f0c513baffaeae7bd817717140770c74528f3e4c3e1cec7871ddd61a"}, + {file = "pydantic_core-2.27.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:062f60e512fc7fff8b8a9d680ff0ddaaef0193dba9fa83e679c0c5f5fbd018bc"}, + {file = "pydantic_core-2.27.1-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:a0697803ed7d4af5e4c1adf1670af078f8fcab7a86350e969f454daf598c4960"}, + {file = "pydantic_core-2.27.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:58ca98a950171f3151c603aeea9303ef6c235f692fe555e883591103da709b23"}, + {file = "pydantic_core-2.27.1-cp313-none-win32.whl", hash = "sha256:8065914ff79f7eab1599bd80406681f0ad08f8e47c880f17b416c9f8f7a26d05"}, + {file = "pydantic_core-2.27.1-cp313-none-win_amd64.whl", hash = "sha256:ba630d5e3db74c79300d9a5bdaaf6200172b107f263c98a0539eeecb857b2337"}, + {file = "pydantic_core-2.27.1-cp313-none-win_arm64.whl", hash = "sha256:45cf8588c066860b623cd11c4ba687f8d7175d5f7ef65f7129df8a394c502de5"}, + {file = "pydantic_core-2.27.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:5897bec80a09b4084aee23f9b73a9477a46c3304ad1d2d07acca19723fb1de62"}, + {file = "pydantic_core-2.27.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d0165ab2914379bd56908c02294ed8405c252250668ebcb438a55494c69f44ab"}, + {file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b9af86e1d8e4cfc82c2022bfaa6f459381a50b94a29e95dcdda8442d6d83864"}, + {file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f6c8a66741c5f5447e047ab0ba7a1c61d1e95580d64bce852e3df1f895c4067"}, + {file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a42d6a8156ff78981f8aa56eb6394114e0dedb217cf8b729f438f643608cbcd"}, + {file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:64c65f40b4cd8b0e049a8edde07e38b476da7e3aaebe63287c899d2cff253fa5"}, + {file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdcf339322a3fae5cbd504edcefddd5a50d9ee00d968696846f089b4432cf78"}, + {file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bf99c8404f008750c846cb4ac4667b798a9f7de673ff719d705d9b2d6de49c5f"}, + {file = "pydantic_core-2.27.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8f1edcea27918d748c7e5e4d917297b2a0ab80cad10f86631e488b7cddf76a36"}, + {file = "pydantic_core-2.27.1-cp38-cp38-musllinux_1_1_armv7l.whl", hash = "sha256:159cac0a3d096f79ab6a44d77a961917219707e2a130739c64d4dd46281f5c2a"}, + {file = "pydantic_core-2.27.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:029d9757eb621cc6e1848fa0b0310310de7301057f623985698ed7ebb014391b"}, + {file = "pydantic_core-2.27.1-cp38-none-win32.whl", hash = "sha256:a28af0695a45f7060e6f9b7092558a928a28553366519f64083c63a44f70e618"}, + {file = "pydantic_core-2.27.1-cp38-none-win_amd64.whl", hash = "sha256:2d4567c850905d5eaaed2f7a404e61012a51caf288292e016360aa2b96ff38d4"}, + {file = "pydantic_core-2.27.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:e9386266798d64eeb19dd3677051f5705bf873e98e15897ddb7d76f477131967"}, + {file = "pydantic_core-2.27.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4228b5b646caa73f119b1ae756216b59cc6e2267201c27d3912b592c5e323b60"}, + {file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b3dfe500de26c52abe0477dde16192ac39c98f05bf2d80e76102d394bd13854"}, + {file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:aee66be87825cdf72ac64cb03ad4c15ffef4143dbf5c113f64a5ff4f81477bf9"}, + {file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b748c44bb9f53031c8cbc99a8a061bc181c1000c60a30f55393b6e9c45cc5bd"}, + {file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ca038c7f6a0afd0b2448941b6ef9d5e1949e999f9e5517692eb6da58e9d44be"}, + {file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e0bd57539da59a3e4671b90a502da9a28c72322a4f17866ba3ac63a82c4498e"}, + {file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ac6c2c45c847bbf8f91930d88716a0fb924b51e0c6dad329b793d670ec5db792"}, + {file = "pydantic_core-2.27.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b94d4ba43739bbe8b0ce4262bcc3b7b9f31459ad120fb595627eaeb7f9b9ca01"}, + {file = "pydantic_core-2.27.1-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:00e6424f4b26fe82d44577b4c842d7df97c20be6439e8e685d0d715feceb9fb9"}, + {file = "pydantic_core-2.27.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:38de0a70160dd97540335b7ad3a74571b24f1dc3ed33f815f0880682e6880131"}, + {file = "pydantic_core-2.27.1-cp39-none-win32.whl", hash = "sha256:7ccebf51efc61634f6c2344da73e366c75e735960b5654b63d7e6f69a5885fa3"}, + {file = "pydantic_core-2.27.1-cp39-none-win_amd64.whl", hash = "sha256:a57847b090d7892f123726202b7daa20df6694cbd583b67a592e856bff603d6c"}, + {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3fa80ac2bd5856580e242dbc202db873c60a01b20309c8319b5c5986fbe53ce6"}, + {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d950caa237bb1954f1b8c9227b5065ba6875ac9771bb8ec790d956a699b78676"}, + {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e4216e64d203e39c62df627aa882f02a2438d18a5f21d7f721621f7a5d3611d"}, + {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02a3d637bd387c41d46b002f0e49c52642281edacd2740e5a42f7017feea3f2c"}, + {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:161c27ccce13b6b0c8689418da3885d3220ed2eae2ea5e9b2f7f3d48f1d52c27"}, + {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:19910754e4cc9c63bc1c7f6d73aa1cfee82f42007e407c0f413695c2f7ed777f"}, + {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:e173486019cc283dc9778315fa29a363579372fe67045e971e89b6365cc035ed"}, + {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:af52d26579b308921b73b956153066481f064875140ccd1dfd4e77db89dbb12f"}, + {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:981fb88516bd1ae8b0cbbd2034678a39dedc98752f264ac9bc5839d3923fa04c"}, + {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5fde892e6c697ce3e30c61b239330fc5d569a71fefd4eb6512fc6caec9dd9e2f"}, + {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:816f5aa087094099fff7edabb5e01cc370eb21aa1a1d44fe2d2aefdfb5599b31"}, + {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c10c309e18e443ddb108f0ef64e8729363adbfd92d6d57beec680f6261556f3"}, + {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98476c98b02c8e9b2eec76ac4156fd006628b1b2d0ef27e548ffa978393fd154"}, + {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c3027001c28434e7ca5a6e1e527487051136aa81803ac812be51802150d880dd"}, + {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:7699b1df36a48169cdebda7ab5a2bac265204003f153b4bd17276153d997670a"}, + {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:1c39b07d90be6b48968ddc8c19e7585052088fd7ec8d568bb31ff64c70ae3c97"}, + {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:46ccfe3032b3915586e469d4972973f893c0a2bb65669194a5bdea9bacc088c2"}, + {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:62ba45e21cf6571d7f716d903b5b7b6d2617e2d5d67c0923dc47b9d41369f840"}, + {file = "pydantic_core-2.27.1.tar.gz", hash = "sha256:62a763352879b84aa31058fc931884055fd75089cccbd9d58bb6afd01141b235"}, ] [package.dependencies] @@ -689,13 +772,13 @@ typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" [[package]] name = "pytest" -version = "8.3.3" +version = "8.3.4" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2"}, - {file = "pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181"}, + {file = "pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6"}, + {file = "pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761"}, ] [package.dependencies] @@ -711,22 +794,36 @@ dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments [[package]] name = "pytest-asyncio" -version = "0.23.8" +version = "0.24.0" description = "Pytest support for asyncio" optional = false python-versions = ">=3.8" files = [ - {file = "pytest_asyncio-0.23.8-py3-none-any.whl", hash = "sha256:50265d892689a5faefb84df80819d1ecef566eb3549cf915dfb33569359d1ce2"}, - {file = "pytest_asyncio-0.23.8.tar.gz", hash = "sha256:759b10b33a6dc61cce40a8bd5205e302978bbbcc00e279a8b61d9a6a3c82e4d3"}, + {file = "pytest_asyncio-0.24.0-py3-none-any.whl", hash = "sha256:a811296ed596b69bf0b6f3dc40f83bcaf341b155a269052d82efa2b25ac7037b"}, + {file = "pytest_asyncio-0.24.0.tar.gz", hash = "sha256:d081d828e576d85f875399194281e92bf8a68d60d72d1a2faf2feddb6c46b276"}, ] [package.dependencies] -pytest = ">=7.0.0,<9" +pytest = ">=8.2,<9" [package.extras] docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"] testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"] +[[package]] +name = "pytest-socket" +version = "0.7.0" +description = "Pytest Plugin to disable socket calls during tests" +optional = false +python-versions = ">=3.8,<4.0" +files = [ + {file = "pytest_socket-0.7.0-py3-none-any.whl", hash = "sha256:7e0f4642177d55d317bbd58fc68c6bd9048d6eadb2d46a89307fa9221336ce45"}, + {file = "pytest_socket-0.7.0.tar.gz", hash = "sha256:71ab048cbbcb085c15a4423b73b619a8b35d6a307f46f78ea46be51b1b7e11b3"}, +] + +[package.dependencies] +pytest = ">=6.2.5" + [[package]] name = "pyyaml" version = "6.0.2" @@ -826,29 +923,29 @@ requests = ">=2.0.1,<3.0.0" [[package]] name = "ruff" -version = "0.7.4" +version = "0.8.2" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.7.4-py3-none-linux_armv6l.whl", hash = "sha256:a4919925e7684a3f18e18243cd6bea7cfb8e968a6eaa8437971f681b7ec51478"}, - {file = "ruff-0.7.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:cfb365c135b830778dda8c04fb7d4280ed0b984e1aec27f574445231e20d6c63"}, - {file = "ruff-0.7.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:63a569b36bc66fbadec5beaa539dd81e0527cb258b94e29e0531ce41bacc1f20"}, - {file = "ruff-0.7.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d06218747d361d06fd2fdac734e7fa92df36df93035db3dc2ad7aa9852cb109"}, - {file = "ruff-0.7.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e0cea28d0944f74ebc33e9f934238f15c758841f9f5edd180b5315c203293452"}, - {file = "ruff-0.7.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:80094ecd4793c68b2571b128f91754d60f692d64bc0d7272ec9197fdd09bf9ea"}, - {file = "ruff-0.7.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:997512325c6620d1c4c2b15db49ef59543ef9cd0f4aa8065ec2ae5103cedc7e7"}, - {file = "ruff-0.7.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00b4cf3a6b5fad6d1a66e7574d78956bbd09abfd6c8a997798f01f5da3d46a05"}, - {file = "ruff-0.7.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7dbdc7d8274e1422722933d1edddfdc65b4336abf0b16dfcb9dedd6e6a517d06"}, - {file = "ruff-0.7.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e92dfb5f00eaedb1501b2f906ccabfd67b2355bdf117fea9719fc99ac2145bc"}, - {file = "ruff-0.7.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:3bd726099f277d735dc38900b6a8d6cf070f80828877941983a57bca1cd92172"}, - {file = "ruff-0.7.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:2e32829c429dd081ee5ba39aef436603e5b22335c3d3fff013cd585806a6486a"}, - {file = "ruff-0.7.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:662a63b4971807623f6f90c1fb664613f67cc182dc4d991471c23c541fee62dd"}, - {file = "ruff-0.7.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:876f5e09eaae3eb76814c1d3b68879891d6fde4824c015d48e7a7da4cf066a3a"}, - {file = "ruff-0.7.4-py3-none-win32.whl", hash = "sha256:75c53f54904be42dd52a548728a5b572344b50d9b2873d13a3f8c5e3b91f5cac"}, - {file = "ruff-0.7.4-py3-none-win_amd64.whl", hash = "sha256:745775c7b39f914238ed1f1b0bebed0b9155a17cd8bc0b08d3c87e4703b990d6"}, - {file = "ruff-0.7.4-py3-none-win_arm64.whl", hash = "sha256:11bff065102c3ae9d3ea4dc9ecdfe5a5171349cdd0787c1fc64761212fc9cf1f"}, - {file = "ruff-0.7.4.tar.gz", hash = "sha256:cd12e35031f5af6b9b93715d8c4f40360070b2041f81273d0527683d5708fce2"}, + {file = "ruff-0.8.2-py3-none-linux_armv6l.whl", hash = "sha256:c49ab4da37e7c457105aadfd2725e24305ff9bc908487a9bf8d548c6dad8bb3d"}, + {file = "ruff-0.8.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ec016beb69ac16be416c435828be702ee694c0d722505f9c1f35e1b9c0cc1bf5"}, + {file = "ruff-0.8.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:f05cdf8d050b30e2ba55c9b09330b51f9f97d36d4673213679b965d25a785f3c"}, + {file = "ruff-0.8.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:60f578c11feb1d3d257b2fb043ddb47501ab4816e7e221fbb0077f0d5d4e7b6f"}, + {file = "ruff-0.8.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cbd5cf9b0ae8f30eebc7b360171bd50f59ab29d39f06a670b3e4501a36ba5897"}, + {file = "ruff-0.8.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b402ddee3d777683de60ff76da801fa7e5e8a71038f57ee53e903afbcefdaa58"}, + {file = "ruff-0.8.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:705832cd7d85605cb7858d8a13d75993c8f3ef1397b0831289109e953d833d29"}, + {file = "ruff-0.8.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:32096b41aaf7a5cc095fa45b4167b890e4c8d3fd217603f3634c92a541de7248"}, + {file = "ruff-0.8.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e769083da9439508833cfc7c23e351e1809e67f47c50248250ce1ac52c21fb93"}, + {file = "ruff-0.8.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fe716592ae8a376c2673fdfc1f5c0c193a6d0411f90a496863c99cd9e2ae25d"}, + {file = "ruff-0.8.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:81c148825277e737493242b44c5388a300584d73d5774defa9245aaef55448b0"}, + {file = "ruff-0.8.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d261d7850c8367704874847d95febc698a950bf061c9475d4a8b7689adc4f7fa"}, + {file = "ruff-0.8.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:1ca4e3a87496dc07d2427b7dd7ffa88a1e597c28dad65ae6433ecb9f2e4f022f"}, + {file = "ruff-0.8.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:729850feed82ef2440aa27946ab39c18cb4a8889c1128a6d589ffa028ddcfc22"}, + {file = "ruff-0.8.2-py3-none-win32.whl", hash = "sha256:ac42caaa0411d6a7d9594363294416e0e48fc1279e1b0e948391695db2b3d5b1"}, + {file = "ruff-0.8.2-py3-none-win_amd64.whl", hash = "sha256:2aae99ec70abf43372612a838d97bfe77d45146254568d94926e8ed5bbb409ea"}, + {file = "ruff-0.8.2-py3-none-win_arm64.whl", hash = "sha256:fb88e2a506b70cfbc2de6fae6681c4f944f7dd5f2fe87233a7233d888bad73e8"}, + {file = "ruff-0.8.2.tar.gz", hash = "sha256:b84f4f414dda8ac7f75075c1fa0b905ac0ff25361f42e6d5da681a465e0f78e5"}, ] [[package]] @@ -864,13 +961,13 @@ files = [ [[package]] name = "syrupy" -version = "4.7.2" +version = "4.8.0" description = "Pytest Snapshot Test Utility" optional = false python-versions = ">=3.8.1" files = [ - {file = "syrupy-4.7.2-py3-none-any.whl", hash = "sha256:eae7ba6be5aed190237caa93be288e97ca1eec5ca58760e4818972a10c4acc64"}, - {file = "syrupy-4.7.2.tar.gz", hash = "sha256:ea45e099f242de1bb53018c238f408a5bb6c82007bc687aefcbeaa0e1c2e935a"}, + {file = "syrupy-4.8.0-py3-none-any.whl", hash = "sha256:544f4ec6306f4b1c460fdab48fd60b2c7fe54a6c0a8243aeea15f9ad9c638c3f"}, + {file = "syrupy-4.8.0.tar.gz", hash = "sha256:648f0e9303aaa8387c8365d7314784c09a6bab0a407455c6a01d6a4f5c6a8ede"}, ] [package.dependencies] @@ -893,13 +990,43 @@ test = ["pytest", "tornado (>=4.5)", "typeguard"] [[package]] name = "tomli" -version = "2.1.0" +version = "2.2.1" description = "A lil' TOML parser" optional = false python-versions = ">=3.8" files = [ - {file = "tomli-2.1.0-py3-none-any.whl", hash = "sha256:a5c57c3d1c56f5ccdf89f6523458f60ef716e210fc47c4cfb188c5ba473e0391"}, - {file = "tomli-2.1.0.tar.gz", hash = "sha256:3f646cae2aec94e17d04973e4249548320197cfabdf130015d023de4b74d8ab8"}, + {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, + {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"}, + {file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"}, + {file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"}, + {file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"}, + {file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"}, + {file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"}, + {file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"}, + {file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"}, + {file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"}, + {file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"}, + {file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"}, + {file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"}, + {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"}, ] [[package]] @@ -933,4 +1060,4 @@ zstd = ["zstandard (>=0.18.0)"] [metadata] lock-version = "2.0" python-versions = ">=3.9,<4.0" -content-hash = "f92550d052b48eb60c41c6baa12c158391f7c1e5453b824ac212aa1e75260b1f" +content-hash = "c7788100e716b6bb0a93ff547aaa451f70b435b0c0405c7a06581ddb7fac6f20" diff --git a/libs/standard-tests/pyproject.toml b/libs/standard-tests/pyproject.toml index cb15fa4968312..2a44bc3b7bf83 100644 --- a/libs/standard-tests/pyproject.toml +++ b/libs/standard-tests/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api" [tool.poetry] name = "langchain-tests" -version = "0.3.4" +version = "0.3.7" description = "Standard tests for LangChain implementations" authors = ["Erick Friis "] readme = "README.md" @@ -19,10 +19,20 @@ disallow_untyped_defs = "True" [tool.poetry.dependencies] python = ">=3.9,<4.0" -langchain-core = "^0.3.19" +langchain-core = "^0.3.22" pytest = ">=7,<9" -httpx = "^0.27.0" +pytest-asyncio = ">=0.20,<1" +httpx = ">=0.25.0,<1" syrupy = "^4" +pytest-socket = ">=0.6.0,<1" + +[[tool.poetry.dependencies.numpy]] +version = "^1.24.0" +python = "<3.12" + +[[tool.poetry.dependencies.numpy]] +version = ">=1.26.2,<3" +python = ">=3.12" [tool.ruff.lint] select = ["E", "F", "I", "T201"] @@ -52,15 +62,6 @@ optional = true optional = true [tool.poetry.group.test.dependencies] -pytest-asyncio = "^0.23.7" - -[[tool.poetry.group.test.dependencies.numpy]] -version = "^1.24.0" -python = "<3.12" - -[[tool.poetry.group.test.dependencies.numpy]] -version = "^1.26.0" -python = ">=3.12" [tool.poetry.group.test_integration.dependencies] diff --git a/libs/standard-tests/tests/unit_tests/test_basic_retriever.py b/libs/standard-tests/tests/unit_tests/test_basic_retriever.py index af5d598c722f2..fb7999a09fb5c 100644 --- a/libs/standard-tests/tests/unit_tests/test_basic_retriever.py +++ b/libs/standard-tests/tests/unit_tests/test_basic_retriever.py @@ -1,6 +1,5 @@ from typing import Any, Type -from langchain_core.callbacks import CallbackManagerForRetrieverRun from langchain_core.documents import Document from langchain_core.retrievers import BaseRetriever @@ -11,9 +10,7 @@ class ParrotRetriever(BaseRetriever): parrot_name: str k: int = 3 - def _get_relevant_documents( - self, query: str, *, run_manager: CallbackManagerForRetrieverRun, **kwargs: Any - ) -> list[Document]: + def _get_relevant_documents(self, query: str, **kwargs: Any) -> list[Document]: k = kwargs.get("k", self.k) return [Document(page_content=f"{self.parrot_name} says: {query}")] * k diff --git a/libs/standard-tests/tests/unit_tests/test_embeddings.py b/libs/standard-tests/tests/unit_tests/test_embeddings.py new file mode 100644 index 0000000000000..d2a551deccafe --- /dev/null +++ b/libs/standard-tests/tests/unit_tests/test_embeddings.py @@ -0,0 +1,26 @@ +from typing import Type + +from langchain_core.embeddings import DeterministicFakeEmbedding, Embeddings + +from langchain_tests.integration_tests import EmbeddingsIntegrationTests +from langchain_tests.unit_tests import EmbeddingsUnitTests + + +class TestFakeEmbeddingsUnit(EmbeddingsUnitTests): + @property + def embeddings_class(self) -> Type[Embeddings]: + return DeterministicFakeEmbedding + + @property + def embedding_model_params(self) -> dict: + return {"size": 6} # embedding dimension + + +class TestFakeEmbeddingsIntegration(EmbeddingsIntegrationTests): + @property + def embeddings_class(self) -> Type[Embeddings]: + return DeterministicFakeEmbedding + + @property + def embedding_model_params(self) -> dict: + return {"size": 6} diff --git a/libs/standard-tests/tests/unit_tests/test_in_memory_vectorstore.py b/libs/standard-tests/tests/unit_tests/test_in_memory_vectorstore.py index 8a3bf5d0a32b2..53b9dd1a9c8d4 100644 --- a/libs/standard-tests/tests/unit_tests/test_in_memory_vectorstore.py +++ b/libs/standard-tests/tests/unit_tests/test_in_memory_vectorstore.py @@ -4,21 +4,11 @@ VectorStore, ) -from langchain_tests.integration_tests.vectorstores import ( - AsyncReadWriteTestSuite, - ReadWriteTestSuite, -) +from langchain_tests.integration_tests.vectorstores import VectorStoreIntegrationTests -class TestInMemoryVectorStore(ReadWriteTestSuite): +class TestInMemoryVectorStore(VectorStoreIntegrationTests): @pytest.fixture def vectorstore(self) -> VectorStore: embeddings = self.get_embeddings() return InMemoryVectorStore(embedding=embeddings) - - -class TestAsyncInMemoryVectorStore(AsyncReadWriteTestSuite): - @pytest.fixture - async def vectorstore(self) -> VectorStore: - embeddings = self.get_embeddings() - return InMemoryVectorStore(embedding=embeddings) diff --git a/libs/text-splitters/langchain_text_splitters/__init__.py b/libs/text-splitters/langchain_text_splitters/__init__.py index 58ad7b0e4c585..65af087fdd85f 100644 --- a/libs/text-splitters/langchain_text_splitters/__init__.py +++ b/libs/text-splitters/langchain_text_splitters/__init__.py @@ -1,6 +1,5 @@ """**Text Splitters** are classes for splitting text. - **Class hierarchy:** .. code-block:: diff --git a/libs/text-splitters/langchain_text_splitters/base.py b/libs/text-splitters/langchain_text_splitters/base.py index 0e0a49c182da7..10dd6903ba172 100644 --- a/libs/text-splitters/langchain_text_splitters/base.py +++ b/libs/text-splitters/langchain_text_splitters/base.py @@ -249,6 +249,21 @@ def __init__( self._disallowed_special = disallowed_special def split_text(self, text: str) -> List[str]: + """Splits the input text into smaller chunks based on tokenization. + + This method uses a custom tokenizer configuration to encode the input text + into tokens, processes the tokens in chunks of a specified size with overlap, + and decodes them back into text chunks. The splitting is performed using the + `split_text_on_tokens` function. + + Args: + text (str): The input text to be split into smaller chunks. + + Returns: + List[str]: A list of text chunks, where each chunk is derived from a portion + of the input text based on the tokenization and chunking rules. + """ + def _encode(_text: str) -> List[int]: return self._tokenizer.encode( _text, diff --git a/libs/text-splitters/langchain_text_splitters/character.py b/libs/text-splitters/langchain_text_splitters/character.py index f65c38869d394..a2918bd27f0ac 100644 --- a/libs/text-splitters/langchain_text_splitters/character.py +++ b/libs/text-splitters/langchain_text_splitters/character.py @@ -115,17 +115,45 @@ def _split_text(self, text: str, separators: List[str]) -> List[str]: return final_chunks def split_text(self, text: str) -> List[str]: + """Split the input text into smaller chunks based on predefined separators. + + Args: + text (str): The input text to be split. + + Returns: + List[str]: A list of text chunks obtained after splitting. + """ return self._split_text(text, self._separators) @classmethod def from_language( cls, language: Language, **kwargs: Any ) -> RecursiveCharacterTextSplitter: + """Return an instance of this class based on a specific language. + + This method initializes the text splitter with language-specific separators. + + Args: + language (Language): The language to configure the text splitter for. + **kwargs (Any): Additional keyword arguments to customize the splitter. + + Returns: + RecursiveCharacterTextSplitter: An instance of the text splitter configured + for the specified language. + """ separators = cls.get_separators_for_language(language) return cls(separators=separators, is_separator_regex=True, **kwargs) @staticmethod def get_separators_for_language(language: Language) -> List[str]: + """Retrieve a list of separators specific to the given language. + + Args: + language (Language): The language for which to get the separators. + + Returns: + List[str]: A list of separators appropriate for the specified language. + """ if language == Language.C or language == Language.CPP: return [ # Split along class definitions diff --git a/libs/text-splitters/langchain_text_splitters/html.py b/libs/text-splitters/langchain_text_splitters/html.py index cdbea7f724b53..241c0981f58f5 100644 --- a/libs/text-splitters/langchain_text_splitters/html.py +++ b/libs/text-splitters/langchain_text_splitters/html.py @@ -21,8 +21,8 @@ class ElementType(TypedDict): class HTMLHeaderTextSplitter: - """ - Splitting HTML files based on specified headers. + """Splitting HTML files based on specified headers. + Requires lxml package. """ @@ -46,7 +46,7 @@ def __init__( def aggregate_elements_to_chunks( self, elements: List[ElementType] ) -> List[Document]: - """Combine elements with common metadata into chunks + """Combine elements with common metadata into chunks. Args: elements: HTML element content with associated identifying info and metadata @@ -72,7 +72,7 @@ def aggregate_elements_to_chunks( ] def split_text_from_url(self, url: str, **kwargs: Any) -> List[Document]: - """Split HTML from web URL + """Split HTML from web URL. Args: url: web URL @@ -83,7 +83,7 @@ def split_text_from_url(self, url: str, **kwargs: Any) -> List[Document]: return self.split_text_from_file(BytesIO(r.content)) def split_text(self, text: str) -> List[Document]: - """Split HTML text string + """Split HTML text string. Args: text: HTML text @@ -91,7 +91,7 @@ def split_text(self, text: str) -> List[Document]: return self.split_text_from_file(StringIO(text)) def split_text_from_file(self, file: Any) -> List[Document]: - """Split HTML file + """Split HTML file. Args: file: HTML file @@ -166,8 +166,8 @@ def split_text_from_file(self, file: Any) -> List[Document]: class HTMLSectionSplitter: - """ - Splitting HTML files based on specified tag and font sizes. + """Splitting HTML files based on specified tag and font sizes. + Requires lxml package. """ @@ -186,6 +186,8 @@ def __init__( xslt_path: path to xslt file for document transformation. Uses a default if not passed. Needed for html contents that using different format and layouts. + **kwargs (Any): Additional optional arguments for customizations. + """ self.headers_to_split_on = dict(headers_to_split_on) @@ -210,7 +212,7 @@ def split_documents(self, documents: Iterable[Document]) -> List[Document]: return text_splitter.split_documents(results) def split_text(self, text: str) -> List[Document]: - """Split HTML text string + """Split HTML text string. Args: text: HTML text @@ -236,6 +238,23 @@ def create_documents( return documents def split_html_by_headers(self, html_doc: str) -> List[Dict[str, Optional[str]]]: + """Split an HTML document into sections based on specified header tags. + + This method uses BeautifulSoup to parse the HTML content and divides it into + sections based on headers defined in `headers_to_split_on`. Each section + contains the header text, content under the header, and the tag name. + + Args: + html_doc (str): The HTML document to be split into sections. + + Returns: + List[Dict[str, Optional[str]]]: A list of dictionaries representing + sections. + Each dictionary contains: + - 'header': The header text or a default title for the first section. + - 'content': The content under the header. + - 'tag_name': The name of the header tag (e.g., "h1", "h2"). + """ try: from bs4 import BeautifulSoup, PageElement # type: ignore[import-untyped] except ImportError as e: @@ -259,7 +278,7 @@ def split_html_by_headers(self, html_doc: str) -> List[Dict[str, Optional[str]]] section_content: List = [] else: current_header = header_element.text.strip() - current_header_tag = header_element.name + current_header_tag = header_element.name # type: ignore[attr-defined] section_content = [] for element in header_element.next_elements: if i + 1 < len(headers) and element == headers[i + 1]: @@ -280,6 +299,18 @@ def split_html_by_headers(self, html_doc: str) -> List[Dict[str, Optional[str]]] return sections def convert_possible_tags_to_header(self, html_content: str) -> str: + """Convert specific HTML tags to headers using an XSLT transformation. + + This method uses an XSLT file to transform the HTML content, converting + certain tags into headers for easier parsing. If no XSLT path is provided, + the HTML content is returned unchanged. + + Args: + html_content (str): The HTML content to be transformed. + + Returns: + str: The transformed HTML content as a string. + """ if self.xslt_path is None: return html_content @@ -299,7 +330,7 @@ def convert_possible_tags_to_header(self, html_content: str) -> str: return str(result) def split_text_from_file(self, file: Any) -> List[Document]: - """Split HTML file + """Split HTML file. Args: file: HTML file diff --git a/libs/text-splitters/langchain_text_splitters/json.py b/libs/text-splitters/langchain_text_splitters/json.py index c83d8b2a42880..c58174dd8b33a 100644 --- a/libs/text-splitters/langchain_text_splitters/json.py +++ b/libs/text-splitters/langchain_text_splitters/json.py @@ -8,9 +8,38 @@ class RecursiveJsonSplitter: + """Splits JSON data into smaller, structured chunks while preserving hierarchy. + + This class provides methods to split JSON data into smaller dictionaries or + JSON-formatted strings based on configurable maximum and minimum chunk sizes. + It supports nested JSON structures, optionally converts lists into dictionaries + for better chunking, and allows the creation of document objects for further use. + + Attributes: + max_chunk_size (int): The maximum size for each chunk. Defaults to 2000. + min_chunk_size (int): The minimum size for each chunk, derived from + `max_chunk_size` if not explicitly provided. + """ + def __init__( self, max_chunk_size: int = 2000, min_chunk_size: Optional[int] = None ): + """Initialize the chunk size configuration for text processing. + + This constructor sets up the maximum and minimum chunk sizes, ensuring that + the `min_chunk_size` defaults to a value slightly smaller than the + `max_chunk_size` if not explicitly provided. + + Args: + max_chunk_size (int): The maximum size for a chunk. Defaults to 2000. + min_chunk_size (Optional[int]): The minimum size for a chunk. If None, + defaults to the maximum chunk size minus 200, with a lower bound of 50. + + Attributes: + max_chunk_size (int): The configured maximum size for each chunk. + min_chunk_size (int): The configured minimum size for each chunk, derived + from `max_chunk_size` if not explicitly provided. + """ super().__init__() self.max_chunk_size = max_chunk_size self.min_chunk_size = ( @@ -51,9 +80,7 @@ def _json_split( current_path: Optional[List[str]] = None, chunks: Optional[List[Dict]] = None, ) -> List[Dict]: - """ - Split json into maximum size dictionaries while preserving structure. - """ + """Split json into maximum size dictionaries while preserving structure.""" current_path = current_path or [] chunks = chunks if chunks is not None else [{}] if isinstance(data, dict): @@ -83,8 +110,7 @@ def split_json( json_data: Dict[str, Any], convert_lists: bool = False, ) -> List[Dict]: - """Splits JSON into a list of JSON chunks""" - + """Splits JSON into a list of JSON chunks.""" if convert_lists: chunks = self._json_split(self._list_to_dict_preprocessing(json_data)) else: @@ -101,8 +127,7 @@ def split_text( convert_lists: bool = False, ensure_ascii: bool = True, ) -> List[str]: - """Splits JSON into a list of JSON formatted strings""" - + """Splits JSON into a list of JSON formatted strings.""" chunks = self.split_json(json_data=json_data, convert_lists=convert_lists) # Convert to string diff --git a/libs/text-splitters/langchain_text_splitters/markdown.py b/libs/text-splitters/langchain_text_splitters/markdown.py index fdcd010f50d43..34c7d2197d238 100644 --- a/libs/text-splitters/langchain_text_splitters/markdown.py +++ b/libs/text-splitters/langchain_text_splitters/markdown.py @@ -45,7 +45,8 @@ def __init__( self.strip_headers = strip_headers def aggregate_lines_to_chunks(self, lines: List[LineType]) -> List[Document]: - """Combine lines with common metadata into chunks + """Combine lines with common metadata into chunks. + Args: lines: Line of text / associated header metadata """ @@ -87,10 +88,11 @@ def aggregate_lines_to_chunks(self, lines: List[LineType]) -> List[Document]: ] def split_text(self, text: str) -> List[Document]: - """Split markdown file - Args: - text: Markdown file""" + """Split markdown file. + Args: + text: Markdown file + """ # Split the input text by newline character ("\n"). lines = text.split("\n") # Final output @@ -225,8 +227,7 @@ class HeaderType(TypedDict): class ExperimentalMarkdownSyntaxTextSplitter: - """ - An experimental text splitter for handling Markdown syntax. + """An experimental text splitter for handling Markdown syntax. This splitter aims to retain the exact whitespace of the original text while extracting structured metadata, such as headers. It is a re-implementation of the @@ -280,6 +281,22 @@ def __init__( return_each_line: bool = False, strip_headers: bool = True, ): + """Initialize the text splitter with header splitting and formatting options. + + This constructor sets up the required configuration for splitting text into + chunks based on specified headers and formatting preferences. + + Args: + headers_to_split_on (Union[List[Tuple[str, str]], None]): + A list of tuples, where each tuple contains a header tag (e.g., "h1") + and its corresponding metadata key. If None, default headers are used. + return_each_line (bool): + Whether to return each line as an individual chunk. + Defaults to False, which aggregates lines into larger chunks. + strip_headers (bool): + Whether to exclude headers from the resulting chunks. + Defaults to True. + """ self.chunks: List[Document] = [] self.current_chunk = Document(page_content="") self.current_header_stack: List[Tuple[int, str]] = [] @@ -292,6 +309,21 @@ def __init__( self.return_each_line = return_each_line def split_text(self, text: str) -> List[Document]: + """Split the input text into structured chunks. + + This method processes the input text line by line, identifying and handling + specific patterns such as headers, code blocks, and horizontal rules to + split it into structured chunks based on headers, code blocks, and + horizontal rules. + + Args: + text (str): The input text to be split into chunks. + + Returns: + List[Document]: A list of `Document` objects representing the structured + chunks of the input text. If `return_each_line` is enabled, each line + is returned as a separate `Document`. + """ raw_lines = text.splitlines(keepends=True) while raw_lines: diff --git a/libs/text-splitters/langchain_text_splitters/sentence_transformers.py b/libs/text-splitters/langchain_text_splitters/sentence_transformers.py index beb314d810d9e..3b19c5edc594d 100644 --- a/libs/text-splitters/langchain_text_splitters/sentence_transformers.py +++ b/libs/text-splitters/langchain_text_splitters/sentence_transformers.py @@ -51,6 +51,20 @@ def _initialize_chunk_configuration( ) def split_text(self, text: str) -> List[str]: + """Splits the input text into smaller components by splitting text on tokens. + + This method encodes the input text using a private `_encode` method, then + strips the start and stop token IDs from the encoded result. It returns the + processed segments as a list of strings. + + Args: + text (str): The input text to be split. + + Returns: + List[str]: A list of string components derived from the input text after + encoding and processing. + """ + def encode_strip_start_and_stop_token_ids(text: str) -> List[int]: return self._encode(text)[1:-1] @@ -64,6 +78,17 @@ def encode_strip_start_and_stop_token_ids(text: str) -> List[int]: return split_text_on_tokens(text=text, tokenizer=tokenizer) def count_tokens(self, *, text: str) -> int: + """Counts the number of tokens in the given text. + + This method encodes the input text using a private `_encode` method and + calculates the total number of tokens in the encoded result. + + Args: + text (str): The input text for which the token count is calculated. + + Returns: + int: The number of tokens in the encoded text. + """ return len(self._encode(text)) _max_length_equal_32_bit_integer: int = 2**32 diff --git a/libs/text-splitters/langchain_text_splitters/spacy.py b/libs/text-splitters/langchain_text_splitters/spacy.py index 447a3e429600c..a15e8b00418a0 100644 --- a/libs/text-splitters/langchain_text_splitters/spacy.py +++ b/libs/text-splitters/langchain_text_splitters/spacy.py @@ -8,7 +8,6 @@ class SpacyTextSplitter(TextSplitter): """Splitting text using Spacy package. - Per default, Spacy's `en_core_web_sm` model is used and its default max_length is 1000000 (it is the length of maximum character this model takes which can be increased for large files). For a faster, but diff --git a/libs/text-splitters/poetry.lock b/libs/text-splitters/poetry.lock index ff73e1b8838ee..1ec7d497bdb72 100644 --- a/libs/text-splitters/poetry.lock +++ b/libs/text-splitters/poetry.lock @@ -13,24 +13,24 @@ files = [ [[package]] name = "anyio" -version = "4.6.2.post1" +version = "4.7.0" description = "High level compatibility layer for multiple asynchronous event loop implementations" optional = false python-versions = ">=3.9" files = [ - {file = "anyio-4.6.2.post1-py3-none-any.whl", hash = "sha256:6d170c36fba3bdd840c73d3868c1e777e33676a69c3a72cf0a0d5d6d8009b61d"}, - {file = "anyio-4.6.2.post1.tar.gz", hash = "sha256:4c8bc31ccdb51c7f7bd251f51c609e038d63e34219b44aa86e47576389880b4c"}, + {file = "anyio-4.7.0-py3-none-any.whl", hash = "sha256:ea60c3723ab42ba6fff7e8ccb0488c898ec538ff4df1f1d5e642c3601d07e352"}, + {file = "anyio-4.7.0.tar.gz", hash = "sha256:2f834749c602966b7d456a7567cafcb309f96482b5081d14ac93ccd457f9dd48"}, ] [package.dependencies] exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""} idna = ">=2.8" sniffio = ">=1.1" -typing-extensions = {version = ">=4.1", markers = "python_version < \"3.11\""} +typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""} [package.extras] -doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] -test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21.0b1)"] +doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx_rtd_theme"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21)"] trio = ["trio (>=0.26.1)"] [[package]] @@ -122,21 +122,18 @@ test = ["dateparser (==1.*)", "pre-commit", "pytest", "pytest-cov", "pytest-mock [[package]] name = "asttokens" -version = "2.4.1" +version = "3.0.0" description = "Annotate AST trees with source code positions" optional = false -python-versions = "*" +python-versions = ">=3.8" files = [ - {file = "asttokens-2.4.1-py2.py3-none-any.whl", hash = "sha256:051ed49c3dcae8913ea7cd08e46a606dba30b79993209636c4875bc1d637bc24"}, - {file = "asttokens-2.4.1.tar.gz", hash = "sha256:b03869718ba9a6eb027e134bfdf69f38a236d681c83c160d510768af11254ba0"}, + {file = "asttokens-3.0.0-py3-none-any.whl", hash = "sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2"}, + {file = "asttokens-3.0.0.tar.gz", hash = "sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7"}, ] -[package.dependencies] -six = ">=1.12.0" - [package.extras] -astroid = ["astroid (>=1,<2)", "astroid (>=2,<4)"] -test = ["astroid (>=1,<2)", "astroid (>=2,<4)", "pytest"] +astroid = ["astroid (>=2,<4)"] +test = ["astroid (>=2,<4)", "pytest", "pytest-cov", "pytest-xdist"] [[package]] name = "async-lru" @@ -225,13 +222,13 @@ css = ["tinycss2 (>=1.1.0,<1.5)"] [[package]] name = "certifi" -version = "2024.8.30" +version = "2024.12.14" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"}, - {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, + {file = "certifi-2024.12.14-py3-none-any.whl", hash = "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56"}, + {file = "certifi-2024.12.14.tar.gz", hash = "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db"}, ] [[package]] @@ -457,37 +454,37 @@ test = ["pytest"] [[package]] name = "debugpy" -version = "1.8.7" +version = "1.8.11" description = "An implementation of the Debug Adapter Protocol for Python" optional = false python-versions = ">=3.8" files = [ - {file = "debugpy-1.8.7-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:95fe04a573b8b22896c404365e03f4eda0ce0ba135b7667a1e57bd079793b96b"}, - {file = "debugpy-1.8.7-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:628a11f4b295ffb4141d8242a9bb52b77ad4a63a2ad19217a93be0f77f2c28c9"}, - {file = "debugpy-1.8.7-cp310-cp310-win32.whl", hash = "sha256:85ce9c1d0eebf622f86cc68618ad64bf66c4fc3197d88f74bb695a416837dd55"}, - {file = "debugpy-1.8.7-cp310-cp310-win_amd64.whl", hash = "sha256:29e1571c276d643757ea126d014abda081eb5ea4c851628b33de0c2b6245b037"}, - {file = "debugpy-1.8.7-cp311-cp311-macosx_14_0_universal2.whl", hash = "sha256:caf528ff9e7308b74a1749c183d6808ffbedbb9fb6af78b033c28974d9b8831f"}, - {file = "debugpy-1.8.7-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cba1d078cf2e1e0b8402e6bda528bf8fda7ccd158c3dba6c012b7897747c41a0"}, - {file = "debugpy-1.8.7-cp311-cp311-win32.whl", hash = "sha256:171899588bcd412151e593bd40d9907133a7622cd6ecdbdb75f89d1551df13c2"}, - {file = "debugpy-1.8.7-cp311-cp311-win_amd64.whl", hash = "sha256:6e1c4ffb0c79f66e89dfd97944f335880f0d50ad29525dc792785384923e2211"}, - {file = "debugpy-1.8.7-cp312-cp312-macosx_14_0_universal2.whl", hash = "sha256:4d27d842311353ede0ad572600c62e4bcd74f458ee01ab0dd3a1a4457e7e3706"}, - {file = "debugpy-1.8.7-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:703c1fd62ae0356e194f3e7b7a92acd931f71fe81c4b3be2c17a7b8a4b546ec2"}, - {file = "debugpy-1.8.7-cp312-cp312-win32.whl", hash = "sha256:2f729228430ef191c1e4df72a75ac94e9bf77413ce5f3f900018712c9da0aaca"}, - {file = "debugpy-1.8.7-cp312-cp312-win_amd64.whl", hash = "sha256:45c30aaefb3e1975e8a0258f5bbd26cd40cde9bfe71e9e5a7ac82e79bad64e39"}, - {file = "debugpy-1.8.7-cp313-cp313-macosx_14_0_universal2.whl", hash = "sha256:d050a1ec7e925f514f0f6594a1e522580317da31fbda1af71d1530d6ea1f2b40"}, - {file = "debugpy-1.8.7-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2f4349a28e3228a42958f8ddaa6333d6f8282d5edaea456070e48609c5983b7"}, - {file = "debugpy-1.8.7-cp313-cp313-win32.whl", hash = "sha256:11ad72eb9ddb436afb8337891a986302e14944f0f755fd94e90d0d71e9100bba"}, - {file = "debugpy-1.8.7-cp313-cp313-win_amd64.whl", hash = "sha256:2efb84d6789352d7950b03d7f866e6d180284bc02c7e12cb37b489b7083d81aa"}, - {file = "debugpy-1.8.7-cp38-cp38-macosx_14_0_x86_64.whl", hash = "sha256:4b908291a1d051ef3331484de8e959ef3e66f12b5e610c203b5b75d2725613a7"}, - {file = "debugpy-1.8.7-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da8df5b89a41f1fd31503b179d0a84a5fdb752dddd5b5388dbd1ae23cda31ce9"}, - {file = "debugpy-1.8.7-cp38-cp38-win32.whl", hash = "sha256:b12515e04720e9e5c2216cc7086d0edadf25d7ab7e3564ec8b4521cf111b4f8c"}, - {file = "debugpy-1.8.7-cp38-cp38-win_amd64.whl", hash = "sha256:93176e7672551cb5281577cdb62c63aadc87ec036f0c6a486f0ded337c504596"}, - {file = "debugpy-1.8.7-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:90d93e4f2db442f8222dec5ec55ccfc8005821028982f1968ebf551d32b28907"}, - {file = "debugpy-1.8.7-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6db2a370e2700557a976eaadb16243ec9c91bd46f1b3bb15376d7aaa7632c81"}, - {file = "debugpy-1.8.7-cp39-cp39-win32.whl", hash = "sha256:a6cf2510740e0c0b4a40330640e4b454f928c7b99b0c9dbf48b11efba08a8cda"}, - {file = "debugpy-1.8.7-cp39-cp39-win_amd64.whl", hash = "sha256:6a9d9d6d31846d8e34f52987ee0f1a904c7baa4912bf4843ab39dadf9b8f3e0d"}, - {file = "debugpy-1.8.7-py2.py3-none-any.whl", hash = "sha256:57b00de1c8d2c84a61b90880f7e5b6deaf4c312ecbde3a0e8912f2a56c4ac9ae"}, - {file = "debugpy-1.8.7.zip", hash = "sha256:18b8f731ed3e2e1df8e9cdaa23fb1fc9c24e570cd0081625308ec51c82efe42e"}, + {file = "debugpy-1.8.11-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:2b26fefc4e31ff85593d68b9022e35e8925714a10ab4858fb1b577a8a48cb8cd"}, + {file = "debugpy-1.8.11-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61bc8b3b265e6949855300e84dc93d02d7a3a637f2aec6d382afd4ceb9120c9f"}, + {file = "debugpy-1.8.11-cp310-cp310-win32.whl", hash = "sha256:c928bbf47f65288574b78518449edaa46c82572d340e2750889bbf8cd92f3737"}, + {file = "debugpy-1.8.11-cp310-cp310-win_amd64.whl", hash = "sha256:8da1db4ca4f22583e834dcabdc7832e56fe16275253ee53ba66627b86e304da1"}, + {file = "debugpy-1.8.11-cp311-cp311-macosx_14_0_universal2.whl", hash = "sha256:85de8474ad53ad546ff1c7c7c89230db215b9b8a02754d41cb5a76f70d0be296"}, + {file = "debugpy-1.8.11-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ffc382e4afa4aee367bf413f55ed17bd91b191dcaf979890af239dda435f2a1"}, + {file = "debugpy-1.8.11-cp311-cp311-win32.whl", hash = "sha256:40499a9979c55f72f4eb2fc38695419546b62594f8af194b879d2a18439c97a9"}, + {file = "debugpy-1.8.11-cp311-cp311-win_amd64.whl", hash = "sha256:987bce16e86efa86f747d5151c54e91b3c1e36acc03ce1ddb50f9d09d16ded0e"}, + {file = "debugpy-1.8.11-cp312-cp312-macosx_14_0_universal2.whl", hash = "sha256:84e511a7545d11683d32cdb8f809ef63fc17ea2a00455cc62d0a4dbb4ed1c308"}, + {file = "debugpy-1.8.11-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce291a5aca4985d82875d6779f61375e959208cdf09fcec40001e65fb0a54768"}, + {file = "debugpy-1.8.11-cp312-cp312-win32.whl", hash = "sha256:28e45b3f827d3bf2592f3cf7ae63282e859f3259db44ed2b129093ca0ac7940b"}, + {file = "debugpy-1.8.11-cp312-cp312-win_amd64.whl", hash = "sha256:44b1b8e6253bceada11f714acf4309ffb98bfa9ac55e4fce14f9e5d4484287a1"}, + {file = "debugpy-1.8.11-cp313-cp313-macosx_14_0_universal2.whl", hash = "sha256:8988f7163e4381b0da7696f37eec7aca19deb02e500245df68a7159739bbd0d3"}, + {file = "debugpy-1.8.11-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c1f6a173d1140e557347419767d2b14ac1c9cd847e0b4c5444c7f3144697e4e"}, + {file = "debugpy-1.8.11-cp313-cp313-win32.whl", hash = "sha256:bb3b15e25891f38da3ca0740271e63ab9db61f41d4d8541745cfc1824252cb28"}, + {file = "debugpy-1.8.11-cp313-cp313-win_amd64.whl", hash = "sha256:d8768edcbeb34da9e11bcb8b5c2e0958d25218df7a6e56adf415ef262cd7b6d1"}, + {file = "debugpy-1.8.11-cp38-cp38-macosx_14_0_x86_64.whl", hash = "sha256:ad7efe588c8f5cf940f40c3de0cd683cc5b76819446abaa50dc0829a30c094db"}, + {file = "debugpy-1.8.11-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:189058d03a40103a57144752652b3ab08ff02b7595d0ce1f651b9acc3a3a35a0"}, + {file = "debugpy-1.8.11-cp38-cp38-win32.whl", hash = "sha256:32db46ba45849daed7ccf3f2e26f7a386867b077f39b2a974bb5c4c2c3b0a280"}, + {file = "debugpy-1.8.11-cp38-cp38-win_amd64.whl", hash = "sha256:116bf8342062246ca749013df4f6ea106f23bc159305843491f64672a55af2e5"}, + {file = "debugpy-1.8.11-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:654130ca6ad5de73d978057eaf9e582244ff72d4574b3e106fb8d3d2a0d32458"}, + {file = "debugpy-1.8.11-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23dc34c5e03b0212fa3c49a874df2b8b1b8fda95160bd79c01eb3ab51ea8d851"}, + {file = "debugpy-1.8.11-cp39-cp39-win32.whl", hash = "sha256:52d8a3166c9f2815bfae05f386114b0b2d274456980d41f320299a8d9a5615a7"}, + {file = "debugpy-1.8.11-cp39-cp39-win_amd64.whl", hash = "sha256:52c3cf9ecda273a19cc092961ee34eb9ba8687d67ba34cc7b79a521c1c64c4c0"}, + {file = "debugpy-1.8.11-py2.py3-none-any.whl", hash = "sha256:0e22f846f4211383e6a416d04b4c13ed174d24cc5d43f5fd52e7821d0ebc8920"}, + {file = "debugpy-1.8.11.tar.gz", hash = "sha256:6ad2688b69235c43b020e04fecccdf6a96c8943ca9c2fb340b8adc103c655e57"}, ] [[package]] @@ -542,13 +539,13 @@ tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipyth [[package]] name = "fastjsonschema" -version = "2.20.0" +version = "2.21.1" description = "Fastest Python implementation of JSON schema" optional = false python-versions = "*" files = [ - {file = "fastjsonschema-2.20.0-py3-none-any.whl", hash = "sha256:5875f0b0fa7a0043a91e93a9b8f793bcbbba9691e7fd83dca95c28ba26d21f0a"}, - {file = "fastjsonschema-2.20.0.tar.gz", hash = "sha256:3d48fc5300ee96f5d116f10fe6f28d938e6008f59a6a025c2649475b87f76a23"}, + {file = "fastjsonschema-2.21.1-py3-none-any.whl", hash = "sha256:c9e5b7e908310918cf494a434eeb31384dd84a98b57a30bcb1f535015b554667"}, + {file = "fastjsonschema-2.21.1.tar.gz", hash = "sha256:794d4f0a58f848961ba16af7b9c85a3e88cd360df008c59aac6fc5ae9323b5d4"}, ] [package.extras] @@ -592,13 +589,13 @@ files = [ [[package]] name = "httpcore" -version = "1.0.6" +version = "1.0.7" description = "A minimal low-level HTTP client." optional = false python-versions = ">=3.8" files = [ - {file = "httpcore-1.0.6-py3-none-any.whl", hash = "sha256:27b59625743b85577a8c0e10e55b50b5368a4f2cfe8cc7bcfa9cf00829c2682f"}, - {file = "httpcore-1.0.6.tar.gz", hash = "sha256:73f6dbd6eb8c21bbf7ef8efad555481853f5f6acdeaff1edb0694289269ee17f"}, + {file = "httpcore-1.0.7-py3-none-any.whl", hash = "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd"}, + {file = "httpcore-1.0.7.tar.gz", hash = "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c"}, ] [package.dependencies] @@ -613,13 +610,13 @@ trio = ["trio (>=0.22.0,<1.0)"] [[package]] name = "httpx" -version = "0.27.2" +version = "0.28.1" description = "The next generation HTTP client." optional = false python-versions = ">=3.8" files = [ - {file = "httpx-0.27.2-py3-none-any.whl", hash = "sha256:7bb2708e112d8fdd7829cd4243970f0c223274051cb35ee80c03301ee29a3df0"}, - {file = "httpx-0.27.2.tar.gz", hash = "sha256:f7c2be1d2f3c3c3160d441802406b206c2b76f5947b11115e6df10c6c65e66c2"}, + {file = "httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad"}, + {file = "httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc"}, ] [package.dependencies] @@ -627,7 +624,6 @@ anyio = "*" certifi = "*" httpcore = "==1.*" idna = "*" -sniffio = "*" [package.extras] brotli = ["brotli", "brotlicffi"] @@ -791,22 +787,22 @@ arrow = ">=0.15.0" [[package]] name = "jedi" -version = "0.19.1" +version = "0.19.2" description = "An autocompletion tool for Python that can be used for text editors." optional = false python-versions = ">=3.6" files = [ - {file = "jedi-0.19.1-py2.py3-none-any.whl", hash = "sha256:e983c654fe5c02867aef4cdfce5a2fbb4a50adc0af145f70504238f18ef5e7e0"}, - {file = "jedi-0.19.1.tar.gz", hash = "sha256:cf0496f3651bc65d7174ac1b7d043eff454892c708a87d1b683e57b569927ffd"}, + {file = "jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9"}, + {file = "jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0"}, ] [package.dependencies] -parso = ">=0.8.3,<0.9.0" +parso = ">=0.8.4,<0.9.0" [package.extras] docs = ["Jinja2 (==2.11.3)", "MarkupSafe (==1.1.1)", "Pygments (==2.8.1)", "alabaster (==0.7.12)", "babel (==2.9.1)", "chardet (==4.0.0)", "commonmark (==0.8.1)", "docutils (==0.17.1)", "future (==0.18.2)", "idna (==2.10)", "imagesize (==1.2.0)", "mock (==1.0.1)", "packaging (==20.9)", "pyparsing (==2.4.7)", "pytz (==2021.1)", "readthedocs-sphinx-ext (==2.1.4)", "recommonmark (==0.5.0)", "requests (==2.25.1)", "six (==1.15.0)", "snowballstemmer (==2.1.0)", "sphinx (==1.8.5)", "sphinx-rtd-theme (==0.4.3)", "sphinxcontrib-serializinghtml (==1.1.4)", "sphinxcontrib-websupport (==1.2.4)", "urllib3 (==1.26.4)"] qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] -testing = ["Django", "attrs", "colorama", "docopt", "pytest (<7.0.0)"] +testing = ["Django", "attrs", "colorama", "docopt", "pytest (<9.0.0)"] [[package]] name = "jinja2" @@ -827,15 +823,18 @@ i18n = ["Babel (>=2.7)"] [[package]] name = "json5" -version = "0.9.25" +version = "0.10.0" description = "A Python implementation of the JSON5 data format." optional = false -python-versions = ">=3.8" +python-versions = ">=3.8.0" files = [ - {file = "json5-0.9.25-py3-none-any.whl", hash = "sha256:34ed7d834b1341a86987ed52f3f76cd8ee184394906b6e22a1e0deb9ab294e8f"}, - {file = "json5-0.9.25.tar.gz", hash = "sha256:548e41b9be043f9426776f05df8635a00fe06104ea51ed24b67f908856e151ae"}, + {file = "json5-0.10.0-py3-none-any.whl", hash = "sha256:19b23410220a7271e8377f81ba8aacba2fdd56947fbb137ee5977cbe1f5e8dfa"}, + {file = "json5-0.10.0.tar.gz", hash = "sha256:e66941c8f0a02026943c52c2eb34ebeb2a6f819a0be05920a6f5243cd30fd559"}, ] +[package.extras] +dev = ["build (==1.2.2.post1)", "coverage (==7.5.3)", "mypy (==1.13.0)", "pip (==24.3.1)", "pylint (==3.2.3)", "ruff (==0.7.3)", "twine (==5.1.1)", "uv (==0.5.1)"] + [[package]] name = "jsonpatch" version = "1.33" @@ -1087,13 +1086,13 @@ test = ["jupyter-server (>=2.0.0)", "pytest (>=7.0)", "pytest-jupyter[server] (> [[package]] name = "jupyterlab" -version = "4.2.5" +version = "4.3.3" description = "JupyterLab computational environment" optional = false python-versions = ">=3.8" files = [ - {file = "jupyterlab-4.2.5-py3-none-any.whl", hash = "sha256:73b6e0775d41a9fee7ee756c80f58a6bed4040869ccc21411dc559818874d321"}, - {file = "jupyterlab-4.2.5.tar.gz", hash = "sha256:ae7f3a1b8cb88b4f55009ce79fa7c06f99d70cd63601ee4aa91815d054f46f75"}, + {file = "jupyterlab-4.3.3-py3-none-any.whl", hash = "sha256:32a8fd30677e734ffcc3916a4758b9dab21b02015b668c60eb36f84357b7d4b1"}, + {file = "jupyterlab-4.3.3.tar.gz", hash = "sha256:76fa39e548fdac94dc1204af5956c556f54c785f70ee26aa47ea08eda4d5bbcd"}, ] [package.dependencies] @@ -1108,15 +1107,15 @@ jupyter-server = ">=2.4.0,<3" jupyterlab-server = ">=2.27.1,<3" notebook-shim = ">=0.2" packaging = "*" -setuptools = ">=40.1.0" +setuptools = ">=40.8.0" tomli = {version = ">=1.2.2", markers = "python_version < \"3.11\""} tornado = ">=6.2.0" traitlets = "*" [package.extras] -dev = ["build", "bump2version", "coverage", "hatch", "pre-commit", "pytest-cov", "ruff (==0.3.5)"] -docs = ["jsx-lexer", "myst-parser", "pydata-sphinx-theme (>=0.13.0)", "pytest", "pytest-check-links", "pytest-jupyter", "sphinx (>=1.8,<7.3.0)", "sphinx-copybutton"] -docs-screenshots = ["altair (==5.3.0)", "ipython (==8.16.1)", "ipywidgets (==8.1.2)", "jupyterlab-geojson (==3.4.0)", "jupyterlab-language-pack-zh-cn (==4.1.post2)", "matplotlib (==3.8.3)", "nbconvert (>=7.0.0)", "pandas (==2.2.1)", "scipy (==1.12.0)", "vega-datasets (==0.9.0)"] +dev = ["build", "bump2version", "coverage", "hatch", "pre-commit", "pytest-cov", "ruff (==0.6.9)"] +docs = ["jsx-lexer", "myst-parser", "pydata-sphinx-theme (>=0.13.0)", "pytest", "pytest-check-links", "pytest-jupyter", "sphinx (>=1.8,<8.1.0)", "sphinx-copybutton"] +docs-screenshots = ["altair (==5.4.1)", "ipython (==8.16.1)", "ipywidgets (==8.1.5)", "jupyterlab-geojson (==3.4.0)", "jupyterlab-language-pack-zh-cn (==4.2.post3)", "matplotlib (==3.9.2)", "nbconvert (>=7.0.0)", "pandas (==2.2.3)", "scipy (==1.14.1)", "vega-datasets (==0.9.0)"] test = ["coverage", "pytest (>=7.0)", "pytest-check-links (>=0.7)", "pytest-console-scripts", "pytest-cov", "pytest-jupyter (>=0.5.3)", "pytest-timeout", "pytest-tornasync", "requests", "requests-cache", "virtualenv"] upgrade-extension = ["copier (>=9,<10)", "jinja2-time (<0.3)", "pydantic (<3.0)", "pyyaml-include (<3.0)", "tomli-w (<2.0)"] @@ -1170,7 +1169,7 @@ files = [ [[package]] name = "langchain-core" -version = "0.3.15" +version = "0.3.25" description = "Building applications with LLMs through composability" optional = false python-versions = ">=3.9,<4.0" @@ -1179,7 +1178,7 @@ develop = true [package.dependencies] jsonpatch = "^1.33" -langsmith = "^0.1.125" +langsmith = ">=0.1.125,<0.3" packaging = ">=23.2,<25" pydantic = [ {version = ">=2.5.2,<3.0.0", markers = "python_full_version < \"3.12.4\""}, @@ -1195,18 +1194,18 @@ url = "../core" [[package]] name = "langsmith" -version = "0.1.137" +version = "0.2.3" description = "Client library to connect to the LangSmith LLM Tracing and Evaluation Platform." optional = false -python-versions = "<4.0,>=3.8.1" +python-versions = "<4.0,>=3.9" files = [ - {file = "langsmith-0.1.137-py3-none-any.whl", hash = "sha256:4256d5c61133749890f7b5c88321dbb133ce0f440c621ea28e76513285859b81"}, - {file = "langsmith-0.1.137.tar.gz", hash = "sha256:56cdfcc6c74cb20a3f437d5bd144feb5bf93f54c5a2918d1e568cbd084a372d4"}, + {file = "langsmith-0.2.3-py3-none-any.whl", hash = "sha256:4958b6e918f57fedba6ddc55b8534d1e06478bb44c779aa73713ce898ca6ae87"}, + {file = "langsmith-0.2.3.tar.gz", hash = "sha256:54c231b07fdeb0f8472925074a0ec0ed2cb654a0437d63c6ccf76a9da635900d"}, ] [package.dependencies] httpx = ">=0.23.0,<1" -orjson = ">=3.9.14,<4.0.0" +orjson = {version = ">=3.9.14,<4.0.0", markers = "platform_python_implementation != \"PyPy\""} pydantic = [ {version = ">=1,<3", markers = "python_full_version < \"3.12.4\""}, {version = ">=2.7.4,<3.0.0", markers = "python_full_version >= \"3.12.4\""}, @@ -1214,6 +1213,9 @@ pydantic = [ requests = ">=2,<3" requests-toolbelt = ">=1.0.0,<2.0.0" +[package.extras] +langsmith-pyo3 = ["langsmith-pyo3 (>=0.1.0rc2,<0.2.0)"] + [[package]] name = "lxml-stubs" version = "0.5.1" @@ -1389,13 +1391,13 @@ files = [ [[package]] name = "nbclient" -version = "0.10.0" +version = "0.10.1" description = "A client library for executing notebooks. Formerly nbconvert's ExecutePreprocessor." optional = false python-versions = ">=3.8.0" files = [ - {file = "nbclient-0.10.0-py3-none-any.whl", hash = "sha256:f13e3529332a1f1f81d82a53210322476a168bb7090a0289c795fe9cc11c9d3f"}, - {file = "nbclient-0.10.0.tar.gz", hash = "sha256:4b3f1b7dba531e498449c4db4f53da339c91d449dc11e9af3a43b4eb5c5abb09"}, + {file = "nbclient-0.10.1-py3-none-any.whl", hash = "sha256:949019b9240d66897e442888cfb618f69ef23dc71c01cb5fced8499c2cfc084d"}, + {file = "nbclient-0.10.1.tar.gz", hash = "sha256:3e93e348ab27e712acd46fccd809139e356eb9a31aab641d1a7991a6eb4e6f68"}, ] [package.dependencies] @@ -1406,7 +1408,7 @@ traitlets = ">=5.4" [package.extras] dev = ["pre-commit"] -docs = ["autodoc-traits", "mock", "moto", "myst-parser", "nbclient[test]", "sphinx (>=1.7)", "sphinx-book-theme", "sphinxcontrib-spelling"] +docs = ["autodoc-traits", "flaky", "ipykernel (>=6.19.3)", "ipython", "ipywidgets", "mock", "moto", "myst-parser", "nbconvert (>=7.0.0)", "pytest (>=7.0,<8)", "pytest-asyncio", "pytest-cov (>=4.0)", "sphinx (>=1.7)", "sphinx-book-theme", "sphinxcontrib-spelling", "testpath", "xmltodict"] test = ["flaky", "ipykernel (>=6.19.3)", "ipython", "ipywidgets", "nbconvert (>=7.0.0)", "pytest (>=7.0,<8)", "pytest-asyncio", "pytest-cov (>=4.0)", "testpath", "xmltodict"] [[package]] @@ -1481,18 +1483,18 @@ files = [ [[package]] name = "notebook" -version = "7.2.2" +version = "7.3.1" description = "Jupyter Notebook - A web-based notebook environment for interactive computing" optional = false python-versions = ">=3.8" files = [ - {file = "notebook-7.2.2-py3-none-any.whl", hash = "sha256:c89264081f671bc02eec0ed470a627ed791b9156cad9285226b31611d3e9fe1c"}, - {file = "notebook-7.2.2.tar.gz", hash = "sha256:2ef07d4220421623ad3fe88118d687bc0450055570cdd160814a59cf3a1c516e"}, + {file = "notebook-7.3.1-py3-none-any.whl", hash = "sha256:212e1486b2230fe22279043f33c7db5cf9a01d29feb063a85cb139747b7c9483"}, + {file = "notebook-7.3.1.tar.gz", hash = "sha256:84381c2a82d867517fd25b86e986dae1fe113a70b98f03edff9b94e499fec8fa"}, ] [package.dependencies] jupyter-server = ">=2.4.0,<3" -jupyterlab = ">=4.2.0,<4.3" +jupyterlab = ">=4.3.2,<4.4" jupyterlab-server = ">=2.27.1,<3" notebook-shim = ">=0.2,<0.3" tornado = ">=6.2.0" @@ -1521,69 +1523,86 @@ test = ["pytest", "pytest-console-scripts", "pytest-jupyter", "pytest-tornasync" [[package]] name = "orjson" -version = "3.10.10" +version = "3.10.12" description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" optional = false python-versions = ">=3.8" files = [ - {file = "orjson-3.10.10-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:b788a579b113acf1c57e0a68e558be71d5d09aa67f62ca1f68e01117e550a998"}, - {file = "orjson-3.10.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:804b18e2b88022c8905bb79bd2cbe59c0cd014b9328f43da8d3b28441995cda4"}, - {file = "orjson-3.10.10-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9972572a1d042ec9ee421b6da69f7cc823da5962237563fa548ab17f152f0b9b"}, - {file = "orjson-3.10.10-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc6993ab1c2ae7dd0711161e303f1db69062955ac2668181bfdf2dd410e65258"}, - {file = "orjson-3.10.10-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d78e4cacced5781b01d9bc0f0cd8b70b906a0e109825cb41c1b03f9c41e4ce86"}, - {file = "orjson-3.10.10-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e6eb2598df518281ba0cbc30d24c5b06124ccf7e19169e883c14e0831217a0bc"}, - {file = "orjson-3.10.10-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:23776265c5215ec532de6238a52707048401a568f0fa0d938008e92a147fe2c7"}, - {file = "orjson-3.10.10-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8cc2a654c08755cef90b468ff17c102e2def0edd62898b2486767204a7f5cc9c"}, - {file = "orjson-3.10.10-cp310-none-win32.whl", hash = "sha256:081b3fc6a86d72efeb67c13d0ea7c030017bd95f9868b1e329a376edc456153b"}, - {file = "orjson-3.10.10-cp310-none-win_amd64.whl", hash = "sha256:ff38c5fb749347768a603be1fb8a31856458af839f31f064c5aa74aca5be9efe"}, - {file = "orjson-3.10.10-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:879e99486c0fbb256266c7c6a67ff84f46035e4f8749ac6317cc83dacd7f993a"}, - {file = "orjson-3.10.10-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:019481fa9ea5ff13b5d5d95e6fd5ab25ded0810c80b150c2c7b1cc8660b662a7"}, - {file = "orjson-3.10.10-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0dd57eff09894938b4c86d4b871a479260f9e156fa7f12f8cad4b39ea8028bb5"}, - {file = "orjson-3.10.10-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dbde6d70cd95ab4d11ea8ac5e738e30764e510fc54d777336eec09bb93b8576c"}, - {file = "orjson-3.10.10-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b2625cb37b8fb42e2147404e5ff7ef08712099197a9cd38895006d7053e69d6"}, - {file = "orjson-3.10.10-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dbf3c20c6a7db69df58672a0d5815647ecf78c8e62a4d9bd284e8621c1fe5ccb"}, - {file = "orjson-3.10.10-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:75c38f5647e02d423807d252ce4528bf6a95bd776af999cb1fb48867ed01d1f6"}, - {file = "orjson-3.10.10-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:23458d31fa50ec18e0ec4b0b4343730928296b11111df5f547c75913714116b2"}, - {file = "orjson-3.10.10-cp311-none-win32.whl", hash = "sha256:2787cd9dedc591c989f3facd7e3e86508eafdc9536a26ec277699c0aa63c685b"}, - {file = "orjson-3.10.10-cp311-none-win_amd64.whl", hash = "sha256:6514449d2c202a75183f807bc755167713297c69f1db57a89a1ef4a0170ee269"}, - {file = "orjson-3.10.10-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:8564f48f3620861f5ef1e080ce7cd122ee89d7d6dacf25fcae675ff63b4d6e05"}, - {file = "orjson-3.10.10-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5bf161a32b479034098c5b81f2608f09167ad2fa1c06abd4e527ea6bf4837a9"}, - {file = "orjson-3.10.10-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:68b65c93617bcafa7f04b74ae8bc2cc214bd5cb45168a953256ff83015c6747d"}, - {file = "orjson-3.10.10-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e8e28406f97fc2ea0c6150f4c1b6e8261453318930b334abc419214c82314f85"}, - {file = "orjson-3.10.10-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e4d0d9fe174cc7a5bdce2e6c378bcdb4c49b2bf522a8f996aa586020e1b96cee"}, - {file = "orjson-3.10.10-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3be81c42f1242cbed03cbb3973501fcaa2675a0af638f8be494eaf37143d999"}, - {file = "orjson-3.10.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:65f9886d3bae65be026219c0a5f32dbbe91a9e6272f56d092ab22561ad0ea33b"}, - {file = "orjson-3.10.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:730ed5350147db7beb23ddaf072f490329e90a1d059711d364b49fe352ec987b"}, - {file = "orjson-3.10.10-cp312-none-win32.whl", hash = "sha256:a8f4bf5f1c85bea2170800020d53a8877812892697f9c2de73d576c9307a8a5f"}, - {file = "orjson-3.10.10-cp312-none-win_amd64.whl", hash = "sha256:384cd13579a1b4cd689d218e329f459eb9ddc504fa48c5a83ef4889db7fd7a4f"}, - {file = "orjson-3.10.10-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:44bffae68c291f94ff5a9b4149fe9d1bdd4cd0ff0fb575bcea8351d48db629a1"}, - {file = "orjson-3.10.10-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e27b4c6437315df3024f0835887127dac2a0a3ff643500ec27088d2588fa5ae1"}, - {file = "orjson-3.10.10-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bca84df16d6b49325a4084fd8b2fe2229cb415e15c46c529f868c3387bb1339d"}, - {file = "orjson-3.10.10-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c14ce70e8f39bd71f9f80423801b5d10bf93d1dceffdecd04df0f64d2c69bc01"}, - {file = "orjson-3.10.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:24ac62336da9bda1bd93c0491eff0613003b48d3cb5d01470842e7b52a40d5b4"}, - {file = "orjson-3.10.10-cp313-none-win32.whl", hash = "sha256:eb0a42831372ec2b05acc9ee45af77bcaccbd91257345f93780a8e654efc75db"}, - {file = "orjson-3.10.10-cp313-none-win_amd64.whl", hash = "sha256:f0c4f37f8bf3f1075c6cc8dd8a9f843689a4b618628f8812d0a71e6968b95ffd"}, - {file = "orjson-3.10.10-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:829700cc18503efc0cf502d630f612884258020d98a317679cd2054af0259568"}, - {file = "orjson-3.10.10-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e0ceb5e0e8c4f010ac787d29ae6299846935044686509e2f0f06ed441c1ca949"}, - {file = "orjson-3.10.10-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0c25908eb86968613216f3db4d3003f1c45d78eb9046b71056ca327ff92bdbd4"}, - {file = "orjson-3.10.10-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:218cb0bc03340144b6328a9ff78f0932e642199ac184dd74b01ad691f42f93ff"}, - {file = "orjson-3.10.10-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e2277ec2cea3775640dc81ab5195bb5b2ada2fe0ea6eee4677474edc75ea6785"}, - {file = "orjson-3.10.10-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:848ea3b55ab5ccc9d7bbd420d69432628b691fba3ca8ae3148c35156cbd282aa"}, - {file = "orjson-3.10.10-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:e3e67b537ac0c835b25b5f7d40d83816abd2d3f4c0b0866ee981a045287a54f3"}, - {file = "orjson-3.10.10-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:7948cfb909353fce2135dcdbe4521a5e7e1159484e0bb024c1722f272488f2b8"}, - {file = "orjson-3.10.10-cp38-none-win32.whl", hash = "sha256:78bee66a988f1a333dc0b6257503d63553b1957889c17b2c4ed72385cd1b96ae"}, - {file = "orjson-3.10.10-cp38-none-win_amd64.whl", hash = "sha256:f1d647ca8d62afeb774340a343c7fc023efacfd3a39f70c798991063f0c681dd"}, - {file = "orjson-3.10.10-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:5a059afddbaa6dd733b5a2d76a90dbc8af790b993b1b5cb97a1176ca713b5df8"}, - {file = "orjson-3.10.10-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f9b5c59f7e2a1a410f971c5ebc68f1995822837cd10905ee255f96074537ee6"}, - {file = "orjson-3.10.10-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d5ef198bafdef4aa9d49a4165ba53ffdc0a9e1c7b6f76178572ab33118afea25"}, - {file = "orjson-3.10.10-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aaf29ce0bb5d3320824ec3d1508652421000ba466abd63bdd52c64bcce9eb1fa"}, - {file = "orjson-3.10.10-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dddd5516bcc93e723d029c1633ae79c4417477b4f57dad9bfeeb6bc0315e654a"}, - {file = "orjson-3.10.10-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a12f2003695b10817f0fa8b8fca982ed7f5761dcb0d93cff4f2f9f6709903fd7"}, - {file = "orjson-3.10.10-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:672f9874a8a8fb9bb1b771331d31ba27f57702c8106cdbadad8bda5d10bc1019"}, - {file = "orjson-3.10.10-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1dcbb0ca5fafb2b378b2c74419480ab2486326974826bbf6588f4dc62137570a"}, - {file = "orjson-3.10.10-cp39-none-win32.whl", hash = "sha256:d9bbd3a4b92256875cb058c3381b782649b9a3c68a4aa9a2fff020c2f9cfc1be"}, - {file = "orjson-3.10.10-cp39-none-win_amd64.whl", hash = "sha256:766f21487a53aee8524b97ca9582d5c6541b03ab6210fbaf10142ae2f3ced2aa"}, - {file = "orjson-3.10.10.tar.gz", hash = "sha256:37949383c4df7b4337ce82ee35b6d7471e55195efa7dcb45ab8226ceadb0fe3b"}, + {file = "orjson-3.10.12-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:ece01a7ec71d9940cc654c482907a6b65df27251255097629d0dea781f255c6d"}, + {file = "orjson-3.10.12-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c34ec9aebc04f11f4b978dd6caf697a2df2dd9b47d35aa4cc606cabcb9df69d7"}, + {file = "orjson-3.10.12-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fd6ec8658da3480939c79b9e9e27e0db31dffcd4ba69c334e98c9976ac29140e"}, + {file = "orjson-3.10.12-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f17e6baf4cf01534c9de8a16c0c611f3d94925d1701bf5f4aff17003677d8ced"}, + {file = "orjson-3.10.12-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6402ebb74a14ef96f94a868569f5dccf70d791de49feb73180eb3c6fda2ade56"}, + {file = "orjson-3.10.12-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0000758ae7c7853e0a4a6063f534c61656ebff644391e1f81698c1b2d2fc8cd2"}, + {file = "orjson-3.10.12-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:888442dcee99fd1e5bd37a4abb94930915ca6af4db50e23e746cdf4d1e63db13"}, + {file = "orjson-3.10.12-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c1f7a3ce79246aa0e92f5458d86c54f257fb5dfdc14a192651ba7ec2c00f8a05"}, + {file = "orjson-3.10.12-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:802a3935f45605c66fb4a586488a38af63cb37aaad1c1d94c982c40dcc452e85"}, + {file = "orjson-3.10.12-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:1da1ef0113a2be19bb6c557fb0ec2d79c92ebd2fed4cfb1b26bab93f021fb885"}, + {file = "orjson-3.10.12-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7a3273e99f367f137d5b3fecb5e9f45bcdbfac2a8b2f32fbc72129bbd48789c2"}, + {file = "orjson-3.10.12-cp310-none-win32.whl", hash = "sha256:475661bf249fd7907d9b0a2a2421b4e684355a77ceef85b8352439a9163418c3"}, + {file = "orjson-3.10.12-cp310-none-win_amd64.whl", hash = "sha256:87251dc1fb2b9e5ab91ce65d8f4caf21910d99ba8fb24b49fd0c118b2362d509"}, + {file = "orjson-3.10.12-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a734c62efa42e7df94926d70fe7d37621c783dea9f707a98cdea796964d4cf74"}, + {file = "orjson-3.10.12-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:750f8b27259d3409eda8350c2919a58b0cfcd2054ddc1bd317a643afc646ef23"}, + {file = "orjson-3.10.12-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bb52c22bfffe2857e7aa13b4622afd0dd9d16ea7cc65fd2bf318d3223b1b6252"}, + {file = "orjson-3.10.12-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:440d9a337ac8c199ff8251e100c62e9488924c92852362cd27af0e67308c16ef"}, + {file = "orjson-3.10.12-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9e15c06491c69997dfa067369baab3bf094ecb74be9912bdc4339972323f252"}, + {file = "orjson-3.10.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:362d204ad4b0b8724cf370d0cd917bb2dc913c394030da748a3bb632445ce7c4"}, + {file = "orjson-3.10.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2b57cbb4031153db37b41622eac67329c7810e5f480fda4cfd30542186f006ae"}, + {file = "orjson-3.10.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:165c89b53ef03ce0d7c59ca5c82fa65fe13ddf52eeb22e859e58c237d4e33b9b"}, + {file = "orjson-3.10.12-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:5dee91b8dfd54557c1a1596eb90bcd47dbcd26b0baaed919e6861f076583e9da"}, + {file = "orjson-3.10.12-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:77a4e1cfb72de6f905bdff061172adfb3caf7a4578ebf481d8f0530879476c07"}, + {file = "orjson-3.10.12-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:038d42c7bc0606443459b8fe2d1f121db474c49067d8d14c6a075bbea8bf14dd"}, + {file = "orjson-3.10.12-cp311-none-win32.whl", hash = "sha256:03b553c02ab39bed249bedd4abe37b2118324d1674e639b33fab3d1dafdf4d79"}, + {file = "orjson-3.10.12-cp311-none-win_amd64.whl", hash = "sha256:8b8713b9e46a45b2af6b96f559bfb13b1e02006f4242c156cbadef27800a55a8"}, + {file = "orjson-3.10.12-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:53206d72eb656ca5ac7d3a7141e83c5bbd3ac30d5eccfe019409177a57634b0d"}, + {file = "orjson-3.10.12-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac8010afc2150d417ebda810e8df08dd3f544e0dd2acab5370cfa6bcc0662f8f"}, + {file = "orjson-3.10.12-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed459b46012ae950dd2e17150e838ab08215421487371fa79d0eced8d1461d70"}, + {file = "orjson-3.10.12-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8dcb9673f108a93c1b52bfc51b0af422c2d08d4fc710ce9c839faad25020bb69"}, + {file = "orjson-3.10.12-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:22a51ae77680c5c4652ebc63a83d5255ac7d65582891d9424b566fb3b5375ee9"}, + {file = "orjson-3.10.12-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:910fdf2ac0637b9a77d1aad65f803bac414f0b06f720073438a7bd8906298192"}, + {file = "orjson-3.10.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:24ce85f7100160936bc2116c09d1a8492639418633119a2224114f67f63a4559"}, + {file = "orjson-3.10.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8a76ba5fc8dd9c913640292df27bff80a685bed3a3c990d59aa6ce24c352f8fc"}, + {file = "orjson-3.10.12-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ff70ef093895fd53f4055ca75f93f047e088d1430888ca1229393a7c0521100f"}, + {file = "orjson-3.10.12-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:f4244b7018b5753ecd10a6d324ec1f347da130c953a9c88432c7fbc8875d13be"}, + {file = "orjson-3.10.12-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:16135ccca03445f37921fa4b585cff9a58aa8d81ebcb27622e69bfadd220b32c"}, + {file = "orjson-3.10.12-cp312-none-win32.whl", hash = "sha256:2d879c81172d583e34153d524fcba5d4adafbab8349a7b9f16ae511c2cee8708"}, + {file = "orjson-3.10.12-cp312-none-win_amd64.whl", hash = "sha256:fc23f691fa0f5c140576b8c365bc942d577d861a9ee1142e4db468e4e17094fb"}, + {file = "orjson-3.10.12-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:47962841b2a8aa9a258b377f5188db31ba49af47d4003a32f55d6f8b19006543"}, + {file = "orjson-3.10.12-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6334730e2532e77b6054e87ca84f3072bee308a45a452ea0bffbbbc40a67e296"}, + {file = "orjson-3.10.12-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:accfe93f42713c899fdac2747e8d0d5c659592df2792888c6c5f829472e4f85e"}, + {file = "orjson-3.10.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a7974c490c014c48810d1dede6c754c3cc46598da758c25ca3b4001ac45b703f"}, + {file = "orjson-3.10.12-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:3f250ce7727b0b2682f834a3facff88e310f52f07a5dcfd852d99637d386e79e"}, + {file = "orjson-3.10.12-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f31422ff9486ae484f10ffc51b5ab2a60359e92d0716fcce1b3593d7bb8a9af6"}, + {file = "orjson-3.10.12-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5f29c5d282bb2d577c2a6bbde88d8fdcc4919c593f806aac50133f01b733846e"}, + {file = "orjson-3.10.12-cp313-none-win32.whl", hash = "sha256:f45653775f38f63dc0e6cd4f14323984c3149c05d6007b58cb154dd080ddc0dc"}, + {file = "orjson-3.10.12-cp313-none-win_amd64.whl", hash = "sha256:229994d0c376d5bdc91d92b3c9e6be2f1fbabd4cc1b59daae1443a46ee5e9825"}, + {file = "orjson-3.10.12-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:7d69af5b54617a5fac5c8e5ed0859eb798e2ce8913262eb522590239db6c6763"}, + {file = "orjson-3.10.12-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ed119ea7d2953365724a7059231a44830eb6bbb0cfead33fcbc562f5fd8f935"}, + {file = "orjson-3.10.12-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9c5fc1238ef197e7cad5c91415f524aaa51e004be5a9b35a1b8a84ade196f73f"}, + {file = "orjson-3.10.12-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:43509843990439b05f848539d6f6198d4ac86ff01dd024b2f9a795c0daeeab60"}, + {file = "orjson-3.10.12-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f72e27a62041cfb37a3de512247ece9f240a561e6c8662276beaf4d53d406db4"}, + {file = "orjson-3.10.12-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a904f9572092bb6742ab7c16c623f0cdccbad9eeb2d14d4aa06284867bddd31"}, + {file = "orjson-3.10.12-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:855c0833999ed5dc62f64552db26f9be767434917d8348d77bacaab84f787d7b"}, + {file = "orjson-3.10.12-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:897830244e2320f6184699f598df7fb9db9f5087d6f3f03666ae89d607e4f8ed"}, + {file = "orjson-3.10.12-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:0b32652eaa4a7539f6f04abc6243619c56f8530c53bf9b023e1269df5f7816dd"}, + {file = "orjson-3.10.12-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:36b4aa31e0f6a1aeeb6f8377769ca5d125db000f05c20e54163aef1d3fe8e833"}, + {file = "orjson-3.10.12-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:5535163054d6cbf2796f93e4f0dbc800f61914c0e3c4ed8499cf6ece22b4a3da"}, + {file = "orjson-3.10.12-cp38-none-win32.whl", hash = "sha256:90a5551f6f5a5fa07010bf3d0b4ca2de21adafbbc0af6cb700b63cd767266cb9"}, + {file = "orjson-3.10.12-cp38-none-win_amd64.whl", hash = "sha256:703a2fb35a06cdd45adf5d733cf613cbc0cb3ae57643472b16bc22d325b5fb6c"}, + {file = "orjson-3.10.12-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:f29de3ef71a42a5822765def1febfb36e0859d33abf5c2ad240acad5c6a1b78d"}, + {file = "orjson-3.10.12-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:de365a42acc65d74953f05e4772c974dad6c51cfc13c3240899f534d611be967"}, + {file = "orjson-3.10.12-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:91a5a0158648a67ff0004cb0df5df7dcc55bfc9ca154d9c01597a23ad54c8d0c"}, + {file = "orjson-3.10.12-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c47ce6b8d90fe9646a25b6fb52284a14ff215c9595914af63a5933a49972ce36"}, + {file = "orjson-3.10.12-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0eee4c2c5bfb5c1b47a5db80d2ac7aaa7e938956ae88089f098aff2c0f35d5d8"}, + {file = "orjson-3.10.12-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:35d3081bbe8b86587eb5c98a73b97f13d8f9fea685cf91a579beddacc0d10566"}, + {file = "orjson-3.10.12-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:73c23a6e90383884068bc2dba83d5222c9fcc3b99a0ed2411d38150734236755"}, + {file = "orjson-3.10.12-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5472be7dc3269b4b52acba1433dac239215366f89dc1d8d0e64029abac4e714e"}, + {file = "orjson-3.10.12-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:7319cda750fca96ae5973efb31b17d97a5c5225ae0bc79bf5bf84df9e1ec2ab6"}, + {file = "orjson-3.10.12-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:74d5ca5a255bf20b8def6a2b96b1e18ad37b4a122d59b154c458ee9494377f80"}, + {file = "orjson-3.10.12-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:ff31d22ecc5fb85ef62c7d4afe8301d10c558d00dd24274d4bbe464380d3cd69"}, + {file = "orjson-3.10.12-cp39-none-win32.whl", hash = "sha256:c22c3ea6fba91d84fcb4cda30e64aff548fcf0c44c876e681f47d61d24b12e6b"}, + {file = "orjson-3.10.12-cp39-none-win_amd64.whl", hash = "sha256:be604f60d45ace6b0b33dd990a66b4526f1a7a186ac411c942674625456ca548"}, + {file = "orjson-3.10.12.tar.gz", hash = "sha256:0a78bbda3aea0f9f079057ee1ee8a1ecf790d4f1af88dd67493c6b8ee52506ff"}, ] [[package]] @@ -1599,13 +1618,13 @@ files = [ [[package]] name = "packaging" -version = "24.1" +version = "24.2" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, - {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, + {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, + {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, ] [[package]] @@ -1681,13 +1700,13 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "prometheus-client" -version = "0.21.0" +version = "0.21.1" description = "Python client for the Prometheus monitoring system." optional = false python-versions = ">=3.8" files = [ - {file = "prometheus_client-0.21.0-py3-none-any.whl", hash = "sha256:4fa6b4dd0ac16d58bb587c04b1caae65b8c5043e85f778f42f5f632f6af2e166"}, - {file = "prometheus_client-0.21.0.tar.gz", hash = "sha256:96c83c606b71ff2b0a433c98889d275f51ffec6c5e267de37c7a2b5c9aa9233e"}, + {file = "prometheus_client-0.21.1-py3-none-any.whl", hash = "sha256:594b45c410d6f4f8888940fe80b5cc2521b305a1fafe1c58609ef715a001f301"}, + {file = "prometheus_client-0.21.1.tar.gz", hash = "sha256:252505a722ac04b0456be05c05f75f45d760c2911ffc45f2a06bcaed9f3ae3fb"}, ] [package.extras] @@ -1775,22 +1794,19 @@ files = [ [[package]] name = "pydantic" -version = "2.9.2" +version = "2.10.3" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic-2.9.2-py3-none-any.whl", hash = "sha256:f048cec7b26778210e28a0459867920654d48e5e62db0958433636cde4254f12"}, - {file = "pydantic-2.9.2.tar.gz", hash = "sha256:d155cef71265d1e9807ed1c32b4c8deec042a44a50a4188b25ac67ecd81a9c0f"}, + {file = "pydantic-2.10.3-py3-none-any.whl", hash = "sha256:be04d85bbc7b65651c5f8e6b9976ed9c6f41782a55524cef079a34a0bb82144d"}, + {file = "pydantic-2.10.3.tar.gz", hash = "sha256:cb5ac360ce894ceacd69c403187900a02c4b20b693a9dd1d643e1effab9eadf9"}, ] [package.dependencies] annotated-types = ">=0.6.0" -pydantic-core = "2.23.4" -typing-extensions = [ - {version = ">=4.6.1", markers = "python_version < \"3.13\""}, - {version = ">=4.12.2", markers = "python_version >= \"3.13\""}, -] +pydantic-core = "2.27.1" +typing-extensions = ">=4.12.2" [package.extras] email = ["email-validator (>=2.0.0)"] @@ -1798,100 +1814,111 @@ timezone = ["tzdata"] [[package]] name = "pydantic-core" -version = "2.23.4" +version = "2.27.1" description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.8" files = [ - {file = "pydantic_core-2.23.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:b10bd51f823d891193d4717448fab065733958bdb6a6b351967bd349d48d5c9b"}, - {file = "pydantic_core-2.23.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4fc714bdbfb534f94034efaa6eadd74e5b93c8fa6315565a222f7b6f42ca1166"}, - {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63e46b3169866bd62849936de036f901a9356e36376079b05efa83caeaa02ceb"}, - {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed1a53de42fbe34853ba90513cea21673481cd81ed1be739f7f2efb931b24916"}, - {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cfdd16ab5e59fc31b5e906d1a3f666571abc367598e3e02c83403acabc092e07"}, - {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255a8ef062cbf6674450e668482456abac99a5583bbafb73f9ad469540a3a232"}, - {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a7cd62e831afe623fbb7aabbb4fe583212115b3ef38a9f6b71869ba644624a2"}, - {file = "pydantic_core-2.23.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f09e2ff1f17c2b51f2bc76d1cc33da96298f0a036a137f5440ab3ec5360b624f"}, - {file = "pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e38e63e6f3d1cec5a27e0afe90a085af8b6806ee208b33030e65b6516353f1a3"}, - {file = "pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0dbd8dbed2085ed23b5c04afa29d8fd2771674223135dc9bc937f3c09284d071"}, - {file = "pydantic_core-2.23.4-cp310-none-win32.whl", hash = "sha256:6531b7ca5f951d663c339002e91aaebda765ec7d61b7d1e3991051906ddde119"}, - {file = "pydantic_core-2.23.4-cp310-none-win_amd64.whl", hash = "sha256:7c9129eb40958b3d4500fa2467e6a83356b3b61bfff1b414c7361d9220f9ae8f"}, - {file = "pydantic_core-2.23.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:77733e3892bb0a7fa797826361ce8a9184d25c8dffaec60b7ffe928153680ba8"}, - {file = "pydantic_core-2.23.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b84d168f6c48fabd1f2027a3d1bdfe62f92cade1fb273a5d68e621da0e44e6d"}, - {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df49e7a0861a8c36d089c1ed57d308623d60416dab2647a4a17fe050ba85de0e"}, - {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ff02b6d461a6de369f07ec15e465a88895f3223eb75073ffea56b84d9331f607"}, - {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:996a38a83508c54c78a5f41456b0103c30508fed9abcad0a59b876d7398f25fd"}, - {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d97683ddee4723ae8c95d1eddac7c192e8c552da0c73a925a89fa8649bf13eea"}, - {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:216f9b2d7713eb98cb83c80b9c794de1f6b7e3145eef40400c62e86cee5f4e1e"}, - {file = "pydantic_core-2.23.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6f783e0ec4803c787bcea93e13e9932edab72068f68ecffdf86a99fd5918878b"}, - {file = "pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d0776dea117cf5272382634bd2a5c1b6eb16767c223c6a5317cd3e2a757c61a0"}, - {file = "pydantic_core-2.23.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d5f7a395a8cf1621939692dba2a6b6a830efa6b3cee787d82c7de1ad2930de64"}, - {file = "pydantic_core-2.23.4-cp311-none-win32.whl", hash = "sha256:74b9127ffea03643e998e0c5ad9bd3811d3dac8c676e47db17b0ee7c3c3bf35f"}, - {file = "pydantic_core-2.23.4-cp311-none-win_amd64.whl", hash = "sha256:98d134c954828488b153d88ba1f34e14259284f256180ce659e8d83e9c05eaa3"}, - {file = "pydantic_core-2.23.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f3e0da4ebaef65158d4dfd7d3678aad692f7666877df0002b8a522cdf088f231"}, - {file = "pydantic_core-2.23.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f69a8e0b033b747bb3e36a44e7732f0c99f7edd5cea723d45bc0d6e95377ffee"}, - {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:723314c1d51722ab28bfcd5240d858512ffd3116449c557a1336cbe3919beb87"}, - {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bb2802e667b7051a1bebbfe93684841cc9351004e2badbd6411bf357ab8d5ac8"}, - {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d18ca8148bebe1b0a382a27a8ee60350091a6ddaf475fa05ef50dc35b5df6327"}, - {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33e3d65a85a2a4a0dc3b092b938a4062b1a05f3a9abde65ea93b233bca0e03f2"}, - {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:128585782e5bfa515c590ccee4b727fb76925dd04a98864182b22e89a4e6ed36"}, - {file = "pydantic_core-2.23.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:68665f4c17edcceecc112dfed5dbe6f92261fb9d6054b47d01bf6371a6196126"}, - {file = "pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:20152074317d9bed6b7a95ade3b7d6054845d70584216160860425f4fbd5ee9e"}, - {file = "pydantic_core-2.23.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9261d3ce84fa1d38ed649c3638feefeae23d32ba9182963e465d58d62203bd24"}, - {file = "pydantic_core-2.23.4-cp312-none-win32.whl", hash = "sha256:4ba762ed58e8d68657fc1281e9bb72e1c3e79cc5d464be146e260c541ec12d84"}, - {file = "pydantic_core-2.23.4-cp312-none-win_amd64.whl", hash = "sha256:97df63000f4fea395b2824da80e169731088656d1818a11b95f3b173747b6cd9"}, - {file = "pydantic_core-2.23.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7530e201d10d7d14abce4fb54cfe5b94a0aefc87da539d0346a484ead376c3cc"}, - {file = "pydantic_core-2.23.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:df933278128ea1cd77772673c73954e53a1c95a4fdf41eef97c2b779271bd0bd"}, - {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cb3da3fd1b6a5d0279a01877713dbda118a2a4fc6f0d821a57da2e464793f05"}, - {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c6dcb030aefb668a2b7009c85b27f90e51e6a3b4d5c9bc4c57631292015b0d"}, - {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:696dd8d674d6ce621ab9d45b205df149399e4bb9aa34102c970b721554828510"}, - {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2971bb5ffe72cc0f555c13e19b23c85b654dd2a8f7ab493c262071377bfce9f6"}, - {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8394d940e5d400d04cad4f75c0598665cbb81aecefaca82ca85bd28264af7f9b"}, - {file = "pydantic_core-2.23.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0dff76e0602ca7d4cdaacc1ac4c005e0ce0dcfe095d5b5259163a80d3a10d327"}, - {file = "pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7d32706badfe136888bdea71c0def994644e09fff0bfe47441deaed8e96fdbc6"}, - {file = "pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ed541d70698978a20eb63d8c5d72f2cc6d7079d9d90f6b50bad07826f1320f5f"}, - {file = "pydantic_core-2.23.4-cp313-none-win32.whl", hash = "sha256:3d5639516376dce1940ea36edf408c554475369f5da2abd45d44621cb616f769"}, - {file = "pydantic_core-2.23.4-cp313-none-win_amd64.whl", hash = "sha256:5a1504ad17ba4210df3a045132a7baeeba5a200e930f57512ee02909fc5c4cb5"}, - {file = "pydantic_core-2.23.4-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d4488a93b071c04dc20f5cecc3631fc78b9789dd72483ba15d423b5b3689b555"}, - {file = "pydantic_core-2.23.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:81965a16b675b35e1d09dd14df53f190f9129c0202356ed44ab2728b1c905658"}, - {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ffa2ebd4c8530079140dd2d7f794a9d9a73cbb8e9d59ffe24c63436efa8f271"}, - {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:61817945f2fe7d166e75fbfb28004034b48e44878177fc54d81688e7b85a3665"}, - {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:29d2c342c4bc01b88402d60189f3df065fb0dda3654744d5a165a5288a657368"}, - {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5e11661ce0fd30a6790e8bcdf263b9ec5988e95e63cf901972107efc49218b13"}, - {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d18368b137c6295db49ce7218b1a9ba15c5bc254c96d7c9f9e924a9bc7825ad"}, - {file = "pydantic_core-2.23.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ec4e55f79b1c4ffb2eecd8a0cfba9955a2588497d96851f4c8f99aa4a1d39b12"}, - {file = "pydantic_core-2.23.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:374a5e5049eda9e0a44c696c7ade3ff355f06b1fe0bb945ea3cac2bc336478a2"}, - {file = "pydantic_core-2.23.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5c364564d17da23db1106787675fc7af45f2f7b58b4173bfdd105564e132e6fb"}, - {file = "pydantic_core-2.23.4-cp38-none-win32.whl", hash = "sha256:d7a80d21d613eec45e3d41eb22f8f94ddc758a6c4720842dc74c0581f54993d6"}, - {file = "pydantic_core-2.23.4-cp38-none-win_amd64.whl", hash = "sha256:5f5ff8d839f4566a474a969508fe1c5e59c31c80d9e140566f9a37bba7b8d556"}, - {file = "pydantic_core-2.23.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a4fa4fc04dff799089689f4fd502ce7d59de529fc2f40a2c8836886c03e0175a"}, - {file = "pydantic_core-2.23.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0a7df63886be5e270da67e0966cf4afbae86069501d35c8c1b3b6c168f42cb36"}, - {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcedcd19a557e182628afa1d553c3895a9f825b936415d0dbd3cd0bbcfd29b4b"}, - {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f54b118ce5de9ac21c363d9b3caa6c800341e8c47a508787e5868c6b79c9323"}, - {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86d2f57d3e1379a9525c5ab067b27dbb8a0642fb5d454e17a9ac434f9ce523e3"}, - {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:de6d1d1b9e5101508cb37ab0d972357cac5235f5c6533d1071964c47139257df"}, - {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1278e0d324f6908e872730c9102b0112477a7f7cf88b308e4fc36ce1bdb6d58c"}, - {file = "pydantic_core-2.23.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9a6b5099eeec78827553827f4c6b8615978bb4b6a88e5d9b93eddf8bb6790f55"}, - {file = "pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e55541f756f9b3ee346b840103f32779c695a19826a4c442b7954550a0972040"}, - {file = "pydantic_core-2.23.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a5c7ba8ffb6d6f8f2ab08743be203654bb1aaa8c9dcb09f82ddd34eadb695605"}, - {file = "pydantic_core-2.23.4-cp39-none-win32.whl", hash = "sha256:37b0fe330e4a58d3c58b24d91d1eb102aeec675a3db4c292ec3928ecd892a9a6"}, - {file = "pydantic_core-2.23.4-cp39-none-win_amd64.whl", hash = "sha256:1498bec4c05c9c787bde9125cfdcc63a41004ff167f495063191b863399b1a29"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f455ee30a9d61d3e1a15abd5068827773d6e4dc513e795f380cdd59932c782d5"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1e90d2e3bd2c3863d48525d297cd143fe541be8bbf6f579504b9712cb6b643ec"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e203fdf807ac7e12ab59ca2bfcabb38c7cf0b33c41efeb00f8e5da1d86af480"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e08277a400de01bc72436a0ccd02bdf596631411f592ad985dcee21445bd0068"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f220b0eea5965dec25480b6333c788fb72ce5f9129e8759ef876a1d805d00801"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d06b0c8da4f16d1d1e352134427cb194a0a6e19ad5db9161bf32b2113409e728"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ba1a0996f6c2773bd83e63f18914c1de3c9dd26d55f4ac302a7efe93fb8e7433"}, - {file = "pydantic_core-2.23.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:9a5bce9d23aac8f0cf0836ecfc033896aa8443b501c58d0602dbfd5bd5b37753"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:78ddaaa81421a29574a682b3179d4cf9e6d405a09b99d93ddcf7e5239c742e21"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:883a91b5dd7d26492ff2f04f40fbb652de40fcc0afe07e8129e8ae779c2110eb"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88ad334a15b32a791ea935af224b9de1bf99bcd62fabf745d5f3442199d86d59"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:233710f069d251feb12a56da21e14cca67994eab08362207785cf8c598e74577"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:19442362866a753485ba5e4be408964644dd6a09123d9416c54cd49171f50744"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:624e278a7d29b6445e4e813af92af37820fafb6dcc55c012c834f9e26f9aaaef"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f5ef8f42bec47f21d07668a043f077d507e5bf4e668d5c6dfe6aaba89de1a5b8"}, - {file = "pydantic_core-2.23.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:aea443fffa9fbe3af1a9ba721a87f926fe548d32cab71d188a6ede77d0ff244e"}, - {file = "pydantic_core-2.23.4.tar.gz", hash = "sha256:2584f7cf844ac4d970fba483a717dbe10c1c1c96a969bf65d61ffe94df1b2863"}, + {file = "pydantic_core-2.27.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:71a5e35c75c021aaf400ac048dacc855f000bdfed91614b4a726f7432f1f3d6a"}, + {file = "pydantic_core-2.27.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f82d068a2d6ecfc6e054726080af69a6764a10015467d7d7b9f66d6ed5afa23b"}, + {file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:121ceb0e822f79163dd4699e4c54f5ad38b157084d97b34de8b232bcaad70278"}, + {file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4603137322c18eaf2e06a4495f426aa8d8388940f3c457e7548145011bb68e05"}, + {file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a33cd6ad9017bbeaa9ed78a2e0752c5e250eafb9534f308e7a5f7849b0b1bfb4"}, + {file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15cc53a3179ba0fcefe1e3ae50beb2784dede4003ad2dfd24f81bba4b23a454f"}, + {file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45d9c5eb9273aa50999ad6adc6be5e0ecea7e09dbd0d31bd0c65a55a2592ca08"}, + {file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8bf7b66ce12a2ac52d16f776b31d16d91033150266eb796967a7e4621707e4f6"}, + {file = "pydantic_core-2.27.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:655d7dd86f26cb15ce8a431036f66ce0318648f8853d709b4167786ec2fa4807"}, + {file = "pydantic_core-2.27.1-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:5556470f1a2157031e676f776c2bc20acd34c1990ca5f7e56f1ebf938b9ab57c"}, + {file = "pydantic_core-2.27.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f69ed81ab24d5a3bd93861c8c4436f54afdf8e8cc421562b0c7504cf3be58206"}, + {file = "pydantic_core-2.27.1-cp310-none-win32.whl", hash = "sha256:f5a823165e6d04ccea61a9f0576f345f8ce40ed533013580e087bd4d7442b52c"}, + {file = "pydantic_core-2.27.1-cp310-none-win_amd64.whl", hash = "sha256:57866a76e0b3823e0b56692d1a0bf722bffb324839bb5b7226a7dbd6c9a40b17"}, + {file = "pydantic_core-2.27.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:ac3b20653bdbe160febbea8aa6c079d3df19310d50ac314911ed8cc4eb7f8cb8"}, + {file = "pydantic_core-2.27.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a5a8e19d7c707c4cadb8c18f5f60c843052ae83c20fa7d44f41594c644a1d330"}, + {file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f7059ca8d64fea7f238994c97d91f75965216bcbe5f695bb44f354893f11d52"}, + {file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bed0f8a0eeea9fb72937ba118f9db0cb7e90773462af7962d382445f3005e5a4"}, + {file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a3cb37038123447cf0f3ea4c74751f6a9d7afef0eb71aa07bf5f652b5e6a132c"}, + {file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:84286494f6c5d05243456e04223d5a9417d7f443c3b76065e75001beb26f88de"}, + {file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acc07b2cfc5b835444b44a9956846b578d27beeacd4b52e45489e93276241025"}, + {file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4fefee876e07a6e9aad7a8c8c9f85b0cdbe7df52b8a9552307b09050f7512c7e"}, + {file = "pydantic_core-2.27.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:258c57abf1188926c774a4c94dd29237e77eda19462e5bb901d88adcab6af919"}, + {file = "pydantic_core-2.27.1-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:35c14ac45fcfdf7167ca76cc80b2001205a8d5d16d80524e13508371fb8cdd9c"}, + {file = "pydantic_core-2.27.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d1b26e1dff225c31897696cab7d4f0a315d4c0d9e8666dbffdb28216f3b17fdc"}, + {file = "pydantic_core-2.27.1-cp311-none-win32.whl", hash = "sha256:2cdf7d86886bc6982354862204ae3b2f7f96f21a3eb0ba5ca0ac42c7b38598b9"}, + {file = "pydantic_core-2.27.1-cp311-none-win_amd64.whl", hash = "sha256:3af385b0cee8df3746c3f406f38bcbfdc9041b5c2d5ce3e5fc6637256e60bbc5"}, + {file = "pydantic_core-2.27.1-cp311-none-win_arm64.whl", hash = "sha256:81f2ec23ddc1b476ff96563f2e8d723830b06dceae348ce02914a37cb4e74b89"}, + {file = "pydantic_core-2.27.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9cbd94fc661d2bab2bc702cddd2d3370bbdcc4cd0f8f57488a81bcce90c7a54f"}, + {file = "pydantic_core-2.27.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5f8c4718cd44ec1580e180cb739713ecda2bdee1341084c1467802a417fe0f02"}, + {file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:15aae984e46de8d376df515f00450d1522077254ef6b7ce189b38ecee7c9677c"}, + {file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1ba5e3963344ff25fc8c40da90f44b0afca8cfd89d12964feb79ac1411a260ac"}, + {file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:992cea5f4f3b29d6b4f7f1726ed8ee46c8331c6b4eed6db5b40134c6fe1768bb"}, + {file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0325336f348dbee6550d129b1627cb8f5351a9dc91aad141ffb96d4937bd9529"}, + {file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7597c07fbd11515f654d6ece3d0e4e5093edc30a436c63142d9a4b8e22f19c35"}, + {file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3bbd5d8cc692616d5ef6fbbbd50dbec142c7e6ad9beb66b78a96e9c16729b089"}, + {file = "pydantic_core-2.27.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:dc61505e73298a84a2f317255fcc72b710b72980f3a1f670447a21efc88f8381"}, + {file = "pydantic_core-2.27.1-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:e1f735dc43da318cad19b4173dd1ffce1d84aafd6c9b782b3abc04a0d5a6f5bb"}, + {file = "pydantic_core-2.27.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:f4e5658dbffe8843a0f12366a4c2d1c316dbe09bb4dfbdc9d2d9cd6031de8aae"}, + {file = "pydantic_core-2.27.1-cp312-none-win32.whl", hash = "sha256:672ebbe820bb37988c4d136eca2652ee114992d5d41c7e4858cdd90ea94ffe5c"}, + {file = "pydantic_core-2.27.1-cp312-none-win_amd64.whl", hash = "sha256:66ff044fd0bb1768688aecbe28b6190f6e799349221fb0de0e6f4048eca14c16"}, + {file = "pydantic_core-2.27.1-cp312-none-win_arm64.whl", hash = "sha256:9a3b0793b1bbfd4146304e23d90045f2a9b5fd5823aa682665fbdaf2a6c28f3e"}, + {file = "pydantic_core-2.27.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f216dbce0e60e4d03e0c4353c7023b202d95cbaeff12e5fd2e82ea0a66905073"}, + {file = "pydantic_core-2.27.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a2e02889071850bbfd36b56fd6bc98945e23670773bc7a76657e90e6b6603c08"}, + {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42b0e23f119b2b456d07ca91b307ae167cc3f6c846a7b169fca5326e32fdc6cf"}, + {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:764be71193f87d460a03f1f7385a82e226639732214b402f9aa61f0d025f0737"}, + {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1c00666a3bd2f84920a4e94434f5974d7bbc57e461318d6bb34ce9cdbbc1f6b2"}, + {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ccaa88b24eebc0f849ce0a4d09e8a408ec5a94afff395eb69baf868f5183107"}, + {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c65af9088ac534313e1963443d0ec360bb2b9cba6c2909478d22c2e363d98a51"}, + {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:206b5cf6f0c513baffaeae7bd817717140770c74528f3e4c3e1cec7871ddd61a"}, + {file = "pydantic_core-2.27.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:062f60e512fc7fff8b8a9d680ff0ddaaef0193dba9fa83e679c0c5f5fbd018bc"}, + {file = "pydantic_core-2.27.1-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:a0697803ed7d4af5e4c1adf1670af078f8fcab7a86350e969f454daf598c4960"}, + {file = "pydantic_core-2.27.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:58ca98a950171f3151c603aeea9303ef6c235f692fe555e883591103da709b23"}, + {file = "pydantic_core-2.27.1-cp313-none-win32.whl", hash = "sha256:8065914ff79f7eab1599bd80406681f0ad08f8e47c880f17b416c9f8f7a26d05"}, + {file = "pydantic_core-2.27.1-cp313-none-win_amd64.whl", hash = "sha256:ba630d5e3db74c79300d9a5bdaaf6200172b107f263c98a0539eeecb857b2337"}, + {file = "pydantic_core-2.27.1-cp313-none-win_arm64.whl", hash = "sha256:45cf8588c066860b623cd11c4ba687f8d7175d5f7ef65f7129df8a394c502de5"}, + {file = "pydantic_core-2.27.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:5897bec80a09b4084aee23f9b73a9477a46c3304ad1d2d07acca19723fb1de62"}, + {file = "pydantic_core-2.27.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d0165ab2914379bd56908c02294ed8405c252250668ebcb438a55494c69f44ab"}, + {file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b9af86e1d8e4cfc82c2022bfaa6f459381a50b94a29e95dcdda8442d6d83864"}, + {file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f6c8a66741c5f5447e047ab0ba7a1c61d1e95580d64bce852e3df1f895c4067"}, + {file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a42d6a8156ff78981f8aa56eb6394114e0dedb217cf8b729f438f643608cbcd"}, + {file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:64c65f40b4cd8b0e049a8edde07e38b476da7e3aaebe63287c899d2cff253fa5"}, + {file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdcf339322a3fae5cbd504edcefddd5a50d9ee00d968696846f089b4432cf78"}, + {file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bf99c8404f008750c846cb4ac4667b798a9f7de673ff719d705d9b2d6de49c5f"}, + {file = "pydantic_core-2.27.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8f1edcea27918d748c7e5e4d917297b2a0ab80cad10f86631e488b7cddf76a36"}, + {file = "pydantic_core-2.27.1-cp38-cp38-musllinux_1_1_armv7l.whl", hash = "sha256:159cac0a3d096f79ab6a44d77a961917219707e2a130739c64d4dd46281f5c2a"}, + {file = "pydantic_core-2.27.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:029d9757eb621cc6e1848fa0b0310310de7301057f623985698ed7ebb014391b"}, + {file = "pydantic_core-2.27.1-cp38-none-win32.whl", hash = "sha256:a28af0695a45f7060e6f9b7092558a928a28553366519f64083c63a44f70e618"}, + {file = "pydantic_core-2.27.1-cp38-none-win_amd64.whl", hash = "sha256:2d4567c850905d5eaaed2f7a404e61012a51caf288292e016360aa2b96ff38d4"}, + {file = "pydantic_core-2.27.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:e9386266798d64eeb19dd3677051f5705bf873e98e15897ddb7d76f477131967"}, + {file = "pydantic_core-2.27.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4228b5b646caa73f119b1ae756216b59cc6e2267201c27d3912b592c5e323b60"}, + {file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b3dfe500de26c52abe0477dde16192ac39c98f05bf2d80e76102d394bd13854"}, + {file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:aee66be87825cdf72ac64cb03ad4c15ffef4143dbf5c113f64a5ff4f81477bf9"}, + {file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b748c44bb9f53031c8cbc99a8a061bc181c1000c60a30f55393b6e9c45cc5bd"}, + {file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ca038c7f6a0afd0b2448941b6ef9d5e1949e999f9e5517692eb6da58e9d44be"}, + {file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e0bd57539da59a3e4671b90a502da9a28c72322a4f17866ba3ac63a82c4498e"}, + {file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ac6c2c45c847bbf8f91930d88716a0fb924b51e0c6dad329b793d670ec5db792"}, + {file = "pydantic_core-2.27.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b94d4ba43739bbe8b0ce4262bcc3b7b9f31459ad120fb595627eaeb7f9b9ca01"}, + {file = "pydantic_core-2.27.1-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:00e6424f4b26fe82d44577b4c842d7df97c20be6439e8e685d0d715feceb9fb9"}, + {file = "pydantic_core-2.27.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:38de0a70160dd97540335b7ad3a74571b24f1dc3ed33f815f0880682e6880131"}, + {file = "pydantic_core-2.27.1-cp39-none-win32.whl", hash = "sha256:7ccebf51efc61634f6c2344da73e366c75e735960b5654b63d7e6f69a5885fa3"}, + {file = "pydantic_core-2.27.1-cp39-none-win_amd64.whl", hash = "sha256:a57847b090d7892f123726202b7daa20df6694cbd583b67a592e856bff603d6c"}, + {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3fa80ac2bd5856580e242dbc202db873c60a01b20309c8319b5c5986fbe53ce6"}, + {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d950caa237bb1954f1b8c9227b5065ba6875ac9771bb8ec790d956a699b78676"}, + {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e4216e64d203e39c62df627aa882f02a2438d18a5f21d7f721621f7a5d3611d"}, + {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02a3d637bd387c41d46b002f0e49c52642281edacd2740e5a42f7017feea3f2c"}, + {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:161c27ccce13b6b0c8689418da3885d3220ed2eae2ea5e9b2f7f3d48f1d52c27"}, + {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:19910754e4cc9c63bc1c7f6d73aa1cfee82f42007e407c0f413695c2f7ed777f"}, + {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:e173486019cc283dc9778315fa29a363579372fe67045e971e89b6365cc035ed"}, + {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:af52d26579b308921b73b956153066481f064875140ccd1dfd4e77db89dbb12f"}, + {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:981fb88516bd1ae8b0cbbd2034678a39dedc98752f264ac9bc5839d3923fa04c"}, + {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5fde892e6c697ce3e30c61b239330fc5d569a71fefd4eb6512fc6caec9dd9e2f"}, + {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:816f5aa087094099fff7edabb5e01cc370eb21aa1a1d44fe2d2aefdfb5599b31"}, + {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c10c309e18e443ddb108f0ef64e8729363adbfd92d6d57beec680f6261556f3"}, + {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98476c98b02c8e9b2eec76ac4156fd006628b1b2d0ef27e548ffa978393fd154"}, + {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c3027001c28434e7ca5a6e1e527487051136aa81803ac812be51802150d880dd"}, + {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:7699b1df36a48169cdebda7ab5a2bac265204003f153b4bd17276153d997670a"}, + {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:1c39b07d90be6b48968ddc8c19e7585052088fd7ec8d568bb31ff64c70ae3c97"}, + {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:46ccfe3032b3915586e469d4972973f893c0a2bb65669194a5bdea9bacc088c2"}, + {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:62ba45e21cf6571d7f716d903b5b7b6d2617e2d5d67c0923dc47b9d41369f840"}, + {file = "pydantic_core-2.27.1.tar.gz", hash = "sha256:62a763352879b84aa31058fc931884055fd75089cccbd9d58bb6afd01141b235"}, ] [package.dependencies] @@ -1913,13 +1940,13 @@ windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pytest" -version = "8.3.3" +version = "8.3.4" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2"}, - {file = "pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181"}, + {file = "pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6"}, + {file = "pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761"}, ] [package.dependencies] @@ -1999,15 +2026,21 @@ six = ">=1.5" [[package]] name = "python-json-logger" -version = "2.0.7" -description = "A python library adding a json log formatter" +version = "3.2.0" +description = "JSON Log Formatter for the Python Logging Package" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "python-json-logger-2.0.7.tar.gz", hash = "sha256:23e7ec02d34237c5aa1e29a070193a4ea87583bb4e7f8fd06d3de8264c4b2e1c"}, - {file = "python_json_logger-2.0.7-py3-none-any.whl", hash = "sha256:f380b826a991ebbe3de4d897aeec42760035ac760345e57b812938dc8b35e2bd"}, + {file = "python_json_logger-3.2.0-py3-none-any.whl", hash = "sha256:d73522ddcfc6d0461394120feaddea9025dc64bf804d96357dd42fa878cc5fe8"}, + {file = "python_json_logger-3.2.0.tar.gz", hash = "sha256:2c11056458d3f56614480b24e9cb28f7aba69cbfbebddbb77c92f0ec0d4947ab"}, ] +[package.dependencies] +typing_extensions = {version = "*", markers = "python_version < \"3.10\""} + +[package.extras] +dev = ["backports.zoneinfo", "black", "build", "freezegun", "mdx_truly_sane_lists", "mike", "mkdocs", "mkdocs-awesome-pages-plugin", "mkdocs-gen-files", "mkdocs-literate-nav", "mkdocs-material (>=8.5)", "mkdocstrings[python]", "msgspec", "msgspec-python313-pre", "mypy", "orjson", "pylint", "pytest", "tzdata", "validate-pyproject[all]"] + [[package]] name = "pywin32" version = "308" @@ -2250,105 +2283,105 @@ rpds-py = ">=0.7.0" [[package]] name = "regex" -version = "2024.9.11" +version = "2024.11.6" description = "Alternative regular expression module, to replace re." optional = false python-versions = ">=3.8" files = [ - {file = "regex-2024.9.11-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1494fa8725c285a81d01dc8c06b55287a1ee5e0e382d8413adc0a9197aac6408"}, - {file = "regex-2024.9.11-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0e12c481ad92d129c78f13a2a3662317e46ee7ef96c94fd332e1c29131875b7d"}, - {file = "regex-2024.9.11-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:16e13a7929791ac1216afde26f712802e3df7bf0360b32e4914dca3ab8baeea5"}, - {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46989629904bad940bbec2106528140a218b4a36bb3042d8406980be1941429c"}, - {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a906ed5e47a0ce5f04b2c981af1c9acf9e8696066900bf03b9d7879a6f679fc8"}, - {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e9a091b0550b3b0207784a7d6d0f1a00d1d1c8a11699c1a4d93db3fbefc3ad35"}, - {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ddcd9a179c0a6fa8add279a4444015acddcd7f232a49071ae57fa6e278f1f71"}, - {file = "regex-2024.9.11-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6b41e1adc61fa347662b09398e31ad446afadff932a24807d3ceb955ed865cc8"}, - {file = "regex-2024.9.11-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ced479f601cd2f8ca1fd7b23925a7e0ad512a56d6e9476f79b8f381d9d37090a"}, - {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:635a1d96665f84b292e401c3d62775851aedc31d4f8784117b3c68c4fcd4118d"}, - {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c0256beda696edcf7d97ef16b2a33a8e5a875affd6fa6567b54f7c577b30a137"}, - {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:3ce4f1185db3fbde8ed8aa223fc9620f276c58de8b0d4f8cc86fd1360829edb6"}, - {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:09d77559e80dcc9d24570da3745ab859a9cf91953062e4ab126ba9d5993688ca"}, - {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7a22ccefd4db3f12b526eccb129390942fe874a3a9fdbdd24cf55773a1faab1a"}, - {file = "regex-2024.9.11-cp310-cp310-win32.whl", hash = "sha256:f745ec09bc1b0bd15cfc73df6fa4f726dcc26bb16c23a03f9e3367d357eeedd0"}, - {file = "regex-2024.9.11-cp310-cp310-win_amd64.whl", hash = "sha256:01c2acb51f8a7d6494c8c5eafe3d8e06d76563d8a8a4643b37e9b2dd8a2ff623"}, - {file = "regex-2024.9.11-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2cce2449e5927a0bf084d346da6cd5eb016b2beca10d0013ab50e3c226ffc0df"}, - {file = "regex-2024.9.11-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3b37fa423beefa44919e009745ccbf353d8c981516e807995b2bd11c2c77d268"}, - {file = "regex-2024.9.11-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:64ce2799bd75039b480cc0360907c4fb2f50022f030bf9e7a8705b636e408fad"}, - {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a4cc92bb6db56ab0c1cbd17294e14f5e9224f0cc6521167ef388332604e92679"}, - {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d05ac6fa06959c4172eccd99a222e1fbf17b5670c4d596cb1e5cde99600674c4"}, - {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:040562757795eeea356394a7fb13076ad4f99d3c62ab0f8bdfb21f99a1f85664"}, - {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6113c008a7780792efc80f9dfe10ba0cd043cbf8dc9a76ef757850f51b4edc50"}, - {file = "regex-2024.9.11-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8e5fb5f77c8745a60105403a774fe2c1759b71d3e7b4ca237a5e67ad066c7199"}, - {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:54d9ff35d4515debf14bc27f1e3b38bfc453eff3220f5bce159642fa762fe5d4"}, - {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:df5cbb1fbc74a8305b6065d4ade43b993be03dbe0f8b30032cced0d7740994bd"}, - {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:7fb89ee5d106e4a7a51bce305ac4efb981536301895f7bdcf93ec92ae0d91c7f"}, - {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:a738b937d512b30bf75995c0159c0ddf9eec0775c9d72ac0202076c72f24aa96"}, - {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e28f9faeb14b6f23ac55bfbbfd3643f5c7c18ede093977f1df249f73fd22c7b1"}, - {file = "regex-2024.9.11-cp311-cp311-win32.whl", hash = "sha256:18e707ce6c92d7282dfce370cd205098384b8ee21544e7cb29b8aab955b66fa9"}, - {file = "regex-2024.9.11-cp311-cp311-win_amd64.whl", hash = "sha256:313ea15e5ff2a8cbbad96ccef6be638393041b0a7863183c2d31e0c6116688cf"}, - {file = "regex-2024.9.11-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b0d0a6c64fcc4ef9c69bd5b3b3626cc3776520a1637d8abaa62b9edc147a58f7"}, - {file = "regex-2024.9.11-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:49b0e06786ea663f933f3710a51e9385ce0cba0ea56b67107fd841a55d56a231"}, - {file = "regex-2024.9.11-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5b513b6997a0b2f10e4fd3a1313568e373926e8c252bd76c960f96fd039cd28d"}, - {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee439691d8c23e76f9802c42a95cfeebf9d47cf4ffd06f18489122dbb0a7ad64"}, - {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a8f877c89719d759e52783f7fe6e1c67121076b87b40542966c02de5503ace42"}, - {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:23b30c62d0f16827f2ae9f2bb87619bc4fba2044911e2e6c2eb1af0161cdb766"}, - {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85ab7824093d8f10d44330fe1e6493f756f252d145323dd17ab6b48733ff6c0a"}, - {file = "regex-2024.9.11-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8dee5b4810a89447151999428fe096977346cf2f29f4d5e29609d2e19e0199c9"}, - {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:98eeee2f2e63edae2181c886d7911ce502e1292794f4c5ee71e60e23e8d26b5d"}, - {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:57fdd2e0b2694ce6fc2e5ccf189789c3e2962916fb38779d3e3521ff8fe7a822"}, - {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:d552c78411f60b1fdaafd117a1fca2f02e562e309223b9d44b7de8be451ec5e0"}, - {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:a0b2b80321c2ed3fcf0385ec9e51a12253c50f146fddb2abbb10f033fe3d049a"}, - {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:18406efb2f5a0e57e3a5881cd9354c1512d3bb4f5c45d96d110a66114d84d23a"}, - {file = "regex-2024.9.11-cp312-cp312-win32.whl", hash = "sha256:e464b467f1588e2c42d26814231edecbcfe77f5ac414d92cbf4e7b55b2c2a776"}, - {file = "regex-2024.9.11-cp312-cp312-win_amd64.whl", hash = "sha256:9e8719792ca63c6b8340380352c24dcb8cd7ec49dae36e963742a275dfae6009"}, - {file = "regex-2024.9.11-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c157bb447303070f256e084668b702073db99bbb61d44f85d811025fcf38f784"}, - {file = "regex-2024.9.11-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4db21ece84dfeefc5d8a3863f101995de646c6cb0536952c321a2650aa202c36"}, - {file = "regex-2024.9.11-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:220e92a30b426daf23bb67a7962900ed4613589bab80382be09b48896d211e92"}, - {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb1ae19e64c14c7ec1995f40bd932448713d3c73509e82d8cd7744dc00e29e86"}, - {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f47cd43a5bfa48f86925fe26fbdd0a488ff15b62468abb5d2a1e092a4fb10e85"}, - {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9d4a76b96f398697fe01117093613166e6aa8195d63f1b4ec3f21ab637632963"}, - {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ea51dcc0835eea2ea31d66456210a4e01a076d820e9039b04ae8d17ac11dee6"}, - {file = "regex-2024.9.11-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7aaa315101c6567a9a45d2839322c51c8d6e81f67683d529512f5bcfb99c802"}, - {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c57d08ad67aba97af57a7263c2d9006d5c404d721c5f7542f077f109ec2a4a29"}, - {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f8404bf61298bb6f8224bb9176c1424548ee1181130818fcd2cbffddc768bed8"}, - {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:dd4490a33eb909ef5078ab20f5f000087afa2a4daa27b4c072ccb3cb3050ad84"}, - {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:eee9130eaad130649fd73e5cd92f60e55708952260ede70da64de420cdcad554"}, - {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6a2644a93da36c784e546de579ec1806bfd2763ef47babc1b03d765fe560c9f8"}, - {file = "regex-2024.9.11-cp313-cp313-win32.whl", hash = "sha256:e997fd30430c57138adc06bba4c7c2968fb13d101e57dd5bb9355bf8ce3fa7e8"}, - {file = "regex-2024.9.11-cp313-cp313-win_amd64.whl", hash = "sha256:042c55879cfeb21a8adacc84ea347721d3d83a159da6acdf1116859e2427c43f"}, - {file = "regex-2024.9.11-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:35f4a6f96aa6cb3f2f7247027b07b15a374f0d5b912c0001418d1d55024d5cb4"}, - {file = "regex-2024.9.11-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:55b96e7ce3a69a8449a66984c268062fbaa0d8ae437b285428e12797baefce7e"}, - {file = "regex-2024.9.11-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cb130fccd1a37ed894824b8c046321540263013da72745d755f2d35114b81a60"}, - {file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:323c1f04be6b2968944d730e5c2091c8c89767903ecaa135203eec4565ed2b2b"}, - {file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be1c8ed48c4c4065ecb19d882a0ce1afe0745dfad8ce48c49586b90a55f02366"}, - {file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b5b029322e6e7b94fff16cd120ab35a253236a5f99a79fb04fda7ae71ca20ae8"}, - {file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6fff13ef6b5f29221d6904aa816c34701462956aa72a77f1f151a8ec4f56aeb"}, - {file = "regex-2024.9.11-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:587d4af3979376652010e400accc30404e6c16b7df574048ab1f581af82065e4"}, - {file = "regex-2024.9.11-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:079400a8269544b955ffa9e31f186f01d96829110a3bf79dc338e9910f794fca"}, - {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:f9268774428ec173654985ce55fc6caf4c6d11ade0f6f914d48ef4719eb05ebb"}, - {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:23f9985c8784e544d53fc2930fc1ac1a7319f5d5332d228437acc9f418f2f168"}, - {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:ae2941333154baff9838e88aa71c1d84f4438189ecc6021a12c7573728b5838e"}, - {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:e93f1c331ca8e86fe877a48ad64e77882c0c4da0097f2212873a69bbfea95d0c"}, - {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:846bc79ee753acf93aef4184c040d709940c9d001029ceb7b7a52747b80ed2dd"}, - {file = "regex-2024.9.11-cp38-cp38-win32.whl", hash = "sha256:c94bb0a9f1db10a1d16c00880bdebd5f9faf267273b8f5bd1878126e0fbde771"}, - {file = "regex-2024.9.11-cp38-cp38-win_amd64.whl", hash = "sha256:2b08fce89fbd45664d3df6ad93e554b6c16933ffa9d55cb7e01182baaf971508"}, - {file = "regex-2024.9.11-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:07f45f287469039ffc2c53caf6803cd506eb5f5f637f1d4acb37a738f71dd066"}, - {file = "regex-2024.9.11-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4838e24ee015101d9f901988001038f7f0d90dc0c3b115541a1365fb439add62"}, - {file = "regex-2024.9.11-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6edd623bae6a737f10ce853ea076f56f507fd7726bee96a41ee3d68d347e4d16"}, - {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c69ada171c2d0e97a4b5aa78fbb835e0ffbb6b13fc5da968c09811346564f0d3"}, - {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:02087ea0a03b4af1ed6ebab2c54d7118127fee8d71b26398e8e4b05b78963199"}, - {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:69dee6a020693d12a3cf892aba4808fe168d2a4cef368eb9bf74f5398bfd4ee8"}, - {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:297f54910247508e6e5cae669f2bc308985c60540a4edd1c77203ef19bfa63ca"}, - {file = "regex-2024.9.11-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ecea58b43a67b1b79805f1a0255730edaf5191ecef84dbc4cc85eb30bc8b63b9"}, - {file = "regex-2024.9.11-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:eab4bb380f15e189d1313195b062a6aa908f5bd687a0ceccd47c8211e9cf0d4a"}, - {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0cbff728659ce4bbf4c30b2a1be040faafaa9eca6ecde40aaff86f7889f4ab39"}, - {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:54c4a097b8bc5bb0dfc83ae498061d53ad7b5762e00f4adaa23bee22b012e6ba"}, - {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:73d6d2f64f4d894c96626a75578b0bf7d9e56dcda8c3d037a2118fdfe9b1c664"}, - {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:e53b5fbab5d675aec9f0c501274c467c0f9a5d23696cfc94247e1fb56501ed89"}, - {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0ffbcf9221e04502fc35e54d1ce9567541979c3fdfb93d2c554f0ca583a19b35"}, - {file = "regex-2024.9.11-cp39-cp39-win32.whl", hash = "sha256:e4c22e1ac1f1ec1e09f72e6c44d8f2244173db7eb9629cc3a346a8d7ccc31142"}, - {file = "regex-2024.9.11-cp39-cp39-win_amd64.whl", hash = "sha256:faa3c142464efec496967359ca99696c896c591c56c53506bac1ad465f66e919"}, - {file = "regex-2024.9.11.tar.gz", hash = "sha256:6c188c307e8433bcb63dc1915022deb553b4203a70722fc542c363bf120a01fd"}, + {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff590880083d60acc0433f9c3f713c51f7ac6ebb9adf889c79a261ecf541aa91"}, + {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:658f90550f38270639e83ce492f27d2c8d2cd63805c65a13a14d36ca126753f0"}, + {file = "regex-2024.11.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:164d8b7b3b4bcb2068b97428060b2a53be050085ef94eca7f240e7947f1b080e"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3660c82f209655a06b587d55e723f0b813d3a7db2e32e5e7dc64ac2a9e86fde"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d22326fcdef5e08c154280b71163ced384b428343ae16a5ab2b3354aed12436e"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1ac758ef6aebfc8943560194e9fd0fa18bcb34d89fd8bd2af18183afd8da3a2"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:997d6a487ff00807ba810e0f8332c18b4eb8d29463cfb7c820dc4b6e7562d0cf"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:02a02d2bb04fec86ad61f3ea7f49c015a0681bf76abb9857f945d26159d2968c"}, + {file = "regex-2024.11.6-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f02f93b92358ee3f78660e43b4b0091229260c5d5c408d17d60bf26b6c900e86"}, + {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:06eb1be98df10e81ebaded73fcd51989dcf534e3c753466e4b60c4697a003b67"}, + {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:040df6fe1a5504eb0f04f048e6d09cd7c7110fef851d7c567a6b6e09942feb7d"}, + {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabbfc59f2c6edba2a6622c647b716e34e8e3867e0ab975412c5c2f79b82da2"}, + {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8447d2d39b5abe381419319f942de20b7ecd60ce86f16a23b0698f22e1b70008"}, + {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:da8f5fc57d1933de22a9e23eec290a0d8a5927a5370d24bda9a6abe50683fe62"}, + {file = "regex-2024.11.6-cp310-cp310-win32.whl", hash = "sha256:b489578720afb782f6ccf2840920f3a32e31ba28a4b162e13900c3e6bd3f930e"}, + {file = "regex-2024.11.6-cp310-cp310-win_amd64.whl", hash = "sha256:5071b2093e793357c9d8b2929dfc13ac5f0a6c650559503bb81189d0a3814519"}, + {file = "regex-2024.11.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5478c6962ad548b54a591778e93cd7c456a7a29f8eca9c49e4f9a806dcc5d638"}, + {file = "regex-2024.11.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2c89a8cc122b25ce6945f0423dc1352cb9593c68abd19223eebbd4e56612c5b7"}, + {file = "regex-2024.11.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:94d87b689cdd831934fa3ce16cc15cd65748e6d689f5d2b8f4f4df2065c9fa20"}, + {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1062b39a0a2b75a9c694f7a08e7183a80c63c0d62b301418ffd9c35f55aaa114"}, + {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:167ed4852351d8a750da48712c3930b031f6efdaa0f22fa1933716bfcd6bf4a3"}, + {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d548dafee61f06ebdb584080621f3e0c23fff312f0de1afc776e2a2ba99a74f"}, + {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2a19f302cd1ce5dd01a9099aaa19cae6173306d1302a43b627f62e21cf18ac0"}, + {file = "regex-2024.11.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bec9931dfb61ddd8ef2ebc05646293812cb6b16b60cf7c9511a832b6f1854b55"}, + {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9714398225f299aa85267fd222f7142fcb5c769e73d7733344efc46f2ef5cf89"}, + {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:202eb32e89f60fc147a41e55cb086db2a3f8cb82f9a9a88440dcfc5d37faae8d"}, + {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:4181b814e56078e9b00427ca358ec44333765f5ca1b45597ec7446d3a1ef6e34"}, + {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:068376da5a7e4da51968ce4c122a7cd31afaaec4fccc7856c92f63876e57b51d"}, + {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ac10f2c4184420d881a3475fb2c6f4d95d53a8d50209a2500723d831036f7c45"}, + {file = "regex-2024.11.6-cp311-cp311-win32.whl", hash = "sha256:c36f9b6f5f8649bb251a5f3f66564438977b7ef8386a52460ae77e6070d309d9"}, + {file = "regex-2024.11.6-cp311-cp311-win_amd64.whl", hash = "sha256:02e28184be537f0e75c1f9b2f8847dc51e08e6e171c6bde130b2687e0c33cf60"}, + {file = "regex-2024.11.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:52fb28f528778f184f870b7cf8f225f5eef0a8f6e3778529bdd40c7b3920796a"}, + {file = "regex-2024.11.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdd6028445d2460f33136c55eeb1f601ab06d74cb3347132e1c24250187500d9"}, + {file = "regex-2024.11.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:805e6b60c54bf766b251e94526ebad60b7de0c70f70a4e6210ee2891acb70bf2"}, + {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b85c2530be953a890eaffde05485238f07029600e8f098cdf1848d414a8b45e4"}, + {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb26437975da7dc36b7efad18aa9dd4ea569d2357ae6b783bf1118dabd9ea577"}, + {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:abfa5080c374a76a251ba60683242bc17eeb2c9818d0d30117b4486be10c59d3"}, + {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b7fa6606c2881c1db9479b0eaa11ed5dfa11c8d60a474ff0e095099f39d98e"}, + {file = "regex-2024.11.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c32f75920cf99fe6b6c539c399a4a128452eaf1af27f39bce8909c9a3fd8cbe"}, + {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:982e6d21414e78e1f51cf595d7f321dcd14de1f2881c5dc6a6e23bbbbd68435e"}, + {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a7c2155f790e2fb448faed6dd241386719802296ec588a8b9051c1f5c481bc29"}, + {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:149f5008d286636e48cd0b1dd65018548944e495b0265b45e1bffecce1ef7f39"}, + {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e5364a4502efca094731680e80009632ad6624084aff9a23ce8c8c6820de3e51"}, + {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0a86e7eeca091c09e021db8eb72d54751e527fa47b8d5787caf96d9831bd02ad"}, + {file = "regex-2024.11.6-cp312-cp312-win32.whl", hash = "sha256:32f9a4c643baad4efa81d549c2aadefaeba12249b2adc5af541759237eee1c54"}, + {file = "regex-2024.11.6-cp312-cp312-win_amd64.whl", hash = "sha256:a93c194e2df18f7d264092dc8539b8ffb86b45b899ab976aa15d48214138e81b"}, + {file = "regex-2024.11.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a6ba92c0bcdf96cbf43a12c717eae4bc98325ca3730f6b130ffa2e3c3c723d84"}, + {file = "regex-2024.11.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:525eab0b789891ac3be914d36893bdf972d483fe66551f79d3e27146191a37d4"}, + {file = "regex-2024.11.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:086a27a0b4ca227941700e0b31425e7a28ef1ae8e5e05a33826e17e47fbfdba0"}, + {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bde01f35767c4a7899b7eb6e823b125a64de314a8ee9791367c9a34d56af18d0"}, + {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b583904576650166b3d920d2bcce13971f6f9e9a396c673187f49811b2769dc7"}, + {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c4de13f06a0d54fa0d5ab1b7138bfa0d883220965a29616e3ea61b35d5f5fc7"}, + {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3cde6e9f2580eb1665965ce9bf17ff4952f34f5b126beb509fee8f4e994f143c"}, + {file = "regex-2024.11.6-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d7f453dca13f40a02b79636a339c5b62b670141e63efd511d3f8f73fba162b3"}, + {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:59dfe1ed21aea057a65c6b586afd2a945de04fc7db3de0a6e3ed5397ad491b07"}, + {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b97c1e0bd37c5cd7902e65f410779d39eeda155800b65fc4d04cc432efa9bc6e"}, + {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f9d1e379028e0fc2ae3654bac3cbbef81bf3fd571272a42d56c24007979bafb6"}, + {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:13291b39131e2d002a7940fb176e120bec5145f3aeb7621be6534e46251912c4"}, + {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f51f88c126370dcec4908576c5a627220da6c09d0bff31cfa89f2523843316d"}, + {file = "regex-2024.11.6-cp313-cp313-win32.whl", hash = "sha256:63b13cfd72e9601125027202cad74995ab26921d8cd935c25f09c630436348ff"}, + {file = "regex-2024.11.6-cp313-cp313-win_amd64.whl", hash = "sha256:2b3361af3198667e99927da8b84c1b010752fa4b1115ee30beaa332cabc3ef1a"}, + {file = "regex-2024.11.6-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:3a51ccc315653ba012774efca4f23d1d2a8a8f278a6072e29c7147eee7da446b"}, + {file = "regex-2024.11.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ad182d02e40de7459b73155deb8996bbd8e96852267879396fb274e8700190e3"}, + {file = "regex-2024.11.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ba9b72e5643641b7d41fa1f6d5abda2c9a263ae835b917348fc3c928182ad467"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40291b1b89ca6ad8d3f2b82782cc33807f1406cf68c8d440861da6304d8ffbbd"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cdf58d0e516ee426a48f7b2c03a332a4114420716d55769ff7108c37a09951bf"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a36fdf2af13c2b14738f6e973aba563623cb77d753bbbd8d414d18bfaa3105dd"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1cee317bfc014c2419a76bcc87f071405e3966da434e03e13beb45f8aced1a6"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50153825ee016b91549962f970d6a4442fa106832e14c918acd1c8e479916c4f"}, + {file = "regex-2024.11.6-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ea1bfda2f7162605f6e8178223576856b3d791109f15ea99a9f95c16a7636fb5"}, + {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:df951c5f4a1b1910f1a99ff42c473ff60f8225baa1cdd3539fe2819d9543e9df"}, + {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:072623554418a9911446278f16ecb398fb3b540147a7828c06e2011fa531e773"}, + {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:f654882311409afb1d780b940234208a252322c24a93b442ca714d119e68086c"}, + {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:89d75e7293d2b3e674db7d4d9b1bee7f8f3d1609428e293771d1a962617150cc"}, + {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:f65557897fc977a44ab205ea871b690adaef6b9da6afda4790a2484b04293a5f"}, + {file = "regex-2024.11.6-cp38-cp38-win32.whl", hash = "sha256:6f44ec28b1f858c98d3036ad5d7d0bfc568bdd7a74f9c24e25f41ef1ebfd81a4"}, + {file = "regex-2024.11.6-cp38-cp38-win_amd64.whl", hash = "sha256:bb8f74f2f10dbf13a0be8de623ba4f9491faf58c24064f32b65679b021ed0001"}, + {file = "regex-2024.11.6-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5704e174f8ccab2026bd2f1ab6c510345ae8eac818b613d7d73e785f1310f839"}, + {file = "regex-2024.11.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:220902c3c5cc6af55d4fe19ead504de80eb91f786dc102fbd74894b1551f095e"}, + {file = "regex-2024.11.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5e7e351589da0850c125f1600a4c4ba3c722efefe16b297de54300f08d734fbf"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5056b185ca113c88e18223183aa1a50e66507769c9640a6ff75859619d73957b"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e34b51b650b23ed3354b5a07aab37034d9f923db2a40519139af34f485f77d0"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5670bce7b200273eee1840ef307bfa07cda90b38ae56e9a6ebcc9f50da9c469b"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08986dce1339bc932923e7d1232ce9881499a0e02925f7402fb7c982515419ef"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:93c0b12d3d3bc25af4ebbf38f9ee780a487e8bf6954c115b9f015822d3bb8e48"}, + {file = "regex-2024.11.6-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:764e71f22ab3b305e7f4c21f1a97e1526a25ebdd22513e251cf376760213da13"}, + {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f056bf21105c2515c32372bbc057f43eb02aae2fda61052e2f7622c801f0b4e2"}, + {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:69ab78f848845569401469da20df3e081e6b5a11cb086de3eed1d48f5ed57c95"}, + {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:86fddba590aad9208e2fa8b43b4c098bb0ec74f15718bb6a704e3c63e2cef3e9"}, + {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:684d7a212682996d21ca12ef3c17353c021fe9de6049e19ac8481ec35574a70f"}, + {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a03e02f48cd1abbd9f3b7e3586d97c8f7a9721c436f51a5245b3b9483044480b"}, + {file = "regex-2024.11.6-cp39-cp39-win32.whl", hash = "sha256:41758407fc32d5c3c5de163888068cfee69cb4c2be844e7ac517a52770f9af57"}, + {file = "regex-2024.11.6-cp39-cp39-win_amd64.whl", hash = "sha256:b2837718570f95dd41675328e111345f9b7095d821bac435aac173ac80b19983"}, + {file = "regex-2024.11.6.tar.gz", hash = "sha256:7ab159b063c52a0333c884e4679f8d7a85112ee3078fe3d9004b2dd875585519"}, ] [[package]] @@ -2413,114 +2446,114 @@ files = [ [[package]] name = "rpds-py" -version = "0.20.0" +version = "0.22.3" description = "Python bindings to Rust's persistent data structures (rpds)" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "rpds_py-0.20.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3ad0fda1635f8439cde85c700f964b23ed5fc2d28016b32b9ee5fe30da5c84e2"}, - {file = "rpds_py-0.20.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9bb4a0d90fdb03437c109a17eade42dfbf6190408f29b2744114d11586611d6f"}, - {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6377e647bbfd0a0b159fe557f2c6c602c159fc752fa316572f012fc0bf67150"}, - {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb851b7df9dda52dc1415ebee12362047ce771fc36914586b2e9fcbd7d293b3e"}, - {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1e0f80b739e5a8f54837be5d5c924483996b603d5502bfff79bf33da06164ee2"}, - {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a8c94dad2e45324fc74dce25e1645d4d14df9a4e54a30fa0ae8bad9a63928e3"}, - {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8e604fe73ba048c06085beaf51147eaec7df856824bfe7b98657cf436623daf"}, - {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:df3de6b7726b52966edf29663e57306b23ef775faf0ac01a3e9f4012a24a4140"}, - {file = "rpds_py-0.20.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf258ede5bc22a45c8e726b29835b9303c285ab46fc7c3a4cc770736b5304c9f"}, - {file = "rpds_py-0.20.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:55fea87029cded5df854ca7e192ec7bdb7ecd1d9a3f63d5c4eb09148acf4a7ce"}, - {file = "rpds_py-0.20.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ae94bd0b2f02c28e199e9bc51485d0c5601f58780636185660f86bf80c89af94"}, - {file = "rpds_py-0.20.0-cp310-none-win32.whl", hash = "sha256:28527c685f237c05445efec62426d285e47a58fb05ba0090a4340b73ecda6dee"}, - {file = "rpds_py-0.20.0-cp310-none-win_amd64.whl", hash = "sha256:238a2d5b1cad28cdc6ed15faf93a998336eb041c4e440dd7f902528b8891b399"}, - {file = "rpds_py-0.20.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:ac2f4f7a98934c2ed6505aead07b979e6f999389f16b714448fb39bbaa86a489"}, - {file = "rpds_py-0.20.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:220002c1b846db9afd83371d08d239fdc865e8f8c5795bbaec20916a76db3318"}, - {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d7919548df3f25374a1f5d01fbcd38dacab338ef5f33e044744b5c36729c8db"}, - {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:758406267907b3781beee0f0edfe4a179fbd97c0be2e9b1154d7f0a1279cf8e5"}, - {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3d61339e9f84a3f0767b1995adfb171a0d00a1185192718a17af6e124728e0f5"}, - {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1259c7b3705ac0a0bd38197565a5d603218591d3f6cee6e614e380b6ba61c6f6"}, - {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c1dc0f53856b9cc9a0ccca0a7cc61d3d20a7088201c0937f3f4048c1718a209"}, - {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7e60cb630f674a31f0368ed32b2a6b4331b8350d67de53c0359992444b116dd3"}, - {file = "rpds_py-0.20.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dbe982f38565bb50cb7fb061ebf762c2f254ca3d8c20d4006878766e84266272"}, - {file = "rpds_py-0.20.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:514b3293b64187172bc77c8fb0cdae26981618021053b30d8371c3a902d4d5ad"}, - {file = "rpds_py-0.20.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d0a26ffe9d4dd35e4dfdd1e71f46401cff0181c75ac174711ccff0459135fa58"}, - {file = "rpds_py-0.20.0-cp311-none-win32.whl", hash = "sha256:89c19a494bf3ad08c1da49445cc5d13d8fefc265f48ee7e7556839acdacf69d0"}, - {file = "rpds_py-0.20.0-cp311-none-win_amd64.whl", hash = "sha256:c638144ce971df84650d3ed0096e2ae7af8e62ecbbb7b201c8935c370df00a2c"}, - {file = "rpds_py-0.20.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a84ab91cbe7aab97f7446652d0ed37d35b68a465aeef8fc41932a9d7eee2c1a6"}, - {file = "rpds_py-0.20.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:56e27147a5a4c2c21633ff8475d185734c0e4befd1c989b5b95a5d0db699b21b"}, - {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2580b0c34583b85efec8c5c5ec9edf2dfe817330cc882ee972ae650e7b5ef739"}, - {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b80d4a7900cf6b66bb9cee5c352b2d708e29e5a37fe9bf784fa97fc11504bf6c"}, - {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50eccbf054e62a7b2209b28dc7a22d6254860209d6753e6b78cfaeb0075d7bee"}, - {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:49a8063ea4296b3a7e81a5dfb8f7b2d73f0b1c20c2af401fb0cdf22e14711a96"}, - {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea438162a9fcbee3ecf36c23e6c68237479f89f962f82dae83dc15feeceb37e4"}, - {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:18d7585c463087bddcfa74c2ba267339f14f2515158ac4db30b1f9cbdb62c8ef"}, - {file = "rpds_py-0.20.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d4c7d1a051eeb39f5c9547e82ea27cbcc28338482242e3e0b7768033cb083821"}, - {file = "rpds_py-0.20.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e4df1e3b3bec320790f699890d41c59d250f6beda159ea3c44c3f5bac1976940"}, - {file = "rpds_py-0.20.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2cf126d33a91ee6eedc7f3197b53e87a2acdac63602c0f03a02dd69e4b138174"}, - {file = "rpds_py-0.20.0-cp312-none-win32.whl", hash = "sha256:8bc7690f7caee50b04a79bf017a8d020c1f48c2a1077ffe172abec59870f1139"}, - {file = "rpds_py-0.20.0-cp312-none-win_amd64.whl", hash = "sha256:0e13e6952ef264c40587d510ad676a988df19adea20444c2b295e536457bc585"}, - {file = "rpds_py-0.20.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:aa9a0521aeca7d4941499a73ad7d4f8ffa3d1affc50b9ea11d992cd7eff18a29"}, - {file = "rpds_py-0.20.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4a1f1d51eccb7e6c32ae89243cb352389228ea62f89cd80823ea7dd1b98e0b91"}, - {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a86a9b96070674fc88b6f9f71a97d2c1d3e5165574615d1f9168ecba4cecb24"}, - {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6c8ef2ebf76df43f5750b46851ed1cdf8f109d7787ca40035fe19fbdc1acc5a7"}, - {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b74b25f024b421d5859d156750ea9a65651793d51b76a2e9238c05c9d5f203a9"}, - {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57eb94a8c16ab08fef6404301c38318e2c5a32216bf5de453e2714c964c125c8"}, - {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1940dae14e715e2e02dfd5b0f64a52e8374a517a1e531ad9412319dc3ac7879"}, - {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d20277fd62e1b992a50c43f13fbe13277a31f8c9f70d59759c88f644d66c619f"}, - {file = "rpds_py-0.20.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:06db23d43f26478303e954c34c75182356ca9aa7797d22c5345b16871ab9c45c"}, - {file = "rpds_py-0.20.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b2a5db5397d82fa847e4c624b0c98fe59d2d9b7cf0ce6de09e4d2e80f8f5b3f2"}, - {file = "rpds_py-0.20.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5a35df9f5548fd79cb2f52d27182108c3e6641a4feb0f39067911bf2adaa3e57"}, - {file = "rpds_py-0.20.0-cp313-none-win32.whl", hash = "sha256:fd2d84f40633bc475ef2d5490b9c19543fbf18596dcb1b291e3a12ea5d722f7a"}, - {file = "rpds_py-0.20.0-cp313-none-win_amd64.whl", hash = "sha256:9bc2d153989e3216b0559251b0c260cfd168ec78b1fac33dd485750a228db5a2"}, - {file = "rpds_py-0.20.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:f2fbf7db2012d4876fb0d66b5b9ba6591197b0f165db8d99371d976546472a24"}, - {file = "rpds_py-0.20.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1e5f3cd7397c8f86c8cc72d5a791071431c108edd79872cdd96e00abd8497d29"}, - {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce9845054c13696f7af7f2b353e6b4f676dab1b4b215d7fe5e05c6f8bb06f965"}, - {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c3e130fd0ec56cb76eb49ef52faead8ff09d13f4527e9b0c400307ff72b408e1"}, - {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4b16aa0107ecb512b568244ef461f27697164d9a68d8b35090e9b0c1c8b27752"}, - {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aa7f429242aae2947246587d2964fad750b79e8c233a2367f71b554e9447949c"}, - {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af0fc424a5842a11e28956e69395fbbeab2c97c42253169d87e90aac2886d751"}, - {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b8c00a3b1e70c1d3891f0db1b05292747f0dbcfb49c43f9244d04c70fbc40eb8"}, - {file = "rpds_py-0.20.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:40ce74fc86ee4645d0a225498d091d8bc61f39b709ebef8204cb8b5a464d3c0e"}, - {file = "rpds_py-0.20.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:4fe84294c7019456e56d93e8ababdad5a329cd25975be749c3f5f558abb48253"}, - {file = "rpds_py-0.20.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:338ca4539aad4ce70a656e5187a3a31c5204f261aef9f6ab50e50bcdffaf050a"}, - {file = "rpds_py-0.20.0-cp38-none-win32.whl", hash = "sha256:54b43a2b07db18314669092bb2de584524d1ef414588780261e31e85846c26a5"}, - {file = "rpds_py-0.20.0-cp38-none-win_amd64.whl", hash = "sha256:a1862d2d7ce1674cffa6d186d53ca95c6e17ed2b06b3f4c476173565c862d232"}, - {file = "rpds_py-0.20.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:3fde368e9140312b6e8b6c09fb9f8c8c2f00999d1823403ae90cc00480221b22"}, - {file = "rpds_py-0.20.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9824fb430c9cf9af743cf7aaf6707bf14323fb51ee74425c380f4c846ea70789"}, - {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11ef6ce74616342888b69878d45e9f779b95d4bd48b382a229fe624a409b72c5"}, - {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c52d3f2f82b763a24ef52f5d24358553e8403ce05f893b5347098014f2d9eff2"}, - {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d35cef91e59ebbeaa45214861874bc6f19eb35de96db73e467a8358d701a96c"}, - {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d72278a30111e5b5525c1dd96120d9e958464316f55adb030433ea905866f4de"}, - {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4c29cbbba378759ac5786730d1c3cb4ec6f8ababf5c42a9ce303dc4b3d08cda"}, - {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6632f2d04f15d1bd6fe0eedd3b86d9061b836ddca4c03d5cf5c7e9e6b7c14580"}, - {file = "rpds_py-0.20.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:d0b67d87bb45ed1cd020e8fbf2307d449b68abc45402fe1a4ac9e46c3c8b192b"}, - {file = "rpds_py-0.20.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ec31a99ca63bf3cd7f1a5ac9fe95c5e2d060d3c768a09bc1d16e235840861420"}, - {file = "rpds_py-0.20.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:22e6c9976e38f4d8c4a63bd8a8edac5307dffd3ee7e6026d97f3cc3a2dc02a0b"}, - {file = "rpds_py-0.20.0-cp39-none-win32.whl", hash = "sha256:569b3ea770c2717b730b61998b6c54996adee3cef69fc28d444f3e7920313cf7"}, - {file = "rpds_py-0.20.0-cp39-none-win_amd64.whl", hash = "sha256:e6900ecdd50ce0facf703f7a00df12374b74bbc8ad9fe0f6559947fb20f82364"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:617c7357272c67696fd052811e352ac54ed1d9b49ab370261a80d3b6ce385045"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9426133526f69fcaba6e42146b4e12d6bc6c839b8b555097020e2b78ce908dcc"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:deb62214c42a261cb3eb04d474f7155279c1a8a8c30ac89b7dcb1721d92c3c02"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fcaeb7b57f1a1e071ebd748984359fef83ecb026325b9d4ca847c95bc7311c92"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d454b8749b4bd70dd0a79f428731ee263fa6995f83ccb8bada706e8d1d3ff89d"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d807dc2051abe041b6649681dce568f8e10668e3c1c6543ebae58f2d7e617855"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3c20f0ddeb6e29126d45f89206b8291352b8c5b44384e78a6499d68b52ae511"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b7f19250ceef892adf27f0399b9e5afad019288e9be756d6919cb58892129f51"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:4f1ed4749a08379555cebf4650453f14452eaa9c43d0a95c49db50c18b7da075"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:dcedf0b42bcb4cfff4101d7771a10532415a6106062f005ab97d1d0ab5681c60"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:39ed0d010457a78f54090fafb5d108501b5aa5604cc22408fc1c0c77eac14344"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:bb273176be34a746bdac0b0d7e4e2c467323d13640b736c4c477881a3220a989"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f918a1a130a6dfe1d7fe0f105064141342e7dd1611f2e6a21cd2f5c8cb1cfb3e"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:f60012a73aa396be721558caa3a6fd49b3dd0033d1675c6d59c4502e870fcf0c"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d2b1ad682a3dfda2a4e8ad8572f3100f95fad98cb99faf37ff0ddfe9cbf9d03"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:614fdafe9f5f19c63ea02817fa4861c606a59a604a77c8cdef5aa01d28b97921"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fa518bcd7600c584bf42e6617ee8132869e877db2f76bcdc281ec6a4113a53ab"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0475242f447cc6cb8a9dd486d68b2ef7fbee84427124c232bff5f63b1fe11e5"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f90a4cd061914a60bd51c68bcb4357086991bd0bb93d8aa66a6da7701370708f"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:def7400461c3a3f26e49078302e1c1b38f6752342c77e3cf72ce91ca69fb1bc1"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:65794e4048ee837494aea3c21a28ad5fc080994dfba5b036cf84de37f7ad5074"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:faefcc78f53a88f3076b7f8be0a8f8d35133a3ecf7f3770895c25f8813460f08"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:5b4f105deeffa28bbcdff6c49b34e74903139afa690e35d2d9e3c2c2fba18cec"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:fdfc3a892927458d98f3d55428ae46b921d1f7543b89382fdb483f5640daaec8"}, - {file = "rpds_py-0.20.0.tar.gz", hash = "sha256:d72a210824facfdaf8768cf2d7ca25a042c30320b3020de2fa04640920d4e121"}, + {file = "rpds_py-0.22.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:6c7b99ca52c2c1752b544e310101b98a659b720b21db00e65edca34483259967"}, + {file = "rpds_py-0.22.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:be2eb3f2495ba669d2a985f9b426c1797b7d48d6963899276d22f23e33d47e37"}, + {file = "rpds_py-0.22.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70eb60b3ae9245ddea20f8a4190bd79c705a22f8028aaf8bbdebe4716c3fab24"}, + {file = "rpds_py-0.22.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4041711832360a9b75cfb11b25a6a97c8fb49c07b8bd43d0d02b45d0b499a4ff"}, + {file = "rpds_py-0.22.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:64607d4cbf1b7e3c3c8a14948b99345eda0e161b852e122c6bb71aab6d1d798c"}, + {file = "rpds_py-0.22.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e69b0a0e2537f26d73b4e43ad7bc8c8efb39621639b4434b76a3de50c6966e"}, + {file = "rpds_py-0.22.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc27863442d388870c1809a87507727b799c8460573cfbb6dc0eeaef5a11b5ec"}, + {file = "rpds_py-0.22.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e79dd39f1e8c3504be0607e5fc6e86bb60fe3584bec8b782578c3b0fde8d932c"}, + {file = "rpds_py-0.22.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e0fa2d4ec53dc51cf7d3bb22e0aa0143966119f42a0c3e4998293a3dd2856b09"}, + {file = "rpds_py-0.22.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fda7cb070f442bf80b642cd56483b5548e43d366fe3f39b98e67cce780cded00"}, + {file = "rpds_py-0.22.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cff63a0272fcd259dcc3be1657b07c929c466b067ceb1c20060e8d10af56f5bf"}, + {file = "rpds_py-0.22.3-cp310-cp310-win32.whl", hash = "sha256:9bd7228827ec7bb817089e2eb301d907c0d9827a9e558f22f762bb690b131652"}, + {file = "rpds_py-0.22.3-cp310-cp310-win_amd64.whl", hash = "sha256:9beeb01d8c190d7581a4d59522cd3d4b6887040dcfc744af99aa59fef3e041a8"}, + {file = "rpds_py-0.22.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d20cfb4e099748ea39e6f7b16c91ab057989712d31761d3300d43134e26e165f"}, + {file = "rpds_py-0.22.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:68049202f67380ff9aa52f12e92b1c30115f32e6895cd7198fa2a7961621fc5a"}, + {file = "rpds_py-0.22.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb4f868f712b2dd4bcc538b0a0c1f63a2b1d584c925e69a224d759e7070a12d5"}, + {file = "rpds_py-0.22.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bc51abd01f08117283c5ebf64844a35144a0843ff7b2983e0648e4d3d9f10dbb"}, + {file = "rpds_py-0.22.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0f3cec041684de9a4684b1572fe28c7267410e02450f4561700ca5a3bc6695a2"}, + {file = "rpds_py-0.22.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7ef9d9da710be50ff6809fed8f1963fecdfecc8b86656cadfca3bc24289414b0"}, + {file = "rpds_py-0.22.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59f4a79c19232a5774aee369a0c296712ad0e77f24e62cad53160312b1c1eaa1"}, + {file = "rpds_py-0.22.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a60bce91f81ddaac922a40bbb571a12c1070cb20ebd6d49c48e0b101d87300d"}, + {file = "rpds_py-0.22.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e89391e6d60251560f0a8f4bd32137b077a80d9b7dbe6d5cab1cd80d2746f648"}, + {file = "rpds_py-0.22.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e3fb866d9932a3d7d0c82da76d816996d1667c44891bd861a0f97ba27e84fc74"}, + {file = "rpds_py-0.22.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1352ae4f7c717ae8cba93421a63373e582d19d55d2ee2cbb184344c82d2ae55a"}, + {file = "rpds_py-0.22.3-cp311-cp311-win32.whl", hash = "sha256:b0b4136a252cadfa1adb705bb81524eee47d9f6aab4f2ee4fa1e9d3cd4581f64"}, + {file = "rpds_py-0.22.3-cp311-cp311-win_amd64.whl", hash = "sha256:8bd7c8cfc0b8247c8799080fbff54e0b9619e17cdfeb0478ba7295d43f635d7c"}, + {file = "rpds_py-0.22.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:27e98004595899949bd7a7b34e91fa7c44d7a97c40fcaf1d874168bb652ec67e"}, + {file = "rpds_py-0.22.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1978d0021e943aae58b9b0b196fb4895a25cc53d3956b8e35e0b7682eefb6d56"}, + {file = "rpds_py-0.22.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:655ca44a831ecb238d124e0402d98f6212ac527a0ba6c55ca26f616604e60a45"}, + {file = "rpds_py-0.22.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:feea821ee2a9273771bae61194004ee2fc33f8ec7db08117ef9147d4bbcbca8e"}, + {file = "rpds_py-0.22.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:22bebe05a9ffc70ebfa127efbc429bc26ec9e9b4ee4d15a740033efda515cf3d"}, + {file = "rpds_py-0.22.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3af6e48651c4e0d2d166dc1b033b7042ea3f871504b6805ba5f4fe31581d8d38"}, + {file = "rpds_py-0.22.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e67ba3c290821343c192f7eae1d8fd5999ca2dc99994114643e2f2d3e6138b15"}, + {file = "rpds_py-0.22.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:02fbb9c288ae08bcb34fb41d516d5eeb0455ac35b5512d03181d755d80810059"}, + {file = "rpds_py-0.22.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f56a6b404f74ab372da986d240e2e002769a7d7102cc73eb238a4f72eec5284e"}, + {file = "rpds_py-0.22.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0a0461200769ab3b9ab7e513f6013b7a97fdeee41c29b9db343f3c5a8e2b9e61"}, + {file = "rpds_py-0.22.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8633e471c6207a039eff6aa116e35f69f3156b3989ea3e2d755f7bc41754a4a7"}, + {file = "rpds_py-0.22.3-cp312-cp312-win32.whl", hash = "sha256:593eba61ba0c3baae5bc9be2f5232430453fb4432048de28399ca7376de9c627"}, + {file = "rpds_py-0.22.3-cp312-cp312-win_amd64.whl", hash = "sha256:d115bffdd417c6d806ea9069237a4ae02f513b778e3789a359bc5856e0404cc4"}, + {file = "rpds_py-0.22.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:ea7433ce7e4bfc3a85654aeb6747babe3f66eaf9a1d0c1e7a4435bbdf27fea84"}, + {file = "rpds_py-0.22.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6dd9412824c4ce1aca56c47b0991e65bebb7ac3f4edccfd3f156150c96a7bf25"}, + {file = "rpds_py-0.22.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20070c65396f7373f5df4005862fa162db5d25d56150bddd0b3e8214e8ef45b4"}, + {file = "rpds_py-0.22.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0b09865a9abc0ddff4e50b5ef65467cd94176bf1e0004184eb915cbc10fc05c5"}, + {file = "rpds_py-0.22.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3453e8d41fe5f17d1f8e9c383a7473cd46a63661628ec58e07777c2fff7196dc"}, + {file = "rpds_py-0.22.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f5d36399a1b96e1a5fdc91e0522544580dbebeb1f77f27b2b0ab25559e103b8b"}, + {file = "rpds_py-0.22.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:009de23c9c9ee54bf11303a966edf4d9087cd43a6003672e6aa7def643d06518"}, + {file = "rpds_py-0.22.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1aef18820ef3e4587ebe8b3bc9ba6e55892a6d7b93bac6d29d9f631a3b4befbd"}, + {file = "rpds_py-0.22.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f60bd8423be1d9d833f230fdbccf8f57af322d96bcad6599e5a771b151398eb2"}, + {file = "rpds_py-0.22.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:62d9cfcf4948683a18a9aff0ab7e1474d407b7bab2ca03116109f8464698ab16"}, + {file = "rpds_py-0.22.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9253fc214112405f0afa7db88739294295f0e08466987f1d70e29930262b4c8f"}, + {file = "rpds_py-0.22.3-cp313-cp313-win32.whl", hash = "sha256:fb0ba113b4983beac1a2eb16faffd76cb41e176bf58c4afe3e14b9c681f702de"}, + {file = "rpds_py-0.22.3-cp313-cp313-win_amd64.whl", hash = "sha256:c58e2339def52ef6b71b8f36d13c3688ea23fa093353f3a4fee2556e62086ec9"}, + {file = "rpds_py-0.22.3-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:f82a116a1d03628a8ace4859556fb39fd1424c933341a08ea3ed6de1edb0283b"}, + {file = "rpds_py-0.22.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3dfcbc95bd7992b16f3f7ba05af8a64ca694331bd24f9157b49dadeeb287493b"}, + {file = "rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59259dc58e57b10e7e18ce02c311804c10c5a793e6568f8af4dead03264584d1"}, + {file = "rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5725dd9cc02068996d4438d397e255dcb1df776b7ceea3b9cb972bdb11260a83"}, + {file = "rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99b37292234e61325e7a5bb9689e55e48c3f5f603af88b1642666277a81f1fbd"}, + {file = "rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:27b1d3b3915a99208fee9ab092b8184c420f2905b7d7feb4aeb5e4a9c509b8a1"}, + {file = "rpds_py-0.22.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f612463ac081803f243ff13cccc648578e2279295048f2a8d5eb430af2bae6e3"}, + {file = "rpds_py-0.22.3-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f73d3fef726b3243a811121de45193c0ca75f6407fe66f3f4e183c983573e130"}, + {file = "rpds_py-0.22.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:3f21f0495edea7fdbaaa87e633a8689cd285f8f4af5c869f27bc8074638ad69c"}, + {file = "rpds_py-0.22.3-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:1e9663daaf7a63ceccbbb8e3808fe90415b0757e2abddbfc2e06c857bf8c5e2b"}, + {file = "rpds_py-0.22.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a76e42402542b1fae59798fab64432b2d015ab9d0c8c47ba7addddbaf7952333"}, + {file = "rpds_py-0.22.3-cp313-cp313t-win32.whl", hash = "sha256:69803198097467ee7282750acb507fba35ca22cc3b85f16cf45fb01cb9097730"}, + {file = "rpds_py-0.22.3-cp313-cp313t-win_amd64.whl", hash = "sha256:f5cf2a0c2bdadf3791b5c205d55a37a54025c6e18a71c71f82bb536cf9a454bf"}, + {file = "rpds_py-0.22.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:378753b4a4de2a7b34063d6f95ae81bfa7b15f2c1a04a9518e8644e81807ebea"}, + {file = "rpds_py-0.22.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3445e07bf2e8ecfeef6ef67ac83de670358abf2996916039b16a218e3d95e97e"}, + {file = "rpds_py-0.22.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b2513ba235829860b13faa931f3b6846548021846ac808455301c23a101689d"}, + {file = "rpds_py-0.22.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eaf16ae9ae519a0e237a0f528fd9f0197b9bb70f40263ee57ae53c2b8d48aeb3"}, + {file = "rpds_py-0.22.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:583f6a1993ca3369e0f80ba99d796d8e6b1a3a2a442dd4e1a79e652116413091"}, + {file = "rpds_py-0.22.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4617e1915a539a0d9a9567795023de41a87106522ff83fbfaf1f6baf8e85437e"}, + {file = "rpds_py-0.22.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c150c7a61ed4a4f4955a96626574e9baf1adf772c2fb61ef6a5027e52803543"}, + {file = "rpds_py-0.22.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2fa4331c200c2521512595253f5bb70858b90f750d39b8cbfd67465f8d1b596d"}, + {file = "rpds_py-0.22.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:214b7a953d73b5e87f0ebece4a32a5bd83c60a3ecc9d4ec8f1dca968a2d91e99"}, + {file = "rpds_py-0.22.3-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:f47ad3d5f3258bd7058d2d506852217865afefe6153a36eb4b6928758041d831"}, + {file = "rpds_py-0.22.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:f276b245347e6e36526cbd4a266a417796fc531ddf391e43574cf6466c492520"}, + {file = "rpds_py-0.22.3-cp39-cp39-win32.whl", hash = "sha256:bbb232860e3d03d544bc03ac57855cd82ddf19c7a07651a7c0fdb95e9efea8b9"}, + {file = "rpds_py-0.22.3-cp39-cp39-win_amd64.whl", hash = "sha256:cfbc454a2880389dbb9b5b398e50d439e2e58669160f27b60e5eca11f68ae17c"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:d48424e39c2611ee1b84ad0f44fb3b2b53d473e65de061e3f460fc0be5f1939d"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:24e8abb5878e250f2eb0d7859a8e561846f98910326d06c0d51381fed59357bd"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b232061ca880db21fa14defe219840ad9b74b6158adb52ddf0e87bead9e8493"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac0a03221cdb5058ce0167ecc92a8c89e8d0decdc9e99a2ec23380793c4dcb96"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eb0c341fa71df5a4595f9501df4ac5abfb5a09580081dffbd1ddd4654e6e9123"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bf9db5488121b596dbfc6718c76092fda77b703c1f7533a226a5a9f65248f8ad"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b8db6b5b2d4491ad5b6bdc2bc7c017eec108acbf4e6785f42a9eb0ba234f4c9"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b3d504047aba448d70cf6fa22e06cb09f7cbd761939fdd47604f5e007675c24e"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:e61b02c3f7a1e0b75e20c3978f7135fd13cb6cf551bf4a6d29b999a88830a338"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:e35ba67d65d49080e8e5a1dd40101fccdd9798adb9b050ff670b7d74fa41c566"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:26fd7cac7dd51011a245f29a2cc6489c4608b5a8ce8d75661bb4a1066c52dfbe"}, + {file = "rpds_py-0.22.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:177c7c0fce2855833819c98e43c262007f42ce86651ffbb84f37883308cb0e7d"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:bb47271f60660803ad11f4c61b42242b8c1312a31c98c578f79ef9387bbde21c"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:70fb28128acbfd264eda9bf47015537ba3fe86e40d046eb2963d75024be4d055"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:44d61b4b7d0c2c9ac019c314e52d7cbda0ae31078aabd0f22e583af3e0d79723"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f0e260eaf54380380ac3808aa4ebe2d8ca28b9087cf411649f96bad6900c728"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b25bc607423935079e05619d7de556c91fb6adeae9d5f80868dde3468657994b"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fb6116dfb8d1925cbdb52595560584db42a7f664617a1f7d7f6e32f138cdf37d"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a63cbdd98acef6570c62b92a1e43266f9e8b21e699c363c0fef13bd530799c11"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2b8f60e1b739a74bab7e01fcbe3dddd4657ec685caa04681df9d562ef15b625f"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:2e8b55d8517a2fda8d95cb45d62a5a8bbf9dd0ad39c5b25c8833efea07b880ca"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:2de29005e11637e7a2361fa151f780ff8eb2543a0da1413bb951e9f14b699ef3"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:666ecce376999bf619756a24ce15bb14c5bfaf04bf00abc7e663ce17c3f34fe7"}, + {file = "rpds_py-0.22.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:5246b14ca64a8675e0a7161f7af68fe3e910e6b90542b4bfb5439ba752191df6"}, + {file = "rpds_py-0.22.3.tar.gz", hash = "sha256:e32fee8ab45d3c2db6da19a5323bc3362237c8b653c70194414b892fd06a080d"}, ] [[package]] @@ -2568,33 +2601,33 @@ win32 = ["pywin32"] [[package]] name = "setuptools" -version = "75.3.0" +version = "75.6.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "setuptools-75.3.0-py3-none-any.whl", hash = "sha256:f2504966861356aa38616760c0f66568e535562374995367b4e69c7143cf6bcd"}, - {file = "setuptools-75.3.0.tar.gz", hash = "sha256:fba5dd4d766e97be1b1681d98712680ae8f2f26d7881245f2ce9e40714f1a686"}, + {file = "setuptools-75.6.0-py3-none-any.whl", hash = "sha256:ce74b49e8f7110f9bf04883b730f4765b774ef3ef28f722cce7c273d253aaf7d"}, + {file = "setuptools-75.6.0.tar.gz", hash = "sha256:8199222558df7c86216af4f84c30e9b34a61d8ba19366cc914424cdbd28252f6"}, ] [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.5.2)"] -core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.collections", "jaraco.functools", "jaraco.text (>=3.7)", "more-itertools", "more-itertools (>=8.8)", "packaging", "packaging (>=24)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.7.0)"] +core = ["importlib_metadata (>=6)", "jaraco.collections", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] enabler = ["pytest-enabler (>=2.2)"] -test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test (>=5.5)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] -type = ["importlib-metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (==1.12.*)", "pytest-mypy"] +test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] +type = ["importlib_metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (>=1.12,<1.14)", "pytest-mypy"] [[package]] name = "six" -version = "1.16.0" +version = "1.17.0" description = "Python 2 and 3 compatibility utilities" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" files = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, + {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, + {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, ] [[package]] @@ -2741,33 +2774,63 @@ test = ["pytest", "ruff"] [[package]] name = "tomli" -version = "2.0.2" +version = "2.2.1" description = "A lil' TOML parser" optional = false python-versions = ">=3.8" files = [ - {file = "tomli-2.0.2-py3-none-any.whl", hash = "sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38"}, - {file = "tomli-2.0.2.tar.gz", hash = "sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed"}, + {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, + {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"}, + {file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"}, + {file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"}, + {file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"}, + {file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"}, + {file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"}, + {file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"}, + {file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"}, + {file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"}, + {file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"}, + {file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"}, + {file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"}, + {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"}, ] [[package]] name = "tornado" -version = "6.4.1" +version = "6.4.2" description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." optional = false python-versions = ">=3.8" files = [ - {file = "tornado-6.4.1-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:163b0aafc8e23d8cdc3c9dfb24c5368af84a81e3364745ccb4427669bf84aec8"}, - {file = "tornado-6.4.1-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:6d5ce3437e18a2b66fbadb183c1d3364fb03f2be71299e7d10dbeeb69f4b2a14"}, - {file = "tornado-6.4.1-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2e20b9113cd7293f164dc46fffb13535266e713cdb87bd2d15ddb336e96cfc4"}, - {file = "tornado-6.4.1-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ae50a504a740365267b2a8d1a90c9fbc86b780a39170feca9bcc1787ff80842"}, - {file = "tornado-6.4.1-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:613bf4ddf5c7a95509218b149b555621497a6cc0d46ac341b30bd9ec19eac7f3"}, - {file = "tornado-6.4.1-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:25486eb223babe3eed4b8aecbac33b37e3dd6d776bc730ca14e1bf93888b979f"}, - {file = "tornado-6.4.1-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:454db8a7ecfcf2ff6042dde58404164d969b6f5d58b926da15e6b23817950fc4"}, - {file = "tornado-6.4.1-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a02a08cc7a9314b006f653ce40483b9b3c12cda222d6a46d4ac63bb6c9057698"}, - {file = "tornado-6.4.1-cp38-abi3-win32.whl", hash = "sha256:d9a566c40b89757c9aa8e6f032bcdb8ca8795d7c1a9762910c722b1635c9de4d"}, - {file = "tornado-6.4.1-cp38-abi3-win_amd64.whl", hash = "sha256:b24b8982ed444378d7f21d563f4180a2de31ced9d8d84443907a0a64da2072e7"}, - {file = "tornado-6.4.1.tar.gz", hash = "sha256:92d3ab53183d8c50f8204a51e6f91d18a15d5ef261e84d452800d4ff6fc504e9"}, + {file = "tornado-6.4.2-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e828cce1123e9e44ae2a50a9de3055497ab1d0aeb440c5ac23064d9e44880da1"}, + {file = "tornado-6.4.2-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:072ce12ada169c5b00b7d92a99ba089447ccc993ea2143c9ede887e0937aa803"}, + {file = "tornado-6.4.2-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a017d239bd1bb0919f72af256a970624241f070496635784d9bf0db640d3fec"}, + {file = "tornado-6.4.2-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c36e62ce8f63409301537222faffcef7dfc5284f27eec227389f2ad11b09d946"}, + {file = "tornado-6.4.2-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bca9eb02196e789c9cb5c3c7c0f04fb447dc2adffd95265b2c7223a8a615ccbf"}, + {file = "tornado-6.4.2-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:304463bd0772442ff4d0f5149c6f1c2135a1fae045adf070821c6cdc76980634"}, + {file = "tornado-6.4.2-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:c82c46813ba483a385ab2a99caeaedf92585a1f90defb5693351fa7e4ea0bf73"}, + {file = "tornado-6.4.2-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:932d195ca9015956fa502c6b56af9eb06106140d844a335590c1ec7f5277d10c"}, + {file = "tornado-6.4.2-cp38-abi3-win32.whl", hash = "sha256:2876cef82e6c5978fde1e0d5b1f919d756968d5b4282418f3146b79b58556482"}, + {file = "tornado-6.4.2-cp38-abi3-win_amd64.whl", hash = "sha256:908b71bf3ff37d81073356a5fadcc660eb10c1476ee6e2725588626ce7e5ca38"}, + {file = "tornado-6.4.2.tar.gz", hash = "sha256:92bad5b4746e9879fd7bf1eb21dce4e3fc5128d71601f80005afa39237ad620b"}, ] [[package]] @@ -2787,13 +2850,13 @@ test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0, [[package]] name = "types-python-dateutil" -version = "2.9.0.20241003" +version = "2.9.0.20241206" description = "Typing stubs for python-dateutil" optional = false python-versions = ">=3.8" files = [ - {file = "types-python-dateutil-2.9.0.20241003.tar.gz", hash = "sha256:58cb85449b2a56d6684e41aeefb4c4280631246a0da1a719bdbe6f3fb0317446"}, - {file = "types_python_dateutil-2.9.0.20241003-py3-none-any.whl", hash = "sha256:250e1d8e80e7bbc3a6c99b907762711d1a1cdd00e978ad39cb5940f6f0a87f3d"}, + {file = "types_python_dateutil-2.9.0.20241206-py3-none-any.whl", hash = "sha256:e248a4bc70a486d3e3ec84d0dc30eec3a5f979d6e7ee4123ae043eedbb987f53"}, + {file = "types_python_dateutil-2.9.0.20241206.tar.gz", hash = "sha256:18f493414c26ffba692a72369fea7a154c502646301ebfe3d56a04b3767284cb"}, ] [[package]] @@ -2854,41 +2917,41 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "watchdog" -version = "5.0.3" +version = "6.0.0" description = "Filesystem events monitoring" optional = false python-versions = ">=3.9" files = [ - {file = "watchdog-5.0.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:85527b882f3facda0579bce9d743ff7f10c3e1e0db0a0d0e28170a7d0e5ce2ea"}, - {file = "watchdog-5.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:53adf73dcdc0ef04f7735066b4a57a4cd3e49ef135daae41d77395f0b5b692cb"}, - {file = "watchdog-5.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e25adddab85f674acac303cf1f5835951345a56c5f7f582987d266679979c75b"}, - {file = "watchdog-5.0.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f01f4a3565a387080dc49bdd1fefe4ecc77f894991b88ef927edbfa45eb10818"}, - {file = "watchdog-5.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:91b522adc25614cdeaf91f7897800b82c13b4b8ac68a42ca959f992f6990c490"}, - {file = "watchdog-5.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d52db5beb5e476e6853da2e2d24dbbbed6797b449c8bf7ea118a4ee0d2c9040e"}, - {file = "watchdog-5.0.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:94d11b07c64f63f49876e0ab8042ae034674c8653bfcdaa8c4b32e71cfff87e8"}, - {file = "watchdog-5.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:349c9488e1d85d0a58e8cb14222d2c51cbc801ce11ac3936ab4c3af986536926"}, - {file = "watchdog-5.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:53a3f10b62c2d569e260f96e8d966463dec1a50fa4f1b22aec69e3f91025060e"}, - {file = "watchdog-5.0.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:950f531ec6e03696a2414b6308f5c6ff9dab7821a768c9d5788b1314e9a46ca7"}, - {file = "watchdog-5.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ae6deb336cba5d71476caa029ceb6e88047fc1dc74b62b7c4012639c0b563906"}, - {file = "watchdog-5.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1021223c08ba8d2d38d71ec1704496471ffd7be42cfb26b87cd5059323a389a1"}, - {file = "watchdog-5.0.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:752fb40efc7cc8d88ebc332b8f4bcbe2b5cc7e881bccfeb8e25054c00c994ee3"}, - {file = "watchdog-5.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a2e8f3f955d68471fa37b0e3add18500790d129cc7efe89971b8a4cc6fdeb0b2"}, - {file = "watchdog-5.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b8ca4d854adcf480bdfd80f46fdd6fb49f91dd020ae11c89b3a79e19454ec627"}, - {file = "watchdog-5.0.3-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:90a67d7857adb1d985aca232cc9905dd5bc4803ed85cfcdcfcf707e52049eda7"}, - {file = "watchdog-5.0.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:720ef9d3a4f9ca575a780af283c8fd3a0674b307651c1976714745090da5a9e8"}, - {file = "watchdog-5.0.3-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:223160bb359281bb8e31c8f1068bf71a6b16a8ad3d9524ca6f523ac666bb6a1e"}, - {file = "watchdog-5.0.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:560135542c91eaa74247a2e8430cf83c4342b29e8ad4f520ae14f0c8a19cfb5b"}, - {file = "watchdog-5.0.3-py3-none-manylinux2014_aarch64.whl", hash = "sha256:dd021efa85970bd4824acacbb922066159d0f9e546389a4743d56919b6758b91"}, - {file = "watchdog-5.0.3-py3-none-manylinux2014_armv7l.whl", hash = "sha256:78864cc8f23dbee55be34cc1494632a7ba30263951b5b2e8fc8286b95845f82c"}, - {file = "watchdog-5.0.3-py3-none-manylinux2014_i686.whl", hash = "sha256:1e9679245e3ea6498494b3028b90c7b25dbb2abe65c7d07423ecfc2d6218ff7c"}, - {file = "watchdog-5.0.3-py3-none-manylinux2014_ppc64.whl", hash = "sha256:9413384f26b5d050b6978e6fcd0c1e7f0539be7a4f1a885061473c5deaa57221"}, - {file = "watchdog-5.0.3-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:294b7a598974b8e2c6123d19ef15de9abcd282b0fbbdbc4d23dfa812959a9e05"}, - {file = "watchdog-5.0.3-py3-none-manylinux2014_s390x.whl", hash = "sha256:26dd201857d702bdf9d78c273cafcab5871dd29343748524695cecffa44a8d97"}, - {file = "watchdog-5.0.3-py3-none-manylinux2014_x86_64.whl", hash = "sha256:0f9332243355643d567697c3e3fa07330a1d1abf981611654a1f2bf2175612b7"}, - {file = "watchdog-5.0.3-py3-none-win32.whl", hash = "sha256:c66f80ee5b602a9c7ab66e3c9f36026590a0902db3aea414d59a2f55188c1f49"}, - {file = "watchdog-5.0.3-py3-none-win_amd64.whl", hash = "sha256:f00b4cf737f568be9665563347a910f8bdc76f88c2970121c86243c8cfdf90e9"}, - {file = "watchdog-5.0.3-py3-none-win_ia64.whl", hash = "sha256:49f4d36cb315c25ea0d946e018c01bb028048023b9e103d3d3943f58e109dd45"}, - {file = "watchdog-5.0.3.tar.gz", hash = "sha256:108f42a7f0345042a854d4d0ad0834b741d421330d5f575b81cb27b883500176"}, + {file = "watchdog-6.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d1cdb490583ebd691c012b3d6dae011000fe42edb7a82ece80965b42abd61f26"}, + {file = "watchdog-6.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bc64ab3bdb6a04d69d4023b29422170b74681784ffb9463ed4870cf2f3e66112"}, + {file = "watchdog-6.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c897ac1b55c5a1461e16dae288d22bb2e412ba9807df8397a635d88f671d36c3"}, + {file = "watchdog-6.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6eb11feb5a0d452ee41f824e271ca311a09e250441c262ca2fd7ebcf2461a06c"}, + {file = "watchdog-6.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ef810fbf7b781a5a593894e4f439773830bdecb885e6880d957d5b9382a960d2"}, + {file = "watchdog-6.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:afd0fe1b2270917c5e23c2a65ce50c2a4abb63daafb0d419fde368e272a76b7c"}, + {file = "watchdog-6.0.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bdd4e6f14b8b18c334febb9c4425a878a2ac20efd1e0b231978e7b150f92a948"}, + {file = "watchdog-6.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c7c15dda13c4eb00d6fb6fc508b3c0ed88b9d5d374056b239c4ad1611125c860"}, + {file = "watchdog-6.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6f10cb2d5902447c7d0da897e2c6768bca89174d0c6e1e30abec5421af97a5b0"}, + {file = "watchdog-6.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:490ab2ef84f11129844c23fb14ecf30ef3d8a6abafd3754a6f75ca1e6654136c"}, + {file = "watchdog-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:76aae96b00ae814b181bb25b1b98076d5fc84e8a53cd8885a318b42b6d3a5134"}, + {file = "watchdog-6.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a175f755fc2279e0b7312c0035d52e27211a5bc39719dd529625b1930917345b"}, + {file = "watchdog-6.0.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e6f0e77c9417e7cd62af82529b10563db3423625c5fce018430b249bf977f9e8"}, + {file = "watchdog-6.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:90c8e78f3b94014f7aaae121e6b909674df5b46ec24d6bebc45c44c56729af2a"}, + {file = "watchdog-6.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e7631a77ffb1f7d2eefa4445ebbee491c720a5661ddf6df3498ebecae5ed375c"}, + {file = "watchdog-6.0.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:c7ac31a19f4545dd92fc25d200694098f42c9a8e391bc00bdd362c5736dbf881"}, + {file = "watchdog-6.0.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9513f27a1a582d9808cf21a07dae516f0fab1cf2d7683a742c498b93eedabb11"}, + {file = "watchdog-6.0.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7a0e56874cfbc4b9b05c60c8a1926fedf56324bb08cfbc188969777940aef3aa"}, + {file = "watchdog-6.0.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:e6439e374fc012255b4ec786ae3c4bc838cd7309a540e5fe0952d03687d8804e"}, + {file = "watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13"}, + {file = "watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379"}, + {file = "watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e"}, + {file = "watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f"}, + {file = "watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26"}, + {file = "watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c"}, + {file = "watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2"}, + {file = "watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a"}, + {file = "watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680"}, + {file = "watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f"}, + {file = "watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282"}, ] [package.extras] @@ -2907,19 +2970,15 @@ files = [ [[package]] name = "webcolors" -version = "24.8.0" +version = "24.11.1" description = "A library for working with the color formats defined by HTML and CSS." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "webcolors-24.8.0-py3-none-any.whl", hash = "sha256:fc4c3b59358ada164552084a8ebee637c221e4059267d0f8325b3b560f6c7f0a"}, - {file = "webcolors-24.8.0.tar.gz", hash = "sha256:08b07af286a01bcd30d583a7acadf629583d1f79bfef27dd2c2c5c263817277d"}, + {file = "webcolors-24.11.1-py3-none-any.whl", hash = "sha256:515291393b4cdf0eb19c155749a096f779f7d909f7cceea072791cb9095b92e9"}, + {file = "webcolors-24.11.1.tar.gz", hash = "sha256:ecb3d768f32202af770477b8b65f318fa4f566c22948673a977b00d589dd80f6"}, ] -[package.extras] -docs = ["furo", "sphinx", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-notfound-page", "sphinxext-opengraph"] -tests = ["coverage[toml]"] - [[package]] name = "webencodings" version = "0.5.1" @@ -2960,13 +3019,13 @@ files = [ [[package]] name = "zipp" -version = "3.20.2" +version = "3.21.0" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "zipp-3.20.2-py3-none-any.whl", hash = "sha256:a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350"}, - {file = "zipp-3.20.2.tar.gz", hash = "sha256:bc9eb26f4506fda01b81bcde0ca78103b6e62f991b381fec825435c836edbc29"}, + {file = "zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931"}, + {file = "zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4"}, ] [package.extras] @@ -2980,4 +3039,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = ">=3.9,<4.0" -content-hash = "1ca2ffa7cad6f306bdedb6aa3497597cd0292172c6a2a48684881cd344458598" +content-hash = "33f08d0158d06d6b63f2b50aeffc043421413d306d0777c78cf0a7a3c67c4457" diff --git a/libs/text-splitters/pyproject.toml b/libs/text-splitters/pyproject.toml index c4c8a7535860e..300e081336bfe 100644 --- a/libs/text-splitters/pyproject.toml +++ b/libs/text-splitters/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api" [tool.poetry] name = "langchain-text-splitters" -version = "0.3.2" +version = "0.3.3" description = "LangChain text splitting utilities" authors = [] license = "MIT" @@ -23,10 +23,11 @@ ignore_missing_imports = "True" [tool.poetry.dependencies] python = ">=3.9,<4.0" -langchain-core = "^0.3.15" +langchain-core = "^0.3.25" [tool.ruff.lint] -select = [ "E", "F", "I", "T201",] +select = [ "E", "F", "I", "T201", "D",] +ignore = [ "D100",] [tool.coverage.run] omit = [ "tests/*",] @@ -48,6 +49,12 @@ optional = true [tool.poetry.group.test] optional = true +[tool.ruff.lint.pydocstyle] +convention = "google" + +[tool.ruff.lint.per-file-ignores] +"tests/**" = [ "D",] + [tool.poetry.group.lint.dependencies] ruff = "^0.5" diff --git a/poetry.lock b/poetry.lock index a651014235914..f53be8c20e771 100644 --- a/poetry.lock +++ b/poetry.lock @@ -161,13 +161,13 @@ files = [ [[package]] name = "anthropic" -version = "0.37.1" +version = "0.40.0" description = "The official Python library for the anthropic API" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "anthropic-0.37.1-py3-none-any.whl", hash = "sha256:8f550f88906823752e2abf99fbe491fbc8d40bce4cb26b9663abdf7be990d721"}, - {file = "anthropic-0.37.1.tar.gz", hash = "sha256:99f688265795daa7ba9256ee68eaf2f05d53cd99d7417f4a0c2dc292c106d00a"}, + {file = "anthropic-0.40.0-py3-none-any.whl", hash = "sha256:442028ae8790ff9e3b6f8912043918755af1230d193904ae2ef78cc22995280c"}, + {file = "anthropic-0.40.0.tar.gz", hash = "sha256:3efeca6d9e97813f93ed34322c6c7ea2279bf0824cd0aa71b59ce222665e2b87"}, ] [package.dependencies] @@ -177,7 +177,6 @@ httpx = ">=0.23.0,<1" jiter = ">=0.4.0,<1" pydantic = ">=1.9.0,<3" sniffio = "*" -tokenizers = ">=0.13.0" typing-extensions = ">=4.7,<5" [package.extras] @@ -1934,6 +1933,27 @@ files = [ {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, ] +[[package]] +name = "html5lib" +version = "1.1" +description = "HTML parser based on the WHATWG HTML specification" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "html5lib-1.1-py2.py3-none-any.whl", hash = "sha256:0d78f8fde1c230e99fe37986a60526d7049ed4bf8a9fadbad5f00e22e58e041d"}, + {file = "html5lib-1.1.tar.gz", hash = "sha256:b2e5b40261e20f354d198eae92afc10d750afb487ed5e50f9c4eaf07c184146f"}, +] + +[package.dependencies] +six = ">=1.9" +webencodings = "*" + +[package.extras] +all = ["chardet (>=2.2)", "genshi", "lxml"] +chardet = ["chardet (>=2.2)"] +genshi = ["genshi"] +lxml = ["lxml"] + [[package]] name = "httpcore" version = "1.0.6" @@ -2794,7 +2814,7 @@ adal = ["adal (>=1.0.2)"] [[package]] name = "langchain" -version = "0.3.4" +version = "0.3.11" description = "Building applications with LLMs through composability" optional = false python-versions = ">=3.9,<4.0" @@ -2804,12 +2824,12 @@ develop = true [package.dependencies] aiohttp = "^3.8.3" async-timeout = {version = "^4.0.0", markers = "python_version < \"3.11\""} -langchain-core = "^0.3.12" +langchain-core = "^0.3.24" langchain-text-splitters = "^0.3.0" -langsmith = "^0.1.17" +langsmith = ">=0.1.17,<0.3" numpy = [ - {version = ">=1,<2", markers = "python_version < \"3.12\""}, - {version = ">=1.26.0,<2.0.0", markers = "python_version >= \"3.12\""}, + {version = ">=1.22.4,<2", markers = "python_version < \"3.12\""}, + {version = ">=1.26.2,<3", markers = "python_version >= \"3.12\""}, ] pydantic = "^2.7.4" PyYAML = ">=5.3" @@ -2823,7 +2843,7 @@ url = "libs/langchain" [[package]] name = "langchain-anthropic" -version = "0.2.3" +version = "0.3.0" description = "An integration package connecting AnthropicMessages and LangChain" optional = false python-versions = ">=3.9,<4.0" @@ -2831,8 +2851,8 @@ files = [] develop = true [package.dependencies] -anthropic = ">=0.30.0,<1" -langchain-core = "^0.3.9" +anthropic = ">=0.39.0,<1" +langchain-core = "^0.3.17" pydantic = "^2.7.4" [package.source] @@ -2866,19 +2886,19 @@ subdirectory = "libs/aws" [[package]] name = "langchain-chroma" -version = "0.1.5" +version = "0.2.0" description = "An integration package connecting Chroma and LangChain" optional = false -python-versions = ">=3.8.1,<4" +python-versions = ">=3.9,<4" files = [] develop = true [package.dependencies] chromadb = ">=0.4.0,<0.6.0,!=0.5.4,!=0.5.5,!=0.5.7,!=0.5.9,!=0.5.10,!=0.5.11,!=0.5.12" -langchain-core = {version = ">=0.1.40,<0.4", markers = "python_version >= \"3.9\""} +langchain-core = ">=0.2.43,<0.4.0,!=0.3.0,!=0.3.1,!=0.3.2,!=0.3.3,!=0.3.4,!=0.3.5,!=0.3.6,!=0.3.7,!=0.3.8,!=0.3.9,!=0.3.10,!=0.3.11,!=0.3.12,!=0.3.13,!=0.3.14" numpy = [ - {version = ">=1,<2", markers = "python_version < \"3.12\""}, - {version = ">=1.26.0,<2.0.0", markers = "python_version >= \"3.12\""}, + {version = ">=1.22.4,<2.0.0", markers = "python_version < \"3.12\""}, + {version = ">=1.26.2,<2.0.0", markers = "python_version >= \"3.12\""}, ] [package.source] @@ -2887,7 +2907,7 @@ url = "libs/partners/chroma" [[package]] name = "langchain-community" -version = "0.3.3" +version = "0.3.11" description = "Community contributed LangChain integrations." optional = false python-versions = ">=3.9,<4.0" @@ -2898,12 +2918,12 @@ develop = true aiohttp = "^3.8.3" dataclasses-json = ">= 0.5.7, < 0.7" httpx-sse = "^0.4.0" -langchain = "^0.3.4" -langchain-core = "^0.3.12" -langsmith = "^0.1.125" +langchain = "^0.3.11" +langchain-core = "^0.3.24" +langsmith = ">=0.1.125,<0.3" numpy = [ - {version = ">=1,<2", markers = "python_version < \"3.12\""}, - {version = ">=1.26.0,<2.0.0", markers = "python_version >= \"3.12\""}, + {version = ">=1.22.4,<2", markers = "python_version < \"3.12\""}, + {version = ">=1.26.2,<3", markers = "python_version >= \"3.12\""}, ] pydantic-settings = "^2.4.0" PyYAML = ">=5.3" @@ -2917,7 +2937,7 @@ url = "libs/community" [[package]] name = "langchain-core" -version = "0.3.20" +version = "0.3.25" description = "Building applications with LLMs through composability" optional = false python-versions = ">=3.9,<4.0" @@ -2926,7 +2946,7 @@ develop = true [package.dependencies] jsonpatch = "^1.33" -langsmith = "^0.1.125" +langsmith = ">=0.1.125,<0.3" packaging = ">=23.2,<25" pydantic = [ {version = ">=2.5.2,<3.0.0", markers = "python_full_version < \"3.12.4\""}, @@ -2962,7 +2982,7 @@ subdirectory = "libs/experimental" [[package]] name = "langchain-fireworks" -version = "0.2.1" +version = "0.2.5" description = "An integration package connecting Fireworks and LangChain" optional = false python-versions = ">=3.9,<4.0" @@ -2972,7 +2992,7 @@ develop = true [package.dependencies] aiohttp = "^3.9.1" fireworks-ai = ">=0.13.0" -langchain-core = "^0.3.9" +langchain-core = "^0.3.15" openai = "^1.10.0" requests = "^2" @@ -3010,7 +3030,7 @@ subdirectory = "libs/vertexai" [[package]] name = "langchain-groq" -version = "0.2.0" +version = "0.2.1" description = "An integration package connecting Groq and LangChain" optional = false python-versions = ">=3.9,<4.0" @@ -3019,7 +3039,7 @@ develop = true [package.dependencies] groq = ">=0.4.1,<1" -langchain-core = "^0.3" +langchain-core = "^0.3.15" [package.source] type = "directory" @@ -3027,7 +3047,7 @@ url = "libs/partners/groq" [[package]] name = "langchain-mistralai" -version = "0.2.0" +version = "0.2.3" description = "An integration package connecting Mistral and LangChain" optional = false python-versions = ">=3.9,<4.0" @@ -3037,7 +3057,7 @@ develop = true [package.dependencies] httpx = ">=0.25.2,<1" httpx-sse = ">=0.3.1,<1" -langchain-core = "^0.3.0" +langchain-core = "^0.3.21" pydantic = ">=2,<3" tokenizers = ">=0.15.1,<1" @@ -3047,7 +3067,7 @@ url = "libs/partners/mistralai" [[package]] name = "langchain-openai" -version = "0.2.4" +version = "0.2.12" description = "An integration package connecting OpenAI and LangChain" optional = false python-versions = ">=3.9,<4.0" @@ -3055,8 +3075,8 @@ files = [] develop = true [package.dependencies] -langchain-core = "^0.3.13" -openai = "^1.52.0" +langchain-core = "^0.3.21" +openai = "^1.55.3" tiktoken = ">=0.7,<1" [package.source] @@ -3065,7 +3085,7 @@ url = "libs/partners/openai" [[package]] name = "langchain-text-splitters" -version = "0.3.0" +version = "0.3.2" description = "LangChain text splitting utilities" optional = false python-versions = ">=3.9,<4.0" @@ -3073,7 +3093,7 @@ files = [] develop = true [package.dependencies] -langchain-core = "^0.3.0" +langchain-core = "^0.3.15" [package.source] type = "directory" @@ -4154,13 +4174,13 @@ sympy = "*" [[package]] name = "openai" -version = "1.52.2" +version = "1.57.4" description = "The official Python library for the openai API" optional = false -python-versions = ">=3.7.1" +python-versions = ">=3.8" files = [ - {file = "openai-1.52.2-py3-none-any.whl", hash = "sha256:57e9e37bc407f39bb6ec3a27d7e8fb9728b2779936daa1fcf95df17d3edfaccc"}, - {file = "openai-1.52.2.tar.gz", hash = "sha256:87b7d0f69d85f5641678d414b7ee3082363647a5c66a462ed7f3ccb59582da0d"}, + {file = "openai-1.57.4-py3-none-any.whl", hash = "sha256:7def1ab2d52f196357ce31b9cfcf4181529ce00838286426bb35be81c035dafb"}, + {file = "openai-1.57.4.tar.gz", hash = "sha256:a8f071a3e9198e2818f63aade68e759417b9f62c0971bdb83de82504b70b77f7"}, ] [package.dependencies] @@ -6646,13 +6666,13 @@ files = [ [[package]] name = "unstructured" -version = "0.15.14" +version = "0.16.11" description = "A library that prepares raw documents for downstream ML tasks." optional = false python-versions = "<3.13,>=3.9.0" files = [ - {file = "unstructured-0.15.14-py3-none-any.whl", hash = "sha256:502903cbcc60844c82f5351a0bc2e77f00f16a144cb884ac44d2f175470a1df8"}, - {file = "unstructured-0.15.14.tar.gz", hash = "sha256:876546c308c257314865996ce15745139c9fd4f79c7b4f09ad9d719d466b5b55"}, + {file = "unstructured-0.16.11-py3-none-any.whl", hash = "sha256:a92d5bc2c2b7bb23369641fb7a7f0daba1775639199306ce4cd83ca564a03763"}, + {file = "unstructured-0.16.11.tar.gz", hash = "sha256:33ebf68aae11ce33c8a96335296557b5abd8ba96eaba3e5a1554c0b9eee40bb5"}, ] [package.dependencies] @@ -6662,6 +6682,7 @@ chardet = "*" dataclasses-json = "*" emoji = "*" filetype = "*" +html5lib = "*" langdetect = "*" lxml = "*" markdown = {version = "*", optional = true, markers = "extra == \"md\""} @@ -6673,76 +6694,30 @@ python-magic = "*" python-oxmsg = "*" rapidfuzz = "*" requests = "*" -tabulate = "*" tqdm = "*" typing-extensions = "*" unstructured-client = "*" wrapt = "*" [package.extras] -airtable = ["pyairtable"] -all-docs = ["effdet", "google-cloud-vision", "markdown", "networkx", "onnx", "openpyxl", "pandas", "pdf2image", "pdfminer.six", "pi-heif", "pikepdf", "pypandoc", "pypdf", "python-docx (>=1.1.2)", "python-pptx (>=1.0.1)", "unstructured-inference (==0.7.36)", "unstructured.pytesseract (>=0.3.12)", "xlrd"] -astradb = ["astrapy"] -azure = ["adlfs", "fsspec"] -azure-cognitive-search = ["azure-search-documents"] -bedrock = ["boto3", "langchain-community"] -biomed = ["bs4"] -box = ["boxfs", "fsspec"] -chroma = ["chromadb (>0.4.14)", "importlib-metadata (>=8.2.0)", "tenacity (==8.5.0)", "typer (<=0.9.0)"] -clarifai = ["clarifai"] -confluence = ["atlassian-python-api"] +all-docs = ["effdet", "google-cloud-vision", "markdown", "networkx", "onnx", "openpyxl", "pandas", "pdf2image", "pdfminer.six", "pi-heif", "pikepdf", "pypandoc", "pypdf", "python-docx (>=1.1.2)", "python-pptx (>=1.0.1)", "unstructured-inference (==0.8.1)", "unstructured.pytesseract (>=0.3.12)", "xlrd"] csv = ["pandas"] -databricks-volumes = ["databricks-sdk"] -delta-table = ["deltalake (<=0.19.1)", "fsspec"] -discord = ["discord-py"] doc = ["python-docx (>=1.1.2)"] docx = ["python-docx (>=1.1.2)"] -dropbox = ["dropboxdrivefs", "fsspec"] -elasticsearch = ["elasticsearch[async]"] -embed-huggingface = ["langchain-huggingface"] -embed-mixedbreadai = ["mixedbread-ai"] -embed-octoai = ["openai", "tiktoken"] -embed-vertexai = ["langchain", "langchain-community", "langchain-google-vertexai"] -embed-voyageai = ["langchain", "langchain-voyageai"] epub = ["pypandoc"] -gcs = ["bs4", "fsspec", "gcsfs"] -github = ["pygithub (>1.58.0)"] -gitlab = ["python-gitlab"] -google-drive = ["google-api-python-client"] -hubspot = ["hubspot-api-client", "urllib3"] huggingface = ["langdetect", "sacremoses", "sentencepiece", "torch", "transformers"] -image = ["effdet", "google-cloud-vision", "onnx", "pdf2image", "pdfminer.six", "pi-heif", "pikepdf", "pypdf", "unstructured-inference (==0.7.36)", "unstructured.pytesseract (>=0.3.12)"] -jira = ["atlassian-python-api"] -kafka = ["confluent-kafka"] -local-inference = ["effdet", "google-cloud-vision", "markdown", "networkx", "onnx", "openpyxl", "pandas", "pdf2image", "pdfminer.six", "pi-heif", "pikepdf", "pypandoc", "pypdf", "python-docx (>=1.1.2)", "python-pptx (>=1.0.1)", "unstructured-inference (==0.7.36)", "unstructured.pytesseract (>=0.3.12)", "xlrd"] +image = ["effdet", "google-cloud-vision", "onnx", "pdf2image", "pdfminer.six", "pi-heif", "pikepdf", "pypdf", "unstructured-inference (==0.8.1)", "unstructured.pytesseract (>=0.3.12)"] +local-inference = ["effdet", "google-cloud-vision", "markdown", "networkx", "onnx", "openpyxl", "pandas", "pdf2image", "pdfminer.six", "pi-heif", "pikepdf", "pypandoc", "pypdf", "python-docx (>=1.1.2)", "python-pptx (>=1.0.1)", "unstructured-inference (==0.8.1)", "unstructured.pytesseract (>=0.3.12)", "xlrd"] md = ["markdown"] -mongodb = ["pymongo"] -notion = ["htmlBuilder", "notion-client"] odt = ["pypandoc", "python-docx (>=1.1.2)"] -onedrive = ["Office365-REST-Python-Client", "bs4", "msal"] -openai = ["langchain-openai"] -opensearch = ["opensearch-py"] org = ["pypandoc"] -outlook = ["Office365-REST-Python-Client", "msal"] paddleocr = ["paddlepaddle (==3.0.0b1)", "unstructured.paddleocr (==2.8.1.0)"] -pdf = ["effdet", "google-cloud-vision", "onnx", "pdf2image", "pdfminer.six", "pi-heif", "pikepdf", "pypdf", "unstructured-inference (==0.7.36)", "unstructured.pytesseract (>=0.3.12)"] -pinecone = ["pinecone-client (>=3.7.1)"] -postgres = ["psycopg2-binary"] +pdf = ["effdet", "google-cloud-vision", "onnx", "pdf2image", "pdfminer.six", "pi-heif", "pikepdf", "pypdf", "unstructured-inference (==0.8.1)", "unstructured.pytesseract (>=0.3.12)"] ppt = ["python-pptx (>=1.0.1)"] pptx = ["python-pptx (>=1.0.1)"] -qdrant = ["qdrant-client"] -reddit = ["praw"] rst = ["pypandoc"] rtf = ["pypandoc"] -s3 = ["fsspec", "s3fs"] -salesforce = ["simple-salesforce"] -sftp = ["fsspec", "paramiko"] -sharepoint = ["Office365-REST-Python-Client", "msal"] -singlestore = ["singlestoredb"] -slack = ["slack-sdk"] tsv = ["pandas"] -weaviate = ["weaviate-client"] -wikipedia = ["wikipedia"] xlsx = ["networkx", "openpyxl", "pandas", "xlrd"] [[package]] @@ -7407,4 +7382,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = ">=3.9,<4.0" -content-hash = "cb6b45ac7f487c6510a0eeef80c6cfd4b163f671eedb42d9ebf5315f42fa1ab1" +content-hash = "138c279994b75a02c377fd5fde3808770c9ae6259c59728b9986480d93790aa1" diff --git a/pyproject.toml b/pyproject.toml index b4172ec49bae5..0466cad3b8ed8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -47,7 +47,8 @@ grandalf = "^0.8" lark = "^1.1.9" pandas = "^2" rank-bm25 = "^0.2.2" -unstructured = { version = "^0.15.12", extras = ["md"], python = "<3.13" } +tabulate = "^0.9.0" +unstructured = { version = "^0.16.11", extras = ["md"], python = "<3.13" } wikipedia = "^1.4.0" pypdf = "^5.0.0" vcrpy = "^6.0.1"