From f2be7a2018257fac45a6ccb948cb8980db8adc99 Mon Sep 17 00:00:00 2001
From: Tim Diekmann <21277928+TimDiekmann@users.noreply.github.com>
Date: Thu, 30 Nov 2023 11:34:10 +0100
Subject: [PATCH] Move Python libraries from `hashintel/hash` into labs (#54)
---
libs/hash-graph-client/python/.gitattributes | 1 +
libs/hash-graph-client/python/.gitignore | 1 +
libs/hash-graph-client/python/LICENSE.md | 606 ++++++++
libs/hash-graph-client/python/README.md | 6 +
.../python/graph_client/__init__.py | 357 +++++
.../python/graph_client/models.py | 1238 +++++++++++++++
.../python/graph_client/py.typed | 0
.../python/graph_client/schema.py | 13 +
libs/hash-graph-client/python/package.json | 25 +
libs/hash-graph-client/python/poetry.lock | 1344 +++++++++++++++++
libs/hash-graph-client/python/poetry.toml | 3 +
libs/hash-graph-client/python/pyproject.toml | 91 ++
.../python/scripts/aliases.json | 4 +
.../python/scripts/codegen.sh | 56 +
.../python/scripts/rebase.py | 37 +
libs/hash-graph-client/python/turbo.json | 49 +
libs/hash-graph-client/python/yarn.lock | 314 ++++
libs/hash-graph-sdk/python/.gitattributes | 2 +
libs/hash-graph-sdk/python/LICENSE.md | 606 ++++++++
libs/hash-graph-sdk/python/README.md | 3 +
.../python/examples/query_company_entities.py | 24 +
.../query_company_entities_limited_axes.py | 35 +
.../examples/query_company_entity_type.py | 31 +
.../python/graph_sdk/__init__.py | 20 +
.../python/graph_sdk/client/__init__.py | 1 +
.../python/graph_sdk/client/_compat.py | 10 +
.../python/graph_sdk/client/blocking.py | 99 ++
.../python/graph_sdk/client/concurrent.py | 266 ++++
.../python/graph_sdk/filter/__init__.py | 20 +
.../python/graph_sdk/filter/base.py | 67 +
.../python/graph_sdk/filter/config.json | 51 +
.../python/graph_sdk/filter/path.py | 226 +++
.../python/graph_sdk/options.py | 372 +++++
libs/hash-graph-sdk/python/graph_sdk/py.typed | 0
libs/hash-graph-sdk/python/graph_sdk/query.py | 366 +++++
libs/hash-graph-sdk/python/graph_sdk/types.py | 212 +++
libs/hash-graph-sdk/python/graph_sdk/utils.py | 92 ++
libs/hash-graph-sdk/python/package.json | 25 +
libs/hash-graph-sdk/python/poetry.lock | 791 ++++++++++
libs/hash-graph-sdk/python/poetry.toml | 3 +
libs/hash-graph-sdk/python/pyproject.toml | 90 ++
.../python/scripts/generate_blocking.py | 219 +++
.../python/scripts/generate_filter.py | 618 ++++++++
libs/hash-graph-sdk/python/turbo.json | 56 +
libs/hash-graph-types/python/LICENSE.md | 606 ++++++++
libs/hash-graph-types/python/README.md | 3 +
.../python/graph_types/__init__.py | 61 +
.../graph_types/_annotations/__init__.py | 4 +
.../python/graph_types/_annotations/const.py | 68 +
.../graph_types/_annotations/require.py | 22 +
.../python/graph_types/_schema.py | 205 +++
.../python/graph_types/base.py | 62 +
.../python/graph_types/data_type.py | 122 ++
.../python/graph_types/entity_type.py | 123 ++
.../python/graph_types/property_type.py | 115 ++
.../python/graph_types/py.typed | 0
libs/hash-graph-types/python/package.json | 19 +
libs/hash-graph-types/python/poetry.lock | 404 +++++
libs/hash-graph-types/python/poetry.toml | 3 +
libs/hash-graph-types/python/pyproject.toml | 82 +
libs/hash-graph-types/python/turbo.json | 41 +
61 files changed, 10390 insertions(+)
create mode 100644 libs/hash-graph-client/python/.gitattributes
create mode 100644 libs/hash-graph-client/python/.gitignore
create mode 100644 libs/hash-graph-client/python/LICENSE.md
create mode 100644 libs/hash-graph-client/python/README.md
create mode 100644 libs/hash-graph-client/python/graph_client/__init__.py
create mode 100644 libs/hash-graph-client/python/graph_client/models.py
create mode 100644 libs/hash-graph-client/python/graph_client/py.typed
create mode 100644 libs/hash-graph-client/python/graph_client/schema.py
create mode 100644 libs/hash-graph-client/python/package.json
create mode 100644 libs/hash-graph-client/python/poetry.lock
create mode 100644 libs/hash-graph-client/python/poetry.toml
create mode 100644 libs/hash-graph-client/python/pyproject.toml
create mode 100644 libs/hash-graph-client/python/scripts/aliases.json
create mode 100755 libs/hash-graph-client/python/scripts/codegen.sh
create mode 100644 libs/hash-graph-client/python/scripts/rebase.py
create mode 100644 libs/hash-graph-client/python/turbo.json
create mode 100644 libs/hash-graph-client/python/yarn.lock
create mode 100644 libs/hash-graph-sdk/python/.gitattributes
create mode 100644 libs/hash-graph-sdk/python/LICENSE.md
create mode 100644 libs/hash-graph-sdk/python/README.md
create mode 100644 libs/hash-graph-sdk/python/examples/query_company_entities.py
create mode 100644 libs/hash-graph-sdk/python/examples/query_company_entities_limited_axes.py
create mode 100644 libs/hash-graph-sdk/python/examples/query_company_entity_type.py
create mode 100644 libs/hash-graph-sdk/python/graph_sdk/__init__.py
create mode 100644 libs/hash-graph-sdk/python/graph_sdk/client/__init__.py
create mode 100644 libs/hash-graph-sdk/python/graph_sdk/client/_compat.py
create mode 100644 libs/hash-graph-sdk/python/graph_sdk/client/blocking.py
create mode 100644 libs/hash-graph-sdk/python/graph_sdk/client/concurrent.py
create mode 100644 libs/hash-graph-sdk/python/graph_sdk/filter/__init__.py
create mode 100644 libs/hash-graph-sdk/python/graph_sdk/filter/base.py
create mode 100644 libs/hash-graph-sdk/python/graph_sdk/filter/config.json
create mode 100644 libs/hash-graph-sdk/python/graph_sdk/filter/path.py
create mode 100644 libs/hash-graph-sdk/python/graph_sdk/options.py
create mode 100644 libs/hash-graph-sdk/python/graph_sdk/py.typed
create mode 100644 libs/hash-graph-sdk/python/graph_sdk/query.py
create mode 100644 libs/hash-graph-sdk/python/graph_sdk/types.py
create mode 100644 libs/hash-graph-sdk/python/graph_sdk/utils.py
create mode 100644 libs/hash-graph-sdk/python/package.json
create mode 100644 libs/hash-graph-sdk/python/poetry.lock
create mode 100644 libs/hash-graph-sdk/python/poetry.toml
create mode 100644 libs/hash-graph-sdk/python/pyproject.toml
create mode 100644 libs/hash-graph-sdk/python/scripts/generate_blocking.py
create mode 100644 libs/hash-graph-sdk/python/scripts/generate_filter.py
create mode 100644 libs/hash-graph-sdk/python/turbo.json
create mode 100644 libs/hash-graph-types/python/LICENSE.md
create mode 100644 libs/hash-graph-types/python/README.md
create mode 100644 libs/hash-graph-types/python/graph_types/__init__.py
create mode 100644 libs/hash-graph-types/python/graph_types/_annotations/__init__.py
create mode 100644 libs/hash-graph-types/python/graph_types/_annotations/const.py
create mode 100644 libs/hash-graph-types/python/graph_types/_annotations/require.py
create mode 100644 libs/hash-graph-types/python/graph_types/_schema.py
create mode 100644 libs/hash-graph-types/python/graph_types/base.py
create mode 100644 libs/hash-graph-types/python/graph_types/data_type.py
create mode 100644 libs/hash-graph-types/python/graph_types/entity_type.py
create mode 100644 libs/hash-graph-types/python/graph_types/property_type.py
create mode 100644 libs/hash-graph-types/python/graph_types/py.typed
create mode 100644 libs/hash-graph-types/python/package.json
create mode 100644 libs/hash-graph-types/python/poetry.lock
create mode 100644 libs/hash-graph-types/python/poetry.toml
create mode 100644 libs/hash-graph-types/python/pyproject.toml
create mode 100644 libs/hash-graph-types/python/turbo.json
diff --git a/libs/hash-graph-client/python/.gitattributes b/libs/hash-graph-client/python/.gitattributes
new file mode 100644
index 0000000..d3f0aed
--- /dev/null
+++ b/libs/hash-graph-client/python/.gitattributes
@@ -0,0 +1 @@
+graph_client/models.py linguist-generated=true
diff --git a/libs/hash-graph-client/python/.gitignore b/libs/hash-graph-client/python/.gitignore
new file mode 100644
index 0000000..3a2a622
--- /dev/null
+++ b/libs/hash-graph-client/python/.gitignore
@@ -0,0 +1 @@
+scripts/openapi.bundle.json
diff --git a/libs/hash-graph-client/python/LICENSE.md b/libs/hash-graph-client/python/LICENSE.md
new file mode 100644
index 0000000..9a70d79
--- /dev/null
+++ b/libs/hash-graph-client/python/LICENSE.md
@@ -0,0 +1,606 @@
+# GNU Affero General Public License
+
+_Version 3, 19 November 2007_
+_Copyright © 2007 Free Software Foundation, Inc. <>_
+
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+
+## Preamble
+
+The GNU Affero General Public License is a free, copyleft license for
+software and other kinds of works, specifically designed to ensure
+cooperation with the community in the case of network server software.
+
+The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+our General Public Licenses are intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.
+
+When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+Developers that use our General Public Licenses protect your rights
+with two steps: **(1)** assert copyright on the software, and **(2)** offer
+you this License which gives you legal permission to copy, distribute
+and/or modify the software.
+
+A secondary benefit of defending all users' freedom is that
+improvements made in alternate versions of the program, if they
+receive widespread use, become available for other developers to
+incorporate. Many developers of free software are heartened and
+encouraged by the resulting cooperation. However, in the case of
+software used on network servers, this result may fail to come about.
+The GNU General Public License permits making a modified version and
+letting the public access it on a server without ever releasing its
+source code to the public.
+
+The GNU Affero General Public License is designed specifically to
+ensure that, in such cases, the modified source code becomes available
+to the community. It requires the operator of a network server to
+provide the source code of the modified version running there to the
+users of that server. Therefore, public use of a modified version, on
+a publicly accessible server, gives the public access to the source
+code of the modified version.
+
+An older license, called the Affero General Public License and
+published by Affero, was designed to accomplish similar goals. This is
+a different license, not a version of the Affero GPL, but Affero has
+released a new version of the Affero GPL which permits relicensing under
+this license.
+
+The precise terms and conditions for copying, distribution and
+modification follow.
+
+## TERMS AND CONDITIONS
+
+### 0. Definitions
+
+“This License” refers to version 3 of the GNU Affero General Public License.
+
+“Copyright” also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+“The Program” refers to any copyrightable work licensed under this
+License. Each licensee is addressed as “you”. “Licensees” and
+“recipients” may be individuals or organizations.
+
+To “modify” a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a “modified version” of the
+earlier work or a work “based on” the earlier work.
+
+A “covered work” means either the unmodified Program or a work based
+on the Program.
+
+To “propagate” a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+To “convey” a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+An interactive user interface displays “Appropriate Legal Notices”
+to the extent that it includes a convenient and prominently visible
+feature that **(1)** displays an appropriate copyright notice, and **(2)**
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+### 1. Source Code
+
+The “source code” for a work means the preferred form of the work
+for making modifications to it. “Object code” means any non-source
+form of a work.
+
+A “Standard Interface” means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+The “System Libraries” of an executable work include anything, other
+than the work as a whole, that **(a)** is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and **(b)** serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+“Major Component”, in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+The “Corresponding Source” for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+The Corresponding Source for a work in source code form is that
+same work.
+
+### 2. Basic Permissions
+
+All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+### 3. Protecting Users' Legal Rights From Anti-Circumvention Law
+
+No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+### 4. Conveying Verbatim Copies
+
+You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+### 5. Conveying Modified Source Versions
+
+You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+- **a)** The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+- **b)** The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section 7.
+ This requirement modifies the requirement in section 4 to
+ “keep intact all notices”.
+- **c)** You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+- **d)** If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+“aggregate” if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+### 6. Conveying Non-Source Forms
+
+You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+- **a)** Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+- **b)** Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either **(1)** a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or **(2)** access to copy the
+ Corresponding Source from a network server at no charge.
+- **c)** Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+- **d)** Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+- **e)** Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+A “User Product” is either **(1)** a “consumer product”, which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or **(2)** anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, “normally used” refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+“Installation Information” for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+### 7. Additional Terms
+
+“Additional permissions” are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+- **a)** Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+- **b)** Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+- **c)** Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+- **d)** Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+- **e)** Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+- **f)** Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+All other non-permissive additional terms are considered “further
+restrictions” within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+### 8. Termination
+
+You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated **(a)**
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and **(b)** permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+### 9. Acceptance Not Required for Having Copies
+
+You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+### 10. Automatic Licensing of Downstream Recipients
+
+Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+An “entity transaction” is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+### 11. Patents
+
+A “contributor” is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's “contributor version”.
+
+A contributor's “essential patent claims” are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, “control” includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+In the following three paragraphs, a “patent license” is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To “grant” such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either **(1)** cause the Corresponding Source to be so
+available, or **(2)** arrange to deprive yourself of the benefit of the
+patent license for this particular work, or **(3)** arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. “Knowingly relying” means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+A patent license is “discriminatory” if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license **(a)** in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or **(b)** primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+### 12. No Surrender of Others' Freedom
+
+If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+### 13. Remote Network Interaction; Use with the GNU General Public License
+
+Notwithstanding any other provision of this License, if you modify the
+Program, your modified version must prominently offer all users
+interacting with it remotely through a computer network (if your version
+supports such interaction) an opportunity to receive the Corresponding
+Source of your version by providing access to the Corresponding Source
+from a network server at no charge, through some standard or customary
+means of facilitating copying of software. This Corresponding Source
+shall include the Corresponding Source for any work covered by version 3
+of the GNU General Public License that is incorporated pursuant to the
+following paragraph.
+
+Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the work with which it is combined will remain governed by version
+3 of the GNU General Public License.
+
+### 14. Revised Versions of this License
+
+The Free Software Foundation may publish revised and/or new versions of
+the GNU Affero General Public License from time to time. Such new versions
+will be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU Affero General
+Public License “or any later version” applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU Affero General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+If the Program specifies that a proxy can decide which future
+versions of the GNU Affero General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+### 15. Disclaimer of Warranty
+
+THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+### 16. Limitation of Liability
+
+IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+### 17. Interpretation of Sections 15 and 16
+
+If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
diff --git a/libs/hash-graph-client/python/README.md b/libs/hash-graph-client/python/README.md
new file mode 100644
index 0000000..fcb98a3
--- /dev/null
+++ b/libs/hash-graph-client/python/README.md
@@ -0,0 +1,6 @@
+# HASH Graph client
+
+OpenAPI automatically generated client for HASH Graph.
+
+Due to current limitations in the auto-generation process, only the models are automatically generated.
+The client code is manually written.
diff --git a/libs/hash-graph-client/python/graph_client/__init__.py b/libs/hash-graph-client/python/graph_client/__init__.py
new file mode 100644
index 0000000..30c9dd6
--- /dev/null
+++ b/libs/hash-graph-client/python/graph_client/__init__.py
@@ -0,0 +1,357 @@
+"""Client for the HASH API."""
+
+from typing import Literal, TypeAlias, TypeVar
+from uuid import UUID
+
+import httpx
+from pydantic import BaseModel
+from yarl import URL
+
+from graph_client.models import (
+ CreateDataTypeRequest,
+ CreateEntityRequest,
+ CreateEntityTypeRequest,
+ CreatePropertyTypeRequest,
+ DataTypeQueryToken,
+ DataTypeStructuralQuery,
+ EntityMetadata,
+ EntityQueryToken,
+ EntityStructuralQuery,
+ EntityTypeQueryToken,
+ EntityTypeStructuralQuery,
+ LoadExternalDataTypeRequest,
+ LoadExternalEntityTypeRequest,
+ LoadExternalPropertyTypeRequest,
+ MaybeListOfOntologyElementMetadata,
+ OntologyElementMetadata,
+ PropertyTypeQueryToken,
+ PropertyTypeStructuralQuery,
+ Selector,
+ Subgraph,
+ UpdateDataTypeRequest,
+ UpdateEntityRequest,
+ UpdateEntityTypeRequest,
+ UpdatePropertyTypeRequest,
+)
+
+T = TypeVar("T", bound=BaseModel)
+
+QueryToken: TypeAlias = (
+ DataTypeQueryToken
+ | PropertyTypeQueryToken
+ | EntityTypeQueryToken
+ | EntityQueryToken
+ | Selector
+ | str
+ | float
+)
+
+__all__ = ["GraphClient", "QueryToken", "models"]
+
+
+async def _send_request(
+ endpoint: URL,
+ method: Literal["POST", "PUT"],
+ body: BaseModel,
+ actor: UUID,
+ response_t: type[T],
+) -> T:
+ """Send a request to the HASH API."""
+ async with httpx.AsyncClient() as client:
+ response = await client.request(
+ method,
+ str(endpoint),
+ json=body.model_dump(by_alias=True, mode="json"),
+ headers={
+ "X-Authenticated-User-Actor-Id": str(actor),
+ },
+ )
+
+ response.raise_for_status()
+
+ json = response.json()
+ return response_t.model_validate(json, strict=False)
+
+
+U = TypeVar("U")
+
+
+def _assert_not_none(value: U | None) -> U:
+ """Assert that the value is not None."""
+ if value is None:
+ msg = "value cannot be None"
+ raise ValueError(msg)
+
+ return value
+
+
+class GraphClient:
+ """Low-level implementation of the client for the HASH API."""
+
+ base: URL
+ actor: UUID | None
+
+ def __init__(self, base: URL, *, actor: UUID | None = None) -> None:
+ """Initialize the client with the base URL."""
+ self.base = base
+ self.actor = actor
+
+ def _actor(self, override: UUID | None = None) -> UUID:
+ """Get the actor for the client."""
+ actor = override or self.actor
+ return _assert_not_none(actor)
+
+ async def query_entity_types(
+ self,
+ query: EntityTypeStructuralQuery,
+ *,
+ actor: UUID | None = None,
+ ) -> Subgraph:
+ """Query the HASH API for entity types."""
+ endpoint = self.base / "entity-types" / "query"
+
+ return await _send_request(
+ endpoint,
+ "POST",
+ query,
+ self._actor(actor),
+ Subgraph,
+ )
+
+ async def load_external_entity_type(
+ self,
+ request: LoadExternalEntityTypeRequest,
+ *,
+ actor: UUID | None = None,
+ ) -> OntologyElementMetadata:
+ """Load an external entity type."""
+ endpoint = self.base / "entity-types" / "load"
+
+ return await _send_request(
+ endpoint,
+ "POST",
+ request,
+ self._actor(actor),
+ OntologyElementMetadata,
+ )
+
+ async def create_entity_types(
+ self,
+ request: CreateEntityTypeRequest,
+ *,
+ actor: UUID | None = None,
+ ) -> MaybeListOfOntologyElementMetadata:
+ """Create an entity type."""
+ endpoint = self.base / "entity-types"
+
+ return await _send_request(
+ endpoint,
+ "POST",
+ request,
+ self._actor(actor),
+ MaybeListOfOntologyElementMetadata,
+ )
+
+ async def update_entity_type(
+ self,
+ request: UpdateEntityTypeRequest,
+ *,
+ actor: UUID | None = None,
+ ) -> OntologyElementMetadata:
+ """Update an entity type."""
+ endpoint = self.base / "entity-types"
+
+ return await _send_request(
+ endpoint,
+ "PUT",
+ request,
+ self._actor(actor),
+ OntologyElementMetadata,
+ )
+
+ async def query_property_types(
+ self,
+ query: PropertyTypeStructuralQuery,
+ *,
+ actor: UUID | None = None,
+ ) -> Subgraph:
+ """Query the HASH API for property types."""
+ endpoint = self.base / "property-types" / "query"
+
+ return await _send_request(
+ endpoint,
+ "POST",
+ query,
+ self._actor(actor),
+ Subgraph,
+ )
+
+ async def load_external_property_type(
+ self,
+ request: LoadExternalPropertyTypeRequest,
+ *,
+ actor: UUID | None = None,
+ ) -> OntologyElementMetadata:
+ """Load an external property type."""
+ endpoint = self.base / "property-types" / "load"
+
+ return await _send_request(
+ endpoint,
+ "POST",
+ request,
+ self._actor(actor),
+ OntologyElementMetadata,
+ )
+
+ async def create_property_types(
+ self,
+ request: CreatePropertyTypeRequest,
+ *,
+ actor: UUID | None = None,
+ ) -> MaybeListOfOntologyElementMetadata:
+ """Create a property type."""
+ endpoint = self.base / "property-types"
+
+ return await _send_request(
+ endpoint,
+ "POST",
+ request,
+ self._actor(actor),
+ MaybeListOfOntologyElementMetadata,
+ )
+
+ async def update_property_type(
+ self,
+ request: UpdatePropertyTypeRequest,
+ *,
+ actor: UUID | None = None,
+ ) -> OntologyElementMetadata:
+ """Update a property type."""
+ endpoint = self.base / "property-types"
+
+ return await _send_request(
+ endpoint,
+ "PUT",
+ request,
+ self._actor(actor),
+ OntologyElementMetadata,
+ )
+
+ async def query_data_types(
+ self,
+ query: DataTypeStructuralQuery,
+ *,
+ actor: UUID | None = None,
+ ) -> Subgraph:
+ """Query the HASH API for data types."""
+ endpoint = self.base / "data-types" / "query"
+
+ return await _send_request(
+ endpoint,
+ "POST",
+ query,
+ self._actor(actor),
+ Subgraph,
+ )
+
+ async def load_external_data_type(
+ self,
+ request: LoadExternalDataTypeRequest,
+ *,
+ actor: UUID | None = None,
+ ) -> OntologyElementMetadata:
+ """Load an external data type."""
+ endpoint = self.base / "data-types" / "load"
+
+ return await _send_request(
+ endpoint,
+ "POST",
+ request,
+ self._actor(actor),
+ OntologyElementMetadata,
+ )
+
+ async def create_data_types(
+ self,
+ request: CreateDataTypeRequest,
+ *,
+ actor: UUID | None = None,
+ ) -> MaybeListOfOntologyElementMetadata:
+ """Create a data type."""
+ endpoint = self.base / "data-types"
+
+ return await _send_request(
+ endpoint,
+ "POST",
+ request,
+ self._actor(actor),
+ MaybeListOfOntologyElementMetadata,
+ )
+
+ async def update_data_type(
+ self,
+ request: UpdateDataTypeRequest,
+ *,
+ actor: UUID | None = None,
+ ) -> OntologyElementMetadata:
+ """Update a data type."""
+ endpoint = self.base / "data-types"
+
+ return await _send_request(
+ endpoint,
+ "PUT",
+ request,
+ self._actor(actor),
+ OntologyElementMetadata,
+ )
+
+ async def query_entities(
+ self,
+ query: EntityStructuralQuery,
+ *,
+ actor: UUID | None = None,
+ ) -> Subgraph:
+ """Query the HASH API for entities."""
+ endpoint = self.base / "entities" / "query"
+
+ return await _send_request(
+ endpoint,
+ "POST",
+ query,
+ self._actor(actor),
+ Subgraph,
+ )
+
+ async def create_entity(
+ self,
+ request: CreateEntityRequest,
+ *,
+ actor: UUID | None = None,
+ ) -> EntityMetadata:
+ """Create an entity."""
+ endpoint = self.base / "entities"
+
+ return await _send_request(
+ endpoint,
+ "POST",
+ request,
+ self._actor(actor),
+ EntityMetadata,
+ )
+
+ async def update_entity(
+ self,
+ request: UpdateEntityRequest,
+ *,
+ actor: UUID | None = None,
+ ) -> EntityMetadata:
+ """Update an entity."""
+ endpoint = self.base / "entities"
+
+ return await _send_request(
+ endpoint,
+ "PUT",
+ request,
+ self._actor(actor),
+ EntityMetadata,
+ )
diff --git a/libs/hash-graph-client/python/graph_client/models.py b/libs/hash-graph-client/python/graph_client/models.py
new file mode 100644
index 0000000..81d638c
--- /dev/null
+++ b/libs/hash-graph-client/python/graph_client/models.py
@@ -0,0 +1,1238 @@
+from __future__ import annotations
+from graph_client.schema import OntologyTypeSchema
+from datetime import datetime
+from enum import Enum
+from typing import Any, Literal
+from uuid import UUID
+from pydantic import AnyUrl, BaseModel, ConfigDict, Field, RootModel
+
+class AccountGroupId(RootModel[UUID]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: UUID
+
+class AccountGroupPermission(Enum):
+ add_member = 'add_member'
+ remove_member = 'remove_member'
+
+class AccountId(RootModel[UUID]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: UUID
+
+class DataTypeOwnerSubjectItem(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ kind: Literal['account']
+ subject_id: AccountId = Field(..., alias='subjectId')
+
+class DataTypeOwnerSubjectItem1(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ kind: Literal['accountGroup']
+ subject_id: AccountGroupId = Field(..., alias='subjectId')
+
+class DataTypeOwnerSubject(RootModel[DataTypeOwnerSubjectItem | DataTypeOwnerSubjectItem1]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: DataTypeOwnerSubjectItem | DataTypeOwnerSubjectItem1 = Field(..., discriminator='kind')
+
+class DataTypePermission(Enum):
+ update = 'update'
+ view = 'view'
+
+class DataTypeQueryToken(Enum):
+ """
+ A single token in a [`DataTypeQueryPath`].
+ """
+ base_url = 'baseUrl'
+ version = 'version'
+ versioned_url = 'versionedUrl'
+ owned_by_id = 'ownedById'
+ record_created_by_id = 'recordCreatedById'
+ record_archived_by_id = 'recordArchivedById'
+ title = 'title'
+ description = 'description'
+ type = 'type'
+
+class DataTypeRelationAndSubjectItem(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ relation: Literal['owner']
+ subject: DataTypeOwnerSubject
+
+class DataTypeViewerSubjectItem(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ kind: Literal['public']
+
+class DataTypeViewerSubject(RootModel[DataTypeViewerSubjectItem]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: DataTypeViewerSubjectItem
+
+class DecisionTime(RootModel[Literal['decisionTime']]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: Literal['decisionTime'] = Field(..., description='Time axis for the decision time.\n\nThis is used as the generic argument to time-related structs and can be used as tag value.')
+
+class EdgeResolveDepths(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ incoming: int = Field(..., ge=0)
+ outgoing: int = Field(..., ge=0)
+
+class EntityEditionId(RootModel[UUID]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: UUID
+
+class EntityEditorSubjectItem(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ kind: Literal['account']
+ subject_id: AccountId = Field(..., alias='subjectId')
+
+class EntityEditorSubjectItem1(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ kind: Literal['accountGroup']
+ subject_id: AccountGroupId = Field(..., alias='subjectId')
+
+class EntityEditorSubject(RootModel[EntityEditorSubjectItem | EntityEditorSubjectItem1]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: EntityEditorSubjectItem | EntityEditorSubjectItem1 = Field(..., discriminator='kind')
+
+class EntityId(RootModel[str]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: str
+
+class EntityOwnerSubjectItem(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ kind: Literal['account']
+ subject_id: AccountId = Field(..., alias='subjectId')
+
+class EntityOwnerSubjectItem1(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ kind: Literal['accountGroup']
+ subject_id: AccountGroupId = Field(..., alias='subjectId')
+
+class EntityOwnerSubject(RootModel[EntityOwnerSubjectItem | EntityOwnerSubjectItem1]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: EntityOwnerSubjectItem | EntityOwnerSubjectItem1 = Field(..., discriminator='kind')
+
+class EntityPermission(Enum):
+ update = 'update'
+ view = 'view'
+
+class EntityProperties(BaseModel):
+ """
+ The properties of an entity.
+
+ When expressed as JSON, this should validate against its respective entity type(s).
+ """
+ model_config = ConfigDict(populate_by_name=True)
+
+class EntityQueryToken(Enum):
+ """
+ A single token in an [`EntityQueryPath`].
+ """
+ uuid = 'uuid'
+ edition_id = 'editionId'
+ archived = 'archived'
+ draft = 'draft'
+ owned_by_id = 'ownedById'
+ record_created_by_id = 'recordCreatedById'
+ type = 'type'
+ properties = 'properties'
+ incoming_links = 'incomingLinks'
+ outgoing_links = 'outgoingLinks'
+ left_entity = 'leftEntity'
+ right_entity = 'rightEntity'
+ left_to_right_order = 'leftToRightOrder'
+ right_to_left_order = 'rightToLeftOrder'
+
+class EntityRecordId(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ edition_id: EntityEditionId = Field(..., alias='editionId')
+ entity_id: EntityId = Field(..., alias='entityId')
+
+class EntityRelationAndSubjectItem(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ relation: Literal['owner']
+ subject: EntityOwnerSubject
+
+class EntityRelationAndSubjectItem1(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ relation: Literal['editor']
+ subject: EntityEditorSubject
+
+class EntityTypeInstantiatorSubjectItem(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ kind: Literal['account']
+ subject_id: AccountId = Field(..., alias='subjectId')
+
+class EntityTypeInstantiatorSubjectItem1(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ kind: Literal['accountGroup']
+ subject_id: AccountGroupId = Field(..., alias='subjectId')
+
+class EntityTypeInstantiatorSubjectItem2(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ kind: Literal['public']
+
+class EntityTypeInstantiatorSubject(RootModel[EntityTypeInstantiatorSubjectItem | EntityTypeInstantiatorSubjectItem1 | EntityTypeInstantiatorSubjectItem2]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: EntityTypeInstantiatorSubjectItem | EntityTypeInstantiatorSubjectItem1 | EntityTypeInstantiatorSubjectItem2 = Field(..., discriminator='kind')
+
+class EntityTypeOwnerSubjectItem(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ kind: Literal['account']
+ subject_id: AccountId = Field(..., alias='subjectId')
+
+class EntityTypeOwnerSubjectItem1(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ kind: Literal['accountGroup']
+ subject_id: AccountGroupId = Field(..., alias='subjectId')
+
+class EntityTypeOwnerSubject(RootModel[EntityTypeOwnerSubjectItem | EntityTypeOwnerSubjectItem1]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: EntityTypeOwnerSubjectItem | EntityTypeOwnerSubjectItem1 = Field(..., discriminator='kind')
+
+class EntityTypePermission(Enum):
+ update = 'update'
+ view = 'view'
+ instantiate = 'instantiate'
+
+class EntityTypeQueryToken(Enum):
+ """
+ A single token in a [`EntityTypeQueryPath`].
+ """
+ base_url = 'baseUrl'
+ version = 'version'
+ versioned_url = 'versionedUrl'
+ owned_by_id = 'ownedById'
+ record_created_by_id = 'recordCreatedById'
+ record_archived_by_id = 'recordArchivedById'
+ title = 'title'
+ description = 'description'
+ examples = 'examples'
+ properties = 'properties'
+ required = 'required'
+ label_property = 'labelProperty'
+ icon = 'icon'
+ links = 'links'
+ inherits_from = 'inheritsFrom'
+ children = 'children'
+
+class EntityTypeRelationAndSubjectItem(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ relation: Literal['owner']
+ subject: EntityTypeOwnerSubject
+
+class EntityTypeRelationAndSubjectItem2(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ relation: Literal['instantiator']
+ subject: EntityTypeInstantiatorSubject
+
+class EntityTypeViewerSubjectItem(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ kind: Literal['public']
+
+class EntityTypeViewerSubject(RootModel[EntityTypeViewerSubjectItem]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: EntityTypeViewerSubjectItem
+
+class EntityUuid(RootModel[UUID]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: UUID
+
+class EntityViewerSubjectItem(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ kind: Literal['public']
+
+class EntityViewerSubjectItem1(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ kind: Literal['account']
+ subject_id: AccountId = Field(..., alias='subjectId')
+
+class EntityViewerSubjectItem2(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ kind: Literal['accountGroup']
+ subject_id: AccountGroupId = Field(..., alias='subjectId')
+
+class EntityViewerSubject(RootModel[EntityViewerSubjectItem | EntityViewerSubjectItem1 | EntityViewerSubjectItem2]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: EntityViewerSubjectItem | EntityViewerSubjectItem1 | EntityViewerSubjectItem2 = Field(..., discriminator='kind')
+
+class ParameterExpression(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ parameter: Any
+
+class KnowledgeGraphEdgeKind(Enum):
+ has_left_entity = 'HAS_LEFT_ENTITY'
+ has_right_entity = 'HAS_RIGHT_ENTITY'
+
+class LinkOrder(RootModel[int]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: int
+
+class ModifyRelationshipOperation(Enum):
+ """
+ Used for mutating a single relationship within the service.
+ """
+ touch = 'touch'
+ create = 'create'
+ delete = 'delete'
+
+class NullableTimestamp(RootModel[datetime | None]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: datetime | None = None
+
+class OntologyEdgeKind(Enum):
+ inherits_from = 'INHERITS_FROM'
+ constrains_values_on = 'CONSTRAINS_VALUES_ON'
+ constrains_properties_on = 'CONSTRAINS_PROPERTIES_ON'
+ constrains_links_on = 'CONSTRAINS_LINKS_ON'
+ constrains_link_destinations_on = 'CONSTRAINS_LINK_DESTINATIONS_ON'
+
+class OntologyTypeRecordId(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ base_url: str = Field(..., alias='baseUrl')
+ version: int = Field(..., ge=0)
+
+class OntologyTypeVersion(RootModel[int]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: int = Field(..., ge=0)
+
+class UnboundedBound(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ kind: Literal['unbounded']
+
+class OutgoingEdgeResolveDepth(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ outgoing: int = Field(..., ge=0)
+
+class OwnedById(RootModel[UUID]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: UUID
+
+class PermissionResponse(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ has_permission: bool
+
+class PropertyTypeOwnerSubjectItem(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ kind: Literal['account']
+ subject_id: AccountId = Field(..., alias='subjectId')
+
+class PropertyTypeOwnerSubjectItem1(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ kind: Literal['accountGroup']
+ subject_id: AccountGroupId = Field(..., alias='subjectId')
+
+class PropertyTypeOwnerSubject(RootModel[PropertyTypeOwnerSubjectItem | PropertyTypeOwnerSubjectItem1]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: PropertyTypeOwnerSubjectItem | PropertyTypeOwnerSubjectItem1 = Field(..., discriminator='kind')
+
+class PropertyTypePermission(Enum):
+ update = 'update'
+ view = 'view'
+
+class PropertyTypeQueryToken(Enum):
+ """
+ A single token in a [`DataTypeQueryPath`].
+ """
+ base_url = 'baseUrl'
+ version = 'version'
+ versioned_url = 'versionedUrl'
+ owned_by_id = 'ownedById'
+ record_created_by_id = 'recordCreatedById'
+ record_archived_by_id = 'recordArchivedById'
+ title = 'title'
+ description = 'description'
+ data_types = 'dataTypes'
+ property_types = 'propertyTypes'
+
+class PropertyTypeRelationAndSubjectItem(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ relation: Literal['owner']
+ subject: PropertyTypeOwnerSubject
+
+class PropertyTypeViewerSubjectItem(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ kind: Literal['public']
+
+class PropertyTypeViewerSubject(RootModel[PropertyTypeViewerSubjectItem]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: PropertyTypeViewerSubjectItem
+
+class PinnedDecisionAxis(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ axis: DecisionTime
+ timestamp: datetime
+
+class UnresolvedPinnedDecisionAxis(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ axis: DecisionTime
+ timestamp: NullableTimestamp
+
+class RecordArchivedById(RootModel[UUID]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: UUID
+
+class RecordCreatedById(RootModel[UUID]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: UUID
+
+class Selector(RootModel[Literal['*']]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: Literal['*']
+
+class SharedEdgeKind(RootModel[Literal['IS_OF_TYPE']]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: Literal['IS_OF_TYPE']
+
+class Timestamp(RootModel[datetime]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: datetime
+
+class TransactionTime(RootModel[Literal['transactionTime']]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: Literal['transactionTime'] = Field(..., description='Time axis for the transaction time.\n\nThis is used as the generic argument to time-related structs and can be used as tag value.')
+
+class ValidationOperation(RootModel[Literal['all']]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: Literal['all']
+
+class WebOwnerSubjectItem(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ kind: Literal['account']
+ subject_id: AccountId = Field(..., alias='subjectId')
+
+class WebOwnerSubjectItem1(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ kind: Literal['accountGroup']
+ subject_id: AccountGroupId = Field(..., alias='subjectId')
+
+class WebOwnerSubject(RootModel[WebOwnerSubjectItem | WebOwnerSubjectItem1]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: WebOwnerSubjectItem | WebOwnerSubjectItem1 = Field(..., discriminator='kind')
+
+class WebPermission(Enum):
+ update = 'update'
+ create_entity = 'create_entity'
+ create_entity_type = 'create_entity_type'
+ create_property_type = 'create_property_type'
+ create_data_type = 'create_data_type'
+
+class WebRelationAndSubjectItem(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ relation: Literal['owner']
+ subject: WebOwnerSubject
+
+class WebRelationAndSubject(RootModel[WebRelationAndSubjectItem]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: WebRelationAndSubjectItem = Field(..., discriminator='relation')
+
+class UpdateDataType(BaseModel):
+ """
+ The contents of a Data Type update request
+ """
+ model_config = ConfigDict(extra='allow', populate_by_name=True)
+ schema_url: Literal['https://blockprotocol.org/types/modules/graph/0.3/schema/data-type'] = Field(..., alias='$schema')
+ kind: Literal['dataType']
+ title: str
+ description: str | None = None
+ type: str
+
+class VersionedURL(RootModel[AnyUrl]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: AnyUrl = Field(..., description='The versioned URL of a Block Protocol ontology type (the $id of the schema). It should be of the form `${baseUrl}v/${versionNumber}`', max_length=2048, title='Versioned URL')
+
+class DataType(OntologyTypeSchema):
+ """
+ Specifies the structure of a Data Type
+ """
+ model_config = ConfigDict(extra='allow', populate_by_name=True)
+ schema_url: Literal['https://blockprotocol.org/types/modules/graph/0.3/schema/data-type'] = Field(..., alias='$schema')
+ kind: Literal['dataType']
+ identifier: VersionedURL = Field(..., alias='$id')
+ title: str
+ description: str | None = None
+ type: str
+
+class BaseURL(RootModel[AnyUrl]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: AnyUrl = Field(..., description='The base URL of a Block Protocol ontology type (the $id of the schema, without the versioned suffix). It should a valid URL, with a trailing slash.', max_length=2048, title='Base URL')
+
+class DataTypeReference(BaseModel):
+ model_config = ConfigDict(extra='forbid', populate_by_name=True)
+ field_ref: VersionedURL = Field(..., alias='$ref')
+
+class EntityTypeReference(BaseModel):
+ model_config = ConfigDict(extra='forbid', populate_by_name=True)
+ field_ref: VersionedURL = Field(..., alias='$ref')
+
+class Items1(BaseModel):
+ """
+ Specifies a set of entity types inside a oneOf
+ """
+ model_config = ConfigDict(extra='forbid', populate_by_name=True)
+ one_of: list[EntityTypeReference] | None = Field(None, alias='oneOf')
+
+class LinkTypeObject(BaseModel):
+ model_config = ConfigDict(extra='forbid', populate_by_name=True)
+ type: Literal['array']
+ ordered: bool
+ items: Items1 = Field(..., description='Specifies a set of entity types inside a oneOf')
+ min_items: int | None = Field(None, alias='minItems', ge=0)
+ max_items: int | None = Field(None, alias='maxItems', ge=0)
+
+class LinkTypeObject1(RootModel[dict[str, LinkTypeObject]]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: dict[str, LinkTypeObject]
+
+class StatusCode(Enum):
+ """
+ The canonical status codes for software within the HASH ecosystem.
+ Sometimes multiple status codes may apply. Services should return the most specific status code
+ that applies. For example, prefer `OutOfRange` over `FailedPrecondition` if both codes
+ apply. Similarly prefer `NotFound` or `AlreadyExists` over `FailedPrecondition`.
+ """
+ aborted = 'ABORTED'
+ already_exists = 'ALREADY_EXISTS'
+ cancelled = 'CANCELLED'
+ data_loss = 'DATA_LOSS'
+ deadline_exceeded = 'DEADLINE_EXCEEDED'
+ failed_precondition = 'FAILED_PRECONDITION'
+ internal = 'INTERNAL'
+ invalid_argument = 'INVALID_ARGUMENT'
+ not_found = 'NOT_FOUND'
+ ok = 'OK'
+ out_of_range = 'OUT_OF_RANGE'
+ permission_denied = 'PERMISSION_DENIED'
+ resource_exhausted = 'RESOURCE_EXHAUSTED'
+ unauthenticated = 'UNAUTHENTICATED'
+ unavailable = 'UNAVAILABLE'
+ unimplemented = 'UNIMPLEMENTED'
+ unknown = 'UNKNOWN'
+
+class RecordStringAny(BaseModel):
+ """
+ Construct a type with a set of properties K of type T
+ """
+ model_config = ConfigDict(populate_by_name=True)
+
+class ErrorInfo(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ reason: str = Field(..., description='The reason of the error. This is a constant value that identifies the proximate cause of\nthe error. Error reasons are unique within a particular domain of errors. This should be at\nmost 63 characters and match a regular expression of `[A-Z][A-Z0-9_]+[A-Z0-9]`, which\nrepresents UPPER_SNAKE_CASE.', title='reason')
+ domain: str = Field(..., description='The logical grouping to which the "reason" belongs.\nThe error domain is typically the registered service name of the tool or product that\ngenerates the error.', title='domain')
+ metadata: RecordStringAny = Field(..., description='Additional structured details about this error.\n\nKeys should match /[a-zA-Z0-9-_]/ and be limited to 64 characters in length. When\nidentifying the current value of an exceeded limit, the units should be contained in the\nkey, not the value. For example, rather than {"instanceLimit": "100/request"}, should be\nreturned as, {"instanceLimitPerRequest": "100"}, if the client exceeds the number of\ninstances that can be created in a single (batch) request.', title='metadata')
+
+class RequestInfo(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ request_id: str = Field(..., alias='requestId', description="An opaque string that should only be interpreted by the service generating it. For example, it\ncan be used to identify requests in the service's logs.", title='requestId')
+ serving_data: str = Field(..., alias='servingData', description='Any data that was used to serve this request. For example, an encrypted stack trace that can be\nsent back to the service provider for debugging.', title='servingData')
+
+class ResourceInfo(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ resource_type: str = Field(..., alias='resourceType', description='A name for the type of resource being accessed.\n\nFor example "SQL table", "Entity", "Property Type", "Redis"; or the type URL of the resource:\ne.g. "https://blockprotocol.org/type-system/0.3/schema/meta/entity-type".', title='resourceType')
+ resource_name: str = Field(..., alias='resourceName', description='The name of the resource being accessed.\n\nFor example, an ontology type ID: `https://hash.ai/@alice/types/entity-type/Person/`, if the current\nerror is [@local/status/StatusCode.PermissionDenied].', title='resourceName')
+ owner: str | None = Field(None, description="The owner of the resource (optional).\n\nFor example, a User's entity ID: `2cfa262a-f49a-4a61-a9c5-80a0c5959994%45e528cb-801d-49d1-8f71-d9e2af38a5e7`;", title='owner')
+ description: str = Field(..., description="Describes what error is encountered when accessing this resource.\n\nFor example, updating a property on a user's entity may require write permission on that property.", title='description')
+
+class Status(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ code: StatusCode = Field(..., title='code')
+ message: str | None = Field(None, description='A developer-facing description of the status.\n\nWhere possible, this should provide guiding advice for debugging and/or handling the error.')
+ contents: list[ErrorInfo | RequestInfo | ResourceInfo]
+
+class PropertyObjectReference(BaseModel):
+ model_config = ConfigDict(extra='forbid', populate_by_name=True)
+ field_ref: BaseURL = Field(..., alias='$ref')
+
+class ArchiveDataTypeRequest(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ type_to_archive: VersionedURL = Field(..., alias='typeToArchive')
+
+class ArchiveEntityTypeRequest(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ type_to_archive: VersionedURL = Field(..., alias='typeToArchive')
+
+class ArchivePropertyTypeRequest(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ type_to_archive: VersionedURL = Field(..., alias='typeToArchive')
+
+class InclusiveBound(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ kind: Literal['inclusive']
+ limit: Timestamp
+
+class ClosedTemporalBound(RootModel[InclusiveBound]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: InclusiveBound = Field(..., discriminator='kind')
+
+class CreateDataTypeRequest(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ owned_by_id: OwnedById = Field(..., alias='ownedById')
+ schema_: DataType | list[DataType] = Field(..., alias='schema')
+
+class CreateWebRequest(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ owned_by_id: OwnedById = Field(..., alias='ownedById')
+ owner: WebOwnerSubject
+
+class DataTypeRelationAndSubjectItem1(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ relation: Literal['viewer']
+ subject: DataTypeViewerSubject
+
+class DataTypeRelationAndSubject(RootModel[DataTypeRelationAndSubjectItem | DataTypeRelationAndSubjectItem1]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: DataTypeRelationAndSubjectItem | DataTypeRelationAndSubjectItem1 = Field(..., discriminator='relation')
+
+class DataTypeVertexId(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ base_id: BaseURL = Field(..., alias='baseId')
+ revision_id: OntologyTypeVersion = Field(..., alias='revisionId')
+
+class EntityLinkOrder(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ left_to_right_order: LinkOrder | None = Field(None, alias='leftToRightOrder')
+ right_to_left_order: LinkOrder | None = Field(None, alias='rightToLeftOrder')
+
+class EntityRelationAndSubjectItem2(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ relation: Literal['viewer']
+ subject: EntityViewerSubject
+
+class EntityRelationAndSubject(RootModel[EntityRelationAndSubjectItem | EntityRelationAndSubjectItem1 | EntityRelationAndSubjectItem2]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: EntityRelationAndSubjectItem | EntityRelationAndSubjectItem1 | EntityRelationAndSubjectItem2 = Field(..., discriminator='relation')
+
+class EntityTypeRelationAndSubjectItem1(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ relation: Literal['viewer']
+ subject: EntityTypeViewerSubject
+
+class EntityTypeRelationAndSubject(RootModel[EntityTypeRelationAndSubjectItem | EntityTypeRelationAndSubjectItem1 | EntityTypeRelationAndSubjectItem2]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: EntityTypeRelationAndSubjectItem | EntityTypeRelationAndSubjectItem1 | EntityTypeRelationAndSubjectItem2 = Field(..., discriminator='relation')
+
+class EntityTypeVertexId(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ base_id: BaseURL = Field(..., alias='baseId')
+ revision_id: OntologyTypeVersion = Field(..., alias='revisionId')
+
+class EntityVertexId(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ base_id: EntityId = Field(..., alias='baseId')
+ revision_id: Timestamp = Field(..., alias='revisionId')
+
+class PathExpression(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ path: list[DataTypeQueryToken | PropertyTypeQueryToken | EntityTypeQueryToken | EntityQueryToken | Selector | str | float]
+
+class FilterExpression(RootModel[PathExpression | ParameterExpression]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: PathExpression | ParameterExpression
+
+class GraphResolveDepths(BaseModel):
+ """
+ TODO: DOC -
+ """
+ model_config = ConfigDict(populate_by_name=True)
+ constrains_link_destinations_on: OutgoingEdgeResolveDepth = Field(..., alias='constrainsLinkDestinationsOn')
+ constrains_links_on: OutgoingEdgeResolveDepth = Field(..., alias='constrainsLinksOn')
+ constrains_properties_on: OutgoingEdgeResolveDepth = Field(..., alias='constrainsPropertiesOn')
+ constrains_values_on: OutgoingEdgeResolveDepth = Field(..., alias='constrainsValuesOn')
+ has_left_entity: EdgeResolveDepths = Field(..., alias='hasLeftEntity')
+ has_right_entity: EdgeResolveDepths = Field(..., alias='hasRightEntity')
+ inherits_from: OutgoingEdgeResolveDepth = Field(..., alias='inheritsFrom')
+ is_of_type: OutgoingEdgeResolveDepth = Field(..., alias='isOfType')
+
+class ExclusiveBound(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ kind: Literal['exclusive']
+ limit: Timestamp
+
+class LimitedTemporalBound(RootModel[InclusiveBound | ExclusiveBound]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: InclusiveBound | ExclusiveBound = Field(..., discriminator='kind')
+
+class LinkData(EntityLinkOrder):
+ """
+ The associated information for 'Link' entities
+ """
+ model_config = ConfigDict(populate_by_name=True)
+ left_entity_id: EntityId = Field(..., alias='leftEntityId')
+ right_entity_id: EntityId = Field(..., alias='rightEntityId')
+
+class LoadExternalDataTypeRequestItem(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ data_type_id: VersionedURL = Field(..., alias='dataTypeId')
+
+class LoadExternalDataTypeRequestItem1(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ schema_: DataType = Field(..., alias='schema')
+
+class LoadExternalDataTypeRequest(RootModel[LoadExternalDataTypeRequestItem | LoadExternalDataTypeRequestItem1]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: LoadExternalDataTypeRequestItem | LoadExternalDataTypeRequestItem1
+
+class LoadExternalEntityTypeRequestItem(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ entity_type_id: VersionedURL = Field(..., alias='entityTypeId')
+
+class LoadExternalPropertyTypeRequestItem(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ property_type_id: VersionedURL = Field(..., alias='propertyTypeId')
+
+class ModifyDataTypeAuthorizationRelationship(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ operation: ModifyRelationshipOperation
+ relation_and_subject: DataTypeRelationAndSubject = Field(..., alias='relationAndSubject')
+ resource: VersionedURL
+
+class ModifyEntityAuthorizationRelationship(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ operation: ModifyRelationshipOperation
+ relation_subject: EntityRelationAndSubject = Field(..., alias='relationSubject')
+ resource: EntityId
+
+class ModifyEntityTypeAuthorizationRelationship(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ operation: ModifyRelationshipOperation
+ relation_and_subject: EntityTypeRelationAndSubject = Field(..., alias='relationAndSubject')
+ resource: VersionedURL
+
+class ModifyWebAuthorizationRelationship(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ operation: ModifyRelationshipOperation
+ relation_and_subject: WebRelationAndSubject = Field(..., alias='relationAndSubject')
+ resource: OwnedById
+
+class OpenTemporalBound(RootModel[ExclusiveBound | UnboundedBound]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: ExclusiveBound | UnboundedBound = Field(..., discriminator='kind')
+
+class PropertyTypeRelationAndSubjectItem1(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ relation: Literal['viewer']
+ subject: PropertyTypeViewerSubject
+
+class PropertyTypeRelationAndSubject(RootModel[PropertyTypeRelationAndSubjectItem | PropertyTypeRelationAndSubjectItem1]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: PropertyTypeRelationAndSubjectItem | PropertyTypeRelationAndSubjectItem1 = Field(..., discriminator='relation')
+
+class PropertyTypeVertexId(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ base_id: BaseURL = Field(..., alias='baseId')
+ revision_id: OntologyTypeVersion = Field(..., alias='revisionId')
+
+class ProvenanceMetadata(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ record_archived_by_id: RecordArchivedById | None = Field(None, alias='recordArchivedById')
+ record_created_by_id: RecordCreatedById = Field(..., alias='recordCreatedById')
+
+class PinnedTransactionAxis(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ axis: TransactionTime
+ timestamp: datetime
+
+class UnresolvedPinnedTransactionAxis(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ axis: TransactionTime
+ timestamp: NullableTimestamp
+
+class TemporalBound(RootModel[UnboundedBound | InclusiveBound | ExclusiveBound]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: UnboundedBound | InclusiveBound | ExclusiveBound = Field(..., discriminator='kind')
+
+class UnarchiveDataTypeRequest(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ type_to_unarchive: VersionedURL = Field(..., alias='typeToUnarchive')
+
+class UnarchiveEntityTypeRequest(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ type_to_unarchive: VersionedURL = Field(..., alias='typeToUnarchive')
+
+class UnarchivePropertyTypeRequest(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ type_to_unarchive: VersionedURL = Field(..., alias='typeToUnarchive')
+
+class UnresolvedRightBoundedTemporalInterval(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ end: LimitedTemporalBound | None = Field(...)
+ start: TemporalBound | None = Field(...)
+
+class UpdateDataTypeRequest(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ schema_: UpdateDataType = Field(..., alias='schema')
+ type_to_update: VersionedURL = Field(..., alias='typeToUpdate')
+
+class UpdateEntityRequest(EntityLinkOrder):
+ model_config = ConfigDict(populate_by_name=True)
+ archived: bool
+ draft: bool
+ entity_id: EntityId = Field(..., alias='entityId')
+ entity_type_id: VersionedURL = Field(..., alias='entityTypeId')
+ properties: EntityProperties
+
+class ValidateEntityRequest(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ entity_type_id: VersionedURL = Field(..., alias='entityTypeId')
+ link_data: LinkData | None = Field(None, alias='linkData')
+ operations: list[ValidationOperation]
+ properties: EntityProperties
+
+class PropertyTypeObjectItem(BaseModel):
+ model_config = ConfigDict(extra='forbid', populate_by_name=True)
+ type: Literal['array']
+ items: PropertyObjectReference
+ min_items: int | None = Field(None, alias='minItems', ge=0)
+ max_items: int | None = Field(None, alias='maxItems', ge=0)
+
+class PropertyTypeObject(RootModel[dict[str, PropertyObjectReference | PropertyTypeObjectItem]]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: dict[str, PropertyObjectReference | PropertyTypeObjectItem] = Field(..., description='A JSON object where each entry is constrained by a property type.', title='Property Type Object')
+
+class PropertyObjectValue(BaseModel):
+ model_config = ConfigDict(extra='forbid', populate_by_name=True)
+ type: Literal['object']
+ properties: PropertyTypeObject
+
+class EntityType(OntologyTypeSchema):
+ """
+ Specifies the structure of a Block Protocol entity type
+ """
+ model_config = ConfigDict(extra='forbid', populate_by_name=True)
+ schema_url: Literal['https://blockprotocol.org/types/modules/graph/0.3/schema/entity-type'] = Field(..., alias='$schema')
+ kind: Literal['entityType']
+ identifier: VersionedURL = Field(..., alias='$id')
+ type: Literal['object']
+ title: str
+ description: str | None = None
+ all_of: list[EntityTypeReference] | None = Field(None, alias='allOf')
+ examples: list[dict[str, Any]] | None = None
+ properties: PropertyTypeObject
+ required: list[BaseURL] | None = None
+ links: LinkTypeObject1 | None = None
+
+class UpdateEntityType(BaseModel):
+ """
+ The contents of an Entity Type update request
+ """
+ model_config = ConfigDict(extra='forbid', populate_by_name=True)
+ schema_url: Literal['https://blockprotocol.org/types/modules/graph/0.3/schema/entity-type'] = Field(..., alias='$schema')
+ kind: Literal['entityType']
+ type: Literal['object']
+ title: str
+ description: str | None = None
+ examples: list[dict[str, Any]] | None = None
+ properties: PropertyTypeObject
+ required: list[BaseURL] | None = None
+ links: LinkTypeObject1 | None = None
+
+class CreateEntityRequest(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ draft: bool
+ entity_type_id: VersionedURL = Field(..., alias='entityTypeId')
+ entity_uuid: EntityUuid | None = Field(None, alias='entityUuid')
+ link_data: LinkData | None = Field(None, alias='linkData')
+ owned_by_id: OwnedById = Field(..., alias='ownedById')
+ owner: OwnedById
+ properties: EntityProperties
+
+class CreateEntityTypeRequest(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ icon: str | None = None
+ label_property: BaseURL | None = Field(None, alias='labelProperty')
+ owned_by_id: OwnedById = Field(..., alias='ownedById')
+ schema_: EntityType | list[EntityType] = Field(..., alias='schema')
+
+class EqualFilter(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ equal: list[FilterExpression] = Field(..., max_length=2, min_length=2)
+
+class NotEqualFilter(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ not_equal: list[FilterExpression] = Field(..., alias='notEqual', max_length=2, min_length=2)
+
+class StartsWithFilter(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ starts_with: list[FilterExpression] = Field(..., alias='startsWith', max_length=2, min_length=2)
+
+class EndsWithFilter(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ ends_with: list[FilterExpression] = Field(..., alias='endsWith', max_length=2, min_length=2)
+
+class ContainsSegmentFilter(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ contains_segment: list[FilterExpression] = Field(..., alias='containsSegment', max_length=2, min_length=2)
+
+class GraphElementVertexId(RootModel[DataTypeVertexId | PropertyTypeVertexId | EntityTypeVertexId | EntityVertexId]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: DataTypeVertexId | PropertyTypeVertexId | EntityTypeVertexId | EntityVertexId
+
+class LeftClosedTemporalInterval(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ end: OpenTemporalBound
+ start: ClosedTemporalBound
+
+class LoadExternalEntityTypeRequestItem1(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ icon: str | None = None
+ label_property: BaseURL | None = None
+ schema_: EntityType = Field(..., alias='schema')
+
+class LoadExternalEntityTypeRequest(RootModel[LoadExternalEntityTypeRequestItem | LoadExternalEntityTypeRequestItem1]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: LoadExternalEntityTypeRequestItem | LoadExternalEntityTypeRequestItem1
+
+class ModifyPropertyTypeAuthorizationRelationship(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ operation: ModifyRelationshipOperation
+ relation_and_subject: PropertyTypeRelationAndSubject = Field(..., alias='relationAndSubject')
+ resource: VersionedURL
+
+class OntologyTemporalMetadata(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ transaction_time: LeftClosedTemporalInterval = Field(..., alias='transactionTime')
+
+class OntologyTypeVertexId(RootModel[DataTypeVertexId | PropertyTypeVertexId | EntityTypeVertexId]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: DataTypeVertexId | PropertyTypeVertexId | EntityTypeVertexId
+
+class UnresolvedVariableDecisionAxis(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ axis: DecisionTime
+ interval: UnresolvedRightBoundedTemporalInterval
+
+class QueryTemporalAxesUnresolvedDecisionTime(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ pinned: UnresolvedPinnedTransactionAxis = Field(..., title='UnresolvedPinnedTransactionAxis')
+ variable: UnresolvedVariableDecisionAxis = Field(..., title='UnresolvedVariableDecisionAxis')
+
+class UnresolvedVariableTransactionAxis(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ axis: TransactionTime
+ interval: UnresolvedRightBoundedTemporalInterval
+
+class QueryTemporalAxesUnresolvedTransactionTime(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ pinned: UnresolvedPinnedDecisionAxis = Field(..., title='UnresolvedPinnedDecisionAxis')
+ variable: UnresolvedVariableTransactionAxis = Field(..., title='UnresolvedVariableTransactionAxis')
+
+class QueryTemporalAxesUnresolved(RootModel[QueryTemporalAxesUnresolvedDecisionTime | QueryTemporalAxesUnresolvedTransactionTime]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: QueryTemporalAxesUnresolvedDecisionTime | QueryTemporalAxesUnresolvedTransactionTime = Field(..., description='Defines the two possible combinations of pinned/variable temporal axes that are used in queries\nthat return [`Subgraph`]s.\n\nThe [`VariableTemporalAxisUnresolved`] is optionally bounded, in the absence of provided\nbounds an inclusive bound at the timestamp at point of resolving is assumed.\n\n[`Subgraph`]: crate::subgraph::Subgraph')
+
+class RightBoundedTemporalInterval(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ end: LimitedTemporalBound
+ start: TemporalBound
+
+class UpdateEntityTypeRequest(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ icon: str | None = None
+ label_property: BaseURL | None = Field(None, alias='labelProperty')
+ schema_: UpdateEntityType = Field(..., alias='schema')
+ type_to_update: VersionedURL = Field(..., alias='typeToUpdate')
+
+class CustomOwnedOntologyElementMetadata(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ owned_by_id: OwnedById = Field(..., alias='ownedById')
+ provenance: ProvenanceMetadata
+ temporal_versioning: OntologyTemporalMetadata = Field(..., alias='temporalVersioning')
+
+class CustomExternalOntologyElementMetadata(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ fetched_at: str = Field(..., alias='fetchedAt')
+ provenance: ProvenanceMetadata
+ temporal_versioning: OntologyTemporalMetadata = Field(..., alias='temporalVersioning')
+
+class CustomOntologyMetadata(RootModel[CustomOwnedOntologyElementMetadata | CustomExternalOntologyElementMetadata]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: CustomOwnedOntologyElementMetadata | CustomExternalOntologyElementMetadata
+
+class EntityIdWithInterval(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ entity_id: EntityId = Field(..., alias='entityId')
+ interval: LeftClosedTemporalInterval
+
+class EntityTemporalMetadata(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ decision_time: LeftClosedTemporalInterval = Field(..., alias='decisionTime')
+ transaction_time: LeftClosedTemporalInterval = Field(..., alias='transactionTime')
+
+class EntityTypeMetadata(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ custom: CustomOntologyMetadata
+ icon: str | None = None
+ label_property: BaseURL | None = Field(None, alias='labelProperty')
+ record_id: OntologyTypeRecordId = Field(..., alias='recordId')
+
+class EntityTypeWithMetadata(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ metadata: EntityTypeMetadata
+ schema_: EntityType = Field(..., alias='schema')
+
+class KnowledgeGraphToKnowledgeGraphOutwardEdge(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ kind: KnowledgeGraphEdgeKind
+ reversed: bool
+ right_endpoint: EntityIdWithInterval = Field(..., alias='rightEndpoint')
+
+class KnowledgeGraphToOntologyOutwardEdge(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ kind: SharedEdgeKind
+ reversed: bool
+ right_endpoint: OntologyTypeVertexId = Field(..., alias='rightEndpoint')
+
+class KnowledgeGraphOutwardEdge(RootModel[KnowledgeGraphToKnowledgeGraphOutwardEdge | KnowledgeGraphToOntologyOutwardEdge]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: KnowledgeGraphToKnowledgeGraphOutwardEdge | KnowledgeGraphToOntologyOutwardEdge
+
+class MaybeListOfEntityTypeMetadata(RootModel[EntityTypeMetadata | list[EntityTypeMetadata]]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: EntityTypeMetadata | list[EntityTypeMetadata]
+
+class OntologyElementMetadata(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ custom: CustomOntologyMetadata
+ record_id: OntologyTypeRecordId = Field(..., alias='recordId')
+
+class OntologyToOntologyOutwardEdge(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ kind: OntologyEdgeKind
+ reversed: bool
+ right_endpoint: OntologyTypeVertexId = Field(..., alias='rightEndpoint')
+
+class OntologyToKnowledgeGraphOutwardEdge(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ kind: SharedEdgeKind
+ reversed: bool
+ right_endpoint: EntityIdWithInterval = Field(..., alias='rightEndpoint')
+
+class OntologyOutwardEdge(RootModel[OntologyToOntologyOutwardEdge | OntologyToKnowledgeGraphOutwardEdge]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: OntologyToOntologyOutwardEdge | OntologyToKnowledgeGraphOutwardEdge
+
+class EntityTypeVertex(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ inner: EntityTypeWithMetadata
+ kind: Literal['entityType']
+
+class VariableDecisionAxis(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ axis: DecisionTime
+ interval: RightBoundedTemporalInterval
+
+class QueryTemporalAxesDecisionTime(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ pinned: PinnedTransactionAxis = Field(..., title='PinnedTransactionAxis')
+ variable: VariableDecisionAxis = Field(..., title='VariableDecisionAxis')
+
+class VariableTransactionAxis(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ axis: TransactionTime
+ interval: RightBoundedTemporalInterval
+
+class QueryTemporalAxesTransactionTime(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ pinned: PinnedDecisionAxis = Field(..., title='PinnedDecisionAxis')
+ variable: VariableTransactionAxis = Field(..., title='VariableTransactionAxis')
+
+class QueryTemporalAxes(RootModel[QueryTemporalAxesDecisionTime | QueryTemporalAxesTransactionTime]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: QueryTemporalAxesDecisionTime | QueryTemporalAxesTransactionTime = Field(..., description='Defines the two possible combinations of pinned/variable temporal axes that are used in\nresponses to queries that return [`Subgraph`]s.\n\nWhen querying the Graph, temporal data is returned. The Graph is implemented as a bitemporal\ndata store, which means the knowledge data contains information about the time of when the\nknowledge was inserted into the Graph, the [`TransactionTime`], and when the knowledge was\ndecided to be inserted, the [`DecisionTime`].\n\nIn order to query data from the Graph, only one of the two time axes can be used. This is\nachieved by using a `TemporalAxes`. The `TemporalAxes` pins one axis to a specified\n[`Timestamp`], while the other axis can be a [`Interval`]. The pinned axis is called the\n[`PinnedTemporalAxis`] and the other axis is called the [`VariableTemporalAxis`]. The returned\ndata will then only contain temporal data that is contained in the [`Interval`] of the\n[`VariableTemporalAxis`] for the given [`Timestamp`] of the [`PinnedTemporalAxis`].\n\n[`Subgraph`]: crate::subgraph::Subgraph\n[`Interval`]: temporal_versioning::Interval')
+
+class SubgraphTemporalAxes(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ initial: QueryTemporalAxesUnresolved
+ resolved: QueryTemporalAxes
+
+class DataTypeWithMetadata(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ metadata: OntologyElementMetadata
+ schema_: DataType = Field(..., alias='schema')
+
+class Edges(RootModel[dict[str, dict[str, list[OntologyOutwardEdge | KnowledgeGraphOutwardEdge]]] | None]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: dict[str, dict[str, list[OntologyOutwardEdge | KnowledgeGraphOutwardEdge]]] | None = None
+
+class EntityMetadata(BaseModel):
+ """
+ The metadata of an [`Entity`] record.
+ """
+ model_config = ConfigDict(populate_by_name=True)
+ archived: bool
+ draft: bool
+ entity_type_id: str = Field(..., alias='entityTypeId')
+ provenance: ProvenanceMetadata
+ record_id: EntityRecordId = Field(..., alias='recordId')
+ temporal_versioning: EntityTemporalMetadata = Field(..., alias='temporalVersioning')
+
+class MaybeListOfOntologyElementMetadata(RootModel[OntologyElementMetadata | list[OntologyElementMetadata]]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: OntologyElementMetadata | list[OntologyElementMetadata]
+
+class DataTypeVertex(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ inner: DataTypeWithMetadata
+ kind: Literal['dataType']
+
+class Entity(BaseModel):
+ """
+ A record of an [`Entity`] that has been persisted in the datastore, with its associated
+ metadata.
+ """
+ model_config = ConfigDict(populate_by_name=True)
+ link_data: LinkData | None = Field(None, alias='linkData')
+ metadata: EntityMetadata
+ properties: EntityProperties
+
+class EntityVertex(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ inner: Entity
+ kind: Literal['entity']
+
+class KnowledgeGraphVertex(RootModel[EntityVertex]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: EntityVertex = Field(..., discriminator='kind')
+
+class KnowledgeGraphVertices(RootModel[dict[str, dict[str, KnowledgeGraphVertex]] | None]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: dict[str, dict[str, KnowledgeGraphVertex]] | None = None
+
+class CreatePropertyTypeRequest(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ owned_by_id: OwnedById = Field(..., alias='ownedById')
+ schema_: PropertyType | list[PropertyType] = Field(..., alias='schema')
+
+class DataTypeStructuralQuery(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ filter: Filter
+ graph_resolve_depths: GraphResolveDepths = Field(..., alias='graphResolveDepths')
+ temporal_axes: QueryTemporalAxesUnresolved = Field(..., alias='temporalAxes')
+
+class EntityStructuralQuery(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ filter: Filter
+ graph_resolve_depths: GraphResolveDepths = Field(..., alias='graphResolveDepths')
+ temporal_axes: QueryTemporalAxesUnresolved = Field(..., alias='temporalAxes')
+
+class EntityTypeStructuralQuery(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ filter: Filter
+ graph_resolve_depths: GraphResolveDepths = Field(..., alias='graphResolveDepths')
+ temporal_axes: QueryTemporalAxesUnresolved = Field(..., alias='temporalAxes')
+
+class AllFilter(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ all: list[Filter]
+
+class AnyFilter(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ any: list[Filter]
+
+class NotFilter(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ not_: Filter = Field(..., alias='not')
+
+class Filter(RootModel[AllFilter | AnyFilter | NotFilter | EqualFilter | NotEqualFilter | StartsWithFilter | EndsWithFilter | ContainsSegmentFilter]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: AllFilter | AnyFilter | NotFilter | EqualFilter | NotEqualFilter | StartsWithFilter | EndsWithFilter | ContainsSegmentFilter
+
+class LoadExternalPropertyTypeRequestItem1(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ schema_: PropertyType = Field(..., alias='schema')
+
+class LoadExternalPropertyTypeRequest(RootModel[LoadExternalPropertyTypeRequestItem | LoadExternalPropertyTypeRequestItem1]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: LoadExternalPropertyTypeRequestItem | LoadExternalPropertyTypeRequestItem1
+
+class PropertyTypeVertex(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ inner: PropertyTypeWithMetadata
+ kind: Literal['propertyType']
+
+class OntologyVertex(RootModel[DataTypeVertex | PropertyTypeVertex | EntityTypeVertex]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: DataTypeVertex | PropertyTypeVertex | EntityTypeVertex = Field(..., discriminator='kind')
+
+class OntologyVertices(RootModel[dict[str, dict[str, OntologyVertex]] | None]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: dict[str, dict[str, OntologyVertex]] | None = None
+
+class PropertyTypeStructuralQuery(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ filter: Filter
+ graph_resolve_depths: GraphResolveDepths = Field(..., alias='graphResolveDepths')
+ temporal_axes: QueryTemporalAxesUnresolved = Field(..., alias='temporalAxes')
+
+class PropertyTypeWithMetadata(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ metadata: OntologyElementMetadata
+ schema_: PropertyType = Field(..., alias='schema')
+
+class Subgraph(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ depths: GraphResolveDepths
+ edges: Edges
+ roots: list[GraphElementVertexId]
+ temporal_axes: SubgraphTemporalAxes = Field(..., alias='temporalAxes')
+ vertices: Vertices
+
+class UpdatePropertyTypeRequest(BaseModel):
+ model_config = ConfigDict(populate_by_name=True)
+ schema_: UpdatePropertyType = Field(..., alias='schema')
+ type_to_update: VersionedURL = Field(..., alias='typeToUpdate')
+
+class Vertex(RootModel[OntologyVertex | KnowledgeGraphVertex]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: OntologyVertex | KnowledgeGraphVertex
+
+class Vertices(RootModel[dict[str, dict[str, KnowledgeGraphVertex | OntologyVertex]] | None]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: dict[str, dict[str, KnowledgeGraphVertex | OntologyVertex]] | None = None
+
+class Items(BaseModel):
+ model_config = ConfigDict(extra='forbid', populate_by_name=True)
+ one_of: list[PropertyValues] = Field(..., alias='oneOf', min_length=1)
+
+class PropertyArrayValue(BaseModel):
+ model_config = ConfigDict(extra='forbid', populate_by_name=True)
+ type: Literal['array']
+ items: Items
+ min_items: int | None = Field(None, alias='minItems', ge=0)
+ max_items: int | None = Field(None, alias='maxItems', ge=0)
+
+class PropertyValues(RootModel[DataTypeReference | PropertyObjectValue | PropertyArrayValue]):
+ model_config = ConfigDict(populate_by_name=True)
+ root: DataTypeReference | PropertyObjectValue | PropertyArrayValue = Field(..., description='The definition of potential property values, either references to data types, objects made up of more property types, or an array where the items are defined from a set of other property values definitions.', title='propertyValues')
+
+class PropertyType(OntologyTypeSchema):
+ """
+ Specifies the structure of a Block Protocol property type
+ """
+ model_config = ConfigDict(extra='forbid', populate_by_name=True)
+ schema_url: Literal['https://blockprotocol.org/types/modules/graph/0.3/schema/property-type'] = Field(..., alias='$schema')
+ kind: Literal['propertyType']
+ identifier: VersionedURL = Field(..., alias='$id')
+ title: str
+ description: str | None = None
+ one_of: list[PropertyValues] = Field(..., alias='oneOf')
+
+class UpdatePropertyType(BaseModel):
+ """
+ The contents of a Property Type update request
+ """
+ model_config = ConfigDict(extra='forbid', populate_by_name=True)
+ schema_url: Literal['https://blockprotocol.org/types/modules/graph/0.3/schema/property-type'] = Field(..., alias='$schema')
+ kind: Literal['propertyType']
+ title: str
+ description: str | None = None
+ one_of: list[PropertyValues] = Field(..., alias='oneOf')
+CreatePropertyTypeRequest.model_rebuild()
+DataTypeStructuralQuery.model_rebuild()
+EntityStructuralQuery.model_rebuild()
+EntityTypeStructuralQuery.model_rebuild()
+AllFilter.model_rebuild()
+AnyFilter.model_rebuild()
+NotFilter.model_rebuild()
+LoadExternalPropertyTypeRequestItem1.model_rebuild()
+PropertyTypeVertex.model_rebuild()
+PropertyTypeWithMetadata.model_rebuild()
+Subgraph.model_rebuild()
+UpdatePropertyTypeRequest.model_rebuild()
+Items.model_rebuild()
\ No newline at end of file
diff --git a/libs/hash-graph-client/python/graph_client/py.typed b/libs/hash-graph-client/python/graph_client/py.typed
new file mode 100644
index 0000000..e69de29
diff --git a/libs/hash-graph-client/python/graph_client/schema.py b/libs/hash-graph-client/python/graph_client/schema.py
new file mode 100644
index 0000000..f01032e
--- /dev/null
+++ b/libs/hash-graph-client/python/graph_client/schema.py
@@ -0,0 +1,13 @@
+"""Base classes for all schemas."""
+
+from abc import ABC
+
+from pydantic import BaseModel
+
+
+class Schema(BaseModel, ABC):
+ """Base class for all schemas."""
+
+
+class OntologyTypeSchema(Schema, ABC):
+ """Base class for all ontology type schemas."""
diff --git a/libs/hash-graph-client/python/package.json b/libs/hash-graph-client/python/package.json
new file mode 100644
index 0000000..603b998
--- /dev/null
+++ b/libs/hash-graph-client/python/package.json
@@ -0,0 +1,25 @@
+{
+ "name": "@local/hash-graph-client-py",
+ "version": "0.0.0-private",
+ "private": true,
+ "description": "Type definitions for the type system and entity validation based on schemas",
+ "scripts": {
+ "build": "poetry build --no-interaction",
+ "codegen": "./scripts/codegen.sh",
+ "fix:black": "poetry run black graph_client",
+ "fix:lock-files": "poetry lock --no-update",
+ "fix:ruff": "poetry run ruff graph_client --fix",
+ "lint:black": "poetry run black --check --diff --color graph_client",
+ "lint:lock-files": "poetry lock --check",
+ "lint:mypy": "poetry run mypy .",
+ "lint:ruff": "poetry run ruff graph_client",
+ "poetry:install": "poetry install --no-root --no-interaction",
+ "poetry:install-production": "poetry install --no-root --no-interaction --only main"
+ },
+ "dependencies": {
+ "@apps/hash-graph": "0.0.0-private"
+ },
+ "devDependencies": {
+ "@redocly/cli": "1.4.1"
+ }
+}
diff --git a/libs/hash-graph-client/python/poetry.lock b/libs/hash-graph-client/python/poetry.lock
new file mode 100644
index 0000000..6cd4dc4
--- /dev/null
+++ b/libs/hash-graph-client/python/poetry.lock
@@ -0,0 +1,1344 @@
+# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand.
+
+[[package]]
+name = "annotated-types"
+version = "0.5.0"
+description = "Reusable constraint types to use with typing.Annotated"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "annotated_types-0.5.0-py3-none-any.whl", hash = "sha256:58da39888f92c276ad970249761ebea80ba544b77acddaa1a4d6cf78287d45fd"},
+ {file = "annotated_types-0.5.0.tar.gz", hash = "sha256:47cdc3490d9ac1506ce92c7aaa76c579dc3509ff11e098fc867e5130ab7be802"},
+]
+
+[[package]]
+name = "anyio"
+version = "3.7.1"
+description = "High level compatibility layer for multiple asynchronous event loop implementations"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "anyio-3.7.1-py3-none-any.whl", hash = "sha256:91dee416e570e92c64041bd18b900d1d6fa78dff7048769ce5ac5ddad004fbb5"},
+ {file = "anyio-3.7.1.tar.gz", hash = "sha256:44a3c9aba0f5defa43261a8b3efb97891f2bd7d804e0e1f56419befa1adfc780"},
+]
+
+[package.dependencies]
+idna = ">=2.8"
+sniffio = ">=1.1"
+
+[package.extras]
+doc = ["Sphinx", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme (>=1.2.2)", "sphinxcontrib-jquery"]
+test = ["anyio[trio]", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "mock (>=4)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"]
+trio = ["trio (<0.22)"]
+
+[[package]]
+name = "argcomplete"
+version = "3.1.1"
+description = "Bash tab completion for argparse"
+optional = false
+python-versions = ">=3.6"
+files = [
+ {file = "argcomplete-3.1.1-py3-none-any.whl", hash = "sha256:35fa893a88deea85ea7b20d241100e64516d6af6d7b0ae2bed1d263d26f70948"},
+ {file = "argcomplete-3.1.1.tar.gz", hash = "sha256:6c4c563f14f01440aaffa3eae13441c5db2357b5eec639abe7c0b15334627dff"},
+]
+
+[package.extras]
+test = ["coverage", "mypy", "pexpect", "ruff", "wheel"]
+
+[[package]]
+name = "attrs"
+version = "23.1.0"
+description = "Classes Without Boilerplate"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "attrs-23.1.0-py3-none-any.whl", hash = "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04"},
+ {file = "attrs-23.1.0.tar.gz", hash = "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015"},
+]
+
+[package.extras]
+cov = ["attrs[tests]", "coverage[toml] (>=5.3)"]
+dev = ["attrs[docs,tests]", "pre-commit"]
+docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"]
+tests = ["attrs[tests-no-zope]", "zope-interface"]
+tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"]
+
+[[package]]
+name = "black"
+version = "23.10.1"
+description = "The uncompromising code formatter."
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "black-23.10.1-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:ec3f8e6234c4e46ff9e16d9ae96f4ef69fa328bb4ad08198c8cee45bb1f08c69"},
+ {file = "black-23.10.1-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:1b917a2aa020ca600483a7b340c165970b26e9029067f019e3755b56e8dd5916"},
+ {file = "black-23.10.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c74de4c77b849e6359c6f01987e94873c707098322b91490d24296f66d067dc"},
+ {file = "black-23.10.1-cp310-cp310-win_amd64.whl", hash = "sha256:7b4d10b0f016616a0d93d24a448100adf1699712fb7a4efd0e2c32bbb219b173"},
+ {file = "black-23.10.1-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:b15b75fc53a2fbcac8a87d3e20f69874d161beef13954747e053bca7a1ce53a0"},
+ {file = "black-23.10.1-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:e293e4c2f4a992b980032bbd62df07c1bcff82d6964d6c9496f2cd726e246ace"},
+ {file = "black-23.10.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d56124b7a61d092cb52cce34182a5280e160e6aff3137172a68c2c2c4b76bcb"},
+ {file = "black-23.10.1-cp311-cp311-win_amd64.whl", hash = "sha256:3f157a8945a7b2d424da3335f7ace89c14a3b0625e6593d21139c2d8214d55ce"},
+ {file = "black-23.10.1-cp38-cp38-macosx_10_16_arm64.whl", hash = "sha256:cfcce6f0a384d0da692119f2d72d79ed07c7159879d0bb1bb32d2e443382bf3a"},
+ {file = "black-23.10.1-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:33d40f5b06be80c1bbce17b173cda17994fbad096ce60eb22054da021bf933d1"},
+ {file = "black-23.10.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:840015166dbdfbc47992871325799fd2dc0dcf9395e401ada6d88fe11498abad"},
+ {file = "black-23.10.1-cp38-cp38-win_amd64.whl", hash = "sha256:037e9b4664cafda5f025a1728c50a9e9aedb99a759c89f760bd83730e76ba884"},
+ {file = "black-23.10.1-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:7cb5936e686e782fddb1c73f8aa6f459e1ad38a6a7b0e54b403f1f05a1507ee9"},
+ {file = "black-23.10.1-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:7670242e90dc129c539e9ca17665e39a146a761e681805c54fbd86015c7c84f7"},
+ {file = "black-23.10.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ed45ac9a613fb52dad3b61c8dea2ec9510bf3108d4db88422bacc7d1ba1243d"},
+ {file = "black-23.10.1-cp39-cp39-win_amd64.whl", hash = "sha256:6d23d7822140e3fef190734216cefb262521789367fbdc0b3f22af6744058982"},
+ {file = "black-23.10.1-py3-none-any.whl", hash = "sha256:d431e6739f727bb2e0495df64a6c7a5310758e87505f5f8cde9ff6c0f2d7e4fe"},
+ {file = "black-23.10.1.tar.gz", hash = "sha256:1f8ce316753428ff68749c65a5f7844631aa18c8679dfd3ca9dc1a289979c258"},
+]
+
+[package.dependencies]
+click = ">=8.0.0"
+mypy-extensions = ">=0.4.3"
+packaging = ">=22.0"
+pathspec = ">=0.9.0"
+platformdirs = ">=2"
+
+[package.extras]
+colorama = ["colorama (>=0.4.3)"]
+d = ["aiohttp (>=3.7.4)"]
+jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"]
+uvloop = ["uvloop (>=0.15.2)"]
+
+[[package]]
+name = "certifi"
+version = "2023.7.22"
+description = "Python package for providing Mozilla's CA Bundle."
+optional = false
+python-versions = ">=3.6"
+files = [
+ {file = "certifi-2023.7.22-py3-none-any.whl", hash = "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9"},
+ {file = "certifi-2023.7.22.tar.gz", hash = "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082"},
+]
+
+[[package]]
+name = "chardet"
+version = "5.2.0"
+description = "Universal encoding detector for Python 3"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970"},
+ {file = "chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7"},
+]
+
+[[package]]
+name = "charset-normalizer"
+version = "3.2.0"
+description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
+optional = false
+python-versions = ">=3.7.0"
+files = [
+ {file = "charset-normalizer-3.2.0.tar.gz", hash = "sha256:3bb3d25a8e6c0aedd251753a79ae98a093c7e7b471faa3aa9a93a81431987ace"},
+ {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b87549028f680ca955556e3bd57013ab47474c3124dc069faa0b6545b6c9710"},
+ {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7c70087bfee18a42b4040bb9ec1ca15a08242cf5867c58726530bdf3945672ed"},
+ {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a103b3a7069b62f5d4890ae1b8f0597618f628b286b03d4bc9195230b154bfa9"},
+ {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94aea8eff76ee6d1cdacb07dd2123a68283cb5569e0250feab1240058f53b623"},
+ {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:db901e2ac34c931d73054d9797383d0f8009991e723dab15109740a63e7f902a"},
+ {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b0dac0ff919ba34d4df1b6131f59ce95b08b9065233446be7e459f95554c0dc8"},
+ {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:193cbc708ea3aca45e7221ae58f0fd63f933753a9bfb498a3b474878f12caaad"},
+ {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09393e1b2a9461950b1c9a45d5fd251dc7c6f228acab64da1c9c0165d9c7765c"},
+ {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:baacc6aee0b2ef6f3d308e197b5d7a81c0e70b06beae1f1fcacffdbd124fe0e3"},
+ {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bf420121d4c8dce6b889f0e8e4ec0ca34b7f40186203f06a946fa0276ba54029"},
+ {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:c04a46716adde8d927adb9457bbe39cf473e1e2c2f5d0a16ceb837e5d841ad4f"},
+ {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:aaf63899c94de41fe3cf934601b0f7ccb6b428c6e4eeb80da72c58eab077b19a"},
+ {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d62e51710986674142526ab9f78663ca2b0726066ae26b78b22e0f5e571238dd"},
+ {file = "charset_normalizer-3.2.0-cp310-cp310-win32.whl", hash = "sha256:04e57ab9fbf9607b77f7d057974694b4f6b142da9ed4a199859d9d4d5c63fe96"},
+ {file = "charset_normalizer-3.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:48021783bdf96e3d6de03a6e39a1171ed5bd7e8bb93fc84cc649d11490f87cea"},
+ {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4957669ef390f0e6719db3613ab3a7631e68424604a7b448f079bee145da6e09"},
+ {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46fb8c61d794b78ec7134a715a3e564aafc8f6b5e338417cb19fe9f57a5a9bf2"},
+ {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f779d3ad205f108d14e99bb3859aa7dd8e9c68874617c72354d7ecaec2a054ac"},
+ {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f25c229a6ba38a35ae6e25ca1264621cc25d4d38dca2942a7fce0b67a4efe918"},
+ {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2efb1bd13885392adfda4614c33d3b68dee4921fd0ac1d3988f8cbb7d589e72a"},
+ {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f30b48dd7fa1474554b0b0f3fdfdd4c13b5c737a3c6284d3cdc424ec0ffff3a"},
+ {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:246de67b99b6851627d945db38147d1b209a899311b1305dd84916f2b88526c6"},
+ {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bd9b3b31adcb054116447ea22caa61a285d92e94d710aa5ec97992ff5eb7cf3"},
+ {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:8c2f5e83493748286002f9369f3e6607c565a6a90425a3a1fef5ae32a36d749d"},
+ {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3170c9399da12c9dc66366e9d14da8bf7147e1e9d9ea566067bbce7bb74bd9c2"},
+ {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7a4826ad2bd6b07ca615c74ab91f32f6c96d08f6fcc3902ceeedaec8cdc3bcd6"},
+ {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:3b1613dd5aee995ec6d4c69f00378bbd07614702a315a2cf6c1d21461fe17c23"},
+ {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9e608aafdb55eb9f255034709e20d5a83b6d60c054df0802fa9c9883d0a937aa"},
+ {file = "charset_normalizer-3.2.0-cp311-cp311-win32.whl", hash = "sha256:f2a1d0fd4242bd8643ce6f98927cf9c04540af6efa92323e9d3124f57727bfc1"},
+ {file = "charset_normalizer-3.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:681eb3d7e02e3c3655d1b16059fbfb605ac464c834a0c629048a30fad2b27489"},
+ {file = "charset_normalizer-3.2.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c57921cda3a80d0f2b8aec7e25c8aa14479ea92b5b51b6876d975d925a2ea346"},
+ {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41b25eaa7d15909cf3ac4c96088c1f266a9a93ec44f87f1d13d4a0e86c81b982"},
+ {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f058f6963fd82eb143c692cecdc89e075fa0828db2e5b291070485390b2f1c9c"},
+ {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7647ebdfb9682b7bb97e2a5e7cb6ae735b1c25008a70b906aecca294ee96cf4"},
+ {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eef9df1eefada2c09a5e7a40991b9fc6ac6ef20b1372abd48d2794a316dc0449"},
+ {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e03b8895a6990c9ab2cdcd0f2fe44088ca1c65ae592b8f795c3294af00a461c3"},
+ {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:ee4006268ed33370957f55bf2e6f4d263eaf4dc3cfc473d1d90baff6ed36ce4a"},
+ {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c4983bf937209c57240cff65906b18bb35e64ae872da6a0db937d7b4af845dd7"},
+ {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:3bb7fda7260735efe66d5107fb7e6af6a7c04c7fce9b2514e04b7a74b06bf5dd"},
+ {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:72814c01533f51d68702802d74f77ea026b5ec52793c791e2da806a3844a46c3"},
+ {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:70c610f6cbe4b9fce272c407dd9d07e33e6bf7b4aa1b7ffb6f6ded8e634e3592"},
+ {file = "charset_normalizer-3.2.0-cp37-cp37m-win32.whl", hash = "sha256:a401b4598e5d3f4a9a811f3daf42ee2291790c7f9d74b18d75d6e21dda98a1a1"},
+ {file = "charset_normalizer-3.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:c0b21078a4b56965e2b12f247467b234734491897e99c1d51cee628da9786959"},
+ {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:95eb302ff792e12aba9a8b8f8474ab229a83c103d74a750ec0bd1c1eea32e669"},
+ {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1a100c6d595a7f316f1b6f01d20815d916e75ff98c27a01ae817439ea7726329"},
+ {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6339d047dab2780cc6220f46306628e04d9750f02f983ddb37439ca47ced7149"},
+ {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4b749b9cc6ee664a3300bb3a273c1ca8068c46be705b6c31cf5d276f8628a94"},
+ {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a38856a971c602f98472050165cea2cdc97709240373041b69030be15047691f"},
+ {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f87f746ee241d30d6ed93969de31e5ffd09a2961a051e60ae6bddde9ec3583aa"},
+ {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89f1b185a01fe560bc8ae5f619e924407efca2191b56ce749ec84982fc59a32a"},
+ {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e1c8a2f4c69e08e89632defbfabec2feb8a8d99edc9f89ce33c4b9e36ab63037"},
+ {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2f4ac36d8e2b4cc1aa71df3dd84ff8efbe3bfb97ac41242fbcfc053c67434f46"},
+ {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a386ebe437176aab38c041de1260cd3ea459c6ce5263594399880bbc398225b2"},
+ {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:ccd16eb18a849fd8dcb23e23380e2f0a354e8daa0c984b8a732d9cfaba3a776d"},
+ {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:e6a5bf2cba5ae1bb80b154ed68a3cfa2fa00fde979a7f50d6598d3e17d9ac20c"},
+ {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:45de3f87179c1823e6d9e32156fb14c1927fcc9aba21433f088fdfb555b77c10"},
+ {file = "charset_normalizer-3.2.0-cp38-cp38-win32.whl", hash = "sha256:1000fba1057b92a65daec275aec30586c3de2401ccdcd41f8a5c1e2c87078706"},
+ {file = "charset_normalizer-3.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:8b2c760cfc7042b27ebdb4a43a4453bd829a5742503599144d54a032c5dc7e9e"},
+ {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:855eafa5d5a2034b4621c74925d89c5efef61418570e5ef9b37717d9c796419c"},
+ {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:203f0c8871d5a7987be20c72442488a0b8cfd0f43b7973771640fc593f56321f"},
+ {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e857a2232ba53ae940d3456f7533ce6ca98b81917d47adc3c7fd55dad8fab858"},
+ {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e86d77b090dbddbe78867a0275cb4df08ea195e660f1f7f13435a4649e954e5"},
+ {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4fb39a81950ec280984b3a44f5bd12819953dc5fa3a7e6fa7a80db5ee853952"},
+ {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dee8e57f052ef5353cf608e0b4c871aee320dd1b87d351c28764fc0ca55f9f4"},
+ {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8700f06d0ce6f128de3ccdbc1acaea1ee264d2caa9ca05daaf492fde7c2a7200"},
+ {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1920d4ff15ce893210c1f0c0e9d19bfbecb7983c76b33f046c13a8ffbd570252"},
+ {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c1c76a1743432b4b60ab3358c937a3fe1341c828ae6194108a94c69028247f22"},
+ {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f7560358a6811e52e9c4d142d497f1a6e10103d3a6881f18d04dbce3729c0e2c"},
+ {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:c8063cf17b19661471ecbdb3df1c84f24ad2e389e326ccaf89e3fb2484d8dd7e"},
+ {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:cd6dbe0238f7743d0efe563ab46294f54f9bc8f4b9bcf57c3c666cc5bc9d1299"},
+ {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:1249cbbf3d3b04902ff081ffbb33ce3377fa6e4c7356f759f3cd076cc138d020"},
+ {file = "charset_normalizer-3.2.0-cp39-cp39-win32.whl", hash = "sha256:6c409c0deba34f147f77efaa67b8e4bb83d2f11c8806405f76397ae5b8c0d1c9"},
+ {file = "charset_normalizer-3.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:7095f6fbfaa55defb6b733cfeb14efaae7a29f0b59d8cf213be4e7ca0b857b80"},
+ {file = "charset_normalizer-3.2.0-py3-none-any.whl", hash = "sha256:8e098148dd37b4ce3baca71fb394c81dc5d9c7728c95df695d2dca218edf40e6"},
+]
+
+[[package]]
+name = "click"
+version = "8.1.6"
+description = "Composable command line interface toolkit"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "click-8.1.6-py3-none-any.whl", hash = "sha256:fa244bb30b3b5ee2cae3da8f55c9e5e0c0e86093306301fb418eb9dc40fbded5"},
+ {file = "click-8.1.6.tar.gz", hash = "sha256:48ee849951919527a045bfe3bf7baa8a959c423134e1a5b98c05c20ba75a1cbd"},
+]
+
+[package.dependencies]
+colorama = {version = "*", markers = "platform_system == \"Windows\""}
+
+[[package]]
+name = "colorama"
+version = "0.4.6"
+description = "Cross-platform colored terminal text."
+optional = false
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
+files = [
+ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
+ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
+]
+
+[[package]]
+name = "datamodel-code-generator"
+version = "0.22.1"
+description = "Datamodel Code Generator"
+optional = false
+python-versions = ">=3.7,<4.0"
+files = [
+ {file = "datamodel_code_generator-0.22.1-py3-none-any.whl", hash = "sha256:ac1fbc4fa778c2a43f740740fd352ca4300f705044e112a0023af8d04f0b61af"},
+ {file = "datamodel_code_generator-0.22.1.tar.gz", hash = "sha256:48c8ce0b38b575bcc573237bb3faab696b072aa131b3f008c848d2c3b24a4417"},
+]
+
+[package.dependencies]
+argcomplete = ">=1.10,<4.0"
+black = ">=19.10b0"
+genson = ">=1.2.1,<2.0"
+inflect = ">=4.1.0,<6.0"
+isort = ">=4.3.21,<6.0"
+jinja2 = ">=2.10.1,<4.0"
+openapi-spec-validator = ">=0.2.8,<=0.5.7"
+packaging = "*"
+prance = ">=0.18.2"
+pydantic = {version = ">=1.10.0,<2.4.0 || >2.4.0,<3.0", extras = ["email"], markers = "python_version >= \"3.11\" and python_version < \"4.0\""}
+PySnooper = ">=0.4.1,<2.0.0"
+toml = ">=0.10.0,<1.0.0"
+
+[package.extras]
+http = ["httpx"]
+
+[[package]]
+name = "dnspython"
+version = "2.4.1"
+description = "DNS toolkit"
+optional = false
+python-versions = ">=3.8,<4.0"
+files = [
+ {file = "dnspython-2.4.1-py3-none-any.whl", hash = "sha256:5b7488477388b8c0b70a8ce93b227c5603bc7b77f1565afe8e729c36c51447d7"},
+ {file = "dnspython-2.4.1.tar.gz", hash = "sha256:c33971c79af5be968bb897e95c2448e11a645ee84d93b265ce0b7aabe5dfdca8"},
+]
+
+[package.extras]
+dnssec = ["cryptography (>=2.6,<42.0)"]
+doh = ["h2 (>=4.1.0)", "httpcore (>=0.17.3)", "httpx (>=0.24.1)"]
+doq = ["aioquic (>=0.9.20)"]
+idna = ["idna (>=2.1,<4.0)"]
+trio = ["trio (>=0.14,<0.23)"]
+wmi = ["wmi (>=1.5.1,<2.0.0)"]
+
+[[package]]
+name = "email-validator"
+version = "2.0.0.post2"
+description = "A robust email address syntax and deliverability validation library."
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "email_validator-2.0.0.post2-py3-none-any.whl", hash = "sha256:2466ba57cda361fb7309fd3d5a225723c788ca4bbad32a0ebd5373b99730285c"},
+ {file = "email_validator-2.0.0.post2.tar.gz", hash = "sha256:1ff6e86044200c56ae23595695c54e9614f4a9551e0e393614f764860b3d7900"},
+]
+
+[package.dependencies]
+dnspython = ">=2.0.0"
+idna = ">=2.0.0"
+
+[[package]]
+name = "genson"
+version = "1.2.2"
+description = "GenSON is a powerful, user-friendly JSON Schema generator."
+optional = false
+python-versions = "*"
+files = [
+ {file = "genson-1.2.2.tar.gz", hash = "sha256:8caf69aa10af7aee0e1a1351d1d06801f4696e005f06cedef438635384346a16"},
+]
+
+[[package]]
+name = "h11"
+version = "0.14.0"
+description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"},
+ {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"},
+]
+
+[[package]]
+name = "httpcore"
+version = "0.18.0"
+description = "A minimal low-level HTTP client."
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "httpcore-0.18.0-py3-none-any.whl", hash = "sha256:adc5398ee0a476567bf87467063ee63584a8bce86078bf748e48754f60202ced"},
+ {file = "httpcore-0.18.0.tar.gz", hash = "sha256:13b5e5cd1dca1a6636a6aaea212b19f4f85cd88c366a2b82304181b769aab3c9"},
+]
+
+[package.dependencies]
+anyio = ">=3.0,<5.0"
+certifi = "*"
+h11 = ">=0.13,<0.15"
+sniffio = "==1.*"
+
+[package.extras]
+http2 = ["h2 (>=3,<5)"]
+socks = ["socksio (==1.*)"]
+
+[[package]]
+name = "httpx"
+version = "0.25.0"
+description = "The next generation HTTP client."
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "httpx-0.25.0-py3-none-any.whl", hash = "sha256:181ea7f8ba3a82578be86ef4171554dd45fec26a02556a744db029a0a27b7100"},
+ {file = "httpx-0.25.0.tar.gz", hash = "sha256:47ecda285389cb32bb2691cc6e069e3ab0205956f681c5b2ad2325719751d875"},
+]
+
+[package.dependencies]
+certifi = "*"
+httpcore = ">=0.18.0,<0.19.0"
+idna = "*"
+sniffio = "*"
+
+[package.extras]
+brotli = ["brotli", "brotlicffi"]
+cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"]
+http2 = ["h2 (>=3,<5)"]
+socks = ["socksio (==1.*)"]
+
+[[package]]
+name = "idna"
+version = "3.4"
+description = "Internationalized Domain Names in Applications (IDNA)"
+optional = false
+python-versions = ">=3.5"
+files = [
+ {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"},
+ {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"},
+]
+
+[[package]]
+name = "inflect"
+version = "5.6.2"
+description = "Correctly generate plurals, singular nouns, ordinals, indefinite articles; convert numbers to words"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "inflect-5.6.2-py3-none-any.whl", hash = "sha256:b45d91a4a28a4e617ff1821117439b06eaa86e2a4573154af0149e9be6687238"},
+ {file = "inflect-5.6.2.tar.gz", hash = "sha256:aadc7ed73928f5e014129794bbac03058cca35d0a973a5fc4eb45c7fa26005f9"},
+]
+
+[package.extras]
+docs = ["jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx"]
+testing = ["pygments", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"]
+
+[[package]]
+name = "isort"
+version = "5.12.0"
+description = "A Python utility / library to sort Python imports."
+optional = false
+python-versions = ">=3.8.0"
+files = [
+ {file = "isort-5.12.0-py3-none-any.whl", hash = "sha256:f84c2818376e66cf843d497486ea8fed8700b340f308f076c6fb1229dff318b6"},
+ {file = "isort-5.12.0.tar.gz", hash = "sha256:8bef7dde241278824a6d83f44a544709b065191b95b6e50894bdc722fcba0504"},
+]
+
+[package.extras]
+colors = ["colorama (>=0.4.3)"]
+pipfile-deprecated-finder = ["pip-shims (>=0.5.2)", "pipreqs", "requirementslib"]
+plugins = ["setuptools"]
+requirements-deprecated-finder = ["pip-api", "pipreqs"]
+
+[[package]]
+name = "jinja2"
+version = "3.1.2"
+description = "A very fast and expressive template engine."
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"},
+ {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"},
+]
+
+[package.dependencies]
+MarkupSafe = ">=2.0"
+
+[package.extras]
+i18n = ["Babel (>=2.7)"]
+
+[[package]]
+name = "jsonschema"
+version = "4.17.3"
+description = "An implementation of JSON Schema validation for Python"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "jsonschema-4.17.3-py3-none-any.whl", hash = "sha256:a870ad254da1a8ca84b6a2905cac29d265f805acc57af304784962a2aa6508f6"},
+ {file = "jsonschema-4.17.3.tar.gz", hash = "sha256:0f864437ab8b6076ba6707453ef8f98a6a0d512a80e93f8abdb676f737ecb60d"},
+]
+
+[package.dependencies]
+attrs = ">=17.4.0"
+pyrsistent = ">=0.14.0,<0.17.0 || >0.17.0,<0.17.1 || >0.17.1,<0.17.2 || >0.17.2"
+
+[package.extras]
+format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"]
+format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"]
+
+[[package]]
+name = "jsonschema-spec"
+version = "0.1.6"
+description = "JSONSchema Spec with object-oriented paths"
+optional = false
+python-versions = ">=3.7.0,<4.0.0"
+files = [
+ {file = "jsonschema_spec-0.1.6-py3-none-any.whl", hash = "sha256:f2206d18c89d1824c1f775ba14ed039743b41a9167bd2c5bdb774b66b3ca0bbf"},
+ {file = "jsonschema_spec-0.1.6.tar.gz", hash = "sha256:90215863b56e212086641956b20127ccbf6d8a3a38343dad01d6a74d19482f76"},
+]
+
+[package.dependencies]
+jsonschema = ">=4.0.0,<4.18.0"
+pathable = ">=0.4.1,<0.5.0"
+PyYAML = ">=5.1"
+requests = ">=2.31.0,<3.0.0"
+
+[[package]]
+name = "lazy-object-proxy"
+version = "1.9.0"
+description = "A fast and thorough lazy object proxy."
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "lazy-object-proxy-1.9.0.tar.gz", hash = "sha256:659fb5809fa4629b8a1ac5106f669cfc7bef26fbb389dda53b3e010d1ac4ebae"},
+ {file = "lazy_object_proxy-1.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b40387277b0ed2d0602b8293b94d7257e17d1479e257b4de114ea11a8cb7f2d7"},
+ {file = "lazy_object_proxy-1.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8c6cfb338b133fbdbc5cfaa10fe3c6aeea827db80c978dbd13bc9dd8526b7d4"},
+ {file = "lazy_object_proxy-1.9.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:721532711daa7db0d8b779b0bb0318fa87af1c10d7fe5e52ef30f8eff254d0cd"},
+ {file = "lazy_object_proxy-1.9.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:66a3de4a3ec06cd8af3f61b8e1ec67614fbb7c995d02fa224813cb7afefee701"},
+ {file = "lazy_object_proxy-1.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1aa3de4088c89a1b69f8ec0dcc169aa725b0ff017899ac568fe44ddc1396df46"},
+ {file = "lazy_object_proxy-1.9.0-cp310-cp310-win32.whl", hash = "sha256:f0705c376533ed2a9e5e97aacdbfe04cecd71e0aa84c7c0595d02ef93b6e4455"},
+ {file = "lazy_object_proxy-1.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:ea806fd4c37bf7e7ad82537b0757999264d5f70c45468447bb2b91afdbe73a6e"},
+ {file = "lazy_object_proxy-1.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:946d27deaff6cf8452ed0dba83ba38839a87f4f7a9732e8f9fd4107b21e6ff07"},
+ {file = "lazy_object_proxy-1.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79a31b086e7e68b24b99b23d57723ef7e2c6d81ed21007b6281ebcd1688acb0a"},
+ {file = "lazy_object_proxy-1.9.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f699ac1c768270c9e384e4cbd268d6e67aebcfae6cd623b4d7c3bfde5a35db59"},
+ {file = "lazy_object_proxy-1.9.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bfb38f9ffb53b942f2b5954e0f610f1e721ccebe9cce9025a38c8ccf4a5183a4"},
+ {file = "lazy_object_proxy-1.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:189bbd5d41ae7a498397287c408617fe5c48633e7755287b21d741f7db2706a9"},
+ {file = "lazy_object_proxy-1.9.0-cp311-cp311-win32.whl", hash = "sha256:81fc4d08b062b535d95c9ea70dbe8a335c45c04029878e62d744bdced5141586"},
+ {file = "lazy_object_proxy-1.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:f2457189d8257dd41ae9b434ba33298aec198e30adf2dcdaaa3a28b9994f6adb"},
+ {file = "lazy_object_proxy-1.9.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d9e25ef10a39e8afe59a5c348a4dbf29b4868ab76269f81ce1674494e2565a6e"},
+ {file = "lazy_object_proxy-1.9.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cbf9b082426036e19c6924a9ce90c740a9861e2bdc27a4834fd0a910742ac1e8"},
+ {file = "lazy_object_proxy-1.9.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f5fa4a61ce2438267163891961cfd5e32ec97a2c444e5b842d574251ade27d2"},
+ {file = "lazy_object_proxy-1.9.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:8fa02eaab317b1e9e03f69aab1f91e120e7899b392c4fc19807a8278a07a97e8"},
+ {file = "lazy_object_proxy-1.9.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e7c21c95cae3c05c14aafffe2865bbd5e377cfc1348c4f7751d9dc9a48ca4bda"},
+ {file = "lazy_object_proxy-1.9.0-cp37-cp37m-win32.whl", hash = "sha256:f12ad7126ae0c98d601a7ee504c1122bcef553d1d5e0c3bfa77b16b3968d2734"},
+ {file = "lazy_object_proxy-1.9.0-cp37-cp37m-win_amd64.whl", hash = "sha256:edd20c5a55acb67c7ed471fa2b5fb66cb17f61430b7a6b9c3b4a1e40293b1671"},
+ {file = "lazy_object_proxy-1.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2d0daa332786cf3bb49e10dc6a17a52f6a8f9601b4cf5c295a4f85854d61de63"},
+ {file = "lazy_object_proxy-1.9.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cd077f3d04a58e83d04b20e334f678c2b0ff9879b9375ed107d5d07ff160171"},
+ {file = "lazy_object_proxy-1.9.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:660c94ea760b3ce47d1855a30984c78327500493d396eac4dfd8bd82041b22be"},
+ {file = "lazy_object_proxy-1.9.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:212774e4dfa851e74d393a2370871e174d7ff0ebc980907723bb67d25c8a7c30"},
+ {file = "lazy_object_proxy-1.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f0117049dd1d5635bbff65444496c90e0baa48ea405125c088e93d9cf4525b11"},
+ {file = "lazy_object_proxy-1.9.0-cp38-cp38-win32.whl", hash = "sha256:0a891e4e41b54fd5b8313b96399f8b0e173bbbfc03c7631f01efbe29bb0bcf82"},
+ {file = "lazy_object_proxy-1.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:9990d8e71b9f6488e91ad25f322898c136b008d87bf852ff65391b004da5e17b"},
+ {file = "lazy_object_proxy-1.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9e7551208b2aded9c1447453ee366f1c4070602b3d932ace044715d89666899b"},
+ {file = "lazy_object_proxy-1.9.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f83ac4d83ef0ab017683d715ed356e30dd48a93746309c8f3517e1287523ef4"},
+ {file = "lazy_object_proxy-1.9.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7322c3d6f1766d4ef1e51a465f47955f1e8123caee67dd641e67d539a534d006"},
+ {file = "lazy_object_proxy-1.9.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:18b78ec83edbbeb69efdc0e9c1cb41a3b1b1ed11ddd8ded602464c3fc6020494"},
+ {file = "lazy_object_proxy-1.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:09763491ce220c0299688940f8dc2c5d05fd1f45af1e42e636b2e8b2303e4382"},
+ {file = "lazy_object_proxy-1.9.0-cp39-cp39-win32.whl", hash = "sha256:9090d8e53235aa280fc9239a86ae3ea8ac58eff66a705fa6aa2ec4968b95c821"},
+ {file = "lazy_object_proxy-1.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:db1c1722726f47e10e0b5fdbf15ac3b8adb58c091d12b3ab713965795036985f"},
+]
+
+[[package]]
+name = "markupsafe"
+version = "2.1.3"
+description = "Safely add untrusted strings to HTML/XML markup."
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa"},
+ {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57"},
+ {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f"},
+ {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52"},
+ {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00"},
+ {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6"},
+ {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779"},
+ {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7"},
+ {file = "MarkupSafe-2.1.3-cp310-cp310-win32.whl", hash = "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431"},
+ {file = "MarkupSafe-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559"},
+ {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c"},
+ {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575"},
+ {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee"},
+ {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2"},
+ {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9"},
+ {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc"},
+ {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9"},
+ {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"},
+ {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"},
+ {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"},
+ {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"},
+ {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"},
+ {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"},
+ {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e"},
+ {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc"},
+ {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48"},
+ {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155"},
+ {file = "MarkupSafe-2.1.3-cp37-cp37m-win32.whl", hash = "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0"},
+ {file = "MarkupSafe-2.1.3-cp37-cp37m-win_amd64.whl", hash = "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24"},
+ {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4"},
+ {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0"},
+ {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee"},
+ {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be"},
+ {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e"},
+ {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8"},
+ {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3"},
+ {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d"},
+ {file = "MarkupSafe-2.1.3-cp38-cp38-win32.whl", hash = "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5"},
+ {file = "MarkupSafe-2.1.3-cp38-cp38-win_amd64.whl", hash = "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc"},
+ {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198"},
+ {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b"},
+ {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58"},
+ {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e"},
+ {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c"},
+ {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636"},
+ {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea"},
+ {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e"},
+ {file = "MarkupSafe-2.1.3-cp39-cp39-win32.whl", hash = "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2"},
+ {file = "MarkupSafe-2.1.3-cp39-cp39-win_amd64.whl", hash = "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba"},
+ {file = "MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"},
+]
+
+[[package]]
+name = "multidict"
+version = "6.0.4"
+description = "multidict implementation"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "multidict-6.0.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b1a97283e0c85772d613878028fec909f003993e1007eafa715b24b377cb9b8"},
+ {file = "multidict-6.0.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:eeb6dcc05e911516ae3d1f207d4b0520d07f54484c49dfc294d6e7d63b734171"},
+ {file = "multidict-6.0.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d6d635d5209b82a3492508cf5b365f3446afb65ae7ebd755e70e18f287b0adf7"},
+ {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c048099e4c9e9d615545e2001d3d8a4380bd403e1a0578734e0d31703d1b0c0b"},
+ {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ea20853c6dbbb53ed34cb4d080382169b6f4554d394015f1bef35e881bf83547"},
+ {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16d232d4e5396c2efbbf4f6d4df89bfa905eb0d4dc5b3549d872ab898451f569"},
+ {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36c63aaa167f6c6b04ef2c85704e93af16c11d20de1d133e39de6a0e84582a93"},
+ {file = "multidict-6.0.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:64bdf1086b6043bf519869678f5f2757f473dee970d7abf6da91ec00acb9cb98"},
+ {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:43644e38f42e3af682690876cff722d301ac585c5b9e1eacc013b7a3f7b696a0"},
+ {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7582a1d1030e15422262de9f58711774e02fa80df0d1578995c76214f6954988"},
+ {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:ddff9c4e225a63a5afab9dd15590432c22e8057e1a9a13d28ed128ecf047bbdc"},
+ {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:ee2a1ece51b9b9e7752e742cfb661d2a29e7bcdba2d27e66e28a99f1890e4fa0"},
+ {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a2e4369eb3d47d2034032a26c7a80fcb21a2cb22e1173d761a162f11e562caa5"},
+ {file = "multidict-6.0.4-cp310-cp310-win32.whl", hash = "sha256:574b7eae1ab267e5f8285f0fe881f17efe4b98c39a40858247720935b893bba8"},
+ {file = "multidict-6.0.4-cp310-cp310-win_amd64.whl", hash = "sha256:4dcbb0906e38440fa3e325df2359ac6cb043df8e58c965bb45f4e406ecb162cc"},
+ {file = "multidict-6.0.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0dfad7a5a1e39c53ed00d2dd0c2e36aed4650936dc18fd9a1826a5ae1cad6f03"},
+ {file = "multidict-6.0.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:64da238a09d6039e3bd39bb3aee9c21a5e34f28bfa5aa22518581f910ff94af3"},
+ {file = "multidict-6.0.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ff959bee35038c4624250473988b24f846cbeb2c6639de3602c073f10410ceba"},
+ {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:01a3a55bd90018c9c080fbb0b9f4891db37d148a0a18722b42f94694f8b6d4c9"},
+ {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c5cb09abb18c1ea940fb99360ea0396f34d46566f157122c92dfa069d3e0e982"},
+ {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:666daae833559deb2d609afa4490b85830ab0dfca811a98b70a205621a6109fe"},
+ {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11bdf3f5e1518b24530b8241529d2050014c884cf18b6fc69c0c2b30ca248710"},
+ {file = "multidict-6.0.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7d18748f2d30f94f498e852c67d61261c643b349b9d2a581131725595c45ec6c"},
+ {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:458f37be2d9e4c95e2d8866a851663cbc76e865b78395090786f6cd9b3bbf4f4"},
+ {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b1a2eeedcead3a41694130495593a559a668f382eee0727352b9a41e1c45759a"},
+ {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7d6ae9d593ef8641544d6263c7fa6408cc90370c8cb2bbb65f8d43e5b0351d9c"},
+ {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:5979b5632c3e3534e42ca6ff856bb24b2e3071b37861c2c727ce220d80eee9ed"},
+ {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dcfe792765fab89c365123c81046ad4103fcabbc4f56d1c1997e6715e8015461"},
+ {file = "multidict-6.0.4-cp311-cp311-win32.whl", hash = "sha256:3601a3cece3819534b11d4efc1eb76047488fddd0c85a3948099d5da4d504636"},
+ {file = "multidict-6.0.4-cp311-cp311-win_amd64.whl", hash = "sha256:81a4f0b34bd92df3da93315c6a59034df95866014ac08535fc819f043bfd51f0"},
+ {file = "multidict-6.0.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:67040058f37a2a51ed8ea8f6b0e6ee5bd78ca67f169ce6122f3e2ec80dfe9b78"},
+ {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:853888594621e6604c978ce2a0444a1e6e70c8d253ab65ba11657659dcc9100f"},
+ {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:39ff62e7d0f26c248b15e364517a72932a611a9b75f35b45be078d81bdb86603"},
+ {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:af048912e045a2dc732847d33821a9d84ba553f5c5f028adbd364dd4765092ac"},
+ {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1e8b901e607795ec06c9e42530788c45ac21ef3aaa11dbd0c69de543bfb79a9"},
+ {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62501642008a8b9871ddfccbf83e4222cf8ac0d5aeedf73da36153ef2ec222d2"},
+ {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:99b76c052e9f1bc0721f7541e5e8c05db3941eb9ebe7b8553c625ef88d6eefde"},
+ {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:509eac6cf09c794aa27bcacfd4d62c885cce62bef7b2c3e8b2e49d365b5003fe"},
+ {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:21a12c4eb6ddc9952c415f24eef97e3e55ba3af61f67c7bc388dcdec1404a067"},
+ {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:5cad9430ab3e2e4fa4a2ef4450f548768400a2ac635841bc2a56a2052cdbeb87"},
+ {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ab55edc2e84460694295f401215f4a58597f8f7c9466faec545093045476327d"},
+ {file = "multidict-6.0.4-cp37-cp37m-win32.whl", hash = "sha256:5a4dcf02b908c3b8b17a45fb0f15b695bf117a67b76b7ad18b73cf8e92608775"},
+ {file = "multidict-6.0.4-cp37-cp37m-win_amd64.whl", hash = "sha256:6ed5f161328b7df384d71b07317f4d8656434e34591f20552c7bcef27b0ab88e"},
+ {file = "multidict-6.0.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5fc1b16f586f049820c5c5b17bb4ee7583092fa0d1c4e28b5239181ff9532e0c"},
+ {file = "multidict-6.0.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1502e24330eb681bdaa3eb70d6358e818e8e8f908a22a1851dfd4e15bc2f8161"},
+ {file = "multidict-6.0.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b692f419760c0e65d060959df05f2a531945af31fda0c8a3b3195d4efd06de11"},
+ {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45e1ecb0379bfaab5eef059f50115b54571acfbe422a14f668fc8c27ba410e7e"},
+ {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ddd3915998d93fbcd2566ddf9cf62cdb35c9e093075f862935573d265cf8f65d"},
+ {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:59d43b61c59d82f2effb39a93c48b845efe23a3852d201ed2d24ba830d0b4cf2"},
+ {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc8e1d0c705233c5dd0c5e6460fbad7827d5d36f310a0fadfd45cc3029762258"},
+ {file = "multidict-6.0.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6aa0418fcc838522256761b3415822626f866758ee0bc6632c9486b179d0b52"},
+ {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6748717bb10339c4760c1e63da040f5f29f5ed6e59d76daee30305894069a660"},
+ {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4d1a3d7ef5e96b1c9e92f973e43aa5e5b96c659c9bc3124acbbd81b0b9c8a951"},
+ {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4372381634485bec7e46718edc71528024fcdc6f835baefe517b34a33c731d60"},
+ {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:fc35cb4676846ef752816d5be2193a1e8367b4c1397b74a565a9d0389c433a1d"},
+ {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:4b9d9e4e2b37daddb5c23ea33a3417901fa7c7b3dee2d855f63ee67a0b21e5b1"},
+ {file = "multidict-6.0.4-cp38-cp38-win32.whl", hash = "sha256:e41b7e2b59679edfa309e8db64fdf22399eec4b0b24694e1b2104fb789207779"},
+ {file = "multidict-6.0.4-cp38-cp38-win_amd64.whl", hash = "sha256:d6c254ba6e45d8e72739281ebc46ea5eb5f101234f3ce171f0e9f5cc86991480"},
+ {file = "multidict-6.0.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:16ab77bbeb596e14212e7bab8429f24c1579234a3a462105cda4a66904998664"},
+ {file = "multidict-6.0.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bc779e9e6f7fda81b3f9aa58e3a6091d49ad528b11ed19f6621408806204ad35"},
+ {file = "multidict-6.0.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4ceef517eca3e03c1cceb22030a3e39cb399ac86bff4e426d4fc6ae49052cc60"},
+ {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:281af09f488903fde97923c7744bb001a9b23b039a909460d0f14edc7bf59706"},
+ {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:52f2dffc8acaba9a2f27174c41c9e57f60b907bb9f096b36b1a1f3be71c6284d"},
+ {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b41156839806aecb3641f3208c0dafd3ac7775b9c4c422d82ee2a45c34ba81ca"},
+ {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5e3fc56f88cc98ef8139255cf8cd63eb2c586531e43310ff859d6bb3a6b51f1"},
+ {file = "multidict-6.0.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8316a77808c501004802f9beebde51c9f857054a0c871bd6da8280e718444449"},
+ {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f70b98cd94886b49d91170ef23ec5c0e8ebb6f242d734ed7ed677b24d50c82cf"},
+ {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bf6774e60d67a9efe02b3616fee22441d86fab4c6d335f9d2051d19d90a40063"},
+ {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:e69924bfcdda39b722ef4d9aa762b2dd38e4632b3641b1d9a57ca9cd18f2f83a"},
+ {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:6b181d8c23da913d4ff585afd1155a0e1194c0b50c54fcfe286f70cdaf2b7176"},
+ {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:52509b5be062d9eafc8170e53026fbc54cf3b32759a23d07fd935fb04fc22d95"},
+ {file = "multidict-6.0.4-cp39-cp39-win32.whl", hash = "sha256:27c523fbfbdfd19c6867af7346332b62b586eed663887392cff78d614f9ec313"},
+ {file = "multidict-6.0.4-cp39-cp39-win_amd64.whl", hash = "sha256:33029f5734336aa0d4c0384525da0387ef89148dc7191aae00ca5fb23d7aafc2"},
+ {file = "multidict-6.0.4.tar.gz", hash = "sha256:3666906492efb76453c0e7b97f2cf459b0682e7402c0489a95484965dbc1da49"},
+]
+
+[[package]]
+name = "mypy"
+version = "1.6.1"
+description = "Optional static typing for Python"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "mypy-1.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e5012e5cc2ac628177eaac0e83d622b2dd499e28253d4107a08ecc59ede3fc2c"},
+ {file = "mypy-1.6.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d8fbb68711905f8912e5af474ca8b78d077447d8f3918997fecbf26943ff3cbb"},
+ {file = "mypy-1.6.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21a1ad938fee7d2d96ca666c77b7c494c3c5bd88dff792220e1afbebb2925b5e"},
+ {file = "mypy-1.6.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b96ae2c1279d1065413965c607712006205a9ac541895004a1e0d4f281f2ff9f"},
+ {file = "mypy-1.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:40b1844d2e8b232ed92e50a4bd11c48d2daa351f9deee6c194b83bf03e418b0c"},
+ {file = "mypy-1.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:81af8adaa5e3099469e7623436881eff6b3b06db5ef75e6f5b6d4871263547e5"},
+ {file = "mypy-1.6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8c223fa57cb154c7eab5156856c231c3f5eace1e0bed9b32a24696b7ba3c3245"},
+ {file = "mypy-1.6.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8032e00ce71c3ceb93eeba63963b864bf635a18f6c0c12da6c13c450eedb183"},
+ {file = "mypy-1.6.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4c46b51de523817a0045b150ed11b56f9fff55f12b9edd0f3ed35b15a2809de0"},
+ {file = "mypy-1.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:19f905bcfd9e167159b3d63ecd8cb5e696151c3e59a1742e79bc3bcb540c42c7"},
+ {file = "mypy-1.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:82e469518d3e9a321912955cc702d418773a2fd1e91c651280a1bda10622f02f"},
+ {file = "mypy-1.6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d4473c22cc296425bbbce7e9429588e76e05bc7342da359d6520b6427bf76660"},
+ {file = "mypy-1.6.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59a0d7d24dfb26729e0a068639a6ce3500e31d6655df8557156c51c1cb874ce7"},
+ {file = "mypy-1.6.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:cfd13d47b29ed3bbaafaff7d8b21e90d827631afda134836962011acb5904b71"},
+ {file = "mypy-1.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:eb4f18589d196a4cbe5290b435d135dee96567e07c2b2d43b5c4621b6501531a"},
+ {file = "mypy-1.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:41697773aa0bf53ff917aa077e2cde7aa50254f28750f9b88884acea38a16169"},
+ {file = "mypy-1.6.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7274b0c57737bd3476d2229c6389b2ec9eefeb090bbaf77777e9d6b1b5a9d143"},
+ {file = "mypy-1.6.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbaf4662e498c8c2e352da5f5bca5ab29d378895fa2d980630656178bd607c46"},
+ {file = "mypy-1.6.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bb8ccb4724f7d8601938571bf3f24da0da791fe2db7be3d9e79849cb64e0ae85"},
+ {file = "mypy-1.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:68351911e85145f582b5aa6cd9ad666c8958bcae897a1bfda8f4940472463c45"},
+ {file = "mypy-1.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:49ae115da099dcc0922a7a895c1eec82c1518109ea5c162ed50e3b3594c71208"},
+ {file = "mypy-1.6.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8b27958f8c76bed8edaa63da0739d76e4e9ad4ed325c814f9b3851425582a3cd"},
+ {file = "mypy-1.6.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:925cd6a3b7b55dfba252b7c4561892311c5358c6b5a601847015a1ad4eb7d332"},
+ {file = "mypy-1.6.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8f57e6b6927a49550da3d122f0cb983d400f843a8a82e65b3b380d3d7259468f"},
+ {file = "mypy-1.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:a43ef1c8ddfdb9575691720b6352761f3f53d85f1b57d7745701041053deff30"},
+ {file = "mypy-1.6.1-py3-none-any.whl", hash = "sha256:4cbe68ef919c28ea561165206a2dcb68591c50f3bcf777932323bc208d949cf1"},
+ {file = "mypy-1.6.1.tar.gz", hash = "sha256:4d01c00d09a0be62a4ca3f933e315455bde83f37f892ba4b08ce92f3cf44bcc1"},
+]
+
+[package.dependencies]
+mypy-extensions = ">=1.0.0"
+typing-extensions = ">=4.1.0"
+
+[package.extras]
+dmypy = ["psutil (>=4.0)"]
+install-types = ["pip"]
+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 = "openapi-schema-validator"
+version = "0.4.4"
+description = "OpenAPI schema validation for Python"
+optional = false
+python-versions = ">=3.7.0,<4.0.0"
+files = [
+ {file = "openapi_schema_validator-0.4.4-py3-none-any.whl", hash = "sha256:79f37f38ef9fd5206b924ed7a6f382cea7b649b3b56383c47f1906082b7b9015"},
+ {file = "openapi_schema_validator-0.4.4.tar.gz", hash = "sha256:c573e2be2c783abae56c5a1486ab716ca96e09d1c3eab56020d1dc680aa57bf8"},
+]
+
+[package.dependencies]
+jsonschema = ">=4.0.0,<4.18.0"
+rfc3339-validator = "*"
+
+[package.extras]
+docs = ["sphinx (>=5.3.0,<6.0.0)", "sphinx-immaterial (>=0.11.0,<0.12.0)"]
+
+[[package]]
+name = "openapi-spec-validator"
+version = "0.5.2"
+description = "OpenAPI 2.0 (aka Swagger) and OpenAPI 3 spec validator"
+optional = false
+python-versions = ">=3.7.0,<4.0.0"
+files = [
+ {file = "openapi_spec_validator-0.5.2-py3-none-any.whl", hash = "sha256:1f8db08ecbcf4ec8c558d65b65b3b7b428f81da6642f2f163e992ae3e17b229c"},
+ {file = "openapi_spec_validator-0.5.2.tar.gz", hash = "sha256:ebed7f1c567780859402ad64b128e17f519d15f605f1b41d1e9a4a7a1690be07"},
+]
+
+[package.dependencies]
+jsonschema = ">=4.0.0,<5.0.0"
+jsonschema-spec = ">=0.1.1,<0.2.0"
+lazy-object-proxy = ">=1.7.1,<2.0.0"
+openapi-schema-validator = ">=0.3.2,<0.5"
+
+[package.extras]
+requests = ["requests"]
+
+[[package]]
+name = "packaging"
+version = "23.1"
+description = "Core utilities for Python packages"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"},
+ {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"},
+]
+
+[[package]]
+name = "pathable"
+version = "0.4.3"
+description = "Object-oriented paths"
+optional = false
+python-versions = ">=3.7.0,<4.0.0"
+files = [
+ {file = "pathable-0.4.3-py3-none-any.whl", hash = "sha256:cdd7b1f9d7d5c8b8d3315dbf5a86b2596053ae845f056f57d97c0eefff84da14"},
+ {file = "pathable-0.4.3.tar.gz", hash = "sha256:5c869d315be50776cc8a993f3af43e0c60dc01506b399643f919034ebf4cdcab"},
+]
+
+[[package]]
+name = "pathspec"
+version = "0.11.2"
+description = "Utility library for gitignore style pattern matching of file paths."
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "pathspec-0.11.2-py3-none-any.whl", hash = "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20"},
+ {file = "pathspec-0.11.2.tar.gz", hash = "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3"},
+]
+
+[[package]]
+name = "platformdirs"
+version = "3.10.0"
+description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "platformdirs-3.10.0-py3-none-any.whl", hash = "sha256:d7c24979f292f916dc9cbf8648319032f551ea8c49a4c9bf2fb556a02070ec1d"},
+ {file = "platformdirs-3.10.0.tar.gz", hash = "sha256:b45696dab2d7cc691a3226759c0d3b00c47c8b6e293d96f6436f733303f77f6d"},
+]
+
+[package.extras]
+docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"]
+test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"]
+
+[[package]]
+name = "prance"
+version = "23.6.21.0"
+description = "Resolving Swagger/OpenAPI 2.0 and 3.0.0 Parser"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "prance-23.6.21.0-py3-none-any.whl", hash = "sha256:6a4276fa07ed9f22feda4331097d7503c4adc3097e46ffae97425f2c1026bd9f"},
+ {file = "prance-23.6.21.0.tar.gz", hash = "sha256:d8c15f8ac34019751cc4945f866d8d964d7888016d10de3592e339567177cabe"},
+]
+
+[package.dependencies]
+chardet = ">=3.0"
+packaging = ">=21.3"
+requests = ">=2.25"
+"ruamel.yaml" = ">=0.17.10"
+six = ">=1.15,<2.0"
+
+[package.extras]
+cli = ["click (>=7.0)"]
+dev = ["bumpversion (>=0.6)", "pytest (>=6.1)", "pytest-cov (>=2.11)", "sphinx (>=3.4)", "towncrier (>=19.2)", "tox (>=3.4)"]
+flex = ["flex (>=6.13,<7.0)"]
+icu = ["PyICU (>=2.4,<3.0)"]
+osv = ["openapi-spec-validator (>=0.5.1,<0.6.0)"]
+ssv = ["swagger-spec-validator (>=2.4,<3.0)"]
+
+[[package]]
+name = "pydantic"
+version = "2.4.2"
+description = "Data validation using Python type hints"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "pydantic-2.4.2-py3-none-any.whl", hash = "sha256:bc3ddf669d234f4220e6e1c4d96b061abe0998185a8d7855c0126782b7abc8c1"},
+ {file = "pydantic-2.4.2.tar.gz", hash = "sha256:94f336138093a5d7f426aac732dcfe7ab4eb4da243c88f891d65deb4a2556ee7"},
+]
+
+[package.dependencies]
+annotated-types = ">=0.4.0"
+email-validator = {version = ">=2.0.0", optional = true, markers = "extra == \"email\""}
+pydantic-core = "2.10.1"
+typing-extensions = ">=4.6.1"
+
+[package.extras]
+email = ["email-validator (>=2.0.0)"]
+
+[[package]]
+name = "pydantic-core"
+version = "2.10.1"
+description = ""
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "pydantic_core-2.10.1-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:d64728ee14e667ba27c66314b7d880b8eeb050e58ffc5fec3b7a109f8cddbd63"},
+ {file = "pydantic_core-2.10.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:48525933fea744a3e7464c19bfede85df4aba79ce90c60b94d8b6e1eddd67096"},
+ {file = "pydantic_core-2.10.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef337945bbd76cce390d1b2496ccf9f90b1c1242a3a7bc242ca4a9fc5993427a"},
+ {file = "pydantic_core-2.10.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a1392e0638af203cee360495fd2cfdd6054711f2db5175b6e9c3c461b76f5175"},
+ {file = "pydantic_core-2.10.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0675ba5d22de54d07bccde38997e780044dcfa9a71aac9fd7d4d7a1d2e3e65f7"},
+ {file = "pydantic_core-2.10.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:128552af70a64660f21cb0eb4876cbdadf1a1f9d5de820fed6421fa8de07c893"},
+ {file = "pydantic_core-2.10.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f6e6aed5818c264412ac0598b581a002a9f050cb2637a84979859e70197aa9e"},
+ {file = "pydantic_core-2.10.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ecaac27da855b8d73f92123e5f03612b04c5632fd0a476e469dfc47cd37d6b2e"},
+ {file = "pydantic_core-2.10.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b3c01c2fb081fced3bbb3da78510693dc7121bb893a1f0f5f4b48013201f362e"},
+ {file = "pydantic_core-2.10.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:92f675fefa977625105708492850bcbc1182bfc3e997f8eecb866d1927c98ae6"},
+ {file = "pydantic_core-2.10.1-cp310-none-win32.whl", hash = "sha256:420a692b547736a8d8703c39ea935ab5d8f0d2573f8f123b0a294e49a73f214b"},
+ {file = "pydantic_core-2.10.1-cp310-none-win_amd64.whl", hash = "sha256:0880e239827b4b5b3e2ce05e6b766a7414e5f5aedc4523be6b68cfbc7f61c5d0"},
+ {file = "pydantic_core-2.10.1-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:073d4a470b195d2b2245d0343569aac7e979d3a0dcce6c7d2af6d8a920ad0bea"},
+ {file = "pydantic_core-2.10.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:600d04a7b342363058b9190d4e929a8e2e715c5682a70cc37d5ded1e0dd370b4"},
+ {file = "pydantic_core-2.10.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39215d809470f4c8d1881758575b2abfb80174a9e8daf8f33b1d4379357e417c"},
+ {file = "pydantic_core-2.10.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eeb3d3d6b399ffe55f9a04e09e635554012f1980696d6b0aca3e6cf42a17a03b"},
+ {file = "pydantic_core-2.10.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a7a7902bf75779bc12ccfc508bfb7a4c47063f748ea3de87135d433a4cca7a2f"},
+ {file = "pydantic_core-2.10.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3625578b6010c65964d177626fde80cf60d7f2e297d56b925cb5cdeda6e9925a"},
+ {file = "pydantic_core-2.10.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:caa48fc31fc7243e50188197b5f0c4228956f97b954f76da157aae7f67269ae8"},
+ {file = "pydantic_core-2.10.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:07ec6d7d929ae9c68f716195ce15e745b3e8fa122fc67698ac6498d802ed0fa4"},
+ {file = "pydantic_core-2.10.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e6f31a17acede6a8cd1ae2d123ce04d8cca74056c9d456075f4f6f85de055607"},
+ {file = "pydantic_core-2.10.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d8f1ebca515a03e5654f88411420fea6380fc841d1bea08effb28184e3d4899f"},
+ {file = "pydantic_core-2.10.1-cp311-none-win32.whl", hash = "sha256:6db2eb9654a85ada248afa5a6db5ff1cf0f7b16043a6b070adc4a5be68c716d6"},
+ {file = "pydantic_core-2.10.1-cp311-none-win_amd64.whl", hash = "sha256:4a5be350f922430997f240d25f8219f93b0c81e15f7b30b868b2fddfc2d05f27"},
+ {file = "pydantic_core-2.10.1-cp311-none-win_arm64.whl", hash = "sha256:5fdb39f67c779b183b0c853cd6b45f7db84b84e0571b3ef1c89cdb1dfc367325"},
+ {file = "pydantic_core-2.10.1-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:b1f22a9ab44de5f082216270552aa54259db20189e68fc12484873d926426921"},
+ {file = "pydantic_core-2.10.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8572cadbf4cfa95fb4187775b5ade2eaa93511f07947b38f4cd67cf10783b118"},
+ {file = "pydantic_core-2.10.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:db9a28c063c7c00844ae42a80203eb6d2d6bbb97070cfa00194dff40e6f545ab"},
+ {file = "pydantic_core-2.10.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0e2a35baa428181cb2270a15864ec6286822d3576f2ed0f4cd7f0c1708472aff"},
+ {file = "pydantic_core-2.10.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05560ab976012bf40f25d5225a58bfa649bb897b87192a36c6fef1ab132540d7"},
+ {file = "pydantic_core-2.10.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d6495008733c7521a89422d7a68efa0a0122c99a5861f06020ef5b1f51f9ba7c"},
+ {file = "pydantic_core-2.10.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14ac492c686defc8e6133e3a2d9eaf5261b3df26b8ae97450c1647286750b901"},
+ {file = "pydantic_core-2.10.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8282bab177a9a3081fd3d0a0175a07a1e2bfb7fcbbd949519ea0980f8a07144d"},
+ {file = "pydantic_core-2.10.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:aafdb89fdeb5fe165043896817eccd6434aee124d5ee9b354f92cd574ba5e78f"},
+ {file = "pydantic_core-2.10.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:f6defd966ca3b187ec6c366604e9296f585021d922e666b99c47e78738b5666c"},
+ {file = "pydantic_core-2.10.1-cp312-none-win32.whl", hash = "sha256:7c4d1894fe112b0864c1fa75dffa045720a194b227bed12f4be7f6045b25209f"},
+ {file = "pydantic_core-2.10.1-cp312-none-win_amd64.whl", hash = "sha256:5994985da903d0b8a08e4935c46ed8daf5be1cf217489e673910951dc533d430"},
+ {file = "pydantic_core-2.10.1-cp312-none-win_arm64.whl", hash = "sha256:0d8a8adef23d86d8eceed3e32e9cca8879c7481c183f84ed1a8edc7df073af94"},
+ {file = "pydantic_core-2.10.1-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:9badf8d45171d92387410b04639d73811b785b5161ecadabf056ea14d62d4ede"},
+ {file = "pydantic_core-2.10.1-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:ebedb45b9feb7258fac0a268a3f6bec0a2ea4d9558f3d6f813f02ff3a6dc6698"},
+ {file = "pydantic_core-2.10.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cfe1090245c078720d250d19cb05d67e21a9cd7c257698ef139bc41cf6c27b4f"},
+ {file = "pydantic_core-2.10.1-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e357571bb0efd65fd55f18db0a2fb0ed89d0bb1d41d906b138f088933ae618bb"},
+ {file = "pydantic_core-2.10.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b3dcd587b69bbf54fc04ca157c2323b8911033e827fffaecf0cafa5a892a0904"},
+ {file = "pydantic_core-2.10.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c120c9ce3b163b985a3b966bb701114beb1da4b0468b9b236fc754783d85aa3"},
+ {file = "pydantic_core-2.10.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15d6bca84ffc966cc9976b09a18cf9543ed4d4ecbd97e7086f9ce9327ea48891"},
+ {file = "pydantic_core-2.10.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5cabb9710f09d5d2e9e2748c3e3e20d991a4c5f96ed8f1132518f54ab2967221"},
+ {file = "pydantic_core-2.10.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:82f55187a5bebae7d81d35b1e9aaea5e169d44819789837cdd4720d768c55d15"},
+ {file = "pydantic_core-2.10.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:1d40f55222b233e98e3921df7811c27567f0e1a4411b93d4c5c0f4ce131bc42f"},
+ {file = "pydantic_core-2.10.1-cp37-none-win32.whl", hash = "sha256:14e09ff0b8fe6e46b93d36a878f6e4a3a98ba5303c76bb8e716f4878a3bee92c"},
+ {file = "pydantic_core-2.10.1-cp37-none-win_amd64.whl", hash = "sha256:1396e81b83516b9d5c9e26a924fa69164156c148c717131f54f586485ac3c15e"},
+ {file = "pydantic_core-2.10.1-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:6835451b57c1b467b95ffb03a38bb75b52fb4dc2762bb1d9dbed8de31ea7d0fc"},
+ {file = "pydantic_core-2.10.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b00bc4619f60c853556b35f83731bd817f989cba3e97dc792bb8c97941b8053a"},
+ {file = "pydantic_core-2.10.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fa467fd300a6f046bdb248d40cd015b21b7576c168a6bb20aa22e595c8ffcdd"},
+ {file = "pydantic_core-2.10.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d99277877daf2efe074eae6338453a4ed54a2d93fb4678ddfe1209a0c93a2468"},
+ {file = "pydantic_core-2.10.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fa7db7558607afeccb33c0e4bf1c9a9a835e26599e76af6fe2fcea45904083a6"},
+ {file = "pydantic_core-2.10.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aad7bd686363d1ce4ee930ad39f14e1673248373f4a9d74d2b9554f06199fb58"},
+ {file = "pydantic_core-2.10.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:443fed67d33aa85357464f297e3d26e570267d1af6fef1c21ca50921d2976302"},
+ {file = "pydantic_core-2.10.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:042462d8d6ba707fd3ce9649e7bf268633a41018d6a998fb5fbacb7e928a183e"},
+ {file = "pydantic_core-2.10.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ecdbde46235f3d560b18be0cb706c8e8ad1b965e5c13bbba7450c86064e96561"},
+ {file = "pydantic_core-2.10.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ed550ed05540c03f0e69e6d74ad58d026de61b9eaebebbaaf8873e585cbb18de"},
+ {file = "pydantic_core-2.10.1-cp38-none-win32.whl", hash = "sha256:8cdbbd92154db2fec4ec973d45c565e767ddc20aa6dbaf50142676484cbff8ee"},
+ {file = "pydantic_core-2.10.1-cp38-none-win_amd64.whl", hash = "sha256:9f6f3e2598604956480f6c8aa24a3384dbf6509fe995d97f6ca6103bb8c2534e"},
+ {file = "pydantic_core-2.10.1-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:655f8f4c8d6a5963c9a0687793da37b9b681d9ad06f29438a3b2326d4e6b7970"},
+ {file = "pydantic_core-2.10.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e570ffeb2170e116a5b17e83f19911020ac79d19c96f320cbfa1fa96b470185b"},
+ {file = "pydantic_core-2.10.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:64322bfa13e44c6c30c518729ef08fda6026b96d5c0be724b3c4ae4da939f875"},
+ {file = "pydantic_core-2.10.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:485a91abe3a07c3a8d1e082ba29254eea3e2bb13cbbd4351ea4e5a21912cc9b0"},
+ {file = "pydantic_core-2.10.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7c2b8eb9fc872e68b46eeaf835e86bccc3a58ba57d0eedc109cbb14177be531"},
+ {file = "pydantic_core-2.10.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a5cb87bdc2e5f620693148b5f8f842d293cae46c5f15a1b1bf7ceeed324a740c"},
+ {file = "pydantic_core-2.10.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:25bd966103890ccfa028841a8f30cebcf5875eeac8c4bde4fe221364c92f0c9a"},
+ {file = "pydantic_core-2.10.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f323306d0556351735b54acbf82904fe30a27b6a7147153cbe6e19aaaa2aa429"},
+ {file = "pydantic_core-2.10.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0c27f38dc4fbf07b358b2bc90edf35e82d1703e22ff2efa4af4ad5de1b3833e7"},
+ {file = "pydantic_core-2.10.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f1365e032a477c1430cfe0cf2856679529a2331426f8081172c4a74186f1d595"},
+ {file = "pydantic_core-2.10.1-cp39-none-win32.whl", hash = "sha256:a1c311fd06ab3b10805abb72109f01a134019739bd3286b8ae1bc2fc4e50c07a"},
+ {file = "pydantic_core-2.10.1-cp39-none-win_amd64.whl", hash = "sha256:ae8a8843b11dc0b03b57b52793e391f0122e740de3df1474814c700d2622950a"},
+ {file = "pydantic_core-2.10.1-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:d43002441932f9a9ea5d6f9efaa2e21458221a3a4b417a14027a1d530201ef1b"},
+ {file = "pydantic_core-2.10.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:fcb83175cc4936a5425dde3356f079ae03c0802bbdf8ff82c035f8a54b333521"},
+ {file = "pydantic_core-2.10.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:962ed72424bf1f72334e2f1e61b68f16c0e596f024ca7ac5daf229f7c26e4208"},
+ {file = "pydantic_core-2.10.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2cf5bb4dd67f20f3bbc1209ef572a259027c49e5ff694fa56bed62959b41e1f9"},
+ {file = "pydantic_core-2.10.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e544246b859f17373bed915182ab841b80849ed9cf23f1f07b73b7c58baee5fb"},
+ {file = "pydantic_core-2.10.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:c0877239307b7e69d025b73774e88e86ce82f6ba6adf98f41069d5b0b78bd1bf"},
+ {file = "pydantic_core-2.10.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:53df009d1e1ba40f696f8995683e067e3967101d4bb4ea6f667931b7d4a01357"},
+ {file = "pydantic_core-2.10.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a1254357f7e4c82e77c348dabf2d55f1d14d19d91ff025004775e70a6ef40ada"},
+ {file = "pydantic_core-2.10.1-pp37-pypy37_pp73-macosx_10_7_x86_64.whl", hash = "sha256:524ff0ca3baea164d6d93a32c58ac79eca9f6cf713586fdc0adb66a8cdeab96a"},
+ {file = "pydantic_core-2.10.1-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f0ac9fb8608dbc6eaf17956bf623c9119b4db7dbb511650910a82e261e6600f"},
+ {file = "pydantic_core-2.10.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:320f14bd4542a04ab23747ff2c8a778bde727158b606e2661349557f0770711e"},
+ {file = "pydantic_core-2.10.1-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:63974d168b6233b4ed6a0046296803cb13c56637a7b8106564ab575926572a55"},
+ {file = "pydantic_core-2.10.1-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:417243bf599ba1f1fef2bb8c543ceb918676954734e2dcb82bf162ae9d7bd514"},
+ {file = "pydantic_core-2.10.1-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:dda81e5ec82485155a19d9624cfcca9be88a405e2857354e5b089c2a982144b2"},
+ {file = "pydantic_core-2.10.1-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:14cfbb00959259e15d684505263d5a21732b31248a5dd4941f73a3be233865b9"},
+ {file = "pydantic_core-2.10.1-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:631cb7415225954fdcc2a024119101946793e5923f6c4d73a5914d27eb3d3a05"},
+ {file = "pydantic_core-2.10.1-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:bec7dd208a4182e99c5b6c501ce0b1f49de2802448d4056091f8e630b28e9a52"},
+ {file = "pydantic_core-2.10.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:149b8a07712f45b332faee1a2258d8ef1fb4a36f88c0c17cb687f205c5dc6e7d"},
+ {file = "pydantic_core-2.10.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4d966c47f9dd73c2d32a809d2be529112d509321c5310ebf54076812e6ecd884"},
+ {file = "pydantic_core-2.10.1-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7eb037106f5c6b3b0b864ad226b0b7ab58157124161d48e4b30c4a43fef8bc4b"},
+ {file = "pydantic_core-2.10.1-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:154ea7c52e32dce13065dbb20a4a6f0cc012b4f667ac90d648d36b12007fa9f7"},
+ {file = "pydantic_core-2.10.1-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e562617a45b5a9da5be4abe72b971d4f00bf8555eb29bb91ec2ef2be348cd132"},
+ {file = "pydantic_core-2.10.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:f23b55eb5464468f9e0e9a9935ce3ed2a870608d5f534025cd5536bca25b1402"},
+ {file = "pydantic_core-2.10.1-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:e9121b4009339b0f751955baf4543a0bfd6bc3f8188f8056b1a25a2d45099934"},
+ {file = "pydantic_core-2.10.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:0523aeb76e03f753b58be33b26540880bac5aa54422e4462404c432230543f33"},
+ {file = "pydantic_core-2.10.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e0e2959ef5d5b8dc9ef21e1a305a21a36e254e6a34432d00c72a92fdc5ecda5"},
+ {file = "pydantic_core-2.10.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da01bec0a26befab4898ed83b362993c844b9a607a86add78604186297eb047e"},
+ {file = "pydantic_core-2.10.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f2e9072d71c1f6cfc79a36d4484c82823c560e6f5599c43c1ca6b5cdbd54f881"},
+ {file = "pydantic_core-2.10.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f36a3489d9e28fe4b67be9992a23029c3cec0babc3bd9afb39f49844a8c721c5"},
+ {file = "pydantic_core-2.10.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f64f82cc3443149292b32387086d02a6c7fb39b8781563e0ca7b8d7d9cf72bd7"},
+ {file = "pydantic_core-2.10.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:b4a6db486ac8e99ae696e09efc8b2b9fea67b63c8f88ba7a1a16c24a057a0776"},
+ {file = "pydantic_core-2.10.1.tar.gz", hash = "sha256:0f8682dbdd2f67f8e1edddcbffcc29f60a6182b4901c367fc8c1c40d30bb0a82"},
+]
+
+[package.dependencies]
+typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0"
+
+[[package]]
+name = "pyrsistent"
+version = "0.19.3"
+description = "Persistent/Functional/Immutable data structures"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "pyrsistent-0.19.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:20460ac0ea439a3e79caa1dbd560344b64ed75e85d8703943e0b66c2a6150e4a"},
+ {file = "pyrsistent-0.19.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c18264cb84b5e68e7085a43723f9e4c1fd1d935ab240ce02c0324a8e01ccb64"},
+ {file = "pyrsistent-0.19.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b774f9288dda8d425adb6544e5903f1fb6c273ab3128a355c6b972b7df39dcf"},
+ {file = "pyrsistent-0.19.3-cp310-cp310-win32.whl", hash = "sha256:5a474fb80f5e0d6c9394d8db0fc19e90fa540b82ee52dba7d246a7791712f74a"},
+ {file = "pyrsistent-0.19.3-cp310-cp310-win_amd64.whl", hash = "sha256:49c32f216c17148695ca0e02a5c521e28a4ee6c5089f97e34fe24163113722da"},
+ {file = "pyrsistent-0.19.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f0774bf48631f3a20471dd7c5989657b639fd2d285b861237ea9e82c36a415a9"},
+ {file = "pyrsistent-0.19.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ab2204234c0ecd8b9368dbd6a53e83c3d4f3cab10ecaf6d0e772f456c442393"},
+ {file = "pyrsistent-0.19.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e42296a09e83028b3476f7073fcb69ffebac0e66dbbfd1bd847d61f74db30f19"},
+ {file = "pyrsistent-0.19.3-cp311-cp311-win32.whl", hash = "sha256:64220c429e42a7150f4bfd280f6f4bb2850f95956bde93c6fda1b70507af6ef3"},
+ {file = "pyrsistent-0.19.3-cp311-cp311-win_amd64.whl", hash = "sha256:016ad1afadf318eb7911baa24b049909f7f3bb2c5b1ed7b6a8f21db21ea3faa8"},
+ {file = "pyrsistent-0.19.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c4db1bd596fefd66b296a3d5d943c94f4fac5bcd13e99bffe2ba6a759d959a28"},
+ {file = "pyrsistent-0.19.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aeda827381f5e5d65cced3024126529ddc4289d944f75e090572c77ceb19adbf"},
+ {file = "pyrsistent-0.19.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:42ac0b2f44607eb92ae88609eda931a4f0dfa03038c44c772e07f43e738bcac9"},
+ {file = "pyrsistent-0.19.3-cp37-cp37m-win32.whl", hash = "sha256:e8f2b814a3dc6225964fa03d8582c6e0b6650d68a232df41e3cc1b66a5d2f8d1"},
+ {file = "pyrsistent-0.19.3-cp37-cp37m-win_amd64.whl", hash = "sha256:c9bb60a40a0ab9aba40a59f68214eed5a29c6274c83b2cc206a359c4a89fa41b"},
+ {file = "pyrsistent-0.19.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a2471f3f8693101975b1ff85ffd19bb7ca7dd7c38f8a81701f67d6b4f97b87d8"},
+ {file = "pyrsistent-0.19.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc5d149f31706762c1f8bda2e8c4f8fead6e80312e3692619a75301d3dbb819a"},
+ {file = "pyrsistent-0.19.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3311cb4237a341aa52ab8448c27e3a9931e2ee09561ad150ba94e4cfd3fc888c"},
+ {file = "pyrsistent-0.19.3-cp38-cp38-win32.whl", hash = "sha256:f0e7c4b2f77593871e918be000b96c8107da48444d57005b6a6bc61fb4331b2c"},
+ {file = "pyrsistent-0.19.3-cp38-cp38-win_amd64.whl", hash = "sha256:c147257a92374fde8498491f53ffa8f4822cd70c0d85037e09028e478cababb7"},
+ {file = "pyrsistent-0.19.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b735e538f74ec31378f5a1e3886a26d2ca6351106b4dfde376a26fc32a044edc"},
+ {file = "pyrsistent-0.19.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99abb85579e2165bd8522f0c0138864da97847875ecbd45f3e7e2af569bfc6f2"},
+ {file = "pyrsistent-0.19.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3a8cb235fa6d3fd7aae6a4f1429bbb1fec1577d978098da1252f0489937786f3"},
+ {file = "pyrsistent-0.19.3-cp39-cp39-win32.whl", hash = "sha256:c74bed51f9b41c48366a286395c67f4e894374306b197e62810e0fdaf2364da2"},
+ {file = "pyrsistent-0.19.3-cp39-cp39-win_amd64.whl", hash = "sha256:878433581fc23e906d947a6814336eee031a00e6defba224234169ae3d3d6a98"},
+ {file = "pyrsistent-0.19.3-py3-none-any.whl", hash = "sha256:ccf0d6bd208f8111179f0c26fdf84ed7c3891982f2edaeae7422575f47e66b64"},
+ {file = "pyrsistent-0.19.3.tar.gz", hash = "sha256:1a2994773706bbb4995c31a97bc94f1418314923bd1048c6d964837040376440"},
+]
+
+[[package]]
+name = "pysnooper"
+version = "1.2.0"
+description = "A poor man's debugger for Python."
+optional = false
+python-versions = "*"
+files = [
+ {file = "PySnooper-1.2.0-py2.py3-none-any.whl", hash = "sha256:aa859aa9a746cffc1f35e4ee469d49c3cc5185b5fc0c571feb3af3c94d2eb625"},
+ {file = "PySnooper-1.2.0.tar.gz", hash = "sha256:810669e162a250a066d8662e573adbc5af770e937c5b5578f28bb7355d1c859b"},
+]
+
+[package.extras]
+tests = ["pytest"]
+
+[[package]]
+name = "pyyaml"
+version = "6.0.1"
+description = "YAML parser and emitter for Python"
+optional = false
+python-versions = ">=3.6"
+files = [
+ {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"},
+ {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"},
+ {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"},
+ {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"},
+ {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"},
+ {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"},
+ {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"},
+ {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"},
+ {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"},
+ {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"},
+ {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"},
+ {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"},
+ {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"},
+ {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"},
+ {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"},
+ {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"},
+ {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"},
+ {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"},
+ {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"},
+ {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"},
+ {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"},
+ {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"},
+ {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"},
+ {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"},
+ {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"},
+ {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"},
+ {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"},
+ {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"},
+ {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"},
+ {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"},
+ {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"},
+ {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"},
+ {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"},
+ {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"},
+ {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"},
+ {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"},
+ {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"},
+ {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"},
+ {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"},
+ {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"},
+]
+
+[[package]]
+name = "requests"
+version = "2.31.0"
+description = "Python HTTP for Humans."
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"},
+ {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"},
+]
+
+[package.dependencies]
+certifi = ">=2017.4.17"
+charset-normalizer = ">=2,<4"
+idna = ">=2.5,<4"
+urllib3 = ">=1.21.1,<3"
+
+[package.extras]
+socks = ["PySocks (>=1.5.6,!=1.5.7)"]
+use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
+
+[[package]]
+name = "rfc3339-validator"
+version = "0.1.4"
+description = "A pure python RFC3339 validator"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+files = [
+ {file = "rfc3339_validator-0.1.4-py2.py3-none-any.whl", hash = "sha256:24f6ec1eda14ef823da9e36ec7113124b39c04d50a4d3d3a3c2859577e7791fa"},
+ {file = "rfc3339_validator-0.1.4.tar.gz", hash = "sha256:138a2abdf93304ad60530167e51d2dfb9549521a836871b88d7f4695d0022f6b"},
+]
+
+[package.dependencies]
+six = "*"
+
+[[package]]
+name = "ruamel-yaml"
+version = "0.17.32"
+description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order"
+optional = false
+python-versions = ">=3"
+files = [
+ {file = "ruamel.yaml-0.17.32-py3-none-any.whl", hash = "sha256:23cd2ed620231677564646b0c6a89d138b6822a0d78656df7abda5879ec4f447"},
+ {file = "ruamel.yaml-0.17.32.tar.gz", hash = "sha256:ec939063761914e14542972a5cba6d33c23b0859ab6342f61cf070cfc600efc2"},
+]
+
+[package.dependencies]
+"ruamel.yaml.clib" = {version = ">=0.2.7", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.12\""}
+
+[package.extras]
+docs = ["ryd"]
+jinja2 = ["ruamel.yaml.jinja2 (>=0.2)"]
+
+[[package]]
+name = "ruamel-yaml-clib"
+version = "0.2.7"
+description = "C version of reader, parser and emitter for ruamel.yaml derived from libyaml"
+optional = false
+python-versions = ">=3.5"
+files = [
+ {file = "ruamel.yaml.clib-0.2.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d5859983f26d8cd7bb5c287ef452e8aacc86501487634573d260968f753e1d71"},
+ {file = "ruamel.yaml.clib-0.2.7-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:debc87a9516b237d0466a711b18b6ebeb17ba9f391eb7f91c649c5c4ec5006c7"},
+ {file = "ruamel.yaml.clib-0.2.7-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:df5828871e6648db72d1c19b4bd24819b80a755c4541d3409f0f7acd0f335c80"},
+ {file = "ruamel.yaml.clib-0.2.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:efa08d63ef03d079dcae1dfe334f6c8847ba8b645d08df286358b1f5293d24ab"},
+ {file = "ruamel.yaml.clib-0.2.7-cp310-cp310-win32.whl", hash = "sha256:763d65baa3b952479c4e972669f679fe490eee058d5aa85da483ebae2009d231"},
+ {file = "ruamel.yaml.clib-0.2.7-cp310-cp310-win_amd64.whl", hash = "sha256:d000f258cf42fec2b1bbf2863c61d7b8918d31ffee905da62dede869254d3b8a"},
+ {file = "ruamel.yaml.clib-0.2.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:045e0626baf1c52e5527bd5db361bc83180faaba2ff586e763d3d5982a876a9e"},
+ {file = "ruamel.yaml.clib-0.2.7-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:1a6391a7cabb7641c32517539ca42cf84b87b667bad38b78d4d42dd23e957c81"},
+ {file = "ruamel.yaml.clib-0.2.7-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:9c7617df90c1365638916b98cdd9be833d31d337dbcd722485597b43c4a215bf"},
+ {file = "ruamel.yaml.clib-0.2.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:41d0f1fa4c6830176eef5b276af04c89320ea616655d01327d5ce65e50575c94"},
+ {file = "ruamel.yaml.clib-0.2.7-cp311-cp311-win32.whl", hash = "sha256:f6d3d39611ac2e4f62c3128a9eed45f19a6608670c5a2f4f07f24e8de3441d38"},
+ {file = "ruamel.yaml.clib-0.2.7-cp311-cp311-win_amd64.whl", hash = "sha256:da538167284de58a52109a9b89b8f6a53ff8437dd6dc26d33b57bf6699153122"},
+ {file = "ruamel.yaml.clib-0.2.7-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:4b3a93bb9bc662fc1f99c5c3ea8e623d8b23ad22f861eb6fce9377ac07ad6072"},
+ {file = "ruamel.yaml.clib-0.2.7-cp36-cp36m-macosx_12_0_arm64.whl", hash = "sha256:a234a20ae07e8469da311e182e70ef6b199d0fbeb6c6cc2901204dd87fb867e8"},
+ {file = "ruamel.yaml.clib-0.2.7-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:15910ef4f3e537eea7fe45f8a5d19997479940d9196f357152a09031c5be59f3"},
+ {file = "ruamel.yaml.clib-0.2.7-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:370445fd795706fd291ab00c9df38a0caed0f17a6fb46b0f607668ecb16ce763"},
+ {file = "ruamel.yaml.clib-0.2.7-cp36-cp36m-win32.whl", hash = "sha256:ecdf1a604009bd35c674b9225a8fa609e0282d9b896c03dd441a91e5f53b534e"},
+ {file = "ruamel.yaml.clib-0.2.7-cp36-cp36m-win_amd64.whl", hash = "sha256:f34019dced51047d6f70cb9383b2ae2853b7fc4dce65129a5acd49f4f9256646"},
+ {file = "ruamel.yaml.clib-0.2.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2aa261c29a5545adfef9296b7e33941f46aa5bbd21164228e833412af4c9c75f"},
+ {file = "ruamel.yaml.clib-0.2.7-cp37-cp37m-macosx_12_0_arm64.whl", hash = "sha256:f01da5790e95815eb5a8a138508c01c758e5f5bc0ce4286c4f7028b8dd7ac3d0"},
+ {file = "ruamel.yaml.clib-0.2.7-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:40d030e2329ce5286d6b231b8726959ebbe0404c92f0a578c0e2482182e38282"},
+ {file = "ruamel.yaml.clib-0.2.7-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:c3ca1fbba4ae962521e5eb66d72998b51f0f4d0f608d3c0347a48e1af262efa7"},
+ {file = "ruamel.yaml.clib-0.2.7-cp37-cp37m-win32.whl", hash = "sha256:7bdb4c06b063f6fd55e472e201317a3bb6cdeeee5d5a38512ea5c01e1acbdd93"},
+ {file = "ruamel.yaml.clib-0.2.7-cp37-cp37m-win_amd64.whl", hash = "sha256:be2a7ad8fd8f7442b24323d24ba0b56c51219513cfa45b9ada3b87b76c374d4b"},
+ {file = "ruamel.yaml.clib-0.2.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:91a789b4aa0097b78c93e3dc4b40040ba55bef518f84a40d4442f713b4094acb"},
+ {file = "ruamel.yaml.clib-0.2.7-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:99e77daab5d13a48a4054803d052ff40780278240a902b880dd37a51ba01a307"},
+ {file = "ruamel.yaml.clib-0.2.7-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:3243f48ecd450eddadc2d11b5feb08aca941b5cd98c9b1db14b2fd128be8c697"},
+ {file = "ruamel.yaml.clib-0.2.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:8831a2cedcd0f0927f788c5bdf6567d9dc9cc235646a434986a852af1cb54b4b"},
+ {file = "ruamel.yaml.clib-0.2.7-cp38-cp38-win32.whl", hash = "sha256:3110a99e0f94a4a3470ff67fc20d3f96c25b13d24c6980ff841e82bafe827cac"},
+ {file = "ruamel.yaml.clib-0.2.7-cp38-cp38-win_amd64.whl", hash = "sha256:92460ce908546ab69770b2e576e4f99fbb4ce6ab4b245345a3869a0a0410488f"},
+ {file = "ruamel.yaml.clib-0.2.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5bc0667c1eb8f83a3752b71b9c4ba55ef7c7058ae57022dd9b29065186a113d9"},
+ {file = "ruamel.yaml.clib-0.2.7-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:4a4d8d417868d68b979076a9be6a38c676eca060785abaa6709c7b31593c35d1"},
+ {file = "ruamel.yaml.clib-0.2.7-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:bf9a6bc4a0221538b1a7de3ed7bca4c93c02346853f44e1cd764be0023cd3640"},
+ {file = "ruamel.yaml.clib-0.2.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:a7b301ff08055d73223058b5c46c55638917f04d21577c95e00e0c4d79201a6b"},
+ {file = "ruamel.yaml.clib-0.2.7-cp39-cp39-win32.whl", hash = "sha256:d5e51e2901ec2366b79f16c2299a03e74ba4531ddcfacc1416639c557aef0ad8"},
+ {file = "ruamel.yaml.clib-0.2.7-cp39-cp39-win_amd64.whl", hash = "sha256:184faeaec61dbaa3cace407cffc5819f7b977e75360e8d5ca19461cd851a5fc5"},
+ {file = "ruamel.yaml.clib-0.2.7.tar.gz", hash = "sha256:1f08fd5a2bea9c4180db71678e850b995d2a5f4537be0e94557668cf0f5f9497"},
+]
+
+[[package]]
+name = "ruff"
+version = "0.1.2"
+description = "An extremely fast Python linter, written in Rust."
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "ruff-0.1.2-py3-none-macosx_10_7_x86_64.whl", hash = "sha256:0d3ee66b825b713611f89aa35d16de984f76f26c50982a25d52cd0910dff3923"},
+ {file = "ruff-0.1.2-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:f85f850a320ff532b8f93e8d1da6a36ef03698c446357c8c43b46ef90bb321eb"},
+ {file = "ruff-0.1.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:809c6d4e45683696d19ca79e4c6bd3b2e9204fe9546923f2eb3b126ec314b0dc"},
+ {file = "ruff-0.1.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:46005e4abb268e93cad065244e17e2ea16b6fcb55a5c473f34fbc1fd01ae34cb"},
+ {file = "ruff-0.1.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10cdb302f519664d5e2cf954562ac86c9d20ca05855e5b5c2f9d542228f45da4"},
+ {file = "ruff-0.1.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:f89ebcbe57a1eab7d7b4ceb57ddf0af9ed13eae24e443a7c1dc078000bd8cc6b"},
+ {file = "ruff-0.1.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7344eaca057d4c32373c9c3a7afb7274f56040c225b6193dd495fcf69453b436"},
+ {file = "ruff-0.1.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dffa25f6e03c4950b6ac6f216bc0f98a4be9719cb0c5260c8e88d1bac36f1683"},
+ {file = "ruff-0.1.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42ddaea52cb7ba7c785e8593a7532866c193bc774fe570f0e4b1ccedd95b83c5"},
+ {file = "ruff-0.1.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a8533efda625bbec0bf27da2886bd641dae0c209104f6c39abc4be5b7b22de2a"},
+ {file = "ruff-0.1.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:b0b1b82221ba7c50e03b7a86b983157b5d3f4d8d4f16728132bdf02c6d651f77"},
+ {file = "ruff-0.1.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:6c1362eb9288f8cc95535294cb03bd4665c8cef86ec32745476a4e5c6817034c"},
+ {file = "ruff-0.1.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:ffa7ef5ded0563329a35bd5a1cfdae40f05a75c0cc2dd30f00b1320b1fb461fc"},
+ {file = "ruff-0.1.2-py3-none-win32.whl", hash = "sha256:6e8073f85e47072256e2e1909f1ae515cf61ff5a4d24730a63b8b4ac24b6704a"},
+ {file = "ruff-0.1.2-py3-none-win_amd64.whl", hash = "sha256:b836ddff662a45385948ee0878b0a04c3a260949905ad861a37b931d6ee1c210"},
+ {file = "ruff-0.1.2-py3-none-win_arm64.whl", hash = "sha256:b0c42d00db5639dbd5f7f9923c63648682dd197bf5de1151b595160c96172691"},
+ {file = "ruff-0.1.2.tar.gz", hash = "sha256:afd4785ae060ce6edcd52436d0c197628a918d6d09e3107a892a1bad6a4c6608"},
+]
+
+[[package]]
+name = "setuptools"
+version = "68.2.2"
+description = "Easily download, build, install, upgrade, and uninstall Python packages"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "setuptools-68.2.2-py3-none-any.whl", hash = "sha256:b454a35605876da60632df1a60f736524eb73cc47bbc9f3f1ef1b644de74fd2a"},
+ {file = "setuptools-68.2.2.tar.gz", hash = "sha256:4ac1475276d2f1c48684874089fefcd83bd7162ddaafb81fac866ba0db282a87"},
+]
+
+[package.extras]
+docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
+testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
+testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.1)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"]
+
+[[package]]
+name = "six"
+version = "1.16.0"
+description = "Python 2 and 3 compatibility utilities"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
+files = [
+ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
+ {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
+]
+
+[[package]]
+name = "sniffio"
+version = "1.3.0"
+description = "Sniff out which async library your code is running under"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"},
+ {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"},
+]
+
+[[package]]
+name = "toml"
+version = "0.10.2"
+description = "Python Library for Tom's Obvious, Minimal Language"
+optional = false
+python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
+files = [
+ {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
+ {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
+]
+
+[[package]]
+name = "typing-extensions"
+version = "4.7.1"
+description = "Backported and Experimental Type Hints for Python 3.7+"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "typing_extensions-4.7.1-py3-none-any.whl", hash = "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36"},
+ {file = "typing_extensions-4.7.1.tar.gz", hash = "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2"},
+]
+
+[[package]]
+name = "urllib3"
+version = "2.0.4"
+description = "HTTP library with thread-safe connection pooling, file post, and more."
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "urllib3-2.0.4-py3-none-any.whl", hash = "sha256:de7df1803967d2c2a98e4b11bb7d6bd9210474c46e8a0401514e3a42a75ebde4"},
+ {file = "urllib3-2.0.4.tar.gz", hash = "sha256:8d22f86aae8ef5e410d4f539fde9ce6b2113a001bb4d189e0aed70642d602b11"},
+]
+
+[package.extras]
+brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"]
+secure = ["certifi", "cryptography (>=1.9)", "idna (>=2.0.0)", "pyopenssl (>=17.1.0)", "urllib3-secure-extra"]
+socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"]
+zstd = ["zstandard (>=0.18.0)"]
+
+[[package]]
+name = "yarl"
+version = "1.9.2"
+description = "Yet another URL library"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "yarl-1.9.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8c2ad583743d16ddbdf6bb14b5cd76bf43b0d0006e918809d5d4ddf7bde8dd82"},
+ {file = "yarl-1.9.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:82aa6264b36c50acfb2424ad5ca537a2060ab6de158a5bd2a72a032cc75b9eb8"},
+ {file = "yarl-1.9.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c0c77533b5ed4bcc38e943178ccae29b9bcf48ffd1063f5821192f23a1bd27b9"},
+ {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee4afac41415d52d53a9833ebae7e32b344be72835bbb589018c9e938045a560"},
+ {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9bf345c3a4f5ba7f766430f97f9cc1320786f19584acc7086491f45524a551ac"},
+ {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a96c19c52ff442a808c105901d0bdfd2e28575b3d5f82e2f5fd67e20dc5f4ea"},
+ {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:891c0e3ec5ec881541f6c5113d8df0315ce5440e244a716b95f2525b7b9f3608"},
+ {file = "yarl-1.9.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c3a53ba34a636a256d767c086ceb111358876e1fb6b50dfc4d3f4951d40133d5"},
+ {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:566185e8ebc0898b11f8026447eacd02e46226716229cea8db37496c8cdd26e0"},
+ {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:2b0738fb871812722a0ac2154be1f049c6223b9f6f22eec352996b69775b36d4"},
+ {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:32f1d071b3f362c80f1a7d322bfd7b2d11e33d2adf395cc1dd4df36c9c243095"},
+ {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:e9fdc7ac0d42bc3ea78818557fab03af6181e076a2944f43c38684b4b6bed8e3"},
+ {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:56ff08ab5df8429901ebdc5d15941b59f6253393cb5da07b4170beefcf1b2528"},
+ {file = "yarl-1.9.2-cp310-cp310-win32.whl", hash = "sha256:8ea48e0a2f931064469bdabca50c2f578b565fc446f302a79ba6cc0ee7f384d3"},
+ {file = "yarl-1.9.2-cp310-cp310-win_amd64.whl", hash = "sha256:50f33040f3836e912ed16d212f6cc1efb3231a8a60526a407aeb66c1c1956dde"},
+ {file = "yarl-1.9.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:646d663eb2232d7909e6601f1a9107e66f9791f290a1b3dc7057818fe44fc2b6"},
+ {file = "yarl-1.9.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:aff634b15beff8902d1f918012fc2a42e0dbae6f469fce134c8a0dc51ca423bb"},
+ {file = "yarl-1.9.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a83503934c6273806aed765035716216cc9ab4e0364f7f066227e1aaea90b8d0"},
+ {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b25322201585c69abc7b0e89e72790469f7dad90d26754717f3310bfe30331c2"},
+ {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:22a94666751778629f1ec4280b08eb11815783c63f52092a5953faf73be24191"},
+ {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ec53a0ea2a80c5cd1ab397925f94bff59222aa3cf9c6da938ce05c9ec20428d"},
+ {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:159d81f22d7a43e6eabc36d7194cb53f2f15f498dbbfa8edc8a3239350f59fe7"},
+ {file = "yarl-1.9.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:832b7e711027c114d79dffb92576acd1bd2decc467dec60e1cac96912602d0e6"},
+ {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:95d2ecefbcf4e744ea952d073c6922e72ee650ffc79028eb1e320e732898d7e8"},
+ {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d4e2c6d555e77b37288eaf45b8f60f0737c9efa3452c6c44626a5455aeb250b9"},
+ {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:783185c75c12a017cc345015ea359cc801c3b29a2966c2655cd12b233bf5a2be"},
+ {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:b8cc1863402472f16c600e3e93d542b7e7542a540f95c30afd472e8e549fc3f7"},
+ {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:822b30a0f22e588b32d3120f6d41e4ed021806418b4c9f0bc3048b8c8cb3f92a"},
+ {file = "yarl-1.9.2-cp311-cp311-win32.whl", hash = "sha256:a60347f234c2212a9f0361955007fcf4033a75bf600a33c88a0a8e91af77c0e8"},
+ {file = "yarl-1.9.2-cp311-cp311-win_amd64.whl", hash = "sha256:be6b3fdec5c62f2a67cb3f8c6dbf56bbf3f61c0f046f84645cd1ca73532ea051"},
+ {file = "yarl-1.9.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:38a3928ae37558bc1b559f67410df446d1fbfa87318b124bf5032c31e3447b74"},
+ {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac9bb4c5ce3975aeac288cfcb5061ce60e0d14d92209e780c93954076c7c4367"},
+ {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3da8a678ca8b96c8606bbb8bfacd99a12ad5dd288bc6f7979baddd62f71c63ef"},
+ {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:13414591ff516e04fcdee8dc051c13fd3db13b673c7a4cb1350e6b2ad9639ad3"},
+ {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf74d08542c3a9ea97bb8f343d4fcbd4d8f91bba5ec9d5d7f792dbe727f88938"},
+ {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e7221580dc1db478464cfeef9b03b95c5852cc22894e418562997df0d074ccc"},
+ {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:494053246b119b041960ddcd20fd76224149cfea8ed8777b687358727911dd33"},
+ {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:52a25809fcbecfc63ac9ba0c0fb586f90837f5425edfd1ec9f3372b119585e45"},
+ {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:e65610c5792870d45d7b68c677681376fcf9cc1c289f23e8e8b39c1485384185"},
+ {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:1b1bba902cba32cdec51fca038fd53f8beee88b77efc373968d1ed021024cc04"},
+ {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:662e6016409828ee910f5d9602a2729a8a57d74b163c89a837de3fea050c7582"},
+ {file = "yarl-1.9.2-cp37-cp37m-win32.whl", hash = "sha256:f364d3480bffd3aa566e886587eaca7c8c04d74f6e8933f3f2c996b7f09bee1b"},
+ {file = "yarl-1.9.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6a5883464143ab3ae9ba68daae8e7c5c95b969462bbe42e2464d60e7e2698368"},
+ {file = "yarl-1.9.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5610f80cf43b6202e2c33ba3ec2ee0a2884f8f423c8f4f62906731d876ef4fac"},
+ {file = "yarl-1.9.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b9a4e67ad7b646cd6f0938c7ebfd60e481b7410f574c560e455e938d2da8e0f4"},
+ {file = "yarl-1.9.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:83fcc480d7549ccebe9415d96d9263e2d4226798c37ebd18c930fce43dfb9574"},
+ {file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5fcd436ea16fee7d4207c045b1e340020e58a2597301cfbcfdbe5abd2356c2fb"},
+ {file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84e0b1599334b1e1478db01b756e55937d4614f8654311eb26012091be109d59"},
+ {file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3458a24e4ea3fd8930e934c129b676c27452e4ebda80fbe47b56d8c6c7a63a9e"},
+ {file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:838162460b3a08987546e881a2bfa573960bb559dfa739e7800ceeec92e64417"},
+ {file = "yarl-1.9.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f4e2d08f07a3d7d3e12549052eb5ad3eab1c349c53ac51c209a0e5991bbada78"},
+ {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:de119f56f3c5f0e2fb4dee508531a32b069a5f2c6e827b272d1e0ff5ac040333"},
+ {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:149ddea5abf329752ea5051b61bd6c1d979e13fbf122d3a1f9f0c8be6cb6f63c"},
+ {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:674ca19cbee4a82c9f54e0d1eee28116e63bc6fd1e96c43031d11cbab8b2afd5"},
+ {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:9b3152f2f5677b997ae6c804b73da05a39daa6a9e85a512e0e6823d81cdad7cc"},
+ {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5415d5a4b080dc9612b1b63cba008db84e908b95848369aa1da3686ae27b6d2b"},
+ {file = "yarl-1.9.2-cp38-cp38-win32.whl", hash = "sha256:f7a3d8146575e08c29ed1cd287068e6d02f1c7bdff8970db96683b9591b86ee7"},
+ {file = "yarl-1.9.2-cp38-cp38-win_amd64.whl", hash = "sha256:63c48f6cef34e6319a74c727376e95626f84ea091f92c0250a98e53e62c77c72"},
+ {file = "yarl-1.9.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:75df5ef94c3fdc393c6b19d80e6ef1ecc9ae2f4263c09cacb178d871c02a5ba9"},
+ {file = "yarl-1.9.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c027a6e96ef77d401d8d5a5c8d6bc478e8042f1e448272e8d9752cb0aff8b5c8"},
+ {file = "yarl-1.9.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f3b078dbe227f79be488ffcfc7a9edb3409d018e0952cf13f15fd6512847f3f7"},
+ {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59723a029760079b7d991a401386390c4be5bfec1e7dd83e25a6a0881859e716"},
+ {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b03917871bf859a81ccb180c9a2e6c1e04d2f6a51d953e6a5cdd70c93d4e5a2a"},
+ {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c1012fa63eb6c032f3ce5d2171c267992ae0c00b9e164efe4d73db818465fac3"},
+ {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a74dcbfe780e62f4b5a062714576f16c2f3493a0394e555ab141bf0d746bb955"},
+ {file = "yarl-1.9.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8c56986609b057b4839968ba901944af91b8e92f1725d1a2d77cbac6972b9ed1"},
+ {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2c315df3293cd521033533d242d15eab26583360b58f7ee5d9565f15fee1bef4"},
+ {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:b7232f8dfbd225d57340e441d8caf8652a6acd06b389ea2d3222b8bc89cbfca6"},
+ {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:53338749febd28935d55b41bf0bcc79d634881195a39f6b2f767870b72514caf"},
+ {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:066c163aec9d3d073dc9ffe5dd3ad05069bcb03fcaab8d221290ba99f9f69ee3"},
+ {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8288d7cd28f8119b07dd49b7230d6b4562f9b61ee9a4ab02221060d21136be80"},
+ {file = "yarl-1.9.2-cp39-cp39-win32.whl", hash = "sha256:b124e2a6d223b65ba8768d5706d103280914d61f5cae3afbc50fc3dfcc016623"},
+ {file = "yarl-1.9.2-cp39-cp39-win_amd64.whl", hash = "sha256:61016e7d582bc46a5378ffdd02cd0314fb8ba52f40f9cf4d9a5e7dbef88dee18"},
+ {file = "yarl-1.9.2.tar.gz", hash = "sha256:04ab9d4b9f587c06d801c2abfe9317b77cdf996c65a90d5e84ecc45010823571"},
+]
+
+[package.dependencies]
+idna = ">=2.0"
+multidict = ">=4.0"
+
+[metadata]
+lock-version = "2.0"
+python-versions = "^3.11"
+content-hash = "d70d80d07934230ece43c57b5fbf6318dd2a46d59d97e28e98d8b16a777d696e"
diff --git a/libs/hash-graph-client/python/poetry.toml b/libs/hash-graph-client/python/poetry.toml
new file mode 100644
index 0000000..a5f04e6
--- /dev/null
+++ b/libs/hash-graph-client/python/poetry.toml
@@ -0,0 +1,3 @@
+[virtualenvs]
+name = ".venv"
+in-project = true
diff --git a/libs/hash-graph-client/python/pyproject.toml b/libs/hash-graph-client/python/pyproject.toml
new file mode 100644
index 0000000..a9fdc63
--- /dev/null
+++ b/libs/hash-graph-client/python/pyproject.toml
@@ -0,0 +1,91 @@
+[tool.poetry]
+name = "hash-graph-client"
+version = "0.0.0"
+description = "OpenAPI client for the HASH Graph API"
+license = "AGPL-3.0-only"
+authors = ["HASH"]
+readme = "README.md"
+packages = [{ include = "graph_client" }]
+
+[tool.poetry.dependencies]
+python = "^3.11"
+
+pydantic = "^2.4.2"
+httpx = "^0.25.0"
+yarl = "^1.9.2"
+
+[tool.poetry.group.dev.dependencies]
+setuptools = "^68.2.2"
+datamodel-code-generator = "^0.22.1"
+
+[tool.poetry.group.lint-tools.dependencies]
+black = "^23.10.1"
+ruff = "^0.1.2"
+mypy = "^1.6.1"
+
+
+[tool.black]
+target-version = ['py311']
+preview = true
+extend-exclude = '''
+(
+ ^/graph_client/models.py
+)
+'''
+
+[tool.ruff]
+select = ["ALL"]
+ignore = [
+ "D203", # this conflicts with `D211`
+ "D213", # this conflicts with `D212`
+ "D401", # Relates to PEP-257 but also conflicts with Google Python Style Guide, generally gets in the way
+ "ANN101", # most type checkers infer the type of `self` automatically
+ "ANN102", # most type checkers infer the type of `cls` automatically
+ "FIX002", # TODOs should be fixed, not removed
+ "TD002", # We don't add authors to TODO strings
+ "PLC0414", # Conflicts with `no_implicit_reexport = true`
+]
+
+target-version = "py311"
+
+extend-exclude = [
+ "graph_client/models.py"
+]
+
+[tool.mypy]
+plugins = "pydantic.mypy"
+
+disallow_untyped_defs = true
+disallow_any_unimported = true
+no_implicit_optional = true
+check_untyped_defs = true
+no_implicit_reexport = true
+strict_optional = true
+show_error_codes = true
+show_traceback = true
+strict_equality = true
+
+# warnings
+warn_unused_configs = true
+warn_return_any = true
+warn_incomplete_stub = true
+warn_unreachable = true
+warn_redundant_casts = true
+warn_unused_ignores = true
+
+# error messages
+pretty = true
+color_output = true
+error_summary = true
+incremental = true
+
+[tool.pydantic-mypy]
+init_forbid_extra = true
+init_typed = true
+warn_required_dynamic_aliases = true
+warn_untyped_fields = true
+
+
+[build-system]
+requires = ["poetry-core"]
+build-backend = "poetry.core.masonry.api"
diff --git a/libs/hash-graph-client/python/scripts/aliases.json b/libs/hash-graph-client/python/scripts/aliases.json
new file mode 100644
index 0000000..cb46eb8
--- /dev/null
+++ b/libs/hash-graph-client/python/scripts/aliases.json
@@ -0,0 +1,4 @@
+{
+ "$id": "identifier",
+ "$schema": "schema_url"
+}
diff --git a/libs/hash-graph-client/python/scripts/codegen.sh b/libs/hash-graph-client/python/scripts/codegen.sh
new file mode 100755
index 0000000..fc0facd
--- /dev/null
+++ b/libs/hash-graph-client/python/scripts/codegen.sh
@@ -0,0 +1,56 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+# Thanks to: https://stackoverflow.com/a/246128/9077988
+SOURCE=${BASH_SOURCE[0]}
+while [ -L "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
+ DIR=$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )
+ SOURCE=$(readlink "$SOURCE")
+ [[ $SOURCE != /* ]] && SOURCE=$DIR/$SOURCE # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
+done
+DIR=$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )
+
+SPEC_DIR="$DIR/../../../../../apps/hash-graph/openapi"
+SPEC="$SPEC_DIR/openapi.json"
+
+# cd up to the package directory (needed for poetry and yarn)
+cd "$DIR/.."
+
+poetry run datamodel-codegen --version
+
+# Take the specification and bundle all models into a single file
+yarn run redocly bundle --format=json "$SPEC" -o "$DIR/openapi.bundle.json"
+
+# we need to do a bit of plumbing, this includes:
+# merging `PropertyObjectReference` from `models/shared.json` into components.schemas, renaming all `#/definitions/` to `#/components/schemas/` (redocly ignores `x-patternProperties` and `patternProeprties`)
+# renaming `x-patternProperties` to `patternProperties`.
+SHARED_MODELS="$SPEC_DIR/models/shared.json"
+jq -s '
+ .[0].components.schemas.PropertyObjectReference = .[1].definitions.PropertyObjectReference
+ | .[0]
+ | walk( if type == "string" then sub("^#/definitions/"; "#/components/schemas/") else . end )
+ | walk( if type == "object" and has("x-patternProperties") then .patternProperties = .["x-patternProperties"] | del(.["x-patternProperties"]) else . end )
+ ' "$DIR/openapi.bundle.json" "$SHARED_MODELS" > "$DIR/openapi.bundle.json.tmp"
+mv "$DIR/openapi.bundle.json.tmp" "$DIR/openapi.bundle.json"
+
+poetry run datamodel-codegen \
+ --input "$DIR/openapi.bundle.json" \
+ --output graph_client/models.py \
+ --output-model-type pydantic_v2.BaseModel \
+ --input-file-type openapi \
+ --use-standard-collections \
+ --use-union-operator \
+ --target-python-version 3.11 \
+ --use-schema-description \
+ --snake-case-field \
+ --disable-timestamp \
+ --enable-version-header \
+ --enum-field-as-literal one \
+ --use-double-quotes \
+ --field-constraints \
+ --allow-population-by-field-name \
+ --strict-nullable \
+ --use-title-as-name \
+ --aliases "$DIR/aliases.json"
+
+poetry run python "$DIR/rebase.py"
diff --git a/libs/hash-graph-client/python/scripts/rebase.py b/libs/hash-graph-client/python/scripts/rebase.py
new file mode 100644
index 0000000..d6574ac
--- /dev/null
+++ b/libs/hash-graph-client/python/scripts/rebase.py
@@ -0,0 +1,37 @@
+# use AST transformations to rewrite the `models.py`, so that
+# A) `models.py` imports `schema.py`
+# B) `models.py` `EntityType`, `PropertyType` and `DataType`
+# inherit from `OntologyTypeSchema`
+
+import ast
+from pathlib import Path
+
+DIRECTORY = Path(__file__).parent.parent
+
+import_statement = ast.ImportFrom(
+ module="graph_client.schema", names=[(ast.alias(name="OntologyTypeSchema"))]
+)
+
+
+def remap(class_def: ast.ClassDef) -> ast.ClassDef:
+ """Remap the class base class to `OntologyTypeSchema`."""
+ if class_def.name not in ("EntityType", "PropertyType", "DataType"):
+ return class_def
+
+ class_def.bases = [ast.Name(id="OntologyTypeSchema", ctx=ast.Load())]
+ return class_def
+
+
+models = DIRECTORY / "graph_client" / "models.py"
+
+contents = models.read_text()
+tree = ast.parse(contents)
+
+for node in ast.walk(tree):
+ if isinstance(node, ast.ClassDef):
+ remap(node)
+
+tree.body.insert(1, import_statement)
+
+contents = ast.unparse(ast.fix_missing_locations(tree))
+models.write_text(contents)
diff --git a/libs/hash-graph-client/python/turbo.json b/libs/hash-graph-client/python/turbo.json
new file mode 100644
index 0000000..25d03f5
--- /dev/null
+++ b/libs/hash-graph-client/python/turbo.json
@@ -0,0 +1,49 @@
+{
+ "extends": ["//"],
+ "pipeline": {
+ "codegen": {
+ "inputs": [
+ "../../../../apps/hash-graph/openapi/openapi.json",
+ "./scripts/codegen.sh"
+ ],
+ "outputs": ["./graph_client/models.py"]
+ },
+ "build": {
+ "dependsOn": ["^build", "codegen"],
+ "inputs": ["./**/*.py", "pyproject.toml", "poetry.lock", "LICENSE*"],
+ "outputs": ["dist/**"]
+ },
+ "poetry:venv": {
+ "cache": false
+ },
+ "poetry:install": {
+ "cache": false
+ },
+ "poetry:install-production": {
+ "cache": false
+ },
+ "fix:black": {
+ "inputs": ["./**/*.py", "pyproject.toml"]
+ },
+ "fix:ruff": {
+ "inputs": ["./**/*.py", "pyproject.toml"]
+ },
+ "fix:lock-files": {
+ "inputs": ["poetry.lock", "pyproject.toml"],
+ "outputs": ["poetry.lock"]
+ },
+ "lint:black": {
+ "inputs": ["./**/*.py", "pyproject.toml"]
+ },
+ "lint:lock-files": {
+ "inputs": ["poetry.lock", "pyproject.toml"]
+ },
+ "lint:ruff": {
+ "inputs": ["./**/*.py", "pyproject.toml"]
+ },
+ "lint:mypy": {
+ "dependsOn": ["build"],
+ "inputs": ["./**/*.py", "pyproject.toml"]
+ }
+ }
+}
diff --git a/libs/hash-graph-client/python/yarn.lock b/libs/hash-graph-client/python/yarn.lock
new file mode 100644
index 0000000..d364a6a
--- /dev/null
+++ b/libs/hash-graph-client/python/yarn.lock
@@ -0,0 +1,314 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"@apidevtools/json-schema-ref-parser@9.0.6":
+ version "9.0.6"
+ resolved "https://registry.yarnpkg.com/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.0.6.tgz#5d9000a3ac1fd25404da886da6b266adcd99cf1c"
+ integrity sha512-M3YgsLjI0lZxvrpeGVk9Ap032W6TPQkH6pRAZz81Ac3WUNF79VQooAFnp8umjvVzUmD93NkogxEwbSce7qMsUg==
+ dependencies:
+ "@jsdevtools/ono" "^7.1.3"
+ call-me-maybe "^1.0.1"
+ js-yaml "^3.13.1"
+
+"@apidevtools/openapi-schemas@^2.1.0":
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/@apidevtools/openapi-schemas/-/openapi-schemas-2.1.0.tgz#9fa08017fb59d80538812f03fc7cac5992caaa17"
+ integrity sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==
+
+"@apidevtools/swagger-cli@4.0.4":
+ version "4.0.4"
+ resolved "https://registry.yarnpkg.com/@apidevtools/swagger-cli/-/swagger-cli-4.0.4.tgz#c645c291f56e4add583111aca9edeee23d60fa10"
+ integrity sha512-hdDT3B6GLVovCsRZYDi3+wMcB1HfetTU20l2DC8zD3iFRNMC6QNAZG5fo/6PYeHWBEv7ri4MvnlKodhNB0nt7g==
+ dependencies:
+ "@apidevtools/swagger-parser" "^10.0.1"
+ chalk "^4.1.0"
+ js-yaml "^3.14.0"
+ yargs "^15.4.1"
+
+"@apidevtools/swagger-methods@^3.0.2":
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/@apidevtools/swagger-methods/-/swagger-methods-3.0.2.tgz#b789a362e055b0340d04712eafe7027ddc1ac267"
+ integrity sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==
+
+"@apidevtools/swagger-parser@^10.0.1":
+ version "10.1.0"
+ resolved "https://registry.yarnpkg.com/@apidevtools/swagger-parser/-/swagger-parser-10.1.0.tgz#a987d71e5be61feb623203be0c96e5985b192ab6"
+ integrity sha512-9Kt7EuS/7WbMAUv2gSziqjvxwDbFSg3Xeyfuj5laUODX8o/k/CpsAKiQ8W7/R88eXFTMbJYg6+7uAmOWNKmwnw==
+ dependencies:
+ "@apidevtools/json-schema-ref-parser" "9.0.6"
+ "@apidevtools/openapi-schemas" "^2.1.0"
+ "@apidevtools/swagger-methods" "^3.0.2"
+ "@jsdevtools/ono" "^7.1.3"
+ ajv "^8.6.3"
+ ajv-draft-04 "^1.0.0"
+ call-me-maybe "^1.0.1"
+
+"@jsdevtools/ono@^7.1.3":
+ version "7.1.3"
+ resolved "https://registry.yarnpkg.com/@jsdevtools/ono/-/ono-7.1.3.tgz#9df03bbd7c696a5c58885c34aa06da41c8543796"
+ integrity sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==
+
+ajv-draft-04@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz#3b64761b268ba0b9e668f0b41ba53fce0ad77fc8"
+ integrity sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==
+
+ajv@^8.6.3:
+ version "8.12.0"
+ resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.12.0.tgz#d1a0527323e22f53562c567c00991577dfbe19d1"
+ integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==
+ dependencies:
+ fast-deep-equal "^3.1.1"
+ json-schema-traverse "^1.0.0"
+ require-from-string "^2.0.2"
+ uri-js "^4.2.2"
+
+ansi-regex@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
+ integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
+
+ansi-styles@^4.0.0, ansi-styles@^4.1.0:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
+ integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
+ dependencies:
+ color-convert "^2.0.1"
+
+argparse@^1.0.7:
+ version "1.0.10"
+ resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
+ integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==
+ dependencies:
+ sprintf-js "~1.0.2"
+
+call-me-maybe@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.2.tgz#03f964f19522ba643b1b0693acb9152fe2074baa"
+ integrity sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==
+
+camelcase@^5.0.0:
+ version "5.3.1"
+ resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
+ integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
+
+chalk@^4.1.0:
+ version "4.1.2"
+ resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
+ integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
+ dependencies:
+ ansi-styles "^4.1.0"
+ supports-color "^7.1.0"
+
+cliui@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1"
+ integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==
+ dependencies:
+ string-width "^4.2.0"
+ strip-ansi "^6.0.0"
+ wrap-ansi "^6.2.0"
+
+color-convert@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
+ integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
+ dependencies:
+ color-name "~1.1.4"
+
+color-name@~1.1.4:
+ version "1.1.4"
+ resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
+ integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
+
+decamelize@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
+ integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==
+
+emoji-regex@^8.0.0:
+ version "8.0.0"
+ resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
+ integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
+
+esprima@^4.0.0:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
+ integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
+
+fast-deep-equal@^3.1.1:
+ version "3.1.3"
+ resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
+ integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
+
+find-up@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19"
+ integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==
+ dependencies:
+ locate-path "^5.0.0"
+ path-exists "^4.0.0"
+
+get-caller-file@^2.0.1:
+ version "2.0.5"
+ resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
+ integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
+
+has-flag@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
+ integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
+
+is-fullwidth-code-point@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d"
+ integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
+
+js-yaml@^3.13.1, js-yaml@^3.14.0:
+ version "3.14.1"
+ resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537"
+ integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==
+ dependencies:
+ argparse "^1.0.7"
+ esprima "^4.0.0"
+
+json-schema-traverse@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2"
+ integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==
+
+locate-path@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0"
+ integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==
+ dependencies:
+ p-locate "^4.1.0"
+
+p-limit@^2.2.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1"
+ integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==
+ dependencies:
+ p-try "^2.0.0"
+
+p-locate@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07"
+ integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==
+ dependencies:
+ p-limit "^2.2.0"
+
+p-try@^2.0.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
+ integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
+
+path-exists@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
+ integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==
+
+punycode@^2.1.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f"
+ integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==
+
+require-directory@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
+ integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==
+
+require-from-string@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909"
+ integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==
+
+require-main-filename@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b"
+ integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==
+
+set-blocking@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
+ integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==
+
+sprintf-js@~1.0.2:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
+ integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==
+
+string-width@^4.1.0, string-width@^4.2.0:
+ version "4.2.3"
+ resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
+ integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
+ dependencies:
+ emoji-regex "^8.0.0"
+ is-fullwidth-code-point "^3.0.0"
+ strip-ansi "^6.0.1"
+
+strip-ansi@^6.0.0, strip-ansi@^6.0.1:
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
+ integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
+ dependencies:
+ ansi-regex "^5.0.1"
+
+supports-color@^7.1.0:
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
+ integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
+ dependencies:
+ has-flag "^4.0.0"
+
+uri-js@^4.2.2:
+ version "4.4.1"
+ resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e"
+ integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==
+ dependencies:
+ punycode "^2.1.0"
+
+which-module@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.1.tgz#776b1fe35d90aebe99e8ac15eb24093389a4a409"
+ integrity sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==
+
+wrap-ansi@^6.2.0:
+ version "6.2.0"
+ resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53"
+ integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==
+ dependencies:
+ ansi-styles "^4.0.0"
+ string-width "^4.1.0"
+ strip-ansi "^6.0.0"
+
+y18n@^4.0.0:
+ version "4.0.3"
+ resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf"
+ integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==
+
+yargs-parser@^18.1.2:
+ version "18.1.3"
+ resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0"
+ integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==
+ dependencies:
+ camelcase "^5.0.0"
+ decamelize "^1.2.0"
+
+yargs@^15.4.1:
+ version "15.4.1"
+ resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8"
+ integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==
+ dependencies:
+ cliui "^6.0.0"
+ decamelize "^1.2.0"
+ find-up "^4.1.0"
+ get-caller-file "^2.0.1"
+ require-directory "^2.1.1"
+ require-main-filename "^2.0.0"
+ set-blocking "^2.0.0"
+ string-width "^4.2.0"
+ which-module "^2.0.0"
+ y18n "^4.0.0"
+ yargs-parser "^18.1.2"
diff --git a/libs/hash-graph-sdk/python/.gitattributes b/libs/hash-graph-sdk/python/.gitattributes
new file mode 100644
index 0000000..d03f222
--- /dev/null
+++ b/libs/hash-graph-sdk/python/.gitattributes
@@ -0,0 +1,2 @@
+./graph_sdk/filter/path.py linguist-generate=true
+./graph_sdk/client/blocking.py linguist-generate=true
diff --git a/libs/hash-graph-sdk/python/LICENSE.md b/libs/hash-graph-sdk/python/LICENSE.md
new file mode 100644
index 0000000..9a70d79
--- /dev/null
+++ b/libs/hash-graph-sdk/python/LICENSE.md
@@ -0,0 +1,606 @@
+# GNU Affero General Public License
+
+_Version 3, 19 November 2007_
+_Copyright © 2007 Free Software Foundation, Inc. <>_
+
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+
+## Preamble
+
+The GNU Affero General Public License is a free, copyleft license for
+software and other kinds of works, specifically designed to ensure
+cooperation with the community in the case of network server software.
+
+The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+our General Public Licenses are intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.
+
+When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+Developers that use our General Public Licenses protect your rights
+with two steps: **(1)** assert copyright on the software, and **(2)** offer
+you this License which gives you legal permission to copy, distribute
+and/or modify the software.
+
+A secondary benefit of defending all users' freedom is that
+improvements made in alternate versions of the program, if they
+receive widespread use, become available for other developers to
+incorporate. Many developers of free software are heartened and
+encouraged by the resulting cooperation. However, in the case of
+software used on network servers, this result may fail to come about.
+The GNU General Public License permits making a modified version and
+letting the public access it on a server without ever releasing its
+source code to the public.
+
+The GNU Affero General Public License is designed specifically to
+ensure that, in such cases, the modified source code becomes available
+to the community. It requires the operator of a network server to
+provide the source code of the modified version running there to the
+users of that server. Therefore, public use of a modified version, on
+a publicly accessible server, gives the public access to the source
+code of the modified version.
+
+An older license, called the Affero General Public License and
+published by Affero, was designed to accomplish similar goals. This is
+a different license, not a version of the Affero GPL, but Affero has
+released a new version of the Affero GPL which permits relicensing under
+this license.
+
+The precise terms and conditions for copying, distribution and
+modification follow.
+
+## TERMS AND CONDITIONS
+
+### 0. Definitions
+
+“This License” refers to version 3 of the GNU Affero General Public License.
+
+“Copyright” also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+“The Program” refers to any copyrightable work licensed under this
+License. Each licensee is addressed as “you”. “Licensees” and
+“recipients” may be individuals or organizations.
+
+To “modify” a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a “modified version” of the
+earlier work or a work “based on” the earlier work.
+
+A “covered work” means either the unmodified Program or a work based
+on the Program.
+
+To “propagate” a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+To “convey” a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+An interactive user interface displays “Appropriate Legal Notices”
+to the extent that it includes a convenient and prominently visible
+feature that **(1)** displays an appropriate copyright notice, and **(2)**
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+### 1. Source Code
+
+The “source code” for a work means the preferred form of the work
+for making modifications to it. “Object code” means any non-source
+form of a work.
+
+A “Standard Interface” means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+The “System Libraries” of an executable work include anything, other
+than the work as a whole, that **(a)** is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and **(b)** serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+“Major Component”, in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+The “Corresponding Source” for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+The Corresponding Source for a work in source code form is that
+same work.
+
+### 2. Basic Permissions
+
+All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+### 3. Protecting Users' Legal Rights From Anti-Circumvention Law
+
+No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+### 4. Conveying Verbatim Copies
+
+You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+### 5. Conveying Modified Source Versions
+
+You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+- **a)** The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+- **b)** The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section 7.
+ This requirement modifies the requirement in section 4 to
+ “keep intact all notices”.
+- **c)** You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+- **d)** If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+“aggregate” if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+### 6. Conveying Non-Source Forms
+
+You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+- **a)** Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+- **b)** Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either **(1)** a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or **(2)** access to copy the
+ Corresponding Source from a network server at no charge.
+- **c)** Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+- **d)** Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+- **e)** Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+A “User Product” is either **(1)** a “consumer product”, which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or **(2)** anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, “normally used” refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+“Installation Information” for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+### 7. Additional Terms
+
+“Additional permissions” are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+- **a)** Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+- **b)** Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+- **c)** Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+- **d)** Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+- **e)** Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+- **f)** Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+All other non-permissive additional terms are considered “further
+restrictions” within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+### 8. Termination
+
+You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated **(a)**
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and **(b)** permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+### 9. Acceptance Not Required for Having Copies
+
+You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+### 10. Automatic Licensing of Downstream Recipients
+
+Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+An “entity transaction” is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+### 11. Patents
+
+A “contributor” is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's “contributor version”.
+
+A contributor's “essential patent claims” are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, “control” includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+In the following three paragraphs, a “patent license” is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To “grant” such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either **(1)** cause the Corresponding Source to be so
+available, or **(2)** arrange to deprive yourself of the benefit of the
+patent license for this particular work, or **(3)** arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. “Knowingly relying” means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+A patent license is “discriminatory” if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license **(a)** in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or **(b)** primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+### 12. No Surrender of Others' Freedom
+
+If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+### 13. Remote Network Interaction; Use with the GNU General Public License
+
+Notwithstanding any other provision of this License, if you modify the
+Program, your modified version must prominently offer all users
+interacting with it remotely through a computer network (if your version
+supports such interaction) an opportunity to receive the Corresponding
+Source of your version by providing access to the Corresponding Source
+from a network server at no charge, through some standard or customary
+means of facilitating copying of software. This Corresponding Source
+shall include the Corresponding Source for any work covered by version 3
+of the GNU General Public License that is incorporated pursuant to the
+following paragraph.
+
+Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the work with which it is combined will remain governed by version
+3 of the GNU General Public License.
+
+### 14. Revised Versions of this License
+
+The Free Software Foundation may publish revised and/or new versions of
+the GNU Affero General Public License from time to time. Such new versions
+will be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU Affero General
+Public License “or any later version” applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU Affero General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+If the Program specifies that a proxy can decide which future
+versions of the GNU Affero General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+### 15. Disclaimer of Warranty
+
+THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+### 16. Limitation of Liability
+
+IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+### 17. Interpretation of Sections 15 and 16
+
+If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
diff --git a/libs/hash-graph-sdk/python/README.md b/libs/hash-graph-sdk/python/README.md
new file mode 100644
index 0000000..ef55bf4
--- /dev/null
+++ b/libs/hash-graph-sdk/python/README.md
@@ -0,0 +1,3 @@
+# HASH Graph API SDK
+
+High-level interface for the HASH Graph API, providing several convenience methods for interacting with the API.
diff --git a/libs/hash-graph-sdk/python/examples/query_company_entities.py b/libs/hash-graph-sdk/python/examples/query_company_entities.py
new file mode 100644
index 0000000..878c5fc
--- /dev/null
+++ b/libs/hash-graph-sdk/python/examples/query_company_entities.py
@@ -0,0 +1,24 @@
+from uuid import UUID
+
+import devtools
+from yarl import URL
+
+from graph_sdk.client.blocking import HASHClient
+from graph_sdk.filter import EntityQueryPath
+from graph_sdk.options import Options
+from graph_sdk.query import Parameter
+from graph_sdk.utils import filter_latest_from_subgraph
+
+COMPANY_URL = "https://blockprotocol.org/@examples/types/entity-type/company/"
+ACTOR_ID = UUID(int=0) # replace with your actor ID
+
+client = HASHClient(URL("http://localhost:4000/"), actor=ACTOR_ID)
+
+subgraph = client.query_entities(
+ EntityQueryPath().type_().base_url() == Parameter(COMPANY_URL),
+ Options(),
+)
+
+buildings = filter_latest_from_subgraph(subgraph)
+
+devtools.pprint(buildings)
diff --git a/libs/hash-graph-sdk/python/examples/query_company_entities_limited_axes.py b/libs/hash-graph-sdk/python/examples/query_company_entities_limited_axes.py
new file mode 100644
index 0000000..7d96c36
--- /dev/null
+++ b/libs/hash-graph-sdk/python/examples/query_company_entities_limited_axes.py
@@ -0,0 +1,35 @@
+from datetime import datetime
+from uuid import UUID
+
+import devtools
+from yarl import URL
+
+from graph_sdk.client.blocking import HASHClient
+from graph_sdk.filter import EntityQueryPath
+from graph_sdk.options import (
+ Options,
+ TemporalAxesBuilder,
+ TemporalBound,
+)
+from graph_sdk.query import Parameter
+from graph_sdk.utils import filter_latest_from_subgraph
+
+COMPANY_URL = "https://blockprotocol.org/@examples/types/entity-type/company/"
+ACTOR_ID = UUID(int=0) # replace with your actor ID
+
+client = HASHClient(URL("http://localhost:4000/"), actor=ACTOR_ID)
+
+options = Options()
+options.temporal_axes = TemporalAxesBuilder.pinned_transaction_time(None).between(
+ start=TemporalBound.unbounded(),
+ end=TemporalBound.exclusive(datetime.fromisoformat("2021-01-01T00:00:00+00:00")),
+)
+
+subgraph = client.query_entities(
+ EntityQueryPath().type_().base_url() == Parameter(COMPANY_URL),
+ options,
+)
+
+buildings = filter_latest_from_subgraph(subgraph)
+
+devtools.pprint(buildings)
diff --git a/libs/hash-graph-sdk/python/examples/query_company_entity_type.py b/libs/hash-graph-sdk/python/examples/query_company_entity_type.py
new file mode 100644
index 0000000..c311b9a
--- /dev/null
+++ b/libs/hash-graph-sdk/python/examples/query_company_entity_type.py
@@ -0,0 +1,31 @@
+from uuid import UUID
+
+from graph_types import EntityTypeReference
+from yarl import URL
+
+from graph_sdk import TypeAPI
+from graph_sdk.utils import async_to_sync, print_schema
+
+graph = TypeAPI(URL("http://localhost:4000/"))
+
+reference = EntityTypeReference(
+ **{"$ref": "https://blockprotocol.org/@examples/types/entity-type/company/v/1"},
+)
+
+entity_type = async_to_sync(
+ reference.create_model(
+ actor_id=UUID(int=0), # replace with your actor ID
+ graph=graph,
+ additional_properties=False,
+ ),
+)
+
+print_schema(
+ entity_type,
+ {
+ "https://blockprotocol.org/@examples/types/property-type/e-mail/": None,
+ "https://blockprotocol.org/@blockprotocol/types/property-type/name/": (
+ "Hello World"
+ ),
+ },
+)
diff --git a/libs/hash-graph-sdk/python/graph_sdk/__init__.py b/libs/hash-graph-sdk/python/graph_sdk/__init__.py
new file mode 100644
index 0000000..4e1ab0f
--- /dev/null
+++ b/libs/hash-graph-sdk/python/graph_sdk/__init__.py
@@ -0,0 +1,20 @@
+"""Graph API SDK.
+
+Even though this is a software development kit for the Graph API,
+the graph API is still considered to be an implementation detail (for now),
+and no guarantees are made about the stability of the API.
+"""
+
+from graph_sdk.client.concurrent import HASHClient
+from graph_sdk.types import TypeAPI
+
+__all__ = [
+ "HASHClient",
+ "TypeAPI",
+ "client",
+ "filter",
+ "options",
+ "query",
+ "types",
+ "utils",
+]
diff --git a/libs/hash-graph-sdk/python/graph_sdk/client/__init__.py b/libs/hash-graph-sdk/python/graph_sdk/client/__init__.py
new file mode 100644
index 0000000..3bc1651
--- /dev/null
+++ b/libs/hash-graph-sdk/python/graph_sdk/client/__init__.py
@@ -0,0 +1 @@
+"""Clients for interacting with the Graph API."""
diff --git a/libs/hash-graph-sdk/python/graph_sdk/client/_compat.py b/libs/hash-graph-sdk/python/graph_sdk/client/_compat.py
new file mode 100644
index 0000000..a33045d
--- /dev/null
+++ b/libs/hash-graph-sdk/python/graph_sdk/client/_compat.py
@@ -0,0 +1,10 @@
+from typing import TypeVar
+
+from pydantic import BaseModel
+
+T = TypeVar("T", bound=BaseModel)
+U = TypeVar("U", bound=BaseModel)
+
+
+def recast(type_: type[T], value: U) -> T:
+ return type_.model_validate(value.model_dump(by_alias=True))
diff --git a/libs/hash-graph-sdk/python/graph_sdk/client/blocking.py b/libs/hash-graph-sdk/python/graph_sdk/client/blocking.py
new file mode 100644
index 0000000..dce75f5
--- /dev/null
+++ b/libs/hash-graph-sdk/python/graph_sdk/client/blocking.py
@@ -0,0 +1,99 @@
+
+# =========================================
+# THIS FILE IS GENERATED, DO NOT CHANGE IT!
+# =========================================
+
+"""Blocking API for the Graph SDK.
+
+This is just a thin wrapper around the async API.
+
+(Usually, one could achieve this by simply wrapping the async API automatically,
+the problem with that approach however is that users loose the ability to look
+at the source code)
+"""
+from graph_sdk.client.concurrent import HASHClient as ConcurrentHASHClient
+from graph_sdk.utils import async_to_sync
+from typing import TypeVar
+from uuid import UUID
+from graph_client import GraphClient as LowLevelClient
+from graph_client.models import CreateDataTypeRequest, CreateEntityTypeRequest, CreatePropertyTypeRequest, DataType, DataTypeStructuralQuery, EntityStructuralQuery, EntityType, EntityTypeStructuralQuery, LoadExternalDataTypeRequest, LoadExternalEntityTypeRequest, LoadExternalPropertyTypeRequest, MaybeListOfOntologyElementMetadata, OntologyElementMetadata, OwnedById, PropertyType, PropertyTypeStructuralQuery, Subgraph, UpdateDataType, UpdateDataTypeRequest, UpdateEntityType, UpdateEntityTypeRequest, UpdatePropertyType, UpdatePropertyTypeRequest, VersionedURL
+from graph_types import DataTypeSchema, EntityTypeSchema, PropertyTypeSchema
+from pydantic_core._pydantic_core import Url
+from yarl import URL
+from graph_sdk.client._compat import recast
+from graph_sdk.options import Options
+from graph_sdk.query import BaseFilter
+T = TypeVar('T')
+
+class HASHClient:
+ """Implementation of the client for the HASH API.
+
+ Exposes several methods for interacting with the API.
+ """
+ inner: ConcurrentHASHClient
+
+ def __init__(self, base: URL, *, actor: UUID | None=None) -> None:
+ """Initialize the client with the base URL."""
+ self.inner = ConcurrentHASHClient(base)
+
+ @property
+ def actor(self) -> UUID | None:
+ """Get the actor for the client."""
+ return self.inner.actor
+
+ @actor.setter
+ def actor(self, actor: UUID | None) -> None:
+ """Set the actor for the client."""
+ self.inner.actor = actor
+
+ def query_data_types(self, query: BaseFilter, options: Options, *, actor: UUID | None=None) -> Subgraph:
+ """Query data types."""
+ return async_to_sync(self.inner.query_data_types(query, options, actor=actor))
+
+ def load_external_data_type(self, url: URL, *, actor: UUID | None=None) -> OntologyElementMetadata:
+ """Load an external data type."""
+ return async_to_sync(self.inner.load_external_data_type(url, actor=actor))
+
+ def create_data_types(self, models: list[DataTypeSchema], owned_by_id: UUID, *, actor: UUID | None=None) -> MaybeListOfOntologyElementMetadata:
+ """Create data types."""
+ return async_to_sync(self.inner.create_data_types(models, owned_by_id, actor=actor))
+
+ def update_data_type(self, model: DataTypeSchema, *, actor: UUID | None=None) -> OntologyElementMetadata:
+ """Update a data type."""
+ return async_to_sync(self.inner.update_data_type(model, actor=actor))
+
+ def query_property_types(self, query: BaseFilter, options: Options, *, actor: UUID | None=None) -> Subgraph:
+ """Query property types."""
+ return async_to_sync(self.inner.query_property_types(query, options, actor=actor))
+
+ def load_external_property_type(self, url: URL, *, actor: UUID | None=None) -> OntologyElementMetadata:
+ """Load an external property type."""
+ return async_to_sync(self.inner.load_external_property_type(url, actor=actor))
+
+ def create_property_types(self, models: list[PropertyTypeSchema], owned_by_id: UUID, *, actor: UUID | None=None) -> MaybeListOfOntologyElementMetadata:
+ """Create property types."""
+ return async_to_sync(self.inner.create_property_types(models, owned_by_id, actor=actor))
+
+ def update_property_type(self, model: PropertyTypeSchema, *, actor: UUID | None=None) -> OntologyElementMetadata:
+ """Update a property type."""
+ return async_to_sync(self.inner.update_property_type(model, actor=actor))
+
+ def query_entity_types(self, query: BaseFilter, options: Options, *, actor: UUID | None=None) -> Subgraph:
+ """Query entity types."""
+ return async_to_sync(self.inner.query_entity_types(query, options, actor=actor))
+
+ def load_external_entity_type(self, url: URL, *, actor: UUID | None=None) -> OntologyElementMetadata:
+ """Load an external entity type."""
+ return async_to_sync(self.inner.load_external_entity_type(url, actor=actor))
+
+ def create_entity_types(self, models: list[EntityTypeSchema], owned_by_id: UUID, *, actor: UUID | None=None) -> MaybeListOfOntologyElementMetadata:
+ """Create entity types."""
+ return async_to_sync(self.inner.create_entity_types(models, owned_by_id, actor=actor))
+
+ def update_entity_type(self, model: EntityTypeSchema, *, actor: UUID | None=None) -> OntologyElementMetadata:
+ """Update an entity type."""
+ return async_to_sync(self.inner.update_entity_type(model, actor=actor))
+
+ def query_entities(self, query: BaseFilter, options: Options, *, actor: UUID | None=None) -> Subgraph:
+ """Query entities."""
+ return async_to_sync(self.inner.query_entities(query, options, actor=actor))
\ No newline at end of file
diff --git a/libs/hash-graph-sdk/python/graph_sdk/client/concurrent.py b/libs/hash-graph-sdk/python/graph_sdk/client/concurrent.py
new file mode 100644
index 0000000..d1b00f0
--- /dev/null
+++ b/libs/hash-graph-sdk/python/graph_sdk/client/concurrent.py
@@ -0,0 +1,266 @@
+"""Concurrent (async) client for the HASH API."""
+
+from typing import TypeVar
+from uuid import UUID
+
+from graph_client import GraphClient as LowLevelClient
+from graph_client.models import (
+ CreateDataTypeRequest,
+ CreateEntityTypeRequest,
+ CreatePropertyTypeRequest,
+ DataType,
+ DataTypeStructuralQuery,
+ EntityStructuralQuery,
+ EntityType,
+ EntityTypeStructuralQuery,
+ LoadExternalDataTypeRequest,
+ LoadExternalEntityTypeRequest,
+ LoadExternalPropertyTypeRequest,
+ MaybeListOfOntologyElementMetadata,
+ OntologyElementMetadata,
+ OwnedById,
+ PropertyType,
+ PropertyTypeStructuralQuery,
+ Subgraph,
+ UpdateDataType,
+ UpdateDataTypeRequest,
+ UpdateEntityType,
+ UpdateEntityTypeRequest,
+ UpdatePropertyType,
+ UpdatePropertyTypeRequest,
+ VersionedURL,
+)
+from graph_types import DataTypeSchema, EntityTypeSchema, PropertyTypeSchema
+from pydantic_core._pydantic_core import Url
+from yarl import URL
+
+from graph_sdk.client._compat import recast
+from graph_sdk.options import Options
+from graph_sdk.query import BaseFilter
+
+T = TypeVar("T")
+
+
+def assert_not_none(value: T | None) -> T:
+ """Assert that the value is not None."""
+ if value is None:
+ msg = "value cannot be None"
+ raise ValueError(msg)
+
+ return value
+
+
+# TODO: H-351: Use hash_graph_client for create_entity
+# https://linear.app/hash/issue/H-351
+class HASHClient:
+ """Implementation of the client for the HASH API.
+
+ Exposes several methods for interacting with the API.
+ """
+
+ inner: LowLevelClient
+
+ def __init__(self, base: URL, *, actor: UUID | None = None) -> None:
+ """Initialize the client with the base URL."""
+ self.inner = LowLevelClient(base, actor=actor)
+ self.actor = None
+
+ @property
+ def actor(self) -> UUID | None:
+ """Get the actor for the client."""
+ return self.inner.actor
+
+ @actor.setter
+ def actor(self, actor: UUID | None) -> None:
+ """Set the actor for the client."""
+ self.inner.actor = actor
+
+ async def query_data_types(
+ self,
+ query: BaseFilter,
+ options: Options,
+ *,
+ actor: UUID | None = None,
+ ) -> Subgraph:
+ """Query data types."""
+ request = DataTypeStructuralQuery(
+ filter=query.to_ffi(),
+ graph_resolve_depths=options.graph_resolve_depth,
+ temporal_axes=options.temporal_axes,
+ )
+
+ return await self.inner.query_data_types(request, actor=actor)
+
+ async def load_external_data_type(
+ self,
+ url: URL,
+ *,
+ actor: UUID | None = None,
+ ) -> OntologyElementMetadata:
+ """Load an external data type."""
+ request = LoadExternalDataTypeRequest(
+ data_type_id=VersionedURL(root=Url(str(url))),
+ )
+
+ return await self.inner.load_external_data_type(request, actor=actor)
+
+ async def create_data_types(
+ self,
+ models: list[DataTypeSchema],
+ owned_by_id: UUID,
+ *,
+ actor: UUID | None = None,
+ ) -> MaybeListOfOntologyElementMetadata:
+ """Create data types."""
+ request = CreateDataTypeRequest(
+ owned_by_id=OwnedById(root=owned_by_id),
+ schema_=[recast(DataType, model) for model in models],
+ )
+
+ return await self.inner.create_data_types(request, actor=actor)
+
+ async def update_data_type(
+ self,
+ model: DataTypeSchema,
+ *,
+ actor: UUID | None = None,
+ ) -> OntologyElementMetadata:
+ """Update a data type."""
+ request = UpdateDataTypeRequest(
+ schema_=recast(UpdateDataType, model),
+ type_to_update=VersionedURL(root=Url(model.identifier)),
+ )
+
+ return await self.inner.update_data_type(request, actor=actor)
+
+ async def query_property_types(
+ self,
+ query: BaseFilter,
+ options: Options,
+ *,
+ actor: UUID | None = None,
+ ) -> Subgraph:
+ """Query property types."""
+ request = PropertyTypeStructuralQuery(
+ filter=query.to_ffi(),
+ graph_resolve_depths=options.graph_resolve_depth,
+ temporal_axes=options.temporal_axes,
+ )
+
+ return await self.inner.query_property_types(request, actor=actor)
+
+ async def load_external_property_type(
+ self,
+ url: URL,
+ *,
+ actor: UUID | None = None,
+ ) -> OntologyElementMetadata:
+ """Load an external property type."""
+ request = LoadExternalPropertyTypeRequest(
+ property_type_id=VersionedURL(root=Url(str(url))),
+ )
+
+ return await self.inner.load_external_property_type(request, actor=actor)
+
+ async def create_property_types(
+ self,
+ models: list[PropertyTypeSchema],
+ owned_by_id: UUID,
+ *,
+ actor: UUID | None = None,
+ ) -> MaybeListOfOntologyElementMetadata:
+ """Create property types."""
+ request = CreatePropertyTypeRequest(
+ owned_by_id=OwnedById(root=owned_by_id),
+ schema_=[recast(PropertyType, model) for model in models],
+ )
+
+ return await self.inner.create_property_types(request, actor=actor)
+
+ async def update_property_type(
+ self,
+ model: PropertyTypeSchema,
+ *,
+ actor: UUID | None = None,
+ ) -> OntologyElementMetadata:
+ """Update a property type."""
+ request = UpdatePropertyTypeRequest(
+ schema_=recast(UpdatePropertyType, model),
+ type_to_update=VersionedURL(root=Url(model.identifier)),
+ )
+
+ return await self.inner.update_property_type(request, actor=actor)
+
+ async def query_entity_types(
+ self,
+ query: BaseFilter,
+ options: Options,
+ *,
+ actor: UUID | None = None,
+ ) -> Subgraph:
+ """Query entity types."""
+ request = EntityTypeStructuralQuery(
+ filter=query.to_ffi(),
+ graph_resolve_depths=options.graph_resolve_depth,
+ temporal_axes=options.temporal_axes,
+ )
+
+ return await self.inner.query_entity_types(request, actor=actor)
+
+ async def load_external_entity_type(
+ self,
+ url: URL,
+ *,
+ actor: UUID | None = None,
+ ) -> OntologyElementMetadata:
+ """Load an external entity type."""
+ request = LoadExternalEntityTypeRequest(
+ entity_type_id=VersionedURL(root=Url(str(url))),
+ )
+
+ return await self.inner.load_external_entity_type(request, actor=actor)
+
+ async def create_entity_types(
+ self,
+ models: list[EntityTypeSchema],
+ owned_by_id: UUID,
+ *,
+ actor: UUID | None = None,
+ ) -> MaybeListOfOntologyElementMetadata:
+ """Create entity types."""
+ request = CreateEntityTypeRequest(
+ owned_by_id=OwnedById(root=owned_by_id),
+ schema_=[recast(EntityType, model) for model in models],
+ )
+
+ return await self.inner.create_entity_types(request, actor=actor)
+
+ async def update_entity_type(
+ self,
+ model: EntityTypeSchema,
+ *,
+ actor: UUID | None = None,
+ ) -> OntologyElementMetadata:
+ """Update an entity type."""
+ request = UpdateEntityTypeRequest(
+ schema_=recast(UpdateEntityType, model),
+ type_to_update=VersionedURL(root=Url(model.identifier)),
+ )
+
+ return await self.inner.update_entity_type(request, actor=actor)
+
+ async def query_entities(
+ self,
+ query: BaseFilter,
+ options: Options,
+ *,
+ actor: UUID | None = None,
+ ) -> Subgraph:
+ """Query entities."""
+ request = EntityStructuralQuery(
+ filter=query.to_ffi(),
+ graph_resolve_depths=options.graph_resolve_depth,
+ temporal_axes=options.temporal_axes,
+ )
+
+ return await self.inner.query_entities(request, actor=actor)
diff --git a/libs/hash-graph-sdk/python/graph_sdk/filter/__init__.py b/libs/hash-graph-sdk/python/graph_sdk/filter/__init__.py
new file mode 100644
index 0000000..0e93d8a
--- /dev/null
+++ b/libs/hash-graph-sdk/python/graph_sdk/filter/__init__.py
@@ -0,0 +1,20 @@
+"""Ergonomic and type-safe filter paths.
+
+To start using this module, choose where you need to "start",
+depending on the query this will be either: `DataTypeQueryPath`,
+`PropertyTypeQueryPath`, `EntityTypeQueryPath`, or `EntityQueryPath`.
+"""
+
+from graph_sdk.filter.path import (
+ DataTypeQueryPath,
+ EntityQueryPath,
+ EntityTypeQueryPath,
+ PropertyTypeQueryPath,
+)
+
+__all__ = [
+ "EntityQueryPath",
+ "EntityTypeQueryPath",
+ "PropertyTypeQueryPath",
+ "DataTypeQueryPath",
+]
diff --git a/libs/hash-graph-sdk/python/graph_sdk/filter/base.py b/libs/hash-graph-sdk/python/graph_sdk/filter/base.py
new file mode 100644
index 0000000..c798792
--- /dev/null
+++ b/libs/hash-graph-sdk/python/graph_sdk/filter/base.py
@@ -0,0 +1,67 @@
+"""Base and generic classes for query paths."""
+
+from abc import ABC
+from typing import Generic, Self, TypeVar
+
+from graph_client import QueryToken
+from graph_client.models import Selector
+
+from graph_sdk.query import Path
+
+
+class AbstractQueryPath(ABC):
+ """Path definition shared across different query paths."""
+
+ path: Path
+
+ def __init__(self) -> None:
+ """Create a new empty path."""
+ self.path = Path()
+
+ @classmethod
+ def from_ffi(cls, value: list[QueryToken]) -> Self:
+ """Initialize the path from it's vector representation."""
+ self = cls()
+ self.path = Path.from_ffi(value)
+ return self
+
+ @classmethod
+ def from_path(cls, path: Path) -> Self:
+ """Initialize this path from a query path object."""
+ self = cls()
+ self.path = path
+ return self
+
+
+T = TypeVar("T", bound=AbstractQueryPath)
+
+
+class SelectorQueryPath(AbstractQueryPath, Generic[T]):
+ """A selector is a path that is used to select a value in an array."""
+
+ cls: type[T]
+
+ def set_cls(self, cls: type[T]) -> Self:
+ """Set the class of the selector."""
+ self.cls = cls
+ return self
+
+ def any_(self) -> T:
+ """Return the path to all values in an array."""
+ return self.cls.from_path(self.path.push(Selector(root="*")))
+
+
+class UntypedQueryPath(AbstractQueryPath):
+ """Navigation through objects, which is largely untyped."""
+
+ def array(self, index: int) -> Self:
+ """Return the path to the array for a property."""
+ return self.from_path(self.path.push(index))
+
+ def key(self, key: str) -> Self:
+ """Return the path to the key for a property."""
+ return self.from_path(self.path.push(key))
+
+ def finish(self) -> Path:
+ """Return the path to the finish for a property."""
+ return self.path
diff --git a/libs/hash-graph-sdk/python/graph_sdk/filter/config.json b/libs/hash-graph-sdk/python/graph_sdk/filter/config.json
new file mode 100644
index 0000000..b2d3b4b
--- /dev/null
+++ b/libs/hash-graph-sdk/python/graph_sdk/filter/config.json
@@ -0,0 +1,51 @@
+{
+ "meta": {
+ "arguments": {
+ "inheritanceDepth": {
+ "type": "uint",
+ "required": false
+ }
+ }
+ },
+ "data_type": {
+ "selector": {},
+ "untyped": [],
+ "direct": {},
+ "arguments": {}
+ },
+ "property_type": {
+ "selector": {
+ "data_types": "data_type",
+ "property_types": "property_type"
+ },
+ "untyped": [],
+ "direct": {},
+ "arguments": {}
+ },
+ "entity_type": {
+ "selector": {
+ "properties": "property_type",
+ "links": "entity_type"
+ },
+ "untyped": [],
+ "direct": {},
+ "arguments": {
+ "type": ["inheritanceDepth"],
+ "children": ["inheritanceDepth"]
+ }
+ },
+ "entity": {
+ "selector": {},
+ "untyped": ["properties"],
+ "direct": {
+ "type": "entity_type",
+ "incoming_links": "entity",
+ "outgoing_links": "entity",
+ "left_entity": "entity",
+ "right_entity": "entity"
+ },
+ "arguments": {
+ "type": ["inheritanceDepth"]
+ }
+ }
+}
diff --git a/libs/hash-graph-sdk/python/graph_sdk/filter/path.py b/libs/hash-graph-sdk/python/graph_sdk/filter/path.py
new file mode 100644
index 0000000..a12ada7
--- /dev/null
+++ b/libs/hash-graph-sdk/python/graph_sdk/filter/path.py
@@ -0,0 +1,226 @@
+
+# =========================================
+# THIS FILE IS GENERATED, DO NOT CHANGE IT!
+# =========================================
+
+"""Definitions for all path objects.
+
+This file is auto-generated. Do not edit!"""
+from typing import Self
+from graph_client.models import DataTypeQueryToken, PropertyTypeQueryToken, EntityTypeQueryToken, EntityQueryToken
+from graph_sdk.filter.base import AbstractQueryPath, UntypedQueryPath, SelectorQueryPath
+from graph_sdk.query import Path
+
+class DataTypeQueryPath(AbstractQueryPath):
+ """A query path for a data type."""
+
+ def base_url(self) -> Path:
+ """Return the path to the base_url attribute of a data type."""
+ return self.path.push(DataTypeQueryToken.base_url)
+
+ def version(self) -> Path:
+ """Return the path to the version attribute of a data type."""
+ return self.path.push(DataTypeQueryToken.version)
+
+ def versioned_url(self) -> Path:
+ """Return the path to the versioned_url attribute of a data type."""
+ return self.path.push(DataTypeQueryToken.versioned_url)
+
+ def owned_by_id(self) -> Path:
+ """Return the path to the owned_by_id attribute of a data type."""
+ return self.path.push(DataTypeQueryToken.owned_by_id)
+
+ def record_created_by_id(self) -> Path:
+ """Return the path to the record_created_by_id attribute of a data type."""
+ return self.path.push(DataTypeQueryToken.record_created_by_id)
+
+ def record_archived_by_id(self) -> Path:
+ """Return the path to the record_archived_by_id attribute of a data type."""
+ return self.path.push(DataTypeQueryToken.record_archived_by_id)
+
+ def title(self) -> Path:
+ """Return the path to the title attribute of a data type."""
+ return self.path.push(DataTypeQueryToken.title)
+
+ def description(self) -> Path:
+ """Return the path to the description attribute of a data type."""
+ return self.path.push(DataTypeQueryToken.description)
+
+ def type_(self) -> Path:
+ """Return the path to the type attribute of a data type."""
+ return self.path.push(DataTypeQueryToken.type)
+
+class PropertyTypeQueryPath(AbstractQueryPath):
+ """A query path for a property type."""
+
+ def base_url(self) -> Path:
+ """Return the path to the base_url attribute of a property type."""
+ return self.path.push(PropertyTypeQueryToken.base_url)
+
+ def version(self) -> Path:
+ """Return the path to the version attribute of a property type."""
+ return self.path.push(PropertyTypeQueryToken.version)
+
+ def versioned_url(self) -> Path:
+ """Return the path to the versioned_url attribute of a property type."""
+ return self.path.push(PropertyTypeQueryToken.versioned_url)
+
+ def owned_by_id(self) -> Path:
+ """Return the path to the owned_by_id attribute of a property type."""
+ return self.path.push(PropertyTypeQueryToken.owned_by_id)
+
+ def record_created_by_id(self) -> Path:
+ """Return the path to the record_created_by_id attribute of a property type."""
+ return self.path.push(PropertyTypeQueryToken.record_created_by_id)
+
+ def record_archived_by_id(self) -> Path:
+ """Return the path to the record_archived_by_id attribute of a property type."""
+ return self.path.push(PropertyTypeQueryToken.record_archived_by_id)
+
+ def title(self) -> Path:
+ """Return the path to the title attribute of a property type."""
+ return self.path.push(PropertyTypeQueryToken.title)
+
+ def description(self) -> Path:
+ """Return the path to the description attribute of a property type."""
+ return self.path.push(PropertyTypeQueryToken.description)
+
+ def data_types(self) -> SelectorQueryPath[DataTypeQueryPath]:
+ """Return the path to the data_types attribute of a property type."""
+ return SelectorQueryPath[DataTypeQueryPath].from_path(self.path.push(PropertyTypeQueryToken.data_types)).set_cls(DataTypeQueryPath)
+
+ def property_types(self) -> SelectorQueryPath[Self]:
+ """Return the path to the property_types attribute of a property type."""
+ return SelectorQueryPath[Self].from_path(self.path.push(PropertyTypeQueryToken.property_types)).set_cls(type(self))
+
+class EntityTypeQueryPath(AbstractQueryPath):
+ """A query path for an entity type."""
+
+ def base_url(self) -> Path:
+ """Return the path to the base_url attribute of an entity type."""
+ return self.path.push(EntityTypeQueryToken.base_url)
+
+ def version(self) -> Path:
+ """Return the path to the version attribute of an entity type."""
+ return self.path.push(EntityTypeQueryToken.version)
+
+ def versioned_url(self) -> Path:
+ """Return the path to the versioned_url attribute of an entity type."""
+ return self.path.push(EntityTypeQueryToken.versioned_url)
+
+ def owned_by_id(self) -> Path:
+ """Return the path to the owned_by_id attribute of an entity type."""
+ return self.path.push(EntityTypeQueryToken.owned_by_id)
+
+ def record_created_by_id(self) -> Path:
+ """Return the path to the record_created_by_id attribute of an entity type."""
+ return self.path.push(EntityTypeQueryToken.record_created_by_id)
+
+ def record_archived_by_id(self) -> Path:
+ """Return the path to the record_archived_by_id attribute of an entity type."""
+ return self.path.push(EntityTypeQueryToken.record_archived_by_id)
+
+ def title(self) -> Path:
+ """Return the path to the title attribute of an entity type."""
+ return self.path.push(EntityTypeQueryToken.title)
+
+ def description(self) -> Path:
+ """Return the path to the description attribute of an entity type."""
+ return self.path.push(EntityTypeQueryToken.description)
+
+ def examples(self) -> Path:
+ """Return the path to the examples attribute of an entity type."""
+ return self.path.push(EntityTypeQueryToken.examples)
+
+ def properties(self) -> SelectorQueryPath[PropertyTypeQueryPath]:
+ """Return the path to the properties attribute of an entity type."""
+ return SelectorQueryPath[PropertyTypeQueryPath].from_path(self.path.push(EntityTypeQueryToken.properties)).set_cls(PropertyTypeQueryPath)
+
+ def required(self) -> Path:
+ """Return the path to the required attribute of an entity type."""
+ return self.path.push(EntityTypeQueryToken.required)
+
+ def label_property(self) -> Path:
+ """Return the path to the label_property attribute of an entity type."""
+ return self.path.push(EntityTypeQueryToken.label_property)
+
+ def icon(self) -> Path:
+ """Return the path to the icon attribute of an entity type."""
+ return self.path.push(EntityTypeQueryToken.icon)
+
+ def links(self) -> SelectorQueryPath[Self]:
+ """Return the path to the links attribute of an entity type."""
+ return SelectorQueryPath[Self].from_path(self.path.push(EntityTypeQueryToken.links)).set_cls(type(self))
+
+ def inherits_from(self) -> Path:
+ """Return the path to the inherits_from attribute of an entity type."""
+ return self.path.push(EntityTypeQueryToken.inherits_from)
+
+ def children(self, *, inheritance_depth: int | None=None) -> Path:
+ """Return the path to the children attribute of an entity type."""
+ args = []
+ if inheritance_depth is not None:
+ args.append(f'inheritanceDepth={inheritance_depth}')
+ return self.path.push(f"{EntityTypeQueryToken.children}({', '.join(args)})" if args else EntityTypeQueryToken.children)
+
+class EntityQueryPath(AbstractQueryPath):
+ """A query path for an entity."""
+
+ def uuid(self) -> Path:
+ """Return the path to the uuid attribute of an entity."""
+ return self.path.push(EntityQueryToken.uuid)
+
+ def edition_id(self) -> Path:
+ """Return the path to the edition_id attribute of an entity."""
+ return self.path.push(EntityQueryToken.edition_id)
+
+ def archived(self) -> Path:
+ """Return the path to the archived attribute of an entity."""
+ return self.path.push(EntityQueryToken.archived)
+
+ def draft(self) -> Path:
+ """Return the path to the draft attribute of an entity."""
+ return self.path.push(EntityQueryToken.draft)
+
+ def owned_by_id(self) -> Path:
+ """Return the path to the owned_by_id attribute of an entity."""
+ return self.path.push(EntityQueryToken.owned_by_id)
+
+ def record_created_by_id(self) -> Path:
+ """Return the path to the record_created_by_id attribute of an entity."""
+ return self.path.push(EntityQueryToken.record_created_by_id)
+
+ def type_(self, *, inheritance_depth: int | None=None) -> EntityTypeQueryPath:
+ """Return the path to the type attribute of an entity."""
+ args = []
+ if inheritance_depth is not None:
+ args.append(f'inheritanceDepth={inheritance_depth}')
+ return EntityTypeQueryPath.from_path(self.path.push(f"{EntityQueryToken.type}({', '.join(args)})" if args else EntityQueryToken.type))
+
+ def properties(self) -> UntypedQueryPath:
+ """Return the path to the properties attribute of an entity."""
+ return UntypedQueryPath.from_path(self.path.push(EntityQueryToken.properties))
+
+ def incoming_links(self) -> Self:
+ """Return the path to the incoming_links attribute of an entity."""
+ return self.from_path(self.path.push(EntityQueryToken.incoming_links))
+
+ def outgoing_links(self) -> Self:
+ """Return the path to the outgoing_links attribute of an entity."""
+ return self.from_path(self.path.push(EntityQueryToken.outgoing_links))
+
+ def left_entity(self) -> Self:
+ """Return the path to the left_entity attribute of an entity."""
+ return self.from_path(self.path.push(EntityQueryToken.left_entity))
+
+ def right_entity(self) -> Self:
+ """Return the path to the right_entity attribute of an entity."""
+ return self.from_path(self.path.push(EntityQueryToken.right_entity))
+
+ def left_to_right_order(self) -> Path:
+ """Return the path to the left_to_right_order attribute of an entity."""
+ return self.path.push(EntityQueryToken.left_to_right_order)
+
+ def right_to_left_order(self) -> Path:
+ """Return the path to the right_to_left_order attribute of an entity."""
+ return self.path.push(EntityQueryToken.right_to_left_order)
\ No newline at end of file
diff --git a/libs/hash-graph-sdk/python/graph_sdk/options.py b/libs/hash-graph-sdk/python/graph_sdk/options.py
new file mode 100644
index 0000000..d1736ea
--- /dev/null
+++ b/libs/hash-graph-sdk/python/graph_sdk/options.py
@@ -0,0 +1,372 @@
+"""Ergonomic API to configure options for structural queries."""
+
+from datetime import datetime
+from typing import Protocol
+
+from graph_client.models import (
+ DecisionTime,
+ EdgeResolveDepths,
+ ExclusiveBound,
+ GraphResolveDepths,
+ InclusiveBound,
+ LimitedTemporalBound,
+ NullableTimestamp,
+ OpenTemporalBound,
+ OutgoingEdgeResolveDepth,
+ QueryTemporalAxesUnresolved,
+ QueryTemporalAxesUnresolvedDecisionTime,
+ QueryTemporalAxesUnresolvedTransactionTime,
+ Timestamp,
+ TransactionTime,
+ UnboundedBound,
+ UnresolvedPinnedDecisionAxis,
+ UnresolvedPinnedTransactionAxis,
+ UnresolvedRightBoundedTemporalInterval,
+ UnresolvedVariableDecisionAxis,
+ UnresolvedVariableTransactionAxis,
+)
+from graph_client.models import (
+ TemporalBound as FFITemporalBound,
+)
+
+
+class ToLimitedTemporalBound(Protocol):
+ """Convert to a limited temporal bound.
+
+ This is implemented by `InclusiveTemporalBound`, and `ExclusiveTemporalBound`.
+ """
+
+ def to_limited_temporal_bound(self) -> LimitedTemporalBound:
+ """Convert to a limited temporal bound."""
+ ...
+
+
+class ToTemporalBound(Protocol):
+ """Convert to a temporal bound.
+
+ This is implemented by `UnboundedTemporalBound`,
+ `InclusiveTemporalBound` and `ExclusiveTemporalBound`.
+ """
+
+ def to_temporal_bound(self) -> FFITemporalBound:
+ """Convert to a temporal bound."""
+ ...
+
+
+class ToOpenTemporalBound(Protocol):
+ """Convert to an open temporal bound.
+
+ This is implemented by `UnboundedTemporalBound` and `ExclusiveTemporalBound`.
+ """
+
+ def to_open_temporal_bound(self) -> OpenTemporalBound:
+ """Convert to an open temporal bound."""
+ ...
+
+
+class TemporalBound:
+ """A temporal bound.
+
+ Do not instantiate this class directly.
+ Instead, use the class methods.
+ """
+
+ @classmethod
+ def unbounded(cls) -> "UnboundedTemporalBound":
+ """Return an unbounded interval."""
+ return UnboundedTemporalBound()
+
+ @classmethod
+ def inclusive(cls, time: datetime) -> "InclusiveTemporalBound":
+ """Return an inclusive interval."""
+ return InclusiveTemporalBound(time)
+
+ @classmethod
+ def exclusive(cls, time: datetime) -> "ExclusiveTemporalBound":
+ """Return an exclusive interval."""
+ return ExclusiveTemporalBound(time)
+
+
+# noinspection PyMethodMayBeStatic
+class UnboundedTemporalBound(TemporalBound):
+ """An unbounded temporal bound."""
+
+ def to_temporal_bound(self) -> FFITemporalBound:
+ """Convert to a temporal bound."""
+ return FFITemporalBound(root=UnboundedBound(kind="unbounded"))
+
+ def to_open_temporal_bound(self) -> OpenTemporalBound:
+ """Convert to an open temporal bound."""
+ return OpenTemporalBound(root=UnboundedBound(kind="unbounded"))
+
+
+class InclusiveTemporalBound(TemporalBound):
+ """An inclusive temporal bound."""
+
+ time: datetime
+
+ def __init__(self, time: datetime) -> None:
+ """Initialize the bound."""
+ self.time = time
+
+ def to_temporal_bound(self) -> FFITemporalBound:
+ """Convert to a temporal bound."""
+ return FFITemporalBound(
+ root=InclusiveBound(
+ kind="inclusive",
+ limit=Timestamp(root=self.time),
+ ),
+ )
+
+ def to_limited_temporal_bound(self) -> LimitedTemporalBound:
+ """Convert to a limited temporal bound."""
+ return LimitedTemporalBound(
+ root=InclusiveBound(
+ kind="inclusive",
+ limit=Timestamp(root=self.time),
+ ),
+ )
+
+
+class ExclusiveTemporalBound(TemporalBound):
+ """An exclusive temporal bound."""
+
+ time: datetime
+
+ def __init__(self, time: datetime) -> None:
+ """Initialize the bound."""
+ self.time = time
+
+ def to_temporal_bound(self) -> FFITemporalBound:
+ """Convert to a temporal bound."""
+ return FFITemporalBound(
+ root=ExclusiveBound(kind="exclusive", limit=Timestamp(root=self.time)),
+ )
+
+ def to_limited_temporal_bound(self) -> LimitedTemporalBound:
+ """Convert to a limited temporal bound."""
+ return LimitedTemporalBound(
+ root=ExclusiveBound(
+ kind="exclusive",
+ limit=Timestamp(root=self.time),
+ ),
+ )
+
+ def to_open_temporal_bound(self) -> OpenTemporalBound:
+ """Convert to an open temporal bound."""
+ return OpenTemporalBound(
+ root=ExclusiveBound(
+ kind="exclusive",
+ limit=Timestamp(root=self.time),
+ ),
+ )
+
+
+class PinnedTransactionTimeTemporalAxisBuilder:
+ """A builder for a pinned transaction time, which is variable by decision time."""
+
+ pinned: datetime | None = None
+
+ def __init__(self, *, pinned: datetime | None = None) -> None:
+ """Initialize the builder."""
+ self.pinned = pinned
+
+ def between(
+ self,
+ *,
+ start: ToTemporalBound | None = None,
+ end: ToLimitedTemporalBound | None = None,
+ ) -> QueryTemporalAxesUnresolved:
+ """Return all entities that are valid between the given times.
+
+ If `start` is None, then the start of the interval is unbounded.
+ If `end` is None, then the end will be the current time.
+ """
+ return QueryTemporalAxesUnresolved(
+ root=QueryTemporalAxesUnresolvedDecisionTime(
+ pinned=UnresolvedPinnedTransactionAxis(
+ axis=TransactionTime(root="transactionTime"),
+ timestamp=NullableTimestamp(root=self.pinned),
+ ),
+ variable=UnresolvedVariableDecisionAxis(
+ axis=DecisionTime(root="decisionTime"),
+ interval=UnresolvedRightBoundedTemporalInterval(
+ start=start.to_temporal_bound() if start else None,
+ end=end.to_limited_temporal_bound() if end else None,
+ ),
+ ),
+ ),
+ )
+
+ def current(self) -> QueryTemporalAxesUnresolved:
+ """Return all entities that are valid at the current time."""
+ return self.between(start=None, end=None)
+
+
+class PinnedDecisionTimeTemporalAxisBuilder:
+ """A builder for a pinned decision time, which is variable by transaction time."""
+
+ pinned: datetime | None = None
+
+ def __init__(self, *, pinned: datetime | None = None) -> None:
+ """Initialize the builder."""
+ self.pinned = pinned
+
+ def between(
+ self,
+ *,
+ start: ToTemporalBound | None = None,
+ end: ToLimitedTemporalBound | None = None,
+ ) -> QueryTemporalAxesUnresolved:
+ """Return all entities that are valid between the given times.
+
+ If `start` is None, then the start of the interval is unbounded.
+ If `end` is None, then the end will be the current time.
+ """
+ return QueryTemporalAxesUnresolved(
+ root=QueryTemporalAxesUnresolvedTransactionTime(
+ pinned=UnresolvedPinnedDecisionAxis(
+ axis=DecisionTime(root="decisionTime"),
+ timestamp=NullableTimestamp(root=self.pinned),
+ ),
+ variable=UnresolvedVariableTransactionAxis(
+ axis=TransactionTime(root="transactionTime"),
+ interval=UnresolvedRightBoundedTemporalInterval(
+ start=start.to_temporal_bound() if start else None,
+ end=end.to_limited_temporal_bound() if end else None,
+ ),
+ ),
+ ),
+ )
+
+ def current(self) -> QueryTemporalAxesUnresolved:
+ """Return all entities that are valid at the current time."""
+ return self.between(start=None, end=None)
+
+
+class TemporalAxesBuilder:
+ """A builder for temporal axes."""
+
+ @classmethod
+ def pinned_transaction_time(
+ cls,
+ time: datetime | None = None,
+ ) -> PinnedTransactionTimeTemporalAxisBuilder:
+ """Return all entities that are available at the given transaction time."""
+ return PinnedTransactionTimeTemporalAxisBuilder(pinned=time)
+
+ @classmethod
+ def pinned_decision_time(
+ cls,
+ time: datetime | None = None,
+ ) -> PinnedDecisionTimeTemporalAxisBuilder:
+ """Return all entities that are available at the given transaction time."""
+ return PinnedDecisionTimeTemporalAxisBuilder(pinned=time)
+
+ @classmethod
+ def current(cls) -> QueryTemporalAxesUnresolved:
+ """Only select the latest entities."""
+ return PinnedDecisionTimeTemporalAxisBuilder(pinned=None).current()
+
+
+class Options:
+ """Options for queries."""
+
+ graph_resolve_depth: GraphResolveDepths
+ temporal_axes: QueryTemporalAxesUnresolved
+
+ def __init__(self) -> None:
+ """Initialize the options."""
+ self.graph_resolve_depth = GraphResolveDepths(
+ constrains_link_destinations_on=OutgoingEdgeResolveDepth(outgoing=0),
+ constrains_links_on=OutgoingEdgeResolveDepth(outgoing=0),
+ constrains_properties_on=OutgoingEdgeResolveDepth(outgoing=0),
+ constrains_values_on=OutgoingEdgeResolveDepth(outgoing=0),
+ has_left_entity=EdgeResolveDepths(incoming=0, outgoing=0),
+ has_right_entity=EdgeResolveDepths(incoming=0, outgoing=0),
+ inherits_from=OutgoingEdgeResolveDepth(outgoing=0),
+ is_of_type=OutgoingEdgeResolveDepth(outgoing=0),
+ )
+
+ self.temporal_axes = TemporalAxesBuilder.current()
+
+ def resolve_type_of_entity(self, *, enable: bool) -> None:
+ """Return the entity type of the entity."""
+ self.graph_resolve_depth.is_of_type = OutgoingEdgeResolveDepth(
+ outgoing=0 if enable else 1,
+ )
+
+ def resolve_inheritance(self, *, depth: int) -> None:
+ """Return the types a type inherits from.
+
+ If `depth` is 0, then types an entity inherits from will not be resolved.
+ """
+ self.graph_resolve_depth.inherits_from = OutgoingEdgeResolveDepth(
+ outgoing=depth,
+ )
+
+ def resolve_left_entity_of_link(
+ self,
+ *,
+ incoming_depth: int = 0,
+ outgoing_depth: int = 0,
+ ) -> None:
+ """Return the left entity of an entity, if it is a link.
+
+ If `*_depth` is 0, then the right entity will not be resolved.
+ """
+ self.graph_resolve_depth.has_left_entity = EdgeResolveDepths(
+ incoming=incoming_depth,
+ outgoing=outgoing_depth,
+ )
+
+ def resolve_right_entity_of_link(
+ self,
+ *,
+ incoming_depth: int = 0,
+ outgoing_depth: int = 0,
+ ) -> None:
+ """Return the right entity of an entity, if it is a link.
+
+ If `*_depth` is 0, then the right entity will not be resolved.
+ """
+ self.graph_resolve_depth.has_right_entity = EdgeResolveDepths(
+ incoming=incoming_depth,
+ outgoing=outgoing_depth,
+ )
+
+ def resolve_data_types(self, *, depth: int) -> None:
+ """Return the data types of the entity.
+
+ If `depth` is 0, then the data types will not be resolved.
+ """
+ self.graph_resolve_depth.constrains_values_on = OutgoingEdgeResolveDepth(
+ outgoing=depth,
+ )
+
+ def resolve_property_types(self, *, depth: int) -> None:
+ """Return the property types of the entity.
+
+ If `depth` is 0, then the property types will not be resolved.
+ """
+ self.graph_resolve_depth.constrains_properties_on = OutgoingEdgeResolveDepth(
+ outgoing=depth,
+ )
+
+ def resolve_link_types(self, *, depth: int) -> None:
+ """Return the entity types of any link left or right entity.
+
+ If `depth` is 0, then the link types will not be resolved.
+ """
+ self.graph_resolve_depth.constrains_links_on = OutgoingEdgeResolveDepth(
+ outgoing=depth,
+ )
+
+ def resolve_link_destinations(self, *, depth: int) -> None:
+ """Return the entity type of any link destination referenced by an entity type.
+
+ If `depth` is 0, then the link destinations will not be resolved.
+ """
+ self.graph_resolve_depth.constrains_link_destinations_on = (
+ OutgoingEdgeResolveDepth(outgoing=depth)
+ )
diff --git a/libs/hash-graph-sdk/python/graph_sdk/py.typed b/libs/hash-graph-sdk/python/graph_sdk/py.typed
new file mode 100644
index 0000000..e69de29
diff --git a/libs/hash-graph-sdk/python/graph_sdk/query.py b/libs/hash-graph-sdk/python/graph_sdk/query.py
new file mode 100644
index 0000000..e995f0c
--- /dev/null
+++ b/libs/hash-graph-sdk/python/graph_sdk/query.py
@@ -0,0 +1,366 @@
+"""Ergonomic API to create different type-checked filters for use in structural queries.
+
+This module provides a set of classes
+that can be used to create type-checked filters for
+use in structural queries.
+
+To combine filters use the `&` and `|` operators. If you prefer explicit operations,
+you can also use `BaseFilter.and_()` and `BaseFilter.or_()` respectively.
+
+To create filters use `==` or `!=` with a `Path` and `Parameter`.
+If you prefer explicit operations you can also use `BaseFilter.equal()`
+and `BaseFilter.not_equal()` respectively.
+
+To create a filter that will always match use `BaseFilter.always()`.
+Use `BaseFilter.never()` to create a filter that will never match.
+"""
+
+from abc import ABC, abstractmethod
+from collections.abc import Sequence
+from enum import Enum
+from typing import Any, Generic, Never, Protocol, Self, TypeVar, assert_never
+
+from graph_client import QueryToken
+from graph_client.models import (
+ AllFilter,
+ AnyFilter,
+ ContainsSegmentFilter,
+ EndsWithFilter,
+ EqualFilter,
+ Filter,
+ FilterExpression,
+ NotEqualFilter,
+ NotFilter,
+ ParameterExpression,
+ PathExpression,
+ StartsWithFilter,
+)
+from pydantic import BaseModel
+
+
+class FFIConversionProtocol(Protocol):
+ """Protocol for converting to and from FFI objects."""
+
+ def to_ffi(self) -> BaseModel:
+ """Converts the object to a FFI object."""
+ ...
+
+
+class UnaryOperation(Enum):
+ """Unary operations."""
+
+ NOT = "not"
+
+
+class BinaryOperation(Enum):
+ """Binary operations."""
+
+ EQUAL = "equal"
+ NOT_EQUAL = "notEqual"
+ STARTS_WITH = "startsWith"
+ ENDS_WITH = "endsWith"
+ CONTAINS_SEGMENT = "containsSegment"
+
+
+class NaryOperation(Enum):
+ """Nary operations."""
+
+ ALL = "all"
+ ANY = "any"
+
+
+L = TypeVar("L", bound="BaseFilterExpression")
+R = TypeVar("R", bound="BaseFilterExpression")
+F = TypeVar("F", bound="BaseFilter")
+
+
+class BaseFilter(ABC):
+ """Protocol for queries."""
+
+ def all_(
+ self,
+ other: Sequence[F],
+ ) -> "NaryFilter[Self | F]":
+ """Combines two or more queries with an AND."""
+ return NaryFilter(NaryOperation.ALL, [self, *other])
+
+ def __and__(self, other: F) -> "NaryFilter[Self | F]":
+ """Combines two queries with an AND."""
+ if isinstance(other, BaseFilter):
+ return self.all_((other,))
+
+ msg: str # type: ignore[unreachable]
+ msg = f"unsupported operand type(s) for &: '{type(self)}' and '{type(other)}'"
+ raise TypeError(msg)
+
+ def any_(
+ self,
+ other: Sequence[F],
+ ) -> "NaryFilter[Self | F]":
+ """Combines two or more queries with an OR."""
+ return NaryFilter(NaryOperation.ANY, [self, *other])
+
+ def __or__(self, other: F) -> "NaryFilter[Self | F]":
+ """Combines two queries with an OR."""
+ if isinstance(other, BaseFilter):
+ return self.any_((other,))
+
+ msg: str # type: ignore[unreachable]
+ msg = f"unsupported operand type(s) for |: '{type(self)}' and '{type(other)}'"
+ raise TypeError(msg)
+
+ def not_(self) -> "UnaryFilter[Self]":
+ """Negates the query."""
+ return UnaryFilter(UnaryOperation.NOT, self)
+
+ def __invert__(self) -> "UnaryFilter[Self]":
+ """Negates the query."""
+ return self.not_()
+
+ @classmethod
+ def always(cls) -> "NaryFilter[Never]":
+ """Returns a query that always returns true.
+
+ Warning:
+ -------
+ Using this query is generally discouraged,
+ and should only be used if there is no other way.
+
+ This will return everything, which means that depending
+ on graph size, this **will** return a lot of data.
+ """
+ return NaryFilter(NaryOperation.ALL, [])
+
+ @classmethod
+ def never(cls) -> "NaryFilter[Never]":
+ """Returns a query that always returns false."""
+ return NaryFilter(NaryOperation.ANY, [])
+
+ @abstractmethod
+ def to_ffi(self) -> Filter:
+ """Converts the query to an FFI query."""
+ ...
+
+
+class UnaryFilter(BaseFilter, Generic[F]):
+ """Unary query."""
+
+ operation: UnaryOperation
+ operand: F
+
+ def __init__(self, operation: UnaryOperation, operand: F) -> None:
+ """Initializes the unary query."""
+ self.operation = operation
+ self.operand = operand
+
+ def __repr__(self) -> str:
+ """Returns a string representation of the unary query."""
+ return f"UnaryFilter({self.operation}, {self.operand})"
+
+ def to_ffi(self) -> Filter:
+ """Converts the query to an FFI query."""
+ operation = self.operation
+
+ if operation == UnaryOperation.NOT:
+ return Filter(root=NotFilter(not_=self.operand.to_ffi()))
+
+ assert_never(operation)
+
+
+class BinaryFilter(BaseFilter, Generic[L, R]):
+ """Binary query."""
+
+ operation: BinaryOperation
+ lhs: L
+ rhs: R
+
+ def __init__(self, operation: BinaryOperation, lhs: L, rhs: R) -> None:
+ """Initializes the binary query."""
+ self.operation = operation
+ self.lhs = lhs
+ self.rhs = rhs
+
+ def __repr__(self) -> str:
+ """Returns a string representation of the binary query."""
+ return f"BinaryFilter({self.operation}, {self.lhs}, {self.rhs})"
+
+ def to_ffi(self) -> Filter:
+ """Converts the query to an FFI query."""
+ operation = self.operation
+
+ match operation:
+ case BinaryOperation.EQUAL:
+ return Filter(
+ root=EqualFilter(
+ equal=[self.lhs.to_ffi(), self.rhs.to_ffi()],
+ ),
+ )
+ case BinaryOperation.NOT_EQUAL:
+ return Filter(
+ root=NotEqualFilter(
+ not_equal=[self.lhs.to_ffi(), self.rhs.to_ffi()],
+ ),
+ )
+ case BinaryOperation.STARTS_WITH:
+ return Filter(
+ root=StartsWithFilter(
+ starts_with=[self.lhs.to_ffi(), self.rhs.to_ffi()],
+ ),
+ )
+ case BinaryOperation.ENDS_WITH:
+ return Filter(
+ root=EndsWithFilter(
+ ends_with=[self.lhs.to_ffi(), self.rhs.to_ffi()],
+ ),
+ )
+ case BinaryOperation.CONTAINS_SEGMENT:
+ return Filter(
+ root=ContainsSegmentFilter(
+ contains_segment=[self.lhs.to_ffi(), self.rhs.to_ffi()],
+ ),
+ )
+ case _:
+ assert_never(operation)
+
+
+# I would love to use TypeVarTuple here, but the ecosystem is not ready yet.
+class NaryFilter(BaseFilter, Generic[F]):
+ """Nary query."""
+
+ operation: NaryOperation
+ operands: list[F]
+
+ def __init__(
+ self,
+ operation: NaryOperation,
+ operands: Sequence[F],
+ ) -> None:
+ """Initializes the nary query."""
+ self.operation = operation
+ self.operands = [*operands]
+
+ def __repr__(self) -> str:
+ """Returns a string representation of the nary query."""
+ return f"NaryFilter({self.operation}, {self.operands})"
+
+ def to_ffi(self) -> Filter:
+ """Converts the query to an FFI query."""
+ operation = self.operation
+
+ match operation:
+ case NaryOperation.ALL:
+ return Filter(
+ root=AllFilter(
+ all=[operand.to_ffi() for operand in self.operands],
+ ),
+ )
+ case NaryOperation.ANY:
+ return Filter(
+ root=AnyFilter(
+ any=[operand.to_ffi() for operand in self.operands],
+ ),
+ )
+ case _:
+ assert_never(operation)
+
+
+class BaseFilterExpression(ABC):
+ """Protocol for filters."""
+
+ def equals(self, other: R) -> BinaryFilter[Self, R]:
+ """Checks if two filters are equal."""
+ return BinaryFilter(BinaryOperation.EQUAL, self, other)
+
+ def __eq__(self, other: R) -> BinaryFilter[Self, R]: # type: ignore[override]
+ """Checks if two filters are equal."""
+ if isinstance(other, BaseFilterExpression):
+ return self.equals(other)
+
+ msg: str # type: ignore[unreachable]
+ msg = f"unsupported operand type(s) for ==: '{type(self)}' and '{type(other)}'"
+ raise TypeError(msg)
+
+ def not_equals(self, other: R) -> BinaryFilter[Self, R]:
+ """Checks if two filters are not equal."""
+ return BinaryFilter(BinaryOperation.NOT_EQUAL, self, other)
+
+ def __ne__(self, other: R) -> BinaryFilter[Self, R]: # type: ignore[override]
+ """Checks if two filters are not equal."""
+ if isinstance(other, BaseFilterExpression):
+ return self.not_equals(other)
+
+ msg: str # type: ignore[unreachable]
+ msg = f"unsupported operand type(s) for !=: '{type(self)}' and '{type(other)}'"
+ raise TypeError(msg)
+
+ def starts_with(self, other: R) -> BinaryFilter[Self, R]:
+ """Checks if the left filter starts with the right filter."""
+ return BinaryFilter(BinaryOperation.STARTS_WITH, self, other)
+
+ def ends_with(self, other: R) -> BinaryFilter[Self, R]:
+ """Checks if the left filter ends with the right filter."""
+ return BinaryFilter(BinaryOperation.ENDS_WITH, self, other)
+
+ def contains_segment(self, other: R) -> BinaryFilter[Self, R]:
+ """Checks if the left filter contains the right filter as a segment."""
+ return BinaryFilter(BinaryOperation.CONTAINS_SEGMENT, self, other)
+
+ @abstractmethod
+ def to_ffi(self) -> FilterExpression:
+ """Converts the filter to an FFI filter."""
+ ...
+
+
+class Parameter(BaseFilterExpression):
+ """Values that are given to queries as parameters."""
+
+ value: Any
+
+ def __init__(self, value: Any) -> None: # noqa: ANN401
+ """Initializes the parameter with a value."""
+ self.value = value
+
+ def __repr__(self) -> str:
+ """Returns a string representation of the parameter."""
+ return f"Parameter({self.value!r})"
+
+ def to_ffi(self) -> FilterExpression:
+ """Converts the parameter to a FFI object."""
+ return FilterExpression(root=ParameterExpression(parameter=self.value))
+
+
+class Path(BaseFilterExpression):
+ """A path to a value in a query."""
+
+ value: list[QueryToken]
+
+ def __init__(self) -> None:
+ """Initialize the path with an empty vector."""
+ self.value = []
+
+ def __repr__(self) -> str:
+ """Return the repr."""
+ return f"{self.__class__.__name__}({self.value!r})"
+
+ @classmethod
+ def from_ffi(cls, value: list[QueryToken]) -> Self:
+ """Initialize the path with a vector."""
+ self = cls()
+ self.value = value[::]
+
+ return self
+
+ def push(self, value: QueryToken) -> Self:
+ """Push a value onto the path."""
+ tokens = self.value[::]
+ tokens.append(value)
+
+ return self.from_ffi(tokens)
+
+ def _finish(self) -> PathExpression:
+ """Finish the path."""
+ return PathExpression(path=self.value)
+
+ def to_ffi(self) -> FilterExpression:
+ """Converts the path to a FFI object."""
+ return FilterExpression(root=self._finish())
diff --git a/libs/hash-graph-sdk/python/graph_sdk/types.py b/libs/hash-graph-sdk/python/graph_sdk/types.py
new file mode 100644
index 0000000..bf36beb
--- /dev/null
+++ b/libs/hash-graph-sdk/python/graph_sdk/types.py
@@ -0,0 +1,212 @@
+"""Create dynamic types from entity, property, and data types via pydantic."""
+
+from uuid import UUID
+
+from graph_types import (
+ DataTypeSchema,
+ EntityTypeSchema,
+ PropertyTypeSchema,
+)
+from yarl import URL
+
+from graph_sdk.client.concurrent import HASHClient
+from graph_sdk.filter import (
+ DataTypeQueryPath,
+ EntityTypeQueryPath,
+ PropertyTypeQueryPath,
+)
+from graph_sdk.options import Options
+from graph_sdk.query import Parameter
+from graph_sdk.utils import (
+ async_to_sync,
+ filter_latest_ontology_types_from_subgraph,
+)
+
+
+class TypeAPI:
+ """GraphAPI for use with hash-graph-types."""
+
+ inner: HASHClient
+
+ def __init__(self, base: URL) -> None:
+ """Initialize the client with the base URL."""
+ self.inner = HASHClient(base)
+
+ async def load_data_type(
+ self,
+ data_type_id: str,
+ *,
+ actor_id: UUID,
+ ) -> DataTypeSchema:
+ """Load an external data type."""
+ await self.inner.load_external_data_type(URL(data_type_id), actor=actor_id)
+
+ return await self.get_data_type(
+ data_type_id,
+ actor_id=actor_id,
+ is_after_load=True,
+ )
+
+ async def get_data_type(
+ self,
+ data_type_id: str,
+ *,
+ actor_id: UUID,
+ is_after_load: bool = False,
+ ) -> DataTypeSchema:
+ """Returns the data type schema for the given data type ID.
+
+ If the data type is not found it will attempt to fetch it and use
+ the actor ID to authenticate the request.
+ TODO: remove this once H-136 is resolved.
+ """
+ subgraph = await self.inner.query_data_types(
+ DataTypeQueryPath().versioned_url() == Parameter(data_type_id),
+ Options(),
+ actor=actor_id,
+ )
+
+ latest = filter_latest_ontology_types_from_subgraph(subgraph)
+ if not latest:
+ if is_after_load:
+ msg = f"Could not find data type {data_type_id}"
+ raise ValueError(msg)
+
+ return await self.load_data_type(data_type_id, actor_id=actor_id)
+
+ vertex = latest[0]
+ data_type = vertex.root.inner.schema_
+
+ return DataTypeSchema(**data_type.model_dump(by_alias=True, mode="json"))
+
+ def get_data_type_sync(
+ self,
+ data_type_id: str,
+ *,
+ actor_id: UUID,
+ ) -> DataTypeSchema:
+ """Returns the data type schema for the given data type ID."""
+ return async_to_sync(self.get_data_type(data_type_id, actor_id=actor_id))
+
+ async def load_property_type(
+ self,
+ property_type_id: str,
+ *,
+ actor_id: UUID,
+ ) -> PropertyTypeSchema:
+ """Load an external property type."""
+ await self.inner.load_external_property_type(
+ URL(property_type_id),
+ actor=actor_id,
+ )
+
+ return await self.get_property_type(
+ property_type_id,
+ actor_id=actor_id,
+ is_after_load=True,
+ )
+
+ async def get_property_type(
+ self,
+ property_type_id: str,
+ *,
+ actor_id: UUID,
+ is_after_load: bool = False,
+ ) -> PropertyTypeSchema:
+ """Returns the property type schema for the given property type ID.
+
+ If the property type is not found it will attempt to fetch it and use
+ the actor ID to authenticate the request.
+ TODO: remove this once H-136 is resolved.
+ """
+ subgraph = await self.inner.query_property_types(
+ PropertyTypeQueryPath().versioned_url() == Parameter(property_type_id),
+ Options(),
+ actor=actor_id,
+ )
+
+ latest = filter_latest_ontology_types_from_subgraph(subgraph)
+ if not latest:
+ if is_after_load:
+ msg = f"Could not find property type {property_type_id}"
+ raise ValueError(msg)
+
+ return await self.load_property_type(property_type_id, actor_id=actor_id)
+
+ vertex = latest[0]
+ property_type = vertex.root.inner.schema_
+
+ return PropertyTypeSchema(
+ **property_type.model_dump(by_alias=True, mode="json"),
+ )
+
+ def get_property_type_sync(
+ self,
+ property_type_id: str,
+ *,
+ actor_id: UUID,
+ ) -> PropertyTypeSchema:
+ """Returns the property type schema for the given property type ID."""
+ return async_to_sync(
+ self.get_property_type(property_type_id, actor_id=actor_id),
+ )
+
+ async def load_entity_type(
+ self,
+ entity_type_id: str,
+ *,
+ actor_id: UUID,
+ ) -> EntityTypeSchema:
+ """Load an external entity type."""
+ await self.inner.load_external_entity_type(
+ URL(entity_type_id),
+ actor=actor_id,
+ )
+
+ return await self.get_entity_type(
+ entity_type_id,
+ actor_id=actor_id,
+ is_after_load=True,
+ )
+
+ async def get_entity_type(
+ self,
+ entity_type_id: str,
+ *,
+ actor_id: UUID,
+ is_after_load: bool = False,
+ ) -> EntityTypeSchema:
+ """Returns the entity type schema for the given entity type ID.
+
+ If the entity type is not found it will attempt to fetch it and use
+ the actor ID to authenticate the request.
+ TODO: remove this once H-136 is resolved.
+ """
+ subgraph = await self.inner.query_entity_types(
+ EntityTypeQueryPath().versioned_url() == Parameter(entity_type_id),
+ Options(),
+ actor=actor_id,
+ )
+
+ latest = filter_latest_ontology_types_from_subgraph(subgraph)
+
+ if not latest:
+ if is_after_load:
+ msg = f"Could not find entity type {entity_type_id}"
+ raise ValueError(msg)
+
+ return await self.load_entity_type(entity_type_id, actor_id=actor_id)
+
+ vertex = latest[0]
+ entity_type = vertex.root.inner.schema_
+
+ return EntityTypeSchema(**entity_type.model_dump(by_alias=True, mode="json"))
+
+ def get_entity_type_sync(
+ self,
+ entity_type_id: str,
+ *,
+ actor_id: UUID,
+ ) -> EntityTypeSchema:
+ """Returns the entity type schema for the given entity type ID."""
+ return async_to_sync(self.get_entity_type(entity_type_id, actor_id=actor_id))
diff --git a/libs/hash-graph-sdk/python/graph_sdk/utils.py b/libs/hash-graph-sdk/python/graph_sdk/utils.py
new file mode 100644
index 0000000..a9e9cca
--- /dev/null
+++ b/libs/hash-graph-sdk/python/graph_sdk/utils.py
@@ -0,0 +1,92 @@
+"""Miscellaneous utilities for the SDK."""
+
+import asyncio
+from collections.abc import Awaitable
+from typing import TYPE_CHECKING, Any, TypeVar
+
+from graph_client.models import KnowledgeGraphVertex, OntologyVertex, Subgraph
+from pydantic import BaseModel, ValidationError
+
+if TYPE_CHECKING:
+ from types import EllipsisType
+
+try:
+ # noinspection PyUnresolvedReferences
+ import devtools
+
+ IS_DEV = True
+except ImportError:
+ IS_DEV = False
+
+T = TypeVar("T")
+
+Missing = ...
+
+
+def async_to_sync(awaitable: Awaitable[T]) -> T:
+ """Run an awaitable and return the result.
+
+ Different from `asyncio.run` in that it does not create a new event loop each time.
+ """
+ response: T | "EllipsisType" = Missing
+
+ async def run_and_capture() -> None:
+ nonlocal response
+ response = await awaitable
+
+ loop = asyncio.get_event_loop()
+ coroutine = run_and_capture()
+ loop.run_until_complete(coroutine)
+
+ if response is Missing:
+ msg = "response was not set"
+ raise ValueError(msg)
+
+ return response
+
+
+def filter_latest_from_subgraph(
+ subgraph: Subgraph,
+) -> list[KnowledgeGraphVertex | OntologyVertex]:
+ """Filters the latest version of each entity from a subgraph."""
+ # is typed wrong, this can never be None
+ vertices = subgraph.vertices.root or {}
+
+ vertices_versions = (vertex for vertex in vertices.values() if vertex)
+ sorted_versions = (
+ sorted(vertex.items(), key=lambda kv: kv[0], reverse=True)
+ for vertex in vertices_versions
+ )
+
+ latest_vertices = (next(iter(entity)) for entity in sorted_versions)
+
+ return [vertex[1] for vertex in latest_vertices if vertex]
+
+
+def filter_latest_ontology_types_from_subgraph(
+ subgraph: Subgraph,
+) -> list[OntologyVertex]:
+ """Filters the latest version of each ontology type from a subgraph."""
+ return [
+ vertex
+ for vertex in filter_latest_from_subgraph(subgraph)
+ if isinstance(vertex, OntologyVertex)
+ ]
+
+
+def print_schema(model: type[BaseModel], data: Any) -> None: # noqa: ANN401
+ """Function to print the schema and the value of a BaseModel.
+
+ Debugging purposes only.
+ """
+ if not IS_DEV:
+ return
+
+ devtools.pprint(model.model_json_schema())
+
+ try:
+ parsed_data = model.model_validate(data)
+ except ValidationError as err:
+ devtools.pprint(err)
+ else:
+ devtools.pprint(parsed_data.model_dump())
diff --git a/libs/hash-graph-sdk/python/package.json b/libs/hash-graph-sdk/python/package.json
new file mode 100644
index 0000000..b3b324e
--- /dev/null
+++ b/libs/hash-graph-sdk/python/package.json
@@ -0,0 +1,25 @@
+{
+ "name": "@local/hash-graph-sdk-py",
+ "version": "0.0.0-private",
+ "private": true,
+ "description": "Type definitions for the type system and entity validation based on schemas",
+ "scripts": {
+ "build": "poetry build --no-interaction",
+ "codegen:blocking": "poetry run python ./scripts/generate_blocking.py",
+ "codegen:filter": "poetry run python ./scripts/generate_filter.py",
+ "fix:black": "poetry run black graph_sdk",
+ "fix:lock-files": "poetry lock --no-update",
+ "fix:ruff": "poetry run ruff graph_sdk --fix",
+ "lint:black": "poetry run black --check --diff --color graph_sdk",
+ "lint:lock-files": "poetry lock --check",
+ "lint:mypy": "poetry run mypy .",
+ "lint:ruff": "poetry run ruff graph_sdk",
+ "poetry:install": "poetry install --no-root --no-interaction",
+ "poetry:install-production": "poetry install --no-root --no-interaction --only main"
+ },
+ "dependencies": {
+ "@local/hash-graph-client-py": "0.0.0-private",
+ "@local/hash-graph-types-py": "0.0.0-private"
+ },
+ "devDependencies": {}
+}
diff --git a/libs/hash-graph-sdk/python/poetry.lock b/libs/hash-graph-sdk/python/poetry.lock
new file mode 100644
index 0000000..42bd30c
--- /dev/null
+++ b/libs/hash-graph-sdk/python/poetry.lock
@@ -0,0 +1,791 @@
+# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand.
+
+[[package]]
+name = "annotated-types"
+version = "0.5.0"
+description = "Reusable constraint types to use with typing.Annotated"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "annotated_types-0.5.0-py3-none-any.whl", hash = "sha256:58da39888f92c276ad970249761ebea80ba544b77acddaa1a4d6cf78287d45fd"},
+ {file = "annotated_types-0.5.0.tar.gz", hash = "sha256:47cdc3490d9ac1506ce92c7aaa76c579dc3509ff11e098fc867e5130ab7be802"},
+]
+
+[[package]]
+name = "anyio"
+version = "3.7.1"
+description = "High level compatibility layer for multiple asynchronous event loop implementations"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "anyio-3.7.1-py3-none-any.whl", hash = "sha256:91dee416e570e92c64041bd18b900d1d6fa78dff7048769ce5ac5ddad004fbb5"},
+ {file = "anyio-3.7.1.tar.gz", hash = "sha256:44a3c9aba0f5defa43261a8b3efb97891f2bd7d804e0e1f56419befa1adfc780"},
+]
+
+[package.dependencies]
+idna = ">=2.8"
+sniffio = ">=1.1"
+
+[package.extras]
+doc = ["Sphinx", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme (>=1.2.2)", "sphinxcontrib-jquery"]
+test = ["anyio[trio]", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "mock (>=4)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"]
+trio = ["trio (<0.22)"]
+
+[[package]]
+name = "asttokens"
+version = "2.2.1"
+description = "Annotate AST trees with source code positions"
+optional = false
+python-versions = "*"
+files = [
+ {file = "asttokens-2.2.1-py2.py3-none-any.whl", hash = "sha256:6b0ac9e93fb0335014d382b8fa9b3afa7df546984258005da0b9e7095b3deb1c"},
+ {file = "asttokens-2.2.1.tar.gz", hash = "sha256:4622110b2a6f30b77e1473affaa97e711bc2f07d3f10848420ff1898edbe94f3"},
+]
+
+[package.dependencies]
+six = "*"
+
+[package.extras]
+test = ["astroid", "pytest"]
+
+[[package]]
+name = "black"
+version = "23.10.1"
+description = "The uncompromising code formatter."
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "black-23.10.1-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:ec3f8e6234c4e46ff9e16d9ae96f4ef69fa328bb4ad08198c8cee45bb1f08c69"},
+ {file = "black-23.10.1-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:1b917a2aa020ca600483a7b340c165970b26e9029067f019e3755b56e8dd5916"},
+ {file = "black-23.10.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c74de4c77b849e6359c6f01987e94873c707098322b91490d24296f66d067dc"},
+ {file = "black-23.10.1-cp310-cp310-win_amd64.whl", hash = "sha256:7b4d10b0f016616a0d93d24a448100adf1699712fb7a4efd0e2c32bbb219b173"},
+ {file = "black-23.10.1-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:b15b75fc53a2fbcac8a87d3e20f69874d161beef13954747e053bca7a1ce53a0"},
+ {file = "black-23.10.1-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:e293e4c2f4a992b980032bbd62df07c1bcff82d6964d6c9496f2cd726e246ace"},
+ {file = "black-23.10.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d56124b7a61d092cb52cce34182a5280e160e6aff3137172a68c2c2c4b76bcb"},
+ {file = "black-23.10.1-cp311-cp311-win_amd64.whl", hash = "sha256:3f157a8945a7b2d424da3335f7ace89c14a3b0625e6593d21139c2d8214d55ce"},
+ {file = "black-23.10.1-cp38-cp38-macosx_10_16_arm64.whl", hash = "sha256:cfcce6f0a384d0da692119f2d72d79ed07c7159879d0bb1bb32d2e443382bf3a"},
+ {file = "black-23.10.1-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:33d40f5b06be80c1bbce17b173cda17994fbad096ce60eb22054da021bf933d1"},
+ {file = "black-23.10.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:840015166dbdfbc47992871325799fd2dc0dcf9395e401ada6d88fe11498abad"},
+ {file = "black-23.10.1-cp38-cp38-win_amd64.whl", hash = "sha256:037e9b4664cafda5f025a1728c50a9e9aedb99a759c89f760bd83730e76ba884"},
+ {file = "black-23.10.1-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:7cb5936e686e782fddb1c73f8aa6f459e1ad38a6a7b0e54b403f1f05a1507ee9"},
+ {file = "black-23.10.1-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:7670242e90dc129c539e9ca17665e39a146a761e681805c54fbd86015c7c84f7"},
+ {file = "black-23.10.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ed45ac9a613fb52dad3b61c8dea2ec9510bf3108d4db88422bacc7d1ba1243d"},
+ {file = "black-23.10.1-cp39-cp39-win_amd64.whl", hash = "sha256:6d23d7822140e3fef190734216cefb262521789367fbdc0b3f22af6744058982"},
+ {file = "black-23.10.1-py3-none-any.whl", hash = "sha256:d431e6739f727bb2e0495df64a6c7a5310758e87505f5f8cde9ff6c0f2d7e4fe"},
+ {file = "black-23.10.1.tar.gz", hash = "sha256:1f8ce316753428ff68749c65a5f7844631aa18c8679dfd3ca9dc1a289979c258"},
+]
+
+[package.dependencies]
+click = ">=8.0.0"
+mypy-extensions = ">=0.4.3"
+packaging = ">=22.0"
+pathspec = ">=0.9.0"
+platformdirs = ">=2"
+
+[package.extras]
+colorama = ["colorama (>=0.4.3)"]
+d = ["aiohttp (>=3.7.4)"]
+jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"]
+uvloop = ["uvloop (>=0.15.2)"]
+
+[[package]]
+name = "certifi"
+version = "2023.7.22"
+description = "Python package for providing Mozilla's CA Bundle."
+optional = false
+python-versions = ">=3.6"
+files = [
+ {file = "certifi-2023.7.22-py3-none-any.whl", hash = "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9"},
+ {file = "certifi-2023.7.22.tar.gz", hash = "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082"},
+]
+
+[[package]]
+name = "click"
+version = "8.1.6"
+description = "Composable command line interface toolkit"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "click-8.1.6-py3-none-any.whl", hash = "sha256:fa244bb30b3b5ee2cae3da8f55c9e5e0c0e86093306301fb418eb9dc40fbded5"},
+ {file = "click-8.1.6.tar.gz", hash = "sha256:48ee849951919527a045bfe3bf7baa8a959c423134e1a5b98c05c20ba75a1cbd"},
+]
+
+[package.dependencies]
+colorama = {version = "*", markers = "platform_system == \"Windows\""}
+
+[[package]]
+name = "colorama"
+version = "0.4.6"
+description = "Cross-platform colored terminal text."
+optional = false
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
+files = [
+ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
+ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
+]
+
+[[package]]
+name = "devtools"
+version = "0.12.2"
+description = "Python's missing debug print command, and more."
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "devtools-0.12.2-py3-none-any.whl", hash = "sha256:c366e3de1df4cdd635f1ad8cbcd3af01a384d7abda71900e68d43b04eb6aaca7"},
+ {file = "devtools-0.12.2.tar.gz", hash = "sha256:efceab184cb35e3a11fa8e602cc4fadacaa2e859e920fc6f87bf130b69885507"},
+]
+
+[package.dependencies]
+asttokens = ">=2.0.0,<3.0.0"
+executing = ">=1.1.1"
+pygments = ">=2.15.0"
+
+[[package]]
+name = "executing"
+version = "1.2.0"
+description = "Get the currently executing AST node of a frame, and other information"
+optional = false
+python-versions = "*"
+files = [
+ {file = "executing-1.2.0-py2.py3-none-any.whl", hash = "sha256:0314a69e37426e3608aada02473b4161d4caf5a4b244d1d0c48072b8fee7bacc"},
+ {file = "executing-1.2.0.tar.gz", hash = "sha256:19da64c18d2d851112f09c287f8d3dbbdf725ab0e569077efb6cdcbd3497c107"},
+]
+
+[package.extras]
+tests = ["asttokens", "littleutils", "pytest", "rich"]
+
+[[package]]
+name = "h11"
+version = "0.14.0"
+description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"},
+ {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"},
+]
+
+[[package]]
+name = "hash-graph-client"
+version = "0.0.0"
+description = "OpenAPI client for the HASH Graph API"
+optional = false
+python-versions = "^3.11"
+files = []
+develop = true
+
+[package.dependencies]
+httpx = "^0.25.0"
+pydantic = "^2.4.2"
+yarl = "^1.9.2"
+
+[package.source]
+type = "directory"
+url = "../../hash-graph-client/python"
+
+[[package]]
+name = "hash-graph-types"
+version = "0.0.0"
+description = "Type definitions for the type system and entity validation based on schemas"
+optional = false
+python-versions = "^3.11"
+files = []
+develop = true
+
+[package.dependencies]
+pydantic = "^2.4.2"
+pydantic_core = "^2.10.1"
+python-slugify = "^8.0.1"
+types-python-slugify = "^8.0"
+
+[package.source]
+type = "directory"
+url = "../../hash-graph-types/python"
+
+[[package]]
+name = "httpcore"
+version = "0.18.0"
+description = "A minimal low-level HTTP client."
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "httpcore-0.18.0-py3-none-any.whl", hash = "sha256:adc5398ee0a476567bf87467063ee63584a8bce86078bf748e48754f60202ced"},
+ {file = "httpcore-0.18.0.tar.gz", hash = "sha256:13b5e5cd1dca1a6636a6aaea212b19f4f85cd88c366a2b82304181b769aab3c9"},
+]
+
+[package.dependencies]
+anyio = ">=3.0,<5.0"
+certifi = "*"
+h11 = ">=0.13,<0.15"
+sniffio = "==1.*"
+
+[package.extras]
+http2 = ["h2 (>=3,<5)"]
+socks = ["socksio (==1.*)"]
+
+[[package]]
+name = "httpx"
+version = "0.25.0"
+description = "The next generation HTTP client."
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "httpx-0.25.0-py3-none-any.whl", hash = "sha256:181ea7f8ba3a82578be86ef4171554dd45fec26a02556a744db029a0a27b7100"},
+ {file = "httpx-0.25.0.tar.gz", hash = "sha256:47ecda285389cb32bb2691cc6e069e3ab0205956f681c5b2ad2325719751d875"},
+]
+
+[package.dependencies]
+certifi = "*"
+httpcore = ">=0.18.0,<0.19.0"
+idna = "*"
+sniffio = "*"
+
+[package.extras]
+brotli = ["brotli", "brotlicffi"]
+cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"]
+http2 = ["h2 (>=3,<5)"]
+socks = ["socksio (==1.*)"]
+
+[[package]]
+name = "idna"
+version = "3.4"
+description = "Internationalized Domain Names in Applications (IDNA)"
+optional = false
+python-versions = ">=3.5"
+files = [
+ {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"},
+ {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"},
+]
+
+[[package]]
+name = "multidict"
+version = "6.0.4"
+description = "multidict implementation"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "multidict-6.0.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b1a97283e0c85772d613878028fec909f003993e1007eafa715b24b377cb9b8"},
+ {file = "multidict-6.0.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:eeb6dcc05e911516ae3d1f207d4b0520d07f54484c49dfc294d6e7d63b734171"},
+ {file = "multidict-6.0.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d6d635d5209b82a3492508cf5b365f3446afb65ae7ebd755e70e18f287b0adf7"},
+ {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c048099e4c9e9d615545e2001d3d8a4380bd403e1a0578734e0d31703d1b0c0b"},
+ {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ea20853c6dbbb53ed34cb4d080382169b6f4554d394015f1bef35e881bf83547"},
+ {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16d232d4e5396c2efbbf4f6d4df89bfa905eb0d4dc5b3549d872ab898451f569"},
+ {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36c63aaa167f6c6b04ef2c85704e93af16c11d20de1d133e39de6a0e84582a93"},
+ {file = "multidict-6.0.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:64bdf1086b6043bf519869678f5f2757f473dee970d7abf6da91ec00acb9cb98"},
+ {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:43644e38f42e3af682690876cff722d301ac585c5b9e1eacc013b7a3f7b696a0"},
+ {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7582a1d1030e15422262de9f58711774e02fa80df0d1578995c76214f6954988"},
+ {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:ddff9c4e225a63a5afab9dd15590432c22e8057e1a9a13d28ed128ecf047bbdc"},
+ {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:ee2a1ece51b9b9e7752e742cfb661d2a29e7bcdba2d27e66e28a99f1890e4fa0"},
+ {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a2e4369eb3d47d2034032a26c7a80fcb21a2cb22e1173d761a162f11e562caa5"},
+ {file = "multidict-6.0.4-cp310-cp310-win32.whl", hash = "sha256:574b7eae1ab267e5f8285f0fe881f17efe4b98c39a40858247720935b893bba8"},
+ {file = "multidict-6.0.4-cp310-cp310-win_amd64.whl", hash = "sha256:4dcbb0906e38440fa3e325df2359ac6cb043df8e58c965bb45f4e406ecb162cc"},
+ {file = "multidict-6.0.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0dfad7a5a1e39c53ed00d2dd0c2e36aed4650936dc18fd9a1826a5ae1cad6f03"},
+ {file = "multidict-6.0.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:64da238a09d6039e3bd39bb3aee9c21a5e34f28bfa5aa22518581f910ff94af3"},
+ {file = "multidict-6.0.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ff959bee35038c4624250473988b24f846cbeb2c6639de3602c073f10410ceba"},
+ {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:01a3a55bd90018c9c080fbb0b9f4891db37d148a0a18722b42f94694f8b6d4c9"},
+ {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c5cb09abb18c1ea940fb99360ea0396f34d46566f157122c92dfa069d3e0e982"},
+ {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:666daae833559deb2d609afa4490b85830ab0dfca811a98b70a205621a6109fe"},
+ {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11bdf3f5e1518b24530b8241529d2050014c884cf18b6fc69c0c2b30ca248710"},
+ {file = "multidict-6.0.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7d18748f2d30f94f498e852c67d61261c643b349b9d2a581131725595c45ec6c"},
+ {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:458f37be2d9e4c95e2d8866a851663cbc76e865b78395090786f6cd9b3bbf4f4"},
+ {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b1a2eeedcead3a41694130495593a559a668f382eee0727352b9a41e1c45759a"},
+ {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7d6ae9d593ef8641544d6263c7fa6408cc90370c8cb2bbb65f8d43e5b0351d9c"},
+ {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:5979b5632c3e3534e42ca6ff856bb24b2e3071b37861c2c727ce220d80eee9ed"},
+ {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dcfe792765fab89c365123c81046ad4103fcabbc4f56d1c1997e6715e8015461"},
+ {file = "multidict-6.0.4-cp311-cp311-win32.whl", hash = "sha256:3601a3cece3819534b11d4efc1eb76047488fddd0c85a3948099d5da4d504636"},
+ {file = "multidict-6.0.4-cp311-cp311-win_amd64.whl", hash = "sha256:81a4f0b34bd92df3da93315c6a59034df95866014ac08535fc819f043bfd51f0"},
+ {file = "multidict-6.0.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:67040058f37a2a51ed8ea8f6b0e6ee5bd78ca67f169ce6122f3e2ec80dfe9b78"},
+ {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:853888594621e6604c978ce2a0444a1e6e70c8d253ab65ba11657659dcc9100f"},
+ {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:39ff62e7d0f26c248b15e364517a72932a611a9b75f35b45be078d81bdb86603"},
+ {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:af048912e045a2dc732847d33821a9d84ba553f5c5f028adbd364dd4765092ac"},
+ {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1e8b901e607795ec06c9e42530788c45ac21ef3aaa11dbd0c69de543bfb79a9"},
+ {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62501642008a8b9871ddfccbf83e4222cf8ac0d5aeedf73da36153ef2ec222d2"},
+ {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:99b76c052e9f1bc0721f7541e5e8c05db3941eb9ebe7b8553c625ef88d6eefde"},
+ {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:509eac6cf09c794aa27bcacfd4d62c885cce62bef7b2c3e8b2e49d365b5003fe"},
+ {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:21a12c4eb6ddc9952c415f24eef97e3e55ba3af61f67c7bc388dcdec1404a067"},
+ {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:5cad9430ab3e2e4fa4a2ef4450f548768400a2ac635841bc2a56a2052cdbeb87"},
+ {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ab55edc2e84460694295f401215f4a58597f8f7c9466faec545093045476327d"},
+ {file = "multidict-6.0.4-cp37-cp37m-win32.whl", hash = "sha256:5a4dcf02b908c3b8b17a45fb0f15b695bf117a67b76b7ad18b73cf8e92608775"},
+ {file = "multidict-6.0.4-cp37-cp37m-win_amd64.whl", hash = "sha256:6ed5f161328b7df384d71b07317f4d8656434e34591f20552c7bcef27b0ab88e"},
+ {file = "multidict-6.0.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5fc1b16f586f049820c5c5b17bb4ee7583092fa0d1c4e28b5239181ff9532e0c"},
+ {file = "multidict-6.0.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1502e24330eb681bdaa3eb70d6358e818e8e8f908a22a1851dfd4e15bc2f8161"},
+ {file = "multidict-6.0.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b692f419760c0e65d060959df05f2a531945af31fda0c8a3b3195d4efd06de11"},
+ {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45e1ecb0379bfaab5eef059f50115b54571acfbe422a14f668fc8c27ba410e7e"},
+ {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ddd3915998d93fbcd2566ddf9cf62cdb35c9e093075f862935573d265cf8f65d"},
+ {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:59d43b61c59d82f2effb39a93c48b845efe23a3852d201ed2d24ba830d0b4cf2"},
+ {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc8e1d0c705233c5dd0c5e6460fbad7827d5d36f310a0fadfd45cc3029762258"},
+ {file = "multidict-6.0.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6aa0418fcc838522256761b3415822626f866758ee0bc6632c9486b179d0b52"},
+ {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6748717bb10339c4760c1e63da040f5f29f5ed6e59d76daee30305894069a660"},
+ {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4d1a3d7ef5e96b1c9e92f973e43aa5e5b96c659c9bc3124acbbd81b0b9c8a951"},
+ {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4372381634485bec7e46718edc71528024fcdc6f835baefe517b34a33c731d60"},
+ {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:fc35cb4676846ef752816d5be2193a1e8367b4c1397b74a565a9d0389c433a1d"},
+ {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:4b9d9e4e2b37daddb5c23ea33a3417901fa7c7b3dee2d855f63ee67a0b21e5b1"},
+ {file = "multidict-6.0.4-cp38-cp38-win32.whl", hash = "sha256:e41b7e2b59679edfa309e8db64fdf22399eec4b0b24694e1b2104fb789207779"},
+ {file = "multidict-6.0.4-cp38-cp38-win_amd64.whl", hash = "sha256:d6c254ba6e45d8e72739281ebc46ea5eb5f101234f3ce171f0e9f5cc86991480"},
+ {file = "multidict-6.0.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:16ab77bbeb596e14212e7bab8429f24c1579234a3a462105cda4a66904998664"},
+ {file = "multidict-6.0.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bc779e9e6f7fda81b3f9aa58e3a6091d49ad528b11ed19f6621408806204ad35"},
+ {file = "multidict-6.0.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4ceef517eca3e03c1cceb22030a3e39cb399ac86bff4e426d4fc6ae49052cc60"},
+ {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:281af09f488903fde97923c7744bb001a9b23b039a909460d0f14edc7bf59706"},
+ {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:52f2dffc8acaba9a2f27174c41c9e57f60b907bb9f096b36b1a1f3be71c6284d"},
+ {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b41156839806aecb3641f3208c0dafd3ac7775b9c4c422d82ee2a45c34ba81ca"},
+ {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5e3fc56f88cc98ef8139255cf8cd63eb2c586531e43310ff859d6bb3a6b51f1"},
+ {file = "multidict-6.0.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8316a77808c501004802f9beebde51c9f857054a0c871bd6da8280e718444449"},
+ {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f70b98cd94886b49d91170ef23ec5c0e8ebb6f242d734ed7ed677b24d50c82cf"},
+ {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bf6774e60d67a9efe02b3616fee22441d86fab4c6d335f9d2051d19d90a40063"},
+ {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:e69924bfcdda39b722ef4d9aa762b2dd38e4632b3641b1d9a57ca9cd18f2f83a"},
+ {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:6b181d8c23da913d4ff585afd1155a0e1194c0b50c54fcfe286f70cdaf2b7176"},
+ {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:52509b5be062d9eafc8170e53026fbc54cf3b32759a23d07fd935fb04fc22d95"},
+ {file = "multidict-6.0.4-cp39-cp39-win32.whl", hash = "sha256:27c523fbfbdfd19c6867af7346332b62b586eed663887392cff78d614f9ec313"},
+ {file = "multidict-6.0.4-cp39-cp39-win_amd64.whl", hash = "sha256:33029f5734336aa0d4c0384525da0387ef89148dc7191aae00ca5fb23d7aafc2"},
+ {file = "multidict-6.0.4.tar.gz", hash = "sha256:3666906492efb76453c0e7b97f2cf459b0682e7402c0489a95484965dbc1da49"},
+]
+
+[[package]]
+name = "mypy"
+version = "1.6.1"
+description = "Optional static typing for Python"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "mypy-1.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e5012e5cc2ac628177eaac0e83d622b2dd499e28253d4107a08ecc59ede3fc2c"},
+ {file = "mypy-1.6.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d8fbb68711905f8912e5af474ca8b78d077447d8f3918997fecbf26943ff3cbb"},
+ {file = "mypy-1.6.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21a1ad938fee7d2d96ca666c77b7c494c3c5bd88dff792220e1afbebb2925b5e"},
+ {file = "mypy-1.6.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b96ae2c1279d1065413965c607712006205a9ac541895004a1e0d4f281f2ff9f"},
+ {file = "mypy-1.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:40b1844d2e8b232ed92e50a4bd11c48d2daa351f9deee6c194b83bf03e418b0c"},
+ {file = "mypy-1.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:81af8adaa5e3099469e7623436881eff6b3b06db5ef75e6f5b6d4871263547e5"},
+ {file = "mypy-1.6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8c223fa57cb154c7eab5156856c231c3f5eace1e0bed9b32a24696b7ba3c3245"},
+ {file = "mypy-1.6.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8032e00ce71c3ceb93eeba63963b864bf635a18f6c0c12da6c13c450eedb183"},
+ {file = "mypy-1.6.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4c46b51de523817a0045b150ed11b56f9fff55f12b9edd0f3ed35b15a2809de0"},
+ {file = "mypy-1.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:19f905bcfd9e167159b3d63ecd8cb5e696151c3e59a1742e79bc3bcb540c42c7"},
+ {file = "mypy-1.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:82e469518d3e9a321912955cc702d418773a2fd1e91c651280a1bda10622f02f"},
+ {file = "mypy-1.6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d4473c22cc296425bbbce7e9429588e76e05bc7342da359d6520b6427bf76660"},
+ {file = "mypy-1.6.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59a0d7d24dfb26729e0a068639a6ce3500e31d6655df8557156c51c1cb874ce7"},
+ {file = "mypy-1.6.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:cfd13d47b29ed3bbaafaff7d8b21e90d827631afda134836962011acb5904b71"},
+ {file = "mypy-1.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:eb4f18589d196a4cbe5290b435d135dee96567e07c2b2d43b5c4621b6501531a"},
+ {file = "mypy-1.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:41697773aa0bf53ff917aa077e2cde7aa50254f28750f9b88884acea38a16169"},
+ {file = "mypy-1.6.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7274b0c57737bd3476d2229c6389b2ec9eefeb090bbaf77777e9d6b1b5a9d143"},
+ {file = "mypy-1.6.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbaf4662e498c8c2e352da5f5bca5ab29d378895fa2d980630656178bd607c46"},
+ {file = "mypy-1.6.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bb8ccb4724f7d8601938571bf3f24da0da791fe2db7be3d9e79849cb64e0ae85"},
+ {file = "mypy-1.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:68351911e85145f582b5aa6cd9ad666c8958bcae897a1bfda8f4940472463c45"},
+ {file = "mypy-1.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:49ae115da099dcc0922a7a895c1eec82c1518109ea5c162ed50e3b3594c71208"},
+ {file = "mypy-1.6.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8b27958f8c76bed8edaa63da0739d76e4e9ad4ed325c814f9b3851425582a3cd"},
+ {file = "mypy-1.6.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:925cd6a3b7b55dfba252b7c4561892311c5358c6b5a601847015a1ad4eb7d332"},
+ {file = "mypy-1.6.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8f57e6b6927a49550da3d122f0cb983d400f843a8a82e65b3b380d3d7259468f"},
+ {file = "mypy-1.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:a43ef1c8ddfdb9575691720b6352761f3f53d85f1b57d7745701041053deff30"},
+ {file = "mypy-1.6.1-py3-none-any.whl", hash = "sha256:4cbe68ef919c28ea561165206a2dcb68591c50f3bcf777932323bc208d949cf1"},
+ {file = "mypy-1.6.1.tar.gz", hash = "sha256:4d01c00d09a0be62a4ca3f933e315455bde83f37f892ba4b08ce92f3cf44bcc1"},
+]
+
+[package.dependencies]
+mypy-extensions = ">=1.0.0"
+typing-extensions = ">=4.1.0"
+
+[package.extras]
+dmypy = ["psutil (>=4.0)"]
+install-types = ["pip"]
+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 = "packaging"
+version = "23.1"
+description = "Core utilities for Python packages"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"},
+ {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"},
+]
+
+[[package]]
+name = "pathspec"
+version = "0.11.2"
+description = "Utility library for gitignore style pattern matching of file paths."
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "pathspec-0.11.2-py3-none-any.whl", hash = "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20"},
+ {file = "pathspec-0.11.2.tar.gz", hash = "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3"},
+]
+
+[[package]]
+name = "platformdirs"
+version = "3.10.0"
+description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "platformdirs-3.10.0-py3-none-any.whl", hash = "sha256:d7c24979f292f916dc9cbf8648319032f551ea8c49a4c9bf2fb556a02070ec1d"},
+ {file = "platformdirs-3.10.0.tar.gz", hash = "sha256:b45696dab2d7cc691a3226759c0d3b00c47c8b6e293d96f6436f733303f77f6d"},
+]
+
+[package.extras]
+docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"]
+test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"]
+
+[[package]]
+name = "pydantic"
+version = "2.4.2"
+description = "Data validation using Python type hints"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "pydantic-2.4.2-py3-none-any.whl", hash = "sha256:bc3ddf669d234f4220e6e1c4d96b061abe0998185a8d7855c0126782b7abc8c1"},
+ {file = "pydantic-2.4.2.tar.gz", hash = "sha256:94f336138093a5d7f426aac732dcfe7ab4eb4da243c88f891d65deb4a2556ee7"},
+]
+
+[package.dependencies]
+annotated-types = ">=0.4.0"
+pydantic-core = "2.10.1"
+typing-extensions = ">=4.6.1"
+
+[package.extras]
+email = ["email-validator (>=2.0.0)"]
+
+[[package]]
+name = "pydantic-core"
+version = "2.10.1"
+description = ""
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "pydantic_core-2.10.1-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:d64728ee14e667ba27c66314b7d880b8eeb050e58ffc5fec3b7a109f8cddbd63"},
+ {file = "pydantic_core-2.10.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:48525933fea744a3e7464c19bfede85df4aba79ce90c60b94d8b6e1eddd67096"},
+ {file = "pydantic_core-2.10.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef337945bbd76cce390d1b2496ccf9f90b1c1242a3a7bc242ca4a9fc5993427a"},
+ {file = "pydantic_core-2.10.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a1392e0638af203cee360495fd2cfdd6054711f2db5175b6e9c3c461b76f5175"},
+ {file = "pydantic_core-2.10.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0675ba5d22de54d07bccde38997e780044dcfa9a71aac9fd7d4d7a1d2e3e65f7"},
+ {file = "pydantic_core-2.10.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:128552af70a64660f21cb0eb4876cbdadf1a1f9d5de820fed6421fa8de07c893"},
+ {file = "pydantic_core-2.10.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f6e6aed5818c264412ac0598b581a002a9f050cb2637a84979859e70197aa9e"},
+ {file = "pydantic_core-2.10.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ecaac27da855b8d73f92123e5f03612b04c5632fd0a476e469dfc47cd37d6b2e"},
+ {file = "pydantic_core-2.10.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b3c01c2fb081fced3bbb3da78510693dc7121bb893a1f0f5f4b48013201f362e"},
+ {file = "pydantic_core-2.10.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:92f675fefa977625105708492850bcbc1182bfc3e997f8eecb866d1927c98ae6"},
+ {file = "pydantic_core-2.10.1-cp310-none-win32.whl", hash = "sha256:420a692b547736a8d8703c39ea935ab5d8f0d2573f8f123b0a294e49a73f214b"},
+ {file = "pydantic_core-2.10.1-cp310-none-win_amd64.whl", hash = "sha256:0880e239827b4b5b3e2ce05e6b766a7414e5f5aedc4523be6b68cfbc7f61c5d0"},
+ {file = "pydantic_core-2.10.1-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:073d4a470b195d2b2245d0343569aac7e979d3a0dcce6c7d2af6d8a920ad0bea"},
+ {file = "pydantic_core-2.10.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:600d04a7b342363058b9190d4e929a8e2e715c5682a70cc37d5ded1e0dd370b4"},
+ {file = "pydantic_core-2.10.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39215d809470f4c8d1881758575b2abfb80174a9e8daf8f33b1d4379357e417c"},
+ {file = "pydantic_core-2.10.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eeb3d3d6b399ffe55f9a04e09e635554012f1980696d6b0aca3e6cf42a17a03b"},
+ {file = "pydantic_core-2.10.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a7a7902bf75779bc12ccfc508bfb7a4c47063f748ea3de87135d433a4cca7a2f"},
+ {file = "pydantic_core-2.10.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3625578b6010c65964d177626fde80cf60d7f2e297d56b925cb5cdeda6e9925a"},
+ {file = "pydantic_core-2.10.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:caa48fc31fc7243e50188197b5f0c4228956f97b954f76da157aae7f67269ae8"},
+ {file = "pydantic_core-2.10.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:07ec6d7d929ae9c68f716195ce15e745b3e8fa122fc67698ac6498d802ed0fa4"},
+ {file = "pydantic_core-2.10.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e6f31a17acede6a8cd1ae2d123ce04d8cca74056c9d456075f4f6f85de055607"},
+ {file = "pydantic_core-2.10.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d8f1ebca515a03e5654f88411420fea6380fc841d1bea08effb28184e3d4899f"},
+ {file = "pydantic_core-2.10.1-cp311-none-win32.whl", hash = "sha256:6db2eb9654a85ada248afa5a6db5ff1cf0f7b16043a6b070adc4a5be68c716d6"},
+ {file = "pydantic_core-2.10.1-cp311-none-win_amd64.whl", hash = "sha256:4a5be350f922430997f240d25f8219f93b0c81e15f7b30b868b2fddfc2d05f27"},
+ {file = "pydantic_core-2.10.1-cp311-none-win_arm64.whl", hash = "sha256:5fdb39f67c779b183b0c853cd6b45f7db84b84e0571b3ef1c89cdb1dfc367325"},
+ {file = "pydantic_core-2.10.1-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:b1f22a9ab44de5f082216270552aa54259db20189e68fc12484873d926426921"},
+ {file = "pydantic_core-2.10.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8572cadbf4cfa95fb4187775b5ade2eaa93511f07947b38f4cd67cf10783b118"},
+ {file = "pydantic_core-2.10.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:db9a28c063c7c00844ae42a80203eb6d2d6bbb97070cfa00194dff40e6f545ab"},
+ {file = "pydantic_core-2.10.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0e2a35baa428181cb2270a15864ec6286822d3576f2ed0f4cd7f0c1708472aff"},
+ {file = "pydantic_core-2.10.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05560ab976012bf40f25d5225a58bfa649bb897b87192a36c6fef1ab132540d7"},
+ {file = "pydantic_core-2.10.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d6495008733c7521a89422d7a68efa0a0122c99a5861f06020ef5b1f51f9ba7c"},
+ {file = "pydantic_core-2.10.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14ac492c686defc8e6133e3a2d9eaf5261b3df26b8ae97450c1647286750b901"},
+ {file = "pydantic_core-2.10.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8282bab177a9a3081fd3d0a0175a07a1e2bfb7fcbbd949519ea0980f8a07144d"},
+ {file = "pydantic_core-2.10.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:aafdb89fdeb5fe165043896817eccd6434aee124d5ee9b354f92cd574ba5e78f"},
+ {file = "pydantic_core-2.10.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:f6defd966ca3b187ec6c366604e9296f585021d922e666b99c47e78738b5666c"},
+ {file = "pydantic_core-2.10.1-cp312-none-win32.whl", hash = "sha256:7c4d1894fe112b0864c1fa75dffa045720a194b227bed12f4be7f6045b25209f"},
+ {file = "pydantic_core-2.10.1-cp312-none-win_amd64.whl", hash = "sha256:5994985da903d0b8a08e4935c46ed8daf5be1cf217489e673910951dc533d430"},
+ {file = "pydantic_core-2.10.1-cp312-none-win_arm64.whl", hash = "sha256:0d8a8adef23d86d8eceed3e32e9cca8879c7481c183f84ed1a8edc7df073af94"},
+ {file = "pydantic_core-2.10.1-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:9badf8d45171d92387410b04639d73811b785b5161ecadabf056ea14d62d4ede"},
+ {file = "pydantic_core-2.10.1-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:ebedb45b9feb7258fac0a268a3f6bec0a2ea4d9558f3d6f813f02ff3a6dc6698"},
+ {file = "pydantic_core-2.10.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cfe1090245c078720d250d19cb05d67e21a9cd7c257698ef139bc41cf6c27b4f"},
+ {file = "pydantic_core-2.10.1-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e357571bb0efd65fd55f18db0a2fb0ed89d0bb1d41d906b138f088933ae618bb"},
+ {file = "pydantic_core-2.10.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b3dcd587b69bbf54fc04ca157c2323b8911033e827fffaecf0cafa5a892a0904"},
+ {file = "pydantic_core-2.10.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c120c9ce3b163b985a3b966bb701114beb1da4b0468b9b236fc754783d85aa3"},
+ {file = "pydantic_core-2.10.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15d6bca84ffc966cc9976b09a18cf9543ed4d4ecbd97e7086f9ce9327ea48891"},
+ {file = "pydantic_core-2.10.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5cabb9710f09d5d2e9e2748c3e3e20d991a4c5f96ed8f1132518f54ab2967221"},
+ {file = "pydantic_core-2.10.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:82f55187a5bebae7d81d35b1e9aaea5e169d44819789837cdd4720d768c55d15"},
+ {file = "pydantic_core-2.10.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:1d40f55222b233e98e3921df7811c27567f0e1a4411b93d4c5c0f4ce131bc42f"},
+ {file = "pydantic_core-2.10.1-cp37-none-win32.whl", hash = "sha256:14e09ff0b8fe6e46b93d36a878f6e4a3a98ba5303c76bb8e716f4878a3bee92c"},
+ {file = "pydantic_core-2.10.1-cp37-none-win_amd64.whl", hash = "sha256:1396e81b83516b9d5c9e26a924fa69164156c148c717131f54f586485ac3c15e"},
+ {file = "pydantic_core-2.10.1-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:6835451b57c1b467b95ffb03a38bb75b52fb4dc2762bb1d9dbed8de31ea7d0fc"},
+ {file = "pydantic_core-2.10.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b00bc4619f60c853556b35f83731bd817f989cba3e97dc792bb8c97941b8053a"},
+ {file = "pydantic_core-2.10.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fa467fd300a6f046bdb248d40cd015b21b7576c168a6bb20aa22e595c8ffcdd"},
+ {file = "pydantic_core-2.10.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d99277877daf2efe074eae6338453a4ed54a2d93fb4678ddfe1209a0c93a2468"},
+ {file = "pydantic_core-2.10.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fa7db7558607afeccb33c0e4bf1c9a9a835e26599e76af6fe2fcea45904083a6"},
+ {file = "pydantic_core-2.10.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aad7bd686363d1ce4ee930ad39f14e1673248373f4a9d74d2b9554f06199fb58"},
+ {file = "pydantic_core-2.10.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:443fed67d33aa85357464f297e3d26e570267d1af6fef1c21ca50921d2976302"},
+ {file = "pydantic_core-2.10.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:042462d8d6ba707fd3ce9649e7bf268633a41018d6a998fb5fbacb7e928a183e"},
+ {file = "pydantic_core-2.10.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ecdbde46235f3d560b18be0cb706c8e8ad1b965e5c13bbba7450c86064e96561"},
+ {file = "pydantic_core-2.10.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ed550ed05540c03f0e69e6d74ad58d026de61b9eaebebbaaf8873e585cbb18de"},
+ {file = "pydantic_core-2.10.1-cp38-none-win32.whl", hash = "sha256:8cdbbd92154db2fec4ec973d45c565e767ddc20aa6dbaf50142676484cbff8ee"},
+ {file = "pydantic_core-2.10.1-cp38-none-win_amd64.whl", hash = "sha256:9f6f3e2598604956480f6c8aa24a3384dbf6509fe995d97f6ca6103bb8c2534e"},
+ {file = "pydantic_core-2.10.1-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:655f8f4c8d6a5963c9a0687793da37b9b681d9ad06f29438a3b2326d4e6b7970"},
+ {file = "pydantic_core-2.10.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e570ffeb2170e116a5b17e83f19911020ac79d19c96f320cbfa1fa96b470185b"},
+ {file = "pydantic_core-2.10.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:64322bfa13e44c6c30c518729ef08fda6026b96d5c0be724b3c4ae4da939f875"},
+ {file = "pydantic_core-2.10.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:485a91abe3a07c3a8d1e082ba29254eea3e2bb13cbbd4351ea4e5a21912cc9b0"},
+ {file = "pydantic_core-2.10.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7c2b8eb9fc872e68b46eeaf835e86bccc3a58ba57d0eedc109cbb14177be531"},
+ {file = "pydantic_core-2.10.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a5cb87bdc2e5f620693148b5f8f842d293cae46c5f15a1b1bf7ceeed324a740c"},
+ {file = "pydantic_core-2.10.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:25bd966103890ccfa028841a8f30cebcf5875eeac8c4bde4fe221364c92f0c9a"},
+ {file = "pydantic_core-2.10.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f323306d0556351735b54acbf82904fe30a27b6a7147153cbe6e19aaaa2aa429"},
+ {file = "pydantic_core-2.10.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0c27f38dc4fbf07b358b2bc90edf35e82d1703e22ff2efa4af4ad5de1b3833e7"},
+ {file = "pydantic_core-2.10.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f1365e032a477c1430cfe0cf2856679529a2331426f8081172c4a74186f1d595"},
+ {file = "pydantic_core-2.10.1-cp39-none-win32.whl", hash = "sha256:a1c311fd06ab3b10805abb72109f01a134019739bd3286b8ae1bc2fc4e50c07a"},
+ {file = "pydantic_core-2.10.1-cp39-none-win_amd64.whl", hash = "sha256:ae8a8843b11dc0b03b57b52793e391f0122e740de3df1474814c700d2622950a"},
+ {file = "pydantic_core-2.10.1-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:d43002441932f9a9ea5d6f9efaa2e21458221a3a4b417a14027a1d530201ef1b"},
+ {file = "pydantic_core-2.10.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:fcb83175cc4936a5425dde3356f079ae03c0802bbdf8ff82c035f8a54b333521"},
+ {file = "pydantic_core-2.10.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:962ed72424bf1f72334e2f1e61b68f16c0e596f024ca7ac5daf229f7c26e4208"},
+ {file = "pydantic_core-2.10.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2cf5bb4dd67f20f3bbc1209ef572a259027c49e5ff694fa56bed62959b41e1f9"},
+ {file = "pydantic_core-2.10.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e544246b859f17373bed915182ab841b80849ed9cf23f1f07b73b7c58baee5fb"},
+ {file = "pydantic_core-2.10.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:c0877239307b7e69d025b73774e88e86ce82f6ba6adf98f41069d5b0b78bd1bf"},
+ {file = "pydantic_core-2.10.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:53df009d1e1ba40f696f8995683e067e3967101d4bb4ea6f667931b7d4a01357"},
+ {file = "pydantic_core-2.10.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a1254357f7e4c82e77c348dabf2d55f1d14d19d91ff025004775e70a6ef40ada"},
+ {file = "pydantic_core-2.10.1-pp37-pypy37_pp73-macosx_10_7_x86_64.whl", hash = "sha256:524ff0ca3baea164d6d93a32c58ac79eca9f6cf713586fdc0adb66a8cdeab96a"},
+ {file = "pydantic_core-2.10.1-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f0ac9fb8608dbc6eaf17956bf623c9119b4db7dbb511650910a82e261e6600f"},
+ {file = "pydantic_core-2.10.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:320f14bd4542a04ab23747ff2c8a778bde727158b606e2661349557f0770711e"},
+ {file = "pydantic_core-2.10.1-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:63974d168b6233b4ed6a0046296803cb13c56637a7b8106564ab575926572a55"},
+ {file = "pydantic_core-2.10.1-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:417243bf599ba1f1fef2bb8c543ceb918676954734e2dcb82bf162ae9d7bd514"},
+ {file = "pydantic_core-2.10.1-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:dda81e5ec82485155a19d9624cfcca9be88a405e2857354e5b089c2a982144b2"},
+ {file = "pydantic_core-2.10.1-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:14cfbb00959259e15d684505263d5a21732b31248a5dd4941f73a3be233865b9"},
+ {file = "pydantic_core-2.10.1-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:631cb7415225954fdcc2a024119101946793e5923f6c4d73a5914d27eb3d3a05"},
+ {file = "pydantic_core-2.10.1-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:bec7dd208a4182e99c5b6c501ce0b1f49de2802448d4056091f8e630b28e9a52"},
+ {file = "pydantic_core-2.10.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:149b8a07712f45b332faee1a2258d8ef1fb4a36f88c0c17cb687f205c5dc6e7d"},
+ {file = "pydantic_core-2.10.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4d966c47f9dd73c2d32a809d2be529112d509321c5310ebf54076812e6ecd884"},
+ {file = "pydantic_core-2.10.1-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7eb037106f5c6b3b0b864ad226b0b7ab58157124161d48e4b30c4a43fef8bc4b"},
+ {file = "pydantic_core-2.10.1-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:154ea7c52e32dce13065dbb20a4a6f0cc012b4f667ac90d648d36b12007fa9f7"},
+ {file = "pydantic_core-2.10.1-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e562617a45b5a9da5be4abe72b971d4f00bf8555eb29bb91ec2ef2be348cd132"},
+ {file = "pydantic_core-2.10.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:f23b55eb5464468f9e0e9a9935ce3ed2a870608d5f534025cd5536bca25b1402"},
+ {file = "pydantic_core-2.10.1-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:e9121b4009339b0f751955baf4543a0bfd6bc3f8188f8056b1a25a2d45099934"},
+ {file = "pydantic_core-2.10.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:0523aeb76e03f753b58be33b26540880bac5aa54422e4462404c432230543f33"},
+ {file = "pydantic_core-2.10.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e0e2959ef5d5b8dc9ef21e1a305a21a36e254e6a34432d00c72a92fdc5ecda5"},
+ {file = "pydantic_core-2.10.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da01bec0a26befab4898ed83b362993c844b9a607a86add78604186297eb047e"},
+ {file = "pydantic_core-2.10.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f2e9072d71c1f6cfc79a36d4484c82823c560e6f5599c43c1ca6b5cdbd54f881"},
+ {file = "pydantic_core-2.10.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f36a3489d9e28fe4b67be9992a23029c3cec0babc3bd9afb39f49844a8c721c5"},
+ {file = "pydantic_core-2.10.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f64f82cc3443149292b32387086d02a6c7fb39b8781563e0ca7b8d7d9cf72bd7"},
+ {file = "pydantic_core-2.10.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:b4a6db486ac8e99ae696e09efc8b2b9fea67b63c8f88ba7a1a16c24a057a0776"},
+ {file = "pydantic_core-2.10.1.tar.gz", hash = "sha256:0f8682dbdd2f67f8e1edddcbffcc29f60a6182b4901c367fc8c1c40d30bb0a82"},
+]
+
+[package.dependencies]
+typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0"
+
+[[package]]
+name = "pygments"
+version = "2.16.1"
+description = "Pygments is a syntax highlighting package written in Python."
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "Pygments-2.16.1-py3-none-any.whl", hash = "sha256:13fc09fa63bc8d8671a6d247e1eb303c4b343eaee81d861f3404db2935653692"},
+ {file = "Pygments-2.16.1.tar.gz", hash = "sha256:1daff0494820c69bc8941e407aa20f577374ee88364ee10a98fdbe0aece96e29"},
+]
+
+[package.extras]
+plugins = ["importlib-metadata"]
+
+[[package]]
+name = "python-slugify"
+version = "8.0.1"
+description = "A Python slugify application that also handles Unicode"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "python-slugify-8.0.1.tar.gz", hash = "sha256:ce0d46ddb668b3be82f4ed5e503dbc33dd815d83e2eb6824211310d3fb172a27"},
+ {file = "python_slugify-8.0.1-py2.py3-none-any.whl", hash = "sha256:70ca6ea68fe63ecc8fa4fcf00ae651fc8a5d02d93dcd12ae6d4fc7ca46c4d395"},
+]
+
+[package.dependencies]
+text-unidecode = ">=1.3"
+
+[package.extras]
+unidecode = ["Unidecode (>=1.1.1)"]
+
+[[package]]
+name = "ruff"
+version = "0.1.2"
+description = "An extremely fast Python linter, written in Rust."
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "ruff-0.1.2-py3-none-macosx_10_7_x86_64.whl", hash = "sha256:0d3ee66b825b713611f89aa35d16de984f76f26c50982a25d52cd0910dff3923"},
+ {file = "ruff-0.1.2-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:f85f850a320ff532b8f93e8d1da6a36ef03698c446357c8c43b46ef90bb321eb"},
+ {file = "ruff-0.1.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:809c6d4e45683696d19ca79e4c6bd3b2e9204fe9546923f2eb3b126ec314b0dc"},
+ {file = "ruff-0.1.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:46005e4abb268e93cad065244e17e2ea16b6fcb55a5c473f34fbc1fd01ae34cb"},
+ {file = "ruff-0.1.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10cdb302f519664d5e2cf954562ac86c9d20ca05855e5b5c2f9d542228f45da4"},
+ {file = "ruff-0.1.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:f89ebcbe57a1eab7d7b4ceb57ddf0af9ed13eae24e443a7c1dc078000bd8cc6b"},
+ {file = "ruff-0.1.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7344eaca057d4c32373c9c3a7afb7274f56040c225b6193dd495fcf69453b436"},
+ {file = "ruff-0.1.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dffa25f6e03c4950b6ac6f216bc0f98a4be9719cb0c5260c8e88d1bac36f1683"},
+ {file = "ruff-0.1.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42ddaea52cb7ba7c785e8593a7532866c193bc774fe570f0e4b1ccedd95b83c5"},
+ {file = "ruff-0.1.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a8533efda625bbec0bf27da2886bd641dae0c209104f6c39abc4be5b7b22de2a"},
+ {file = "ruff-0.1.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:b0b1b82221ba7c50e03b7a86b983157b5d3f4d8d4f16728132bdf02c6d651f77"},
+ {file = "ruff-0.1.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:6c1362eb9288f8cc95535294cb03bd4665c8cef86ec32745476a4e5c6817034c"},
+ {file = "ruff-0.1.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:ffa7ef5ded0563329a35bd5a1cfdae40f05a75c0cc2dd30f00b1320b1fb461fc"},
+ {file = "ruff-0.1.2-py3-none-win32.whl", hash = "sha256:6e8073f85e47072256e2e1909f1ae515cf61ff5a4d24730a63b8b4ac24b6704a"},
+ {file = "ruff-0.1.2-py3-none-win_amd64.whl", hash = "sha256:b836ddff662a45385948ee0878b0a04c3a260949905ad861a37b931d6ee1c210"},
+ {file = "ruff-0.1.2-py3-none-win_arm64.whl", hash = "sha256:b0c42d00db5639dbd5f7f9923c63648682dd197bf5de1151b595160c96172691"},
+ {file = "ruff-0.1.2.tar.gz", hash = "sha256:afd4785ae060ce6edcd52436d0c197628a918d6d09e3107a892a1bad6a4c6608"},
+]
+
+[[package]]
+name = "setuptools"
+version = "68.2.2"
+description = "Easily download, build, install, upgrade, and uninstall Python packages"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "setuptools-68.2.2-py3-none-any.whl", hash = "sha256:b454a35605876da60632df1a60f736524eb73cc47bbc9f3f1ef1b644de74fd2a"},
+ {file = "setuptools-68.2.2.tar.gz", hash = "sha256:4ac1475276d2f1c48684874089fefcd83bd7162ddaafb81fac866ba0db282a87"},
+]
+
+[package.extras]
+docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
+testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
+testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.1)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"]
+
+[[package]]
+name = "six"
+version = "1.16.0"
+description = "Python 2 and 3 compatibility utilities"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
+files = [
+ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
+ {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
+]
+
+[[package]]
+name = "sniffio"
+version = "1.3.0"
+description = "Sniff out which async library your code is running under"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"},
+ {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"},
+]
+
+[[package]]
+name = "text-unidecode"
+version = "1.3"
+description = "The most basic Text::Unidecode port"
+optional = false
+python-versions = "*"
+files = [
+ {file = "text-unidecode-1.3.tar.gz", hash = "sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93"},
+ {file = "text_unidecode-1.3-py2.py3-none-any.whl", hash = "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8"},
+]
+
+[[package]]
+name = "types-python-slugify"
+version = "8.0.0.3"
+description = "Typing stubs for python-slugify"
+optional = false
+python-versions = "*"
+files = [
+ {file = "types-python-slugify-8.0.0.3.tar.gz", hash = "sha256:868e6610ab9a01c01b2ccc1b982363e694d6bbb4fcf32e0d82688c89dceb4e2c"},
+ {file = "types_python_slugify-8.0.0.3-py3-none-any.whl", hash = "sha256:2353c161c79ab6cce955b50720c6cd03586ec297558122236d130e4a19f21209"},
+]
+
+[[package]]
+name = "typing-extensions"
+version = "4.7.1"
+description = "Backported and Experimental Type Hints for Python 3.7+"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "typing_extensions-4.7.1-py3-none-any.whl", hash = "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36"},
+ {file = "typing_extensions-4.7.1.tar.gz", hash = "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2"},
+]
+
+[[package]]
+name = "yarl"
+version = "1.9.2"
+description = "Yet another URL library"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "yarl-1.9.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8c2ad583743d16ddbdf6bb14b5cd76bf43b0d0006e918809d5d4ddf7bde8dd82"},
+ {file = "yarl-1.9.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:82aa6264b36c50acfb2424ad5ca537a2060ab6de158a5bd2a72a032cc75b9eb8"},
+ {file = "yarl-1.9.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c0c77533b5ed4bcc38e943178ccae29b9bcf48ffd1063f5821192f23a1bd27b9"},
+ {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee4afac41415d52d53a9833ebae7e32b344be72835bbb589018c9e938045a560"},
+ {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9bf345c3a4f5ba7f766430f97f9cc1320786f19584acc7086491f45524a551ac"},
+ {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a96c19c52ff442a808c105901d0bdfd2e28575b3d5f82e2f5fd67e20dc5f4ea"},
+ {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:891c0e3ec5ec881541f6c5113d8df0315ce5440e244a716b95f2525b7b9f3608"},
+ {file = "yarl-1.9.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c3a53ba34a636a256d767c086ceb111358876e1fb6b50dfc4d3f4951d40133d5"},
+ {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:566185e8ebc0898b11f8026447eacd02e46226716229cea8db37496c8cdd26e0"},
+ {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:2b0738fb871812722a0ac2154be1f049c6223b9f6f22eec352996b69775b36d4"},
+ {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:32f1d071b3f362c80f1a7d322bfd7b2d11e33d2adf395cc1dd4df36c9c243095"},
+ {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:e9fdc7ac0d42bc3ea78818557fab03af6181e076a2944f43c38684b4b6bed8e3"},
+ {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:56ff08ab5df8429901ebdc5d15941b59f6253393cb5da07b4170beefcf1b2528"},
+ {file = "yarl-1.9.2-cp310-cp310-win32.whl", hash = "sha256:8ea48e0a2f931064469bdabca50c2f578b565fc446f302a79ba6cc0ee7f384d3"},
+ {file = "yarl-1.9.2-cp310-cp310-win_amd64.whl", hash = "sha256:50f33040f3836e912ed16d212f6cc1efb3231a8a60526a407aeb66c1c1956dde"},
+ {file = "yarl-1.9.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:646d663eb2232d7909e6601f1a9107e66f9791f290a1b3dc7057818fe44fc2b6"},
+ {file = "yarl-1.9.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:aff634b15beff8902d1f918012fc2a42e0dbae6f469fce134c8a0dc51ca423bb"},
+ {file = "yarl-1.9.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a83503934c6273806aed765035716216cc9ab4e0364f7f066227e1aaea90b8d0"},
+ {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b25322201585c69abc7b0e89e72790469f7dad90d26754717f3310bfe30331c2"},
+ {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:22a94666751778629f1ec4280b08eb11815783c63f52092a5953faf73be24191"},
+ {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ec53a0ea2a80c5cd1ab397925f94bff59222aa3cf9c6da938ce05c9ec20428d"},
+ {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:159d81f22d7a43e6eabc36d7194cb53f2f15f498dbbfa8edc8a3239350f59fe7"},
+ {file = "yarl-1.9.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:832b7e711027c114d79dffb92576acd1bd2decc467dec60e1cac96912602d0e6"},
+ {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:95d2ecefbcf4e744ea952d073c6922e72ee650ffc79028eb1e320e732898d7e8"},
+ {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d4e2c6d555e77b37288eaf45b8f60f0737c9efa3452c6c44626a5455aeb250b9"},
+ {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:783185c75c12a017cc345015ea359cc801c3b29a2966c2655cd12b233bf5a2be"},
+ {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:b8cc1863402472f16c600e3e93d542b7e7542a540f95c30afd472e8e549fc3f7"},
+ {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:822b30a0f22e588b32d3120f6d41e4ed021806418b4c9f0bc3048b8c8cb3f92a"},
+ {file = "yarl-1.9.2-cp311-cp311-win32.whl", hash = "sha256:a60347f234c2212a9f0361955007fcf4033a75bf600a33c88a0a8e91af77c0e8"},
+ {file = "yarl-1.9.2-cp311-cp311-win_amd64.whl", hash = "sha256:be6b3fdec5c62f2a67cb3f8c6dbf56bbf3f61c0f046f84645cd1ca73532ea051"},
+ {file = "yarl-1.9.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:38a3928ae37558bc1b559f67410df446d1fbfa87318b124bf5032c31e3447b74"},
+ {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac9bb4c5ce3975aeac288cfcb5061ce60e0d14d92209e780c93954076c7c4367"},
+ {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3da8a678ca8b96c8606bbb8bfacd99a12ad5dd288bc6f7979baddd62f71c63ef"},
+ {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:13414591ff516e04fcdee8dc051c13fd3db13b673c7a4cb1350e6b2ad9639ad3"},
+ {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf74d08542c3a9ea97bb8f343d4fcbd4d8f91bba5ec9d5d7f792dbe727f88938"},
+ {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e7221580dc1db478464cfeef9b03b95c5852cc22894e418562997df0d074ccc"},
+ {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:494053246b119b041960ddcd20fd76224149cfea8ed8777b687358727911dd33"},
+ {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:52a25809fcbecfc63ac9ba0c0fb586f90837f5425edfd1ec9f3372b119585e45"},
+ {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:e65610c5792870d45d7b68c677681376fcf9cc1c289f23e8e8b39c1485384185"},
+ {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:1b1bba902cba32cdec51fca038fd53f8beee88b77efc373968d1ed021024cc04"},
+ {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:662e6016409828ee910f5d9602a2729a8a57d74b163c89a837de3fea050c7582"},
+ {file = "yarl-1.9.2-cp37-cp37m-win32.whl", hash = "sha256:f364d3480bffd3aa566e886587eaca7c8c04d74f6e8933f3f2c996b7f09bee1b"},
+ {file = "yarl-1.9.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6a5883464143ab3ae9ba68daae8e7c5c95b969462bbe42e2464d60e7e2698368"},
+ {file = "yarl-1.9.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5610f80cf43b6202e2c33ba3ec2ee0a2884f8f423c8f4f62906731d876ef4fac"},
+ {file = "yarl-1.9.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b9a4e67ad7b646cd6f0938c7ebfd60e481b7410f574c560e455e938d2da8e0f4"},
+ {file = "yarl-1.9.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:83fcc480d7549ccebe9415d96d9263e2d4226798c37ebd18c930fce43dfb9574"},
+ {file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5fcd436ea16fee7d4207c045b1e340020e58a2597301cfbcfdbe5abd2356c2fb"},
+ {file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84e0b1599334b1e1478db01b756e55937d4614f8654311eb26012091be109d59"},
+ {file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3458a24e4ea3fd8930e934c129b676c27452e4ebda80fbe47b56d8c6c7a63a9e"},
+ {file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:838162460b3a08987546e881a2bfa573960bb559dfa739e7800ceeec92e64417"},
+ {file = "yarl-1.9.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f4e2d08f07a3d7d3e12549052eb5ad3eab1c349c53ac51c209a0e5991bbada78"},
+ {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:de119f56f3c5f0e2fb4dee508531a32b069a5f2c6e827b272d1e0ff5ac040333"},
+ {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:149ddea5abf329752ea5051b61bd6c1d979e13fbf122d3a1f9f0c8be6cb6f63c"},
+ {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:674ca19cbee4a82c9f54e0d1eee28116e63bc6fd1e96c43031d11cbab8b2afd5"},
+ {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:9b3152f2f5677b997ae6c804b73da05a39daa6a9e85a512e0e6823d81cdad7cc"},
+ {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5415d5a4b080dc9612b1b63cba008db84e908b95848369aa1da3686ae27b6d2b"},
+ {file = "yarl-1.9.2-cp38-cp38-win32.whl", hash = "sha256:f7a3d8146575e08c29ed1cd287068e6d02f1c7bdff8970db96683b9591b86ee7"},
+ {file = "yarl-1.9.2-cp38-cp38-win_amd64.whl", hash = "sha256:63c48f6cef34e6319a74c727376e95626f84ea091f92c0250a98e53e62c77c72"},
+ {file = "yarl-1.9.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:75df5ef94c3fdc393c6b19d80e6ef1ecc9ae2f4263c09cacb178d871c02a5ba9"},
+ {file = "yarl-1.9.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c027a6e96ef77d401d8d5a5c8d6bc478e8042f1e448272e8d9752cb0aff8b5c8"},
+ {file = "yarl-1.9.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f3b078dbe227f79be488ffcfc7a9edb3409d018e0952cf13f15fd6512847f3f7"},
+ {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59723a029760079b7d991a401386390c4be5bfec1e7dd83e25a6a0881859e716"},
+ {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b03917871bf859a81ccb180c9a2e6c1e04d2f6a51d953e6a5cdd70c93d4e5a2a"},
+ {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c1012fa63eb6c032f3ce5d2171c267992ae0c00b9e164efe4d73db818465fac3"},
+ {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a74dcbfe780e62f4b5a062714576f16c2f3493a0394e555ab141bf0d746bb955"},
+ {file = "yarl-1.9.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8c56986609b057b4839968ba901944af91b8e92f1725d1a2d77cbac6972b9ed1"},
+ {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2c315df3293cd521033533d242d15eab26583360b58f7ee5d9565f15fee1bef4"},
+ {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:b7232f8dfbd225d57340e441d8caf8652a6acd06b389ea2d3222b8bc89cbfca6"},
+ {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:53338749febd28935d55b41bf0bcc79d634881195a39f6b2f767870b72514caf"},
+ {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:066c163aec9d3d073dc9ffe5dd3ad05069bcb03fcaab8d221290ba99f9f69ee3"},
+ {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8288d7cd28f8119b07dd49b7230d6b4562f9b61ee9a4ab02221060d21136be80"},
+ {file = "yarl-1.9.2-cp39-cp39-win32.whl", hash = "sha256:b124e2a6d223b65ba8768d5706d103280914d61f5cae3afbc50fc3dfcc016623"},
+ {file = "yarl-1.9.2-cp39-cp39-win_amd64.whl", hash = "sha256:61016e7d582bc46a5378ffdd02cd0314fb8ba52f40f9cf4d9a5e7dbef88dee18"},
+ {file = "yarl-1.9.2.tar.gz", hash = "sha256:04ab9d4b9f587c06d801c2abfe9317b77cdf996c65a90d5e84ecc45010823571"},
+]
+
+[package.dependencies]
+idna = ">=2.0"
+multidict = ">=4.0"
+
+[metadata]
+lock-version = "2.0"
+python-versions = "^3.11"
+content-hash = "901e0f877711bdb1830e251e1d315406aabe994e48c90731a6c0346cbfbeff08"
diff --git a/libs/hash-graph-sdk/python/poetry.toml b/libs/hash-graph-sdk/python/poetry.toml
new file mode 100644
index 0000000..a5f04e6
--- /dev/null
+++ b/libs/hash-graph-sdk/python/poetry.toml
@@ -0,0 +1,3 @@
+[virtualenvs]
+name = ".venv"
+in-project = true
diff --git a/libs/hash-graph-sdk/python/pyproject.toml b/libs/hash-graph-sdk/python/pyproject.toml
new file mode 100644
index 0000000..50a2d8f
--- /dev/null
+++ b/libs/hash-graph-sdk/python/pyproject.toml
@@ -0,0 +1,90 @@
+[tool.poetry]
+name = "hash-graph-sdk"
+version = "0.0.0"
+description = "SDK for the HASH Graph API"
+license = "AGPL-3.0-only"
+authors = ["HASH"]
+readme = "README.md"
+packages = [{ include = "graph_sdk" }]
+
+[tool.poetry.dependencies]
+python = "^3.11"
+
+pydantic = "^2.4.2"
+yarl = "^1.9.2"
+
+[tool.poetry.group.dev.dependencies]
+setuptools = "^68.2.2"
+devtools = "^0.12.2"
+
+[tool.poetry.group.lint-tools.dependencies]
+black = "^23.10.1"
+ruff = "^0.1.2"
+mypy = "^1.6.1"
+
+hash-graph-client = { path = "../../hash-graph-client/python", develop = true }
+hash-graph-types = { path = "../../hash-graph-types/python", develop = true }
+
+
+[tool.black]
+target-version = ['py311']
+preview = true
+extend-exclude = '''
+(
+ ^/graph_sdk/client/blocking.py$ | ^/graph_sdk/filter/path.py$
+)
+'''
+
+[tool.ruff]
+select = ["ALL"]
+extend-exclude = ["graph_sdk/client/blocking.py", "graph_sdk/filter/path.py"]
+ignore = [
+ "D203", # this conflicts with `D211`
+ "D213", # this conflicts with `D212`
+ "D401", # Relates to PEP-257 but also conflicts with Google Python Style Guide, generally gets in the way
+ "ANN101", # most type checkers infer the type of `self` automatically
+ "ANN102", # most type checkers infer the type of `cls` automatically
+ "FIX002", # TODOs should be fixed, not removed
+ "TD002", # We don't add authors to TODO strings
+ "PLC0414", # Conflicts with `no_implicit_reexport = true`
+]
+
+target-version = "py311"
+
+[tool.mypy]
+plugins = "pydantic.mypy"
+
+disallow_untyped_defs = true
+disallow_any_unimported = true
+no_implicit_optional = true
+check_untyped_defs = true
+no_implicit_reexport = true
+strict_optional = true
+show_error_codes = true
+show_traceback = true
+strict_equality = true
+
+# warnings
+warn_unused_configs = true
+warn_return_any = true
+warn_incomplete_stub = true
+warn_unreachable = true
+warn_redundant_casts = true
+warn_unused_ignores = true
+
+# error messages
+pretty = true
+color_output = true
+error_summary = true
+incremental = true
+
+[tool.pydantic-mypy]
+init_forbid_extra = true
+init_typed = true
+warn_required_dynamic_aliases = true
+warn_untyped_fields = true
+
+
+[build-system]
+requires = ["poetry-core"]
+build-backend = "poetry.core.masonry.api"
diff --git a/libs/hash-graph-sdk/python/scripts/generate_blocking.py b/libs/hash-graph-sdk/python/scripts/generate_blocking.py
new file mode 100644
index 0000000..97268aa
--- /dev/null
+++ b/libs/hash-graph-sdk/python/scripts/generate_blocking.py
@@ -0,0 +1,219 @@
+# type: ignore
+"""Script to generate the blocking implementation of the client."""
+
+import ast
+from pathlib import Path
+
+ROOT = Path(__file__).parent.parent
+
+concurrent_contents = (ROOT / "graph_sdk" / "client" / "concurrent.py").read_text()
+blocking_ast = ast.parse(concurrent_contents, "concurrent.py", type_comments=True)
+
+# The transformation that we want to do is pretty simple:
+# * copy over all contents, pick the HASHClient class
+# * add imports to utils and the concurrent client
+# * go over each method that is sync, call the inner client instead
+# * go over each async method, wrap it in a sync method
+
+# generate a new top level type comment for the blocking client
+blocking_ast_doc: ast.Expr = blocking_ast.body[0]
+
+blocking_ast_doc.value = ast.Constant(value="""Blocking API for the Graph SDK.
+
+This is just a thin wrapper around the async API.
+
+(Usually, one could achieve this by simply wrapping the async API automatically,
+the problem with that approach however is that users loose the ability to look
+at the source code)
+""")
+
+# add imports to utils and the concurrent client
+blocking_ast.body.insert(
+ 1,
+ ast.ImportFrom(
+ module="graph_sdk.utils",
+ names=[ast.alias(name="async_to_sync")],
+ ),
+)
+blocking_ast.body.insert(
+ 1,
+ ast.ImportFrom(
+ module="graph_sdk.client.concurrent",
+ names=[ast.alias(name="HASHClient", asname="ConcurrentHASHClient")],
+ ),
+)
+
+# find the HASHClient class
+client_ast: ast.ClassDef = next(
+ statement for statement in blocking_ast.body if isinstance(statement, ast.ClassDef)
+)
+
+# remove all field annotations (AnnAssign)
+client_ast.body = [
+ statement
+ for statement in client_ast.body
+ if not isinstance(statement, ast.AnnAssign)
+]
+
+
+# add a new field annotation for the inner client
+client_ast.body.insert(
+ 1,
+ ast.AnnAssign(
+ target=ast.Name(id="inner", ctx=ast.Store()),
+ annotation=ast.Name(id="ConcurrentHASHClient", ctx=ast.Load()),
+ simple=1,
+ ),
+)
+
+# change the __init__ method to just init the inner client
+client_init = next(
+ statement
+ for statement in client_ast.body
+ if isinstance(statement, ast.FunctionDef) and statement.name == "__init__"
+)
+
+client_init_args = client_init.args
+
+client_init.body = [
+ client_init.body[0],
+ ast.Assign(
+ targets=[
+ ast.Attribute(
+ value=ast.Name(id="self", ctx=ast.Load()),
+ attr="inner",
+ ctx=ast.Store(),
+ ),
+ ],
+ value=ast.Call(
+ func=ast.Name(id="ConcurrentHASHClient", ctx=ast.Load()),
+ args=[
+ ast.Name(arg.arg) for arg in client_init_args.args if arg.arg != "self"
+ ],
+ keywords=[],
+ ),
+ ),
+]
+
+
+def call_inner(function_def: ast.FunctionDef) -> ast.Call:
+ """Call the inner client method."""
+ args = function_def.args
+
+ return ast.Call(
+ func=(
+ ast.Attribute(
+ value=ast.Attribute(
+ value=ast.Name(id="self", ctx=ast.Load()),
+ attr="inner",
+ ctx=ast.Load(),
+ ),
+ attr=function_def.name,
+ ctx=ast.Load(),
+ )
+ ),
+ args=[
+ ast.Name(arg.arg, ctx=ast.Load()) for arg in args.args if arg.arg != "self"
+ ],
+ keywords=[
+ ast.keyword(arg=keyword.arg, value=ast.Name(keyword.arg, ctx=ast.Load()))
+ for keyword in (args.kwarg or [])
+ ],
+ )
+
+
+# # go over each method that is sync, call the inner client instead
+for method in client_ast.body:
+ if not isinstance(method, ast.FunctionDef):
+ continue
+
+ if method.name in ("__init__", "__doc__", "actor"):
+ continue
+
+ method_args = method.args
+
+ method.body = [
+ method.body[0],
+ ast.Return(value=call_inner(method)),
+ ]
+
+# go over each async method, wrap it in the async_to_sync method and return it
+for method in client_ast.body:
+ if not isinstance(method, ast.AsyncFunctionDef):
+ continue
+
+ method_args = method.args
+ method_name = method.name
+ method_doc = method.body[0]
+ method_returns = method.returns
+
+ client_ast.body.append(
+ ast.FunctionDef(
+ name=method_name,
+ args=method_args,
+ body=[
+ method_doc,
+ ast.Return(
+ value=ast.Call(
+ func=ast.Name(id="async_to_sync", ctx=ast.Load()),
+ args=[
+ ast.Call(
+ func=ast.Attribute(
+ value=ast.Attribute(
+ value=ast.Name(id="self", ctx=ast.Load()),
+ attr="inner",
+ ctx=ast.Load(),
+ ),
+ attr=method.name,
+ ctx=ast.Load(),
+ ),
+ args=[
+ ast.Name(arg.arg, ctx=ast.Load())
+ for arg in method_args.args
+ if arg.arg != "self"
+ ],
+ keywords=[
+ ast.keyword(
+ arg=keyword.arg,
+ value=ast.Name(keyword.arg, ctx=ast.Load()),
+ )
+ for keyword in (
+ (method_args.kwarg or [])
+ + (method_args.kwonlyargs or [])
+ )
+ ],
+ ),
+ ],
+ keywords=[],
+ ),
+ ),
+ ],
+ returns=method_returns,
+ decorator_list=[],
+ ),
+ )
+
+# remove all async methods
+client_ast.body = [
+ statement
+ for statement in client_ast.body
+ if not isinstance(statement, ast.AsyncFunctionDef)
+]
+
+# remove with_actor and assert_not_none
+blocking_ast.body = [
+ statement
+ for statement in blocking_ast.body
+ if not (
+ isinstance(statement, ast.FunctionDef)
+ and statement.name in ("with_actor", "assert_not_none")
+ )
+]
+
+output = ast.unparse(ast.fix_missing_locations(blocking_ast))
+(ROOT / "graph_sdk" / "client" / "blocking.py").write_text(f"""
+# =========================================
+# THIS FILE IS GENERATED, DO NOT CHANGE IT!
+# =========================================
+
+{output}""")
diff --git a/libs/hash-graph-sdk/python/scripts/generate_filter.py b/libs/hash-graph-sdk/python/scripts/generate_filter.py
new file mode 100644
index 0000000..52f5e16
--- /dev/null
+++ b/libs/hash-graph-sdk/python/scripts/generate_filter.py
@@ -0,0 +1,618 @@
+"""Generate `graph_sdk.filter.path` from `graph_client.models`.
+
+We need to generate 4 classes:
+
+- DataTypeQueryPath
+- PropertyTypeQueryPath
+- EntityTypeQueryPath
+- EntityQueryPath
+
+To generate them we have a configuration file that is located
+under `graph_sdk/filter/config.json`.
+
+The configuration file is a record with the following fields:
+- `data_type`
+- `property_type`
+- `entity_type`
+- `entity`
+
+Each entry has the following fields:
+- `selector`: Fields that require an intermediary selector, and then continue
+ with another type. It is a record, where the key is the field name
+ and the value is the type to continue with.
+ (one of `data_type`, `property_type`, `entity_type`, `entity`)
+- `untyped`: Fields that go into untyped territory through the properties selector.
+- `direct`: Fields that do not require an intermediary selector, and then continue
+ with another type. It is a record, where the key is the field name and the
+ value is the type to continue with.
+ (one of `data_type`, `property_type`, `entity_type`, `entity`)
+- `arguments`: Fields that have (optional) arguments,
+ these arguments follow as `key-value` pairs after the name of the property.
+"""
+# ruff: noqa: ERA001
+import ast
+import json
+from enum import Enum
+from pathlib import Path
+from typing import Literal, TypedDict, assert_never
+
+from graph_client.models import (
+ DataTypeQueryToken,
+ EntityQueryToken,
+ EntityTypeQueryToken,
+ PropertyTypeQueryToken,
+)
+
+DIRECTORY = Path(__file__).parent.parent
+
+TypeId = Literal["data_type", "property_type", "entity_type", "entity"]
+
+
+class Argument(TypedDict):
+ """Type information for an argument."""
+
+ type: Literal["uint"]
+ required: bool
+
+
+class Meta(TypedDict):
+ """Additional configuration file information."""
+
+ arguments: dict[str, Argument]
+
+
+class ConfigurationEntry(TypedDict):
+ """A configuration entry."""
+
+ selector: dict[str, TypeId]
+ untyped: list[str]
+ direct: dict[str, TypeId]
+ arguments: dict[str, list[str]]
+
+
+class Configuration(TypedDict):
+ """The configuration file."""
+
+ meta: Meta
+
+ data_type: ConfigurationEntry
+ property_type: ConfigurationEntry
+ entity_type: ConfigurationEntry
+ entity: ConfigurationEntry
+
+
+def is_builtin(name: str) -> bool:
+ """Check if the given name is a builtin."""
+ return name in dir(__builtins__)
+
+
+def is_keyword(name: str) -> bool:
+ """Check if the given name is a keyword."""
+ try:
+ ast.parse(f"def {name}(): ...")
+ except SyntaxError:
+ return True
+
+ return False
+
+
+def is_keyword_or_builtin(name: str) -> bool:
+ """Check if the given name is a keyword or builtin."""
+ return is_builtin(name) or is_keyword(name)
+
+
+def to_snake_case(name: str) -> str:
+ """Convert a camel case name to snake case."""
+ return "".join(
+ f"_{char.lower()}" if char.isupper() else char for char in name
+ ).lstrip("_")
+
+
+def get_class_name(
+ name: Literal["data_type", "property_type", "entity_type", "entity"],
+) -> str:
+ """Get the class name for the given name."""
+ return {
+ "data_type": "DataTypeQueryPath",
+ "property_type": "PropertyTypeQueryPath",
+ "entity_type": "EntityTypeQueryPath",
+ "entity": "EntityQueryPath",
+ }[name]
+
+
+def get_human_readable_name(token: Enum) -> str:
+ """Get the human readable name for the given token."""
+ return {
+ "DataTypeQueryToken": "a data type",
+ "PropertyTypeQueryToken": "a property type",
+ "EntityTypeQueryToken": "an entity type",
+ "EntityQueryToken": "an entity",
+ }[type(token).__name__]
+
+
+def load_attribute(
+ path: list[str],
+ ctx: ast.Load | ast.Store | None = None,
+) -> ast.Attribute | ast.Name:
+ """Create a dotted attribute."""
+ if len(path) == 1:
+ return ast.Name(id=path[0], ctx=ctx)
+
+ return ast.Attribute(
+ value=load_attribute(path[:-1], ctx=ctx),
+ attr=path[-1],
+ ctx=ast.Load(),
+ )
+
+
+def argument_annotation(argument: Argument) -> ast.expr:
+ """Generate the annotation for an argument."""
+ type_ = None
+
+ if argument["type"] == "uint":
+ type_ = ast.Name(id="int", ctx=ast.Load())
+
+ if type_ is None:
+ assert_never(argument["type"])
+
+ if not argument["required"]:
+ return ast.BinOp(left=type_, op=ast.BitOr(), right=ast.Name(id="None"))
+
+ return type_
+
+
+def create_method(
+ *,
+ name: str,
+ args: dict[str, Argument],
+ body: list[ast.stmt],
+ returns: ast.expr,
+) -> ast.FunctionDef:
+ """Create a method with arguments."""
+ keywords = [
+ ast.arg(arg=to_snake_case(name), annotation=argument_annotation(info))
+ for name, info in args.items()
+ ]
+
+ keyword_defaults = [
+ None if info["required"] else ast.Constant(value=None) for info in args.values()
+ ]
+
+ return ast.FunctionDef(
+ name=name,
+ args=ast.arguments(
+ posonlyargs=[],
+ args=[ast.arg(arg="self", annotation=None)],
+ vararg=None,
+ kwonlyargs=keywords,
+ kw_defaults=keyword_defaults,
+ kwarg=None,
+ defaults=[],
+ ),
+ body=body,
+ decorator_list=[],
+ returns=returns,
+ )
+
+
+def generate_method_docstring(
+ token: Enum,
+ name: str,
+) -> ast.Expr:
+ """Generate a docstring for the given method."""
+ return ast.Expr(
+ value=ast.Constant(
+ value=(
+ f"Return the path to the {name} attribute "
+ f"of {get_human_readable_name(token)}."
+ ),
+ ),
+ )
+
+
+def generate_method_argument_body(args: dict[str, Argument]) -> list[ast.stmt]:
+ """Generate the body for the method arguments."""
+ if not args:
+ return []
+
+ # we need to create additional body statements, that will compile to:
+ # args = []
+ # if is not None:
+ # args.append(f"{}={}")
+ #
+ statements: list[ast.stmt] = [
+ ast.Assign(
+ [ast.Name(id="args", ctx=ast.Store())],
+ ast.List(elts=[], ctx=ast.Load()),
+ ),
+ ]
+
+ for name, info in args.items():
+ variable = ast.Name(id=to_snake_case(name), ctx=ast.Load())
+
+ append_value = ast.JoinedStr(
+ values=[
+ ast.Constant(value=f"{name}="),
+ ast.FormattedValue(value=variable, conversion=-1, format_spec=None),
+ ],
+ )
+
+ if info["required"]:
+ statements.append(
+ ast.Expr(
+ value=ast.Call(
+ func=load_attribute(["args", "append"]),
+ args=[append_value],
+ keywords=[],
+ ),
+ ),
+ )
+ else:
+ statements.append(
+ ast.If(
+ test=ast.Compare(
+ left=variable,
+ ops=[ast.IsNot()],
+ comparators=[ast.Name(id="None", ctx=ast.Load())],
+ ),
+ body=[
+ ast.Expr(
+ value=ast.Call(
+ func=load_attribute(["args", "append"]),
+ args=[append_value],
+ keywords=[],
+ ),
+ ),
+ ],
+ orelse=[],
+ ),
+ )
+
+ return statements
+
+
+def generate_push_call(token: Enum, name: str, args: dict[str, Argument]) -> ast.Call:
+ """Generate the call to push."""
+ if not args:
+ return ast.Call(
+ func=load_attribute(["self", "path", "push"]),
+ args=[load_attribute([type(token).__name__, name])],
+ keywords=[],
+ )
+
+ # we need to generate:
+ # self.path.push(f"{.}({ ", ".join() })"
+ # if args else .)
+ return ast.Call(
+ func=load_attribute(["self", "path", "push"]),
+ args=[
+ ast.IfExp(
+ test=ast.Name(id="args", ctx=ast.Load()),
+ body=ast.JoinedStr(
+ values=[
+ ast.FormattedValue(
+ value=load_attribute([type(token).__name__, name]),
+ conversion=-1,
+ format_spec=None,
+ ),
+ ast.Constant(value="("),
+ ast.FormattedValue(
+ value=ast.Call(
+ func=ast.Attribute(
+ value=ast.Constant(value=", "),
+ attr="join",
+ ),
+ args=[ast.Name(id="args", ctx=ast.Load())],
+ keywords=[],
+ ),
+ conversion=-1,
+ format_spec=None,
+ ),
+ ast.Constant(value=")"),
+ ],
+ ),
+ orelse=load_attribute([type(token).__name__, name]),
+ ),
+ ],
+ keywords=[],
+ )
+
+
+def generate_plain_method(
+ token: Enum,
+ name: str,
+ args: dict[str, Argument],
+) -> ast.FunctionDef:
+ """Generate a method that ends the path."""
+ function_name = name
+ while is_keyword_or_builtin(function_name):
+ function_name += "_"
+
+ return create_method(
+ name=function_name,
+ body=[
+ generate_method_docstring(token, name),
+ *generate_method_argument_body(args),
+ ast.Return(value=generate_push_call(token, name, args)),
+ ],
+ args=args,
+ returns=ast.Name(id="Path", ctx=ast.Load()),
+ )
+
+
+def generate_selector_method(
+ class_name: str,
+ token: Enum,
+ name: str,
+ args: dict[str, Argument],
+ next_type: TypeId,
+) -> ast.FunctionDef:
+ """Generate a method that continues the path after a selector to a type."""
+ next_class_name = get_class_name(next_type)
+ next_class_name_set: ast.expr = ast.Name(id=next_class_name, ctx=ast.Load())
+
+ if class_name == next_class_name:
+ next_class_name = "Self"
+ next_class_name_set = ast.Call(
+ func=ast.Name(id="type", ctx=ast.Load()),
+ args=[ast.Name(id="self", ctx=ast.Load())],
+ keywords=[],
+ )
+
+ function_name = name
+ while is_keyword_or_builtin(function_name):
+ function_name += "_"
+
+ return create_method(
+ name=function_name,
+ body=[
+ generate_method_docstring(token, name),
+ *generate_method_argument_body(args),
+ ast.Return(
+ value=ast.Call(
+ func=ast.Attribute(
+ ast.Call(
+ func=ast.Attribute(
+ value=ast.Subscript(
+ value=ast.Name(
+ id="SelectorQueryPath",
+ ctx=ast.Load(),
+ ),
+ slice=ast.Name(id=next_class_name, ctx=ast.Load()),
+ ),
+ attr="from_path",
+ ctx=ast.Load(),
+ ),
+ args=[generate_push_call(token, name, args)],
+ keywords=[],
+ ),
+ attr="set_cls",
+ ctx=ast.Load(),
+ ),
+ args=[next_class_name_set],
+ keywords=[],
+ ),
+ ),
+ ],
+ args=args,
+ returns=ast.Subscript(
+ value=ast.Name(id="SelectorQueryPath", ctx=ast.Load()),
+ slice=ast.Name(id=next_class_name),
+ ),
+ )
+
+
+def generate_untyped_method(
+ token: Enum,
+ name: str,
+ args: dict[str, Argument],
+) -> ast.FunctionDef:
+ """Generate a method that continues the path with a wildcard selector."""
+ function_name = name
+ while is_keyword_or_builtin(function_name):
+ function_name += "_"
+
+ return create_method(
+ name=function_name,
+ body=[
+ generate_method_docstring(token, name),
+ *generate_method_argument_body(args),
+ ast.Return(
+ value=ast.Call(
+ func=load_attribute(["UntypedQueryPath", "from_path"]),
+ args=[generate_push_call(token, name, args)],
+ keywords=[],
+ ),
+ ),
+ ],
+ args=args,
+ returns=ast.Name(id="UntypedQueryPath", ctx=ast.Load()),
+ )
+
+
+def generate_direct_method(
+ class_name: str,
+ token: Enum,
+ name: str,
+ args: dict[str, Argument],
+ next_type: TypeId,
+) -> ast.FunctionDef:
+ """Generate a method that continues the path with the specified type."""
+ next_class_name = get_class_name(next_type)
+ call_from_path_on = next_class_name
+
+ if class_name == next_class_name:
+ next_class_name = "Self"
+ call_from_path_on = "self"
+
+ function_name = name
+ while is_keyword_or_builtin(function_name):
+ function_name += "_"
+
+ return create_method(
+ name=function_name,
+ body=[
+ generate_method_docstring(token, name),
+ *generate_method_argument_body(args),
+ ast.Return(
+ value=ast.Call(
+ func=load_attribute([call_from_path_on, "from_path"]),
+ args=[generate_push_call(token, name, args)],
+ keywords=[],
+ ),
+ ),
+ ],
+ args=args,
+ returns=ast.Name(id=next_class_name, ctx=ast.Load()),
+ )
+
+
+def generate_method(
+ class_name: str,
+ tokens: Enum,
+ method_name: str,
+ *,
+ config: ConfigurationEntry,
+ meta: Meta,
+) -> ast.FunctionDef:
+ """Generate a method for a path class."""
+ arguments = config["arguments"].get(method_name, [])
+ args = {key: meta["arguments"][key] for key in arguments}
+
+ if selector := config["selector"].get(method_name, None):
+ return generate_selector_method(class_name, tokens, method_name, args, selector)
+
+ if method_name in config["untyped"]:
+ return generate_untyped_method(tokens, method_name, args)
+
+ if direct := config["direct"].get(method_name, None):
+ return generate_direct_method(class_name, tokens, method_name, args, direct)
+
+ return generate_plain_method(tokens, method_name, args)
+
+
+def generate_path(
+ id_: TypeId,
+ config: ConfigurationEntry,
+ meta: Meta,
+) -> ast.ClassDef:
+ """Generate a path class."""
+ class_name = get_class_name(id_)
+
+ tokens: type[Enum] = {
+ "data_type": DataTypeQueryToken,
+ "property_type": PropertyTypeQueryToken,
+ "entity_type": EntityTypeQueryToken,
+ "entity": EntityQueryToken,
+ }[id_]
+
+ body = [
+ generate_method(
+ class_name,
+ token,
+ token.name,
+ config=config,
+ meta=meta,
+ )
+ for token in tokens
+ ]
+
+ return ast.ClassDef(
+ name=class_name,
+ bases=[ast.Name(id="AbstractQueryPath", ctx=ast.Load())],
+ keywords=[],
+ body=[
+ ast.Expr(
+ value=ast.Constant(
+ value=(
+ "A query path for"
+ f" {get_human_readable_name(next(iter(tokens)))}."
+ ),
+ ),
+ ),
+ *body,
+ ],
+ decorator_list=[],
+ )
+
+
+def imports() -> list[ast.ImportFrom]:
+ """Generate the imports for the module."""
+ return [
+ ast.ImportFrom(
+ module="typing",
+ names=[
+ ast.alias(name="Self", asname=None),
+ ],
+ ),
+ ast.ImportFrom(
+ module="graph_client.models",
+ names=[
+ ast.alias(name="DataTypeQueryToken", asname=None),
+ ast.alias(name="PropertyTypeQueryToken", asname=None),
+ ast.alias(name="EntityTypeQueryToken", asname=None),
+ ast.alias(name="EntityQueryToken", asname=None),
+ ],
+ ),
+ ast.ImportFrom(
+ module="graph_sdk.filter.base",
+ names=[
+ ast.alias(name="AbstractQueryPath", asname=None),
+ ast.alias(name="UntypedQueryPath", asname=None),
+ ast.alias(name="SelectorQueryPath", asname=None),
+ ],
+ ),
+ ast.ImportFrom(
+ module="graph_sdk.query",
+ names=[
+ ast.alias(name="Path", asname=None),
+ ],
+ ),
+ ]
+
+
+def doc_comment() -> ast.Expr:
+ """Add a doc comment to the top of the file."""
+ return ast.Expr(
+ value=ast.Constant(
+ value=(
+ "Definitions for all path objects.\n\nThis file is auto-generated. Do"
+ " not edit!"
+ ),
+ ),
+ )
+
+
+configuration: Configuration = json.loads(
+ (DIRECTORY / "graph_sdk" / "filter" / "config.json").read_text(),
+)
+
+
+module = ast.Module(
+ body=[
+ doc_comment(),
+ *imports(),
+ generate_path("data_type", configuration["data_type"], configuration["meta"]),
+ generate_path(
+ "property_type",
+ configuration["property_type"],
+ configuration["meta"],
+ ),
+ generate_path(
+ "entity_type",
+ configuration["entity_type"],
+ configuration["meta"],
+ ),
+ generate_path("entity", configuration["entity"], configuration["meta"]),
+ ],
+ type_ignores=[],
+)
+
+output = ast.unparse(ast.fix_missing_locations(module))
+
+(DIRECTORY / "graph_sdk" / "filter" / "path.py").write_text(f"""
+# =========================================
+# THIS FILE IS GENERATED, DO NOT CHANGE IT!
+# =========================================
+
+{output}""")
diff --git a/libs/hash-graph-sdk/python/turbo.json b/libs/hash-graph-sdk/python/turbo.json
new file mode 100644
index 0000000..fbe9ae6
--- /dev/null
+++ b/libs/hash-graph-sdk/python/turbo.json
@@ -0,0 +1,56 @@
+{
+ "extends": ["//"],
+ "pipeline": {
+ "codegen:blocking": {
+ "inputs": [
+ "./graph_sdk/client/concurrent.py",
+ "./scripts/generate_blocking.py"
+ ],
+ "outputs": ["./graph_sdk/client/blocking.py"]
+ },
+ "codegen:filter": {
+ "inputs": [
+ "../../../../apps/hash-graph/openapi/openapi.json",
+ "./scripts/generate_filter.py"
+ ],
+ "outputs": ["./graph_sdk/filter/path.py"]
+ },
+ "build": {
+ "dependsOn": ["^build", "codegen"],
+ "inputs": ["./**/*.py", "pyproject.toml", "poetry.lock", "LICENSE*"],
+ "outputs": ["dist/**"]
+ },
+ "poetry:venv": {
+ "cache": false
+ },
+ "poetry:install": {
+ "cache": false
+ },
+ "poetry:install-production": {
+ "cache": false
+ },
+ "fix:black": {
+ "inputs": ["./**/*.py", "pyproject.toml"]
+ },
+ "fix:ruff": {
+ "inputs": ["./**/*.py", "pyproject.toml"]
+ },
+ "fix:lock-files": {
+ "inputs": ["poetry.lock", "pyproject.toml"],
+ "outputs": ["poetry.lock"]
+ },
+ "lint:black": {
+ "inputs": ["./**/*.py", "pyproject.toml"]
+ },
+ "lint:lock-files": {
+ "inputs": ["poetry.lock", "pyproject.toml"]
+ },
+ "lint:ruff": {
+ "inputs": ["./**/*.py", "pyproject.toml"]
+ },
+ "lint:mypy": {
+ "dependsOn": ["build"],
+ "inputs": ["./**/*.py", "pyproject.toml"]
+ }
+ }
+}
diff --git a/libs/hash-graph-types/python/LICENSE.md b/libs/hash-graph-types/python/LICENSE.md
new file mode 100644
index 0000000..9a70d79
--- /dev/null
+++ b/libs/hash-graph-types/python/LICENSE.md
@@ -0,0 +1,606 @@
+# GNU Affero General Public License
+
+_Version 3, 19 November 2007_
+_Copyright © 2007 Free Software Foundation, Inc. <>_
+
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+
+## Preamble
+
+The GNU Affero General Public License is a free, copyleft license for
+software and other kinds of works, specifically designed to ensure
+cooperation with the community in the case of network server software.
+
+The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+our General Public Licenses are intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.
+
+When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+Developers that use our General Public Licenses protect your rights
+with two steps: **(1)** assert copyright on the software, and **(2)** offer
+you this License which gives you legal permission to copy, distribute
+and/or modify the software.
+
+A secondary benefit of defending all users' freedom is that
+improvements made in alternate versions of the program, if they
+receive widespread use, become available for other developers to
+incorporate. Many developers of free software are heartened and
+encouraged by the resulting cooperation. However, in the case of
+software used on network servers, this result may fail to come about.
+The GNU General Public License permits making a modified version and
+letting the public access it on a server without ever releasing its
+source code to the public.
+
+The GNU Affero General Public License is designed specifically to
+ensure that, in such cases, the modified source code becomes available
+to the community. It requires the operator of a network server to
+provide the source code of the modified version running there to the
+users of that server. Therefore, public use of a modified version, on
+a publicly accessible server, gives the public access to the source
+code of the modified version.
+
+An older license, called the Affero General Public License and
+published by Affero, was designed to accomplish similar goals. This is
+a different license, not a version of the Affero GPL, but Affero has
+released a new version of the Affero GPL which permits relicensing under
+this license.
+
+The precise terms and conditions for copying, distribution and
+modification follow.
+
+## TERMS AND CONDITIONS
+
+### 0. Definitions
+
+“This License” refers to version 3 of the GNU Affero General Public License.
+
+“Copyright” also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+“The Program” refers to any copyrightable work licensed under this
+License. Each licensee is addressed as “you”. “Licensees” and
+“recipients” may be individuals or organizations.
+
+To “modify” a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a “modified version” of the
+earlier work or a work “based on” the earlier work.
+
+A “covered work” means either the unmodified Program or a work based
+on the Program.
+
+To “propagate” a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+To “convey” a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+An interactive user interface displays “Appropriate Legal Notices”
+to the extent that it includes a convenient and prominently visible
+feature that **(1)** displays an appropriate copyright notice, and **(2)**
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+### 1. Source Code
+
+The “source code” for a work means the preferred form of the work
+for making modifications to it. “Object code” means any non-source
+form of a work.
+
+A “Standard Interface” means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+The “System Libraries” of an executable work include anything, other
+than the work as a whole, that **(a)** is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and **(b)** serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+“Major Component”, in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+The “Corresponding Source” for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+The Corresponding Source for a work in source code form is that
+same work.
+
+### 2. Basic Permissions
+
+All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+### 3. Protecting Users' Legal Rights From Anti-Circumvention Law
+
+No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+### 4. Conveying Verbatim Copies
+
+You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+### 5. Conveying Modified Source Versions
+
+You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+- **a)** The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+- **b)** The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section 7.
+ This requirement modifies the requirement in section 4 to
+ “keep intact all notices”.
+- **c)** You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+- **d)** If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+“aggregate” if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+### 6. Conveying Non-Source Forms
+
+You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+- **a)** Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+- **b)** Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either **(1)** a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or **(2)** access to copy the
+ Corresponding Source from a network server at no charge.
+- **c)** Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+- **d)** Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+- **e)** Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+A “User Product” is either **(1)** a “consumer product”, which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or **(2)** anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, “normally used” refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+“Installation Information” for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+### 7. Additional Terms
+
+“Additional permissions” are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+- **a)** Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+- **b)** Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+- **c)** Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+- **d)** Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+- **e)** Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+- **f)** Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+All other non-permissive additional terms are considered “further
+restrictions” within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+### 8. Termination
+
+You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated **(a)**
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and **(b)** permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+### 9. Acceptance Not Required for Having Copies
+
+You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+### 10. Automatic Licensing of Downstream Recipients
+
+Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+An “entity transaction” is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+### 11. Patents
+
+A “contributor” is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's “contributor version”.
+
+A contributor's “essential patent claims” are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, “control” includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+In the following three paragraphs, a “patent license” is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To “grant” such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either **(1)** cause the Corresponding Source to be so
+available, or **(2)** arrange to deprive yourself of the benefit of the
+patent license for this particular work, or **(3)** arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. “Knowingly relying” means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+A patent license is “discriminatory” if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license **(a)** in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or **(b)** primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+### 12. No Surrender of Others' Freedom
+
+If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+### 13. Remote Network Interaction; Use with the GNU General Public License
+
+Notwithstanding any other provision of this License, if you modify the
+Program, your modified version must prominently offer all users
+interacting with it remotely through a computer network (if your version
+supports such interaction) an opportunity to receive the Corresponding
+Source of your version by providing access to the Corresponding Source
+from a network server at no charge, through some standard or customary
+means of facilitating copying of software. This Corresponding Source
+shall include the Corresponding Source for any work covered by version 3
+of the GNU General Public License that is incorporated pursuant to the
+following paragraph.
+
+Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the work with which it is combined will remain governed by version
+3 of the GNU General Public License.
+
+### 14. Revised Versions of this License
+
+The Free Software Foundation may publish revised and/or new versions of
+the GNU Affero General Public License from time to time. Such new versions
+will be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU Affero General
+Public License “or any later version” applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU Affero General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+If the Program specifies that a proxy can decide which future
+versions of the GNU Affero General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+### 15. Disclaimer of Warranty
+
+THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+### 16. Limitation of Liability
+
+IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+### 17. Interpretation of Sections 15 and 16
+
+If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
diff --git a/libs/hash-graph-types/python/README.md b/libs/hash-graph-types/python/README.md
new file mode 100644
index 0000000..175ab51
--- /dev/null
+++ b/libs/hash-graph-types/python/README.md
@@ -0,0 +1,3 @@
+# HASH Graph types
+
+Utility library to convert Block Protocol ontology schemas to Python Pydantic models.
diff --git a/libs/hash-graph-types/python/graph_types/__init__.py b/libs/hash-graph-types/python/graph_types/__init__.py
new file mode 100644
index 0000000..9194706
--- /dev/null
+++ b/libs/hash-graph-types/python/graph_types/__init__.py
@@ -0,0 +1,61 @@
+"""Replicates the Block Protocol type system for use in Python."""
+
+from typing import Protocol
+from uuid import UUID
+
+# This solution is not great as it _could_ lead to circular imports. However,
+# it is the best solution we have for now. The alternative is to have a single
+# file that contains all the schemas, but that is not very maintainable.
+#
+# If we run into issues with circular imports, we can refactor this to use
+# direct imports. For example, instead of importing `DataTypeSchema` from
+# `graph_types`, we can import it from `graph_types.data_type`.
+from .data_type import DataTypeReference as DataTypeReference
+from .data_type import DataTypeSchema as DataTypeSchema
+from .entity_type import EntityTypeReference as EntityTypeReference
+from .entity_type import EntityTypeSchema as EntityTypeSchema
+from .property_type import PropertyTypeReference as PropertyTypeReference
+from .property_type import PropertyTypeSchema as PropertyTypeSchema
+
+
+class GraphAPIProtocol(Protocol):
+ """Defines the interface for a graph API."""
+
+ async def get_data_type(
+ self,
+ data_type_id: str,
+ *,
+ actor_id: UUID,
+ ) -> DataTypeSchema:
+ """Returns the data type schema for the given data type ID.
+
+ If the data type is not found it will attempt to fetch it and use
+ the actor ID to authenticate the request.
+ """
+ ...
+
+ async def get_property_type(
+ self,
+ property_type_id: str,
+ *,
+ actor_id: UUID,
+ ) -> PropertyTypeSchema:
+ """Returns the property type schema for the given property type ID.
+
+ If the property type is not found it will attempt to fetch it and use
+ the actor ID to authenticate the request.
+ """
+ ...
+
+ async def get_entity_type(
+ self,
+ entity_type_id: str,
+ *,
+ actor_id: UUID,
+ ) -> EntityTypeSchema:
+ """Returns the entity type schema for the given entity type ID.
+
+ If the entity type is not found it will attempt to fetch it and use
+ the actor ID to authenticate the request.
+ """
+ ...
diff --git a/libs/hash-graph-types/python/graph_types/_annotations/__init__.py b/libs/hash-graph-types/python/graph_types/_annotations/__init__.py
new file mode 100644
index 0000000..623ff0a
--- /dev/null
+++ b/libs/hash-graph-types/python/graph_types/_annotations/__init__.py
@@ -0,0 +1,4 @@
+from .const import constant
+from .require import not_required
+
+__all__ = ["constant", "not_required"]
diff --git a/libs/hash-graph-types/python/graph_types/_annotations/const.py b/libs/hash-graph-types/python/graph_types/_annotations/const.py
new file mode 100644
index 0000000..65810ef
--- /dev/null
+++ b/libs/hash-graph-types/python/graph_types/_annotations/const.py
@@ -0,0 +1,68 @@
+from typing import Annotated, Any, TypeVar, cast, overload
+
+from pydantic import GetCoreSchemaHandler, GetJsonSchemaHandler
+from pydantic.json_schema import JsonSchemaValue
+from pydantic_core import CoreSchema, core_schema
+
+T = TypeVar("T")
+
+
+class ConstAnnotation:
+ const: Any
+
+ @classmethod
+ def __get_pydantic_core_schema__(
+ cls,
+ source_type: Any, # noqa: ANN401
+ handler: GetCoreSchemaHandler,
+ ) -> CoreSchema:
+ schema = handler(source_type)
+
+ return core_schema.no_info_after_validator_function(
+ cls.validate_const,
+ schema,
+ )
+
+ @classmethod
+ def __get_pydantic_json_schema__(
+ cls,
+ schema: CoreSchema,
+ handler: GetJsonSchemaHandler,
+ ) -> JsonSchemaValue:
+ json_schema = handler(schema)
+ json_schema.update(const=cls.const)
+
+ return json_schema
+
+ @classmethod
+ def validate_const(cls, v: T) -> T:
+ if v != cls.const:
+ msg = f"Value must be {cls.const}"
+ raise ValueError(msg)
+
+ return v
+
+
+_Undefined = object()
+
+
+@overload
+def constant(const_: T, /) -> type[T]: ...
+
+
+@overload
+def constant(type_: type[T], const_: T, /) -> type[T]: ...
+
+
+def constant(type_: type[T] | T, const_: T | object = _Undefined, /) -> type[T]:
+ if const_ is _Undefined:
+ value = cast(T, type_)
+ ty = cast(type[T], type(const_))
+ else:
+ value = cast(T, const_)
+ ty = cast(type[T], type_)
+
+ class Annotation(ConstAnnotation):
+ const = value
+
+ return cast(type[T], Annotated[ty, Annotation])
diff --git a/libs/hash-graph-types/python/graph_types/_annotations/require.py b/libs/hash-graph-types/python/graph_types/_annotations/require.py
new file mode 100644
index 0000000..6cf5b12
--- /dev/null
+++ b/libs/hash-graph-types/python/graph_types/_annotations/require.py
@@ -0,0 +1,22 @@
+from typing import Annotated, Any, TypeVar, cast
+
+from pydantic import GetCoreSchemaHandler
+from pydantic_core import CoreSchema, core_schema
+
+T = TypeVar("T")
+
+
+class NotRequiredAnnotation:
+ @classmethod
+ def __get_pydantic_core_schema__(
+ cls,
+ source_type: Any, # noqa: ANN401
+ handler: GetCoreSchemaHandler,
+ ) -> CoreSchema:
+ schema = handler(source_type)
+
+ return core_schema.nullable_schema(schema)
+
+
+def not_required(type_: type[T]) -> type[T]:
+ return cast(type[T], Annotated[type_, NotRequiredAnnotation])
diff --git a/libs/hash-graph-types/python/graph_types/_schema.py b/libs/hash-graph-types/python/graph_types/_schema.py
new file mode 100644
index 0000000..c714f24
--- /dev/null
+++ b/libs/hash-graph-types/python/graph_types/_schema.py
@@ -0,0 +1,205 @@
+import asyncio
+from abc import ABC, abstractmethod
+from collections.abc import Awaitable
+from typing import TYPE_CHECKING, Annotated, Any, Generic, Literal, TypeVar, cast
+from uuid import UUID
+
+from pydantic import (
+ BaseModel,
+ ConfigDict,
+ Field,
+ GetJsonSchemaHandler,
+ conlist,
+ create_model,
+)
+from pydantic.fields import FieldInfo
+from pydantic.json_schema import JsonSchemaValue
+from pydantic_core import CoreSchema
+
+from ._annotations import not_required
+
+if TYPE_CHECKING:
+ from . import GraphAPIProtocol
+ from .base import OntologyTypeInfo
+
+
+class Schema(BaseModel, ABC):
+ @abstractmethod
+ async def create_model(
+ self,
+ *,
+ actor_id: UUID,
+ graph: "GraphAPIProtocol",
+ additional_properties: bool,
+ ) -> type[BaseModel] | Annotated[Any, ...]: # noqa: ANN401
+ ...
+
+
+class OntologyTypeSchema(Schema, ABC):
+ """Common base class for all ontology types."""
+
+ identifier: str = Field(..., alias="$id")
+ title: str
+ description: str | None = None
+ kind: Literal["dataType", "propertyType", "entityType"]
+ schema_url: str = Field(..., alias="$schema")
+
+ def type_info(self) -> "OntologyTypeInfo":
+ from .base import OntologyTypeInfo
+
+ return OntologyTypeInfo(
+ identifier=self.identifier,
+ title=self.title,
+ description=self.description,
+ kind=self.kind,
+ schema_url=self.schema_url,
+ )
+
+
+T = TypeVar("T", bound=Schema)
+
+
+class OneOf(Schema, Generic[T]):
+ one_of: list[T] = Field(..., alias="oneOf")
+
+ async def create_model(
+ self,
+ *,
+ actor_id: UUID,
+ graph: "GraphAPIProtocol",
+ additional_properties: bool,
+ ) -> object:
+ types = await asyncio.gather(
+ *[
+ value.create_model(
+ actor_id=actor_id,
+ graph=graph,
+ additional_properties=additional_properties,
+ )
+ for value in self.one_of
+ ],
+ )
+
+ union = None
+ for type_ in types:
+ union = type_ if union is None else union | type_
+
+ if union is None:
+ msg = "No types provided"
+ raise ValueError(msg)
+
+ class OneOfSchema:
+ @classmethod
+ def __get_pydantic_json_schema__(
+ cls,
+ schema: CoreSchema,
+ handler: GetJsonSchemaHandler,
+ ) -> JsonSchemaValue:
+ json_schema = handler(schema)
+ if any_of := json_schema.pop("anyOf", None):
+ json_schema["oneOf"] = any_of
+ return json_schema
+
+ return Annotated[union, OneOfSchema]
+
+
+class Array(Schema, Generic[T]):
+ ty: Literal["array"] = Field(..., alias="type")
+ items: T
+ min_items: int | None = Field(default=None, alias="minItems")
+ max_items: int | None = Field(default=None, alias="maxItems")
+
+ async def create_model(
+ self,
+ *,
+ actor_id: UUID,
+ graph: "GraphAPIProtocol",
+ additional_properties: bool,
+ ) -> type[list[BaseModel]]:
+ type_items = await self.items.create_model(
+ actor_id=actor_id,
+ graph=graph,
+ additional_properties=additional_properties,
+ )
+
+ type_ = conlist(
+ type_items,
+ min_length=self.min_items,
+ max_length=self.max_items,
+ )
+
+ return cast(type[list[BaseModel]], type_)
+
+
+U = TypeVar("U")
+
+
+class Object(Schema, Generic[T]):
+ ty: Literal["object"] = Field(..., alias="type")
+ properties: dict[str, T]
+ required: list[str] | None = None
+
+ async def create_model(
+ self,
+ *,
+ actor_id: UUID,
+ graph: "GraphAPIProtocol",
+ additional_properties: bool,
+ ) -> type[BaseModel]:
+ async def async_value(
+ key: str,
+ value: Awaitable[type[BaseModel] | Any],
+ ) -> tuple[str, type[BaseModel] | Any]:
+ return key, await value
+
+ def field_type(key: str, type_: type[U]) -> type[U]:
+ if self.required is None or key not in self.required:
+ return not_required(type_)
+
+ return type_
+
+ def schema_extra(extra: dict[str, Any]) -> None:
+ any_of = extra.pop("anyOf", None)
+
+ extra.clear()
+ extra.update(any_of[0])
+
+ def field_info(key: str) -> FieldInfo:
+ # cast is necessary here because `Field`
+ # return `FieldInfo`, even though it doesn't.
+ if self.required is None or key not in self.required:
+ return cast(FieldInfo, Field(None, json_schema_extra=schema_extra))
+
+ return cast(FieldInfo, Field(...))
+
+ types = dict(
+ await asyncio.gather(
+ *(
+ async_value(
+ key,
+ value.create_model(
+ actor_id=actor_id,
+ graph=graph,
+ additional_properties=additional_properties,
+ ),
+ )
+ for key, value in self.properties.items()
+ ),
+ ),
+ )
+
+ types = {
+ key: (field_type(key, value), field_info(key))
+ for key, value in types.items()
+ }
+
+ if additional_properties:
+ config = ConfigDict(extra="allow")
+ else:
+ config = ConfigDict(extra="forbid")
+
+ return create_model(
+ "DictSchema",
+ __config__=config,
+ **types,
+ )
diff --git a/libs/hash-graph-types/python/graph_types/base.py b/libs/hash-graph-types/python/graph_types/base.py
new file mode 100644
index 0000000..747cb23
--- /dev/null
+++ b/libs/hash-graph-types/python/graph_types/base.py
@@ -0,0 +1,62 @@
+"""Base classes for all graph types created by `.create_model()`.
+
+These classes are primarily used as markers
+"""
+
+from abc import ABC
+from typing import ClassVar
+
+from pydantic import BaseModel, ConfigDict, Field, GetJsonSchemaHandler
+from pydantic.json_schema import JsonSchemaValue
+from pydantic_core import CoreSchema
+
+from . import EntityTypeReference
+
+
+class OntologyTypeInfo(BaseModel):
+ """Information about a type."""
+
+ model_config = ConfigDict(populate_by_name=True)
+
+ identifier: str = Field(..., alias="$id")
+ schema_url: str = Field(..., alias="$schema")
+ title: str
+ description: str | None = None
+ kind: str
+
+
+class OntologyType(BaseModel, ABC):
+ """Base class for all graph ontology types."""
+
+ info: ClassVar[OntologyTypeInfo]
+
+ @classmethod
+ def __get_pydantic_json_schema__(
+ cls,
+ schema: CoreSchema,
+ handler: GetJsonSchemaHandler,
+ ) -> JsonSchemaValue:
+ """Update the schema with additional type information."""
+ json_schema = handler(schema)
+ json_schema.update(cls.info.model_dump(by_alias=True))
+ return json_schema
+
+
+class EntityTypeInfo(OntologyTypeInfo):
+ """Information about an entity type."""
+
+ all_of: list[EntityTypeReference] | None = Field(..., alias="allOf")
+
+
+class EntityType(OntologyType, ABC):
+ """Base class for all entity types."""
+
+ info: ClassVar[EntityTypeInfo]
+
+
+class PropertyType(OntologyType, ABC):
+ """Base class for all property types."""
+
+
+class DataType(OntologyType, ABC):
+ """Base class for all data types."""
diff --git a/libs/hash-graph-types/python/graph_types/data_type.py b/libs/hash-graph-types/python/graph_types/data_type.py
new file mode 100644
index 0000000..720c20d
--- /dev/null
+++ b/libs/hash-graph-types/python/graph_types/data_type.py
@@ -0,0 +1,122 @@
+"""A data type schema as defined by the Block Protocol."""
+
+from typing import (
+ TYPE_CHECKING,
+ Any,
+ Literal,
+ TypeAlias,
+ assert_never,
+ cast,
+)
+from uuid import UUID
+
+from pydantic import (
+ BaseModel,
+ Field,
+ RootModel,
+ create_model,
+)
+from slugify import slugify
+
+from ._annotations import constant
+from ._schema import OntologyTypeSchema, Schema
+
+if TYPE_CHECKING:
+ from . import GraphAPIProtocol
+ from .base import DataType as DataTypeBase
+
+__all__ = ["DataTypeSchema", "DataTypeReference"]
+
+DataType: TypeAlias = str | float | bool | None | list[Any] | dict[str, Any]
+
+
+class DataTypeReference(Schema):
+ """A reference to a data type schema."""
+
+ ref: str = Field(..., alias="$ref")
+
+ async def create_model(
+ self,
+ *,
+ actor_id: UUID,
+ graph: "GraphAPIProtocol",
+ additional_properties: bool,
+ ) -> type["DataTypeBase"]:
+ """Creates a model from the referenced data type schema."""
+ schema = await graph.get_data_type(self.ref, actor_id=actor_id)
+ return await schema.create_model(
+ actor_id=actor_id,
+ graph=graph,
+ additional_properties=additional_properties,
+ )
+
+
+class DataTypeSchema(OntologyTypeSchema, extra="allow"):
+ """A data type schema as defined by the Block Protocol.
+
+ see https://blockprotocol.org/types/modules/graph/0.3/schema/data-type
+ """
+
+ kind: Literal["dataType"]
+ ty: Literal["string", "number", "boolean", "null", "array", "object"] = Field(
+ ...,
+ alias="type",
+ )
+
+ def _type(self) -> type[DataType]:
+ match self.ty:
+ case "string":
+ return str
+ case "number":
+ return float
+ case "boolean":
+ return bool
+ case "null":
+ return type(None)
+ case "array":
+ return list[Any]
+ case "object":
+ return dict[str, Any]
+ case _ as unreachable:
+ assert_never(unreachable)
+
+ async def create_model(
+ self,
+ *,
+ actor_id: UUID,
+ graph: "GraphAPIProtocol",
+ additional_properties: bool, # noqa: ARG002
+ ) -> type["DataTypeBase"]:
+ """Create an annotated type from this schema."""
+ from .base import DataType as DataTypeBase
+
+ # Custom data types will require an actor ID and the graph to be passed in
+ _actor_id = actor_id
+ _graph = graph
+
+ type_ = self._type()
+ if "const" in (self.model_extra or {}):
+ # `const` can only be in `model_extra`, therefore it is safe to index!
+ const = self.model_extra["const"] # type: ignore[index]
+ type_ = constant(type_, const)
+
+ class_name = slugify(
+ self.identifier,
+ regex_pattern=r"[^a-z0-9_]+",
+ separator="_",
+ )
+
+ base: type[BaseModel] = type(
+ f"{class_name}Base",
+ (DataTypeBase,),
+ {"info": self.type_info()},
+ )
+
+ return cast(
+ type[DataTypeBase],
+ create_model(
+ class_name,
+ __base__=(base, RootModel),
+ root=(type_, Field(...)),
+ ),
+ )
diff --git a/libs/hash-graph-types/python/graph_types/entity_type.py b/libs/hash-graph-types/python/graph_types/entity_type.py
new file mode 100644
index 0000000..00d1fd2
--- /dev/null
+++ b/libs/hash-graph-types/python/graph_types/entity_type.py
@@ -0,0 +1,123 @@
+"""An entity type schema as defined by the Block Protocol."""
+
+from typing import (
+ TYPE_CHECKING,
+ Any,
+ Literal,
+ Never,
+ cast,
+)
+from uuid import UUID
+
+from pydantic import (
+ BaseModel,
+ ConfigDict,
+ Field,
+ create_model,
+)
+from slugify import slugify
+
+from ._schema import Array, Object, OneOf, OntologyTypeSchema, Schema
+from .property_type import PropertyTypeReference
+
+if TYPE_CHECKING:
+ from . import GraphAPIProtocol
+ from .base import EntityType, EntityTypeInfo
+
+__all__ = ["EntityTypeSchema", "EntityTypeReference"]
+
+
+class EntityTypeReference(Schema):
+ """A reference to an entity type schema."""
+
+ ref: str = Field(..., alias="$ref")
+
+ async def create_model(
+ self,
+ *,
+ actor_id: UUID,
+ graph: "GraphAPIProtocol",
+ additional_properties: bool,
+ ) -> type["EntityType"]:
+ """Creates a model from the referenced entity type schema."""
+ schema = await graph.get_entity_type(self.ref, actor_id=actor_id)
+ return await schema.create_model(
+ actor_id=actor_id,
+ graph=graph,
+ additional_properties=additional_properties,
+ )
+
+
+class EmptyDict(Schema):
+ model_config = ConfigDict(title=None, extra="forbid")
+
+ async def create_model(
+ self,
+ *,
+ actor_id: UUID,
+ graph: "GraphAPIProtocol",
+ additional_properties: bool,
+ ) -> Never:
+ raise NotImplementedError
+
+
+class EntityTypeSchema(
+ OntologyTypeSchema,
+ Object[PropertyTypeReference | Array[PropertyTypeReference]],
+):
+ """An entity type schema as defined by the Block Protocol.
+
+ see https://blockprotocol.org/types/modules/graph/0.3/schema/entity-type
+ """
+
+ kind: Literal["entityType"]
+ examples: list[dict[str, Any]] | None = None
+ links: dict[str, Array[OneOf[EntityTypeReference] | EmptyDict]] | None = None
+ all_of: list[EntityTypeReference] | None = Field(None, alias="allOf")
+
+ def type_info(self) -> "EntityTypeInfo":
+ """Return the type information for this schema."""
+ from .base import EntityTypeInfo
+
+ original = super().type_info()
+
+ return EntityTypeInfo.model_validate(
+ (original.model_dump() | {"all_of": self.all_of or []}),
+ )
+
+ async def create_model(
+ self,
+ *,
+ actor_id: UUID,
+ graph: "GraphAPIProtocol",
+ additional_properties: bool,
+ ) -> type["EntityType"]:
+ """Create an annotated type from this schema."""
+ from .base import EntityType
+
+ # Take the fields from Object and create a new model, with a new baseclass.
+ proxy = await Object.create_model(
+ self,
+ actor_id=actor_id,
+ graph=graph,
+ additional_properties=additional_properties,
+ )
+
+ class_name = slugify(
+ self.identifier,
+ regex_pattern=r"[^a-z0-9_]+",
+ separator="_",
+ )
+
+ base: type[BaseModel] = type(
+ f"{class_name}Base",
+ (EntityType,),
+ {"info": self.type_info()},
+ )
+
+ model = create_model(
+ class_name,
+ __base__=(base, proxy),
+ )
+
+ return cast(type[EntityType], model)
diff --git a/libs/hash-graph-types/python/graph_types/property_type.py b/libs/hash-graph-types/python/graph_types/property_type.py
new file mode 100644
index 0000000..523080f
--- /dev/null
+++ b/libs/hash-graph-types/python/graph_types/property_type.py
@@ -0,0 +1,115 @@
+"""A property type schema as defined by the Block Protocol."""
+
+from typing import (
+ TYPE_CHECKING,
+ Annotated,
+ Any,
+ Literal,
+ cast,
+)
+from uuid import UUID
+
+from pydantic import (
+ BaseModel,
+ Field,
+ RootModel,
+ create_model,
+)
+from slugify import slugify
+
+from ._schema import Array, Object, OneOf, OntologyTypeSchema, Schema
+from .data_type import DataTypeReference
+
+if TYPE_CHECKING:
+ from . import GraphAPIProtocol
+ from .base import PropertyType
+
+__all__ = ["PropertyTypeSchema", "PropertyTypeReference"]
+
+
+class PropertyTypeReference(Schema):
+ """A reference to a property type schema."""
+
+ ref: str = Field(..., alias="$ref")
+
+ async def create_model(
+ self,
+ *,
+ actor_id: UUID,
+ graph: "GraphAPIProtocol",
+ additional_properties: bool,
+ ) -> type["PropertyType"]:
+ """Creates a model from the referenced property type schema."""
+ schema = await graph.get_property_type(self.ref, actor_id=actor_id)
+ return await schema.create_model(
+ actor_id=actor_id,
+ graph=graph,
+ additional_properties=additional_properties,
+ )
+
+
+class PropertyValue(RootModel, Schema):
+ root: (
+ DataTypeReference
+ | Object[PropertyTypeReference | Array[PropertyTypeReference]]
+ | Array[OneOf["PropertyValue"]]
+ )
+
+ async def create_model(
+ self,
+ *,
+ actor_id: UUID,
+ graph: "GraphAPIProtocol",
+ additional_properties: bool,
+ ) -> type[RootModel] | Annotated[Any, ...]: # noqa: ANN401
+ return await self.root.create_model(
+ actor_id=actor_id,
+ graph=graph,
+ additional_properties=additional_properties,
+ )
+
+
+class PropertyTypeSchema(OntologyTypeSchema, OneOf[PropertyValue]):
+ """A property type schema as defined by the Block Protocol.
+
+ see https://blockprotocol.org/types/modules/graph/0.3/schema/property-type
+ """
+
+ kind: Literal["propertyType"]
+
+ async def create_model(
+ self,
+ *,
+ actor_id: UUID,
+ graph: "GraphAPIProtocol",
+ additional_properties: bool,
+ ) -> type["PropertyType"]:
+ """Create an annotated type from this schema."""
+ from .base import PropertyType
+
+ inner = await OneOf.create_model(
+ self,
+ actor_id=actor_id,
+ graph=graph,
+ additional_properties=additional_properties,
+ )
+
+ class_name = slugify(
+ self.identifier,
+ regex_pattern=r"[^a-z0-9_]+",
+ separator="_",
+ )
+
+ base: type[BaseModel] = type(
+ f"{class_name}Base",
+ (PropertyType,),
+ {"info": self.type_info()},
+ )
+
+ model = create_model(
+ class_name,
+ __base__=(base, RootModel),
+ root=(inner, Field(...)),
+ )
+
+ return cast(type[PropertyType], model)
diff --git a/libs/hash-graph-types/python/graph_types/py.typed b/libs/hash-graph-types/python/graph_types/py.typed
new file mode 100644
index 0000000..e69de29
diff --git a/libs/hash-graph-types/python/package.json b/libs/hash-graph-types/python/package.json
new file mode 100644
index 0000000..de10003
--- /dev/null
+++ b/libs/hash-graph-types/python/package.json
@@ -0,0 +1,19 @@
+{
+ "name": "@local/hash-graph-types-py",
+ "version": "0.0.0-private",
+ "private": true,
+ "description": "Type definitions for the type system and entity validation based on schemas",
+ "scripts": {
+ "build": "poetry build --no-interaction",
+ "fix:black": "poetry run black graph_types",
+ "fix:lock-files": "poetry lock --no-update",
+ "fix:ruff": "poetry run ruff graph_types --fix",
+ "lint:black": "poetry run black --check --diff --color graph_types",
+ "lint:lock-files": "poetry lock --check",
+ "lint:mypy": "poetry run mypy .",
+ "lint:ruff": "poetry run ruff graph_types",
+ "poetry:install": "poetry install --no-root --no-interaction",
+ "poetry:install-production": "poetry install --no-root --no-interaction --only main"
+ },
+ "dependencies": {}
+}
diff --git a/libs/hash-graph-types/python/poetry.lock b/libs/hash-graph-types/python/poetry.lock
new file mode 100644
index 0000000..fdce036
--- /dev/null
+++ b/libs/hash-graph-types/python/poetry.lock
@@ -0,0 +1,404 @@
+# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand.
+
+[[package]]
+name = "annotated-types"
+version = "0.5.0"
+description = "Reusable constraint types to use with typing.Annotated"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "annotated_types-0.5.0-py3-none-any.whl", hash = "sha256:58da39888f92c276ad970249761ebea80ba544b77acddaa1a4d6cf78287d45fd"},
+ {file = "annotated_types-0.5.0.tar.gz", hash = "sha256:47cdc3490d9ac1506ce92c7aaa76c579dc3509ff11e098fc867e5130ab7be802"},
+]
+
+[[package]]
+name = "black"
+version = "23.10.1"
+description = "The uncompromising code formatter."
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "black-23.10.1-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:ec3f8e6234c4e46ff9e16d9ae96f4ef69fa328bb4ad08198c8cee45bb1f08c69"},
+ {file = "black-23.10.1-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:1b917a2aa020ca600483a7b340c165970b26e9029067f019e3755b56e8dd5916"},
+ {file = "black-23.10.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c74de4c77b849e6359c6f01987e94873c707098322b91490d24296f66d067dc"},
+ {file = "black-23.10.1-cp310-cp310-win_amd64.whl", hash = "sha256:7b4d10b0f016616a0d93d24a448100adf1699712fb7a4efd0e2c32bbb219b173"},
+ {file = "black-23.10.1-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:b15b75fc53a2fbcac8a87d3e20f69874d161beef13954747e053bca7a1ce53a0"},
+ {file = "black-23.10.1-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:e293e4c2f4a992b980032bbd62df07c1bcff82d6964d6c9496f2cd726e246ace"},
+ {file = "black-23.10.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d56124b7a61d092cb52cce34182a5280e160e6aff3137172a68c2c2c4b76bcb"},
+ {file = "black-23.10.1-cp311-cp311-win_amd64.whl", hash = "sha256:3f157a8945a7b2d424da3335f7ace89c14a3b0625e6593d21139c2d8214d55ce"},
+ {file = "black-23.10.1-cp38-cp38-macosx_10_16_arm64.whl", hash = "sha256:cfcce6f0a384d0da692119f2d72d79ed07c7159879d0bb1bb32d2e443382bf3a"},
+ {file = "black-23.10.1-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:33d40f5b06be80c1bbce17b173cda17994fbad096ce60eb22054da021bf933d1"},
+ {file = "black-23.10.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:840015166dbdfbc47992871325799fd2dc0dcf9395e401ada6d88fe11498abad"},
+ {file = "black-23.10.1-cp38-cp38-win_amd64.whl", hash = "sha256:037e9b4664cafda5f025a1728c50a9e9aedb99a759c89f760bd83730e76ba884"},
+ {file = "black-23.10.1-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:7cb5936e686e782fddb1c73f8aa6f459e1ad38a6a7b0e54b403f1f05a1507ee9"},
+ {file = "black-23.10.1-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:7670242e90dc129c539e9ca17665e39a146a761e681805c54fbd86015c7c84f7"},
+ {file = "black-23.10.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ed45ac9a613fb52dad3b61c8dea2ec9510bf3108d4db88422bacc7d1ba1243d"},
+ {file = "black-23.10.1-cp39-cp39-win_amd64.whl", hash = "sha256:6d23d7822140e3fef190734216cefb262521789367fbdc0b3f22af6744058982"},
+ {file = "black-23.10.1-py3-none-any.whl", hash = "sha256:d431e6739f727bb2e0495df64a6c7a5310758e87505f5f8cde9ff6c0f2d7e4fe"},
+ {file = "black-23.10.1.tar.gz", hash = "sha256:1f8ce316753428ff68749c65a5f7844631aa18c8679dfd3ca9dc1a289979c258"},
+]
+
+[package.dependencies]
+click = ">=8.0.0"
+mypy-extensions = ">=0.4.3"
+packaging = ">=22.0"
+pathspec = ">=0.9.0"
+platformdirs = ">=2"
+
+[package.extras]
+colorama = ["colorama (>=0.4.3)"]
+d = ["aiohttp (>=3.7.4)"]
+jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"]
+uvloop = ["uvloop (>=0.15.2)"]
+
+[[package]]
+name = "click"
+version = "8.1.6"
+description = "Composable command line interface toolkit"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "click-8.1.6-py3-none-any.whl", hash = "sha256:fa244bb30b3b5ee2cae3da8f55c9e5e0c0e86093306301fb418eb9dc40fbded5"},
+ {file = "click-8.1.6.tar.gz", hash = "sha256:48ee849951919527a045bfe3bf7baa8a959c423134e1a5b98c05c20ba75a1cbd"},
+]
+
+[package.dependencies]
+colorama = {version = "*", markers = "platform_system == \"Windows\""}
+
+[[package]]
+name = "colorama"
+version = "0.4.6"
+description = "Cross-platform colored terminal text."
+optional = false
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
+files = [
+ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
+ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
+]
+
+[[package]]
+name = "mypy"
+version = "1.6.1"
+description = "Optional static typing for Python"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "mypy-1.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e5012e5cc2ac628177eaac0e83d622b2dd499e28253d4107a08ecc59ede3fc2c"},
+ {file = "mypy-1.6.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d8fbb68711905f8912e5af474ca8b78d077447d8f3918997fecbf26943ff3cbb"},
+ {file = "mypy-1.6.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21a1ad938fee7d2d96ca666c77b7c494c3c5bd88dff792220e1afbebb2925b5e"},
+ {file = "mypy-1.6.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b96ae2c1279d1065413965c607712006205a9ac541895004a1e0d4f281f2ff9f"},
+ {file = "mypy-1.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:40b1844d2e8b232ed92e50a4bd11c48d2daa351f9deee6c194b83bf03e418b0c"},
+ {file = "mypy-1.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:81af8adaa5e3099469e7623436881eff6b3b06db5ef75e6f5b6d4871263547e5"},
+ {file = "mypy-1.6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8c223fa57cb154c7eab5156856c231c3f5eace1e0bed9b32a24696b7ba3c3245"},
+ {file = "mypy-1.6.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8032e00ce71c3ceb93eeba63963b864bf635a18f6c0c12da6c13c450eedb183"},
+ {file = "mypy-1.6.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4c46b51de523817a0045b150ed11b56f9fff55f12b9edd0f3ed35b15a2809de0"},
+ {file = "mypy-1.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:19f905bcfd9e167159b3d63ecd8cb5e696151c3e59a1742e79bc3bcb540c42c7"},
+ {file = "mypy-1.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:82e469518d3e9a321912955cc702d418773a2fd1e91c651280a1bda10622f02f"},
+ {file = "mypy-1.6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d4473c22cc296425bbbce7e9429588e76e05bc7342da359d6520b6427bf76660"},
+ {file = "mypy-1.6.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59a0d7d24dfb26729e0a068639a6ce3500e31d6655df8557156c51c1cb874ce7"},
+ {file = "mypy-1.6.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:cfd13d47b29ed3bbaafaff7d8b21e90d827631afda134836962011acb5904b71"},
+ {file = "mypy-1.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:eb4f18589d196a4cbe5290b435d135dee96567e07c2b2d43b5c4621b6501531a"},
+ {file = "mypy-1.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:41697773aa0bf53ff917aa077e2cde7aa50254f28750f9b88884acea38a16169"},
+ {file = "mypy-1.6.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7274b0c57737bd3476d2229c6389b2ec9eefeb090bbaf77777e9d6b1b5a9d143"},
+ {file = "mypy-1.6.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbaf4662e498c8c2e352da5f5bca5ab29d378895fa2d980630656178bd607c46"},
+ {file = "mypy-1.6.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bb8ccb4724f7d8601938571bf3f24da0da791fe2db7be3d9e79849cb64e0ae85"},
+ {file = "mypy-1.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:68351911e85145f582b5aa6cd9ad666c8958bcae897a1bfda8f4940472463c45"},
+ {file = "mypy-1.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:49ae115da099dcc0922a7a895c1eec82c1518109ea5c162ed50e3b3594c71208"},
+ {file = "mypy-1.6.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8b27958f8c76bed8edaa63da0739d76e4e9ad4ed325c814f9b3851425582a3cd"},
+ {file = "mypy-1.6.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:925cd6a3b7b55dfba252b7c4561892311c5358c6b5a601847015a1ad4eb7d332"},
+ {file = "mypy-1.6.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8f57e6b6927a49550da3d122f0cb983d400f843a8a82e65b3b380d3d7259468f"},
+ {file = "mypy-1.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:a43ef1c8ddfdb9575691720b6352761f3f53d85f1b57d7745701041053deff30"},
+ {file = "mypy-1.6.1-py3-none-any.whl", hash = "sha256:4cbe68ef919c28ea561165206a2dcb68591c50f3bcf777932323bc208d949cf1"},
+ {file = "mypy-1.6.1.tar.gz", hash = "sha256:4d01c00d09a0be62a4ca3f933e315455bde83f37f892ba4b08ce92f3cf44bcc1"},
+]
+
+[package.dependencies]
+mypy-extensions = ">=1.0.0"
+typing-extensions = ">=4.1.0"
+
+[package.extras]
+dmypy = ["psutil (>=4.0)"]
+install-types = ["pip"]
+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 = "packaging"
+version = "23.1"
+description = "Core utilities for Python packages"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"},
+ {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"},
+]
+
+[[package]]
+name = "pathspec"
+version = "0.11.2"
+description = "Utility library for gitignore style pattern matching of file paths."
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "pathspec-0.11.2-py3-none-any.whl", hash = "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20"},
+ {file = "pathspec-0.11.2.tar.gz", hash = "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3"},
+]
+
+[[package]]
+name = "platformdirs"
+version = "3.10.0"
+description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "platformdirs-3.10.0-py3-none-any.whl", hash = "sha256:d7c24979f292f916dc9cbf8648319032f551ea8c49a4c9bf2fb556a02070ec1d"},
+ {file = "platformdirs-3.10.0.tar.gz", hash = "sha256:b45696dab2d7cc691a3226759c0d3b00c47c8b6e293d96f6436f733303f77f6d"},
+]
+
+[package.extras]
+docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"]
+test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"]
+
+[[package]]
+name = "pydantic"
+version = "2.4.2"
+description = "Data validation using Python type hints"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "pydantic-2.4.2-py3-none-any.whl", hash = "sha256:bc3ddf669d234f4220e6e1c4d96b061abe0998185a8d7855c0126782b7abc8c1"},
+ {file = "pydantic-2.4.2.tar.gz", hash = "sha256:94f336138093a5d7f426aac732dcfe7ab4eb4da243c88f891d65deb4a2556ee7"},
+]
+
+[package.dependencies]
+annotated-types = ">=0.4.0"
+pydantic-core = "2.10.1"
+typing-extensions = ">=4.6.1"
+
+[package.extras]
+email = ["email-validator (>=2.0.0)"]
+
+[[package]]
+name = "pydantic-core"
+version = "2.10.1"
+description = ""
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "pydantic_core-2.10.1-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:d64728ee14e667ba27c66314b7d880b8eeb050e58ffc5fec3b7a109f8cddbd63"},
+ {file = "pydantic_core-2.10.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:48525933fea744a3e7464c19bfede85df4aba79ce90c60b94d8b6e1eddd67096"},
+ {file = "pydantic_core-2.10.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef337945bbd76cce390d1b2496ccf9f90b1c1242a3a7bc242ca4a9fc5993427a"},
+ {file = "pydantic_core-2.10.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a1392e0638af203cee360495fd2cfdd6054711f2db5175b6e9c3c461b76f5175"},
+ {file = "pydantic_core-2.10.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0675ba5d22de54d07bccde38997e780044dcfa9a71aac9fd7d4d7a1d2e3e65f7"},
+ {file = "pydantic_core-2.10.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:128552af70a64660f21cb0eb4876cbdadf1a1f9d5de820fed6421fa8de07c893"},
+ {file = "pydantic_core-2.10.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f6e6aed5818c264412ac0598b581a002a9f050cb2637a84979859e70197aa9e"},
+ {file = "pydantic_core-2.10.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ecaac27da855b8d73f92123e5f03612b04c5632fd0a476e469dfc47cd37d6b2e"},
+ {file = "pydantic_core-2.10.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b3c01c2fb081fced3bbb3da78510693dc7121bb893a1f0f5f4b48013201f362e"},
+ {file = "pydantic_core-2.10.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:92f675fefa977625105708492850bcbc1182bfc3e997f8eecb866d1927c98ae6"},
+ {file = "pydantic_core-2.10.1-cp310-none-win32.whl", hash = "sha256:420a692b547736a8d8703c39ea935ab5d8f0d2573f8f123b0a294e49a73f214b"},
+ {file = "pydantic_core-2.10.1-cp310-none-win_amd64.whl", hash = "sha256:0880e239827b4b5b3e2ce05e6b766a7414e5f5aedc4523be6b68cfbc7f61c5d0"},
+ {file = "pydantic_core-2.10.1-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:073d4a470b195d2b2245d0343569aac7e979d3a0dcce6c7d2af6d8a920ad0bea"},
+ {file = "pydantic_core-2.10.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:600d04a7b342363058b9190d4e929a8e2e715c5682a70cc37d5ded1e0dd370b4"},
+ {file = "pydantic_core-2.10.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39215d809470f4c8d1881758575b2abfb80174a9e8daf8f33b1d4379357e417c"},
+ {file = "pydantic_core-2.10.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eeb3d3d6b399ffe55f9a04e09e635554012f1980696d6b0aca3e6cf42a17a03b"},
+ {file = "pydantic_core-2.10.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a7a7902bf75779bc12ccfc508bfb7a4c47063f748ea3de87135d433a4cca7a2f"},
+ {file = "pydantic_core-2.10.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3625578b6010c65964d177626fde80cf60d7f2e297d56b925cb5cdeda6e9925a"},
+ {file = "pydantic_core-2.10.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:caa48fc31fc7243e50188197b5f0c4228956f97b954f76da157aae7f67269ae8"},
+ {file = "pydantic_core-2.10.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:07ec6d7d929ae9c68f716195ce15e745b3e8fa122fc67698ac6498d802ed0fa4"},
+ {file = "pydantic_core-2.10.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e6f31a17acede6a8cd1ae2d123ce04d8cca74056c9d456075f4f6f85de055607"},
+ {file = "pydantic_core-2.10.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d8f1ebca515a03e5654f88411420fea6380fc841d1bea08effb28184e3d4899f"},
+ {file = "pydantic_core-2.10.1-cp311-none-win32.whl", hash = "sha256:6db2eb9654a85ada248afa5a6db5ff1cf0f7b16043a6b070adc4a5be68c716d6"},
+ {file = "pydantic_core-2.10.1-cp311-none-win_amd64.whl", hash = "sha256:4a5be350f922430997f240d25f8219f93b0c81e15f7b30b868b2fddfc2d05f27"},
+ {file = "pydantic_core-2.10.1-cp311-none-win_arm64.whl", hash = "sha256:5fdb39f67c779b183b0c853cd6b45f7db84b84e0571b3ef1c89cdb1dfc367325"},
+ {file = "pydantic_core-2.10.1-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:b1f22a9ab44de5f082216270552aa54259db20189e68fc12484873d926426921"},
+ {file = "pydantic_core-2.10.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8572cadbf4cfa95fb4187775b5ade2eaa93511f07947b38f4cd67cf10783b118"},
+ {file = "pydantic_core-2.10.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:db9a28c063c7c00844ae42a80203eb6d2d6bbb97070cfa00194dff40e6f545ab"},
+ {file = "pydantic_core-2.10.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0e2a35baa428181cb2270a15864ec6286822d3576f2ed0f4cd7f0c1708472aff"},
+ {file = "pydantic_core-2.10.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05560ab976012bf40f25d5225a58bfa649bb897b87192a36c6fef1ab132540d7"},
+ {file = "pydantic_core-2.10.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d6495008733c7521a89422d7a68efa0a0122c99a5861f06020ef5b1f51f9ba7c"},
+ {file = "pydantic_core-2.10.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14ac492c686defc8e6133e3a2d9eaf5261b3df26b8ae97450c1647286750b901"},
+ {file = "pydantic_core-2.10.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8282bab177a9a3081fd3d0a0175a07a1e2bfb7fcbbd949519ea0980f8a07144d"},
+ {file = "pydantic_core-2.10.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:aafdb89fdeb5fe165043896817eccd6434aee124d5ee9b354f92cd574ba5e78f"},
+ {file = "pydantic_core-2.10.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:f6defd966ca3b187ec6c366604e9296f585021d922e666b99c47e78738b5666c"},
+ {file = "pydantic_core-2.10.1-cp312-none-win32.whl", hash = "sha256:7c4d1894fe112b0864c1fa75dffa045720a194b227bed12f4be7f6045b25209f"},
+ {file = "pydantic_core-2.10.1-cp312-none-win_amd64.whl", hash = "sha256:5994985da903d0b8a08e4935c46ed8daf5be1cf217489e673910951dc533d430"},
+ {file = "pydantic_core-2.10.1-cp312-none-win_arm64.whl", hash = "sha256:0d8a8adef23d86d8eceed3e32e9cca8879c7481c183f84ed1a8edc7df073af94"},
+ {file = "pydantic_core-2.10.1-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:9badf8d45171d92387410b04639d73811b785b5161ecadabf056ea14d62d4ede"},
+ {file = "pydantic_core-2.10.1-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:ebedb45b9feb7258fac0a268a3f6bec0a2ea4d9558f3d6f813f02ff3a6dc6698"},
+ {file = "pydantic_core-2.10.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cfe1090245c078720d250d19cb05d67e21a9cd7c257698ef139bc41cf6c27b4f"},
+ {file = "pydantic_core-2.10.1-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e357571bb0efd65fd55f18db0a2fb0ed89d0bb1d41d906b138f088933ae618bb"},
+ {file = "pydantic_core-2.10.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b3dcd587b69bbf54fc04ca157c2323b8911033e827fffaecf0cafa5a892a0904"},
+ {file = "pydantic_core-2.10.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c120c9ce3b163b985a3b966bb701114beb1da4b0468b9b236fc754783d85aa3"},
+ {file = "pydantic_core-2.10.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15d6bca84ffc966cc9976b09a18cf9543ed4d4ecbd97e7086f9ce9327ea48891"},
+ {file = "pydantic_core-2.10.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5cabb9710f09d5d2e9e2748c3e3e20d991a4c5f96ed8f1132518f54ab2967221"},
+ {file = "pydantic_core-2.10.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:82f55187a5bebae7d81d35b1e9aaea5e169d44819789837cdd4720d768c55d15"},
+ {file = "pydantic_core-2.10.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:1d40f55222b233e98e3921df7811c27567f0e1a4411b93d4c5c0f4ce131bc42f"},
+ {file = "pydantic_core-2.10.1-cp37-none-win32.whl", hash = "sha256:14e09ff0b8fe6e46b93d36a878f6e4a3a98ba5303c76bb8e716f4878a3bee92c"},
+ {file = "pydantic_core-2.10.1-cp37-none-win_amd64.whl", hash = "sha256:1396e81b83516b9d5c9e26a924fa69164156c148c717131f54f586485ac3c15e"},
+ {file = "pydantic_core-2.10.1-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:6835451b57c1b467b95ffb03a38bb75b52fb4dc2762bb1d9dbed8de31ea7d0fc"},
+ {file = "pydantic_core-2.10.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b00bc4619f60c853556b35f83731bd817f989cba3e97dc792bb8c97941b8053a"},
+ {file = "pydantic_core-2.10.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fa467fd300a6f046bdb248d40cd015b21b7576c168a6bb20aa22e595c8ffcdd"},
+ {file = "pydantic_core-2.10.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d99277877daf2efe074eae6338453a4ed54a2d93fb4678ddfe1209a0c93a2468"},
+ {file = "pydantic_core-2.10.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fa7db7558607afeccb33c0e4bf1c9a9a835e26599e76af6fe2fcea45904083a6"},
+ {file = "pydantic_core-2.10.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aad7bd686363d1ce4ee930ad39f14e1673248373f4a9d74d2b9554f06199fb58"},
+ {file = "pydantic_core-2.10.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:443fed67d33aa85357464f297e3d26e570267d1af6fef1c21ca50921d2976302"},
+ {file = "pydantic_core-2.10.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:042462d8d6ba707fd3ce9649e7bf268633a41018d6a998fb5fbacb7e928a183e"},
+ {file = "pydantic_core-2.10.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ecdbde46235f3d560b18be0cb706c8e8ad1b965e5c13bbba7450c86064e96561"},
+ {file = "pydantic_core-2.10.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ed550ed05540c03f0e69e6d74ad58d026de61b9eaebebbaaf8873e585cbb18de"},
+ {file = "pydantic_core-2.10.1-cp38-none-win32.whl", hash = "sha256:8cdbbd92154db2fec4ec973d45c565e767ddc20aa6dbaf50142676484cbff8ee"},
+ {file = "pydantic_core-2.10.1-cp38-none-win_amd64.whl", hash = "sha256:9f6f3e2598604956480f6c8aa24a3384dbf6509fe995d97f6ca6103bb8c2534e"},
+ {file = "pydantic_core-2.10.1-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:655f8f4c8d6a5963c9a0687793da37b9b681d9ad06f29438a3b2326d4e6b7970"},
+ {file = "pydantic_core-2.10.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e570ffeb2170e116a5b17e83f19911020ac79d19c96f320cbfa1fa96b470185b"},
+ {file = "pydantic_core-2.10.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:64322bfa13e44c6c30c518729ef08fda6026b96d5c0be724b3c4ae4da939f875"},
+ {file = "pydantic_core-2.10.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:485a91abe3a07c3a8d1e082ba29254eea3e2bb13cbbd4351ea4e5a21912cc9b0"},
+ {file = "pydantic_core-2.10.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7c2b8eb9fc872e68b46eeaf835e86bccc3a58ba57d0eedc109cbb14177be531"},
+ {file = "pydantic_core-2.10.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a5cb87bdc2e5f620693148b5f8f842d293cae46c5f15a1b1bf7ceeed324a740c"},
+ {file = "pydantic_core-2.10.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:25bd966103890ccfa028841a8f30cebcf5875eeac8c4bde4fe221364c92f0c9a"},
+ {file = "pydantic_core-2.10.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f323306d0556351735b54acbf82904fe30a27b6a7147153cbe6e19aaaa2aa429"},
+ {file = "pydantic_core-2.10.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0c27f38dc4fbf07b358b2bc90edf35e82d1703e22ff2efa4af4ad5de1b3833e7"},
+ {file = "pydantic_core-2.10.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f1365e032a477c1430cfe0cf2856679529a2331426f8081172c4a74186f1d595"},
+ {file = "pydantic_core-2.10.1-cp39-none-win32.whl", hash = "sha256:a1c311fd06ab3b10805abb72109f01a134019739bd3286b8ae1bc2fc4e50c07a"},
+ {file = "pydantic_core-2.10.1-cp39-none-win_amd64.whl", hash = "sha256:ae8a8843b11dc0b03b57b52793e391f0122e740de3df1474814c700d2622950a"},
+ {file = "pydantic_core-2.10.1-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:d43002441932f9a9ea5d6f9efaa2e21458221a3a4b417a14027a1d530201ef1b"},
+ {file = "pydantic_core-2.10.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:fcb83175cc4936a5425dde3356f079ae03c0802bbdf8ff82c035f8a54b333521"},
+ {file = "pydantic_core-2.10.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:962ed72424bf1f72334e2f1e61b68f16c0e596f024ca7ac5daf229f7c26e4208"},
+ {file = "pydantic_core-2.10.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2cf5bb4dd67f20f3bbc1209ef572a259027c49e5ff694fa56bed62959b41e1f9"},
+ {file = "pydantic_core-2.10.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e544246b859f17373bed915182ab841b80849ed9cf23f1f07b73b7c58baee5fb"},
+ {file = "pydantic_core-2.10.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:c0877239307b7e69d025b73774e88e86ce82f6ba6adf98f41069d5b0b78bd1bf"},
+ {file = "pydantic_core-2.10.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:53df009d1e1ba40f696f8995683e067e3967101d4bb4ea6f667931b7d4a01357"},
+ {file = "pydantic_core-2.10.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a1254357f7e4c82e77c348dabf2d55f1d14d19d91ff025004775e70a6ef40ada"},
+ {file = "pydantic_core-2.10.1-pp37-pypy37_pp73-macosx_10_7_x86_64.whl", hash = "sha256:524ff0ca3baea164d6d93a32c58ac79eca9f6cf713586fdc0adb66a8cdeab96a"},
+ {file = "pydantic_core-2.10.1-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f0ac9fb8608dbc6eaf17956bf623c9119b4db7dbb511650910a82e261e6600f"},
+ {file = "pydantic_core-2.10.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:320f14bd4542a04ab23747ff2c8a778bde727158b606e2661349557f0770711e"},
+ {file = "pydantic_core-2.10.1-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:63974d168b6233b4ed6a0046296803cb13c56637a7b8106564ab575926572a55"},
+ {file = "pydantic_core-2.10.1-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:417243bf599ba1f1fef2bb8c543ceb918676954734e2dcb82bf162ae9d7bd514"},
+ {file = "pydantic_core-2.10.1-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:dda81e5ec82485155a19d9624cfcca9be88a405e2857354e5b089c2a982144b2"},
+ {file = "pydantic_core-2.10.1-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:14cfbb00959259e15d684505263d5a21732b31248a5dd4941f73a3be233865b9"},
+ {file = "pydantic_core-2.10.1-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:631cb7415225954fdcc2a024119101946793e5923f6c4d73a5914d27eb3d3a05"},
+ {file = "pydantic_core-2.10.1-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:bec7dd208a4182e99c5b6c501ce0b1f49de2802448d4056091f8e630b28e9a52"},
+ {file = "pydantic_core-2.10.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:149b8a07712f45b332faee1a2258d8ef1fb4a36f88c0c17cb687f205c5dc6e7d"},
+ {file = "pydantic_core-2.10.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4d966c47f9dd73c2d32a809d2be529112d509321c5310ebf54076812e6ecd884"},
+ {file = "pydantic_core-2.10.1-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7eb037106f5c6b3b0b864ad226b0b7ab58157124161d48e4b30c4a43fef8bc4b"},
+ {file = "pydantic_core-2.10.1-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:154ea7c52e32dce13065dbb20a4a6f0cc012b4f667ac90d648d36b12007fa9f7"},
+ {file = "pydantic_core-2.10.1-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e562617a45b5a9da5be4abe72b971d4f00bf8555eb29bb91ec2ef2be348cd132"},
+ {file = "pydantic_core-2.10.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:f23b55eb5464468f9e0e9a9935ce3ed2a870608d5f534025cd5536bca25b1402"},
+ {file = "pydantic_core-2.10.1-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:e9121b4009339b0f751955baf4543a0bfd6bc3f8188f8056b1a25a2d45099934"},
+ {file = "pydantic_core-2.10.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:0523aeb76e03f753b58be33b26540880bac5aa54422e4462404c432230543f33"},
+ {file = "pydantic_core-2.10.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e0e2959ef5d5b8dc9ef21e1a305a21a36e254e6a34432d00c72a92fdc5ecda5"},
+ {file = "pydantic_core-2.10.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da01bec0a26befab4898ed83b362993c844b9a607a86add78604186297eb047e"},
+ {file = "pydantic_core-2.10.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f2e9072d71c1f6cfc79a36d4484c82823c560e6f5599c43c1ca6b5cdbd54f881"},
+ {file = "pydantic_core-2.10.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f36a3489d9e28fe4b67be9992a23029c3cec0babc3bd9afb39f49844a8c721c5"},
+ {file = "pydantic_core-2.10.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f64f82cc3443149292b32387086d02a6c7fb39b8781563e0ca7b8d7d9cf72bd7"},
+ {file = "pydantic_core-2.10.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:b4a6db486ac8e99ae696e09efc8b2b9fea67b63c8f88ba7a1a16c24a057a0776"},
+ {file = "pydantic_core-2.10.1.tar.gz", hash = "sha256:0f8682dbdd2f67f8e1edddcbffcc29f60a6182b4901c367fc8c1c40d30bb0a82"},
+]
+
+[package.dependencies]
+typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0"
+
+[[package]]
+name = "python-slugify"
+version = "8.0.1"
+description = "A Python slugify application that also handles Unicode"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "python-slugify-8.0.1.tar.gz", hash = "sha256:ce0d46ddb668b3be82f4ed5e503dbc33dd815d83e2eb6824211310d3fb172a27"},
+ {file = "python_slugify-8.0.1-py2.py3-none-any.whl", hash = "sha256:70ca6ea68fe63ecc8fa4fcf00ae651fc8a5d02d93dcd12ae6d4fc7ca46c4d395"},
+]
+
+[package.dependencies]
+text-unidecode = ">=1.3"
+
+[package.extras]
+unidecode = ["Unidecode (>=1.1.1)"]
+
+[[package]]
+name = "ruff"
+version = "0.1.2"
+description = "An extremely fast Python linter, written in Rust."
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "ruff-0.1.2-py3-none-macosx_10_7_x86_64.whl", hash = "sha256:0d3ee66b825b713611f89aa35d16de984f76f26c50982a25d52cd0910dff3923"},
+ {file = "ruff-0.1.2-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:f85f850a320ff532b8f93e8d1da6a36ef03698c446357c8c43b46ef90bb321eb"},
+ {file = "ruff-0.1.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:809c6d4e45683696d19ca79e4c6bd3b2e9204fe9546923f2eb3b126ec314b0dc"},
+ {file = "ruff-0.1.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:46005e4abb268e93cad065244e17e2ea16b6fcb55a5c473f34fbc1fd01ae34cb"},
+ {file = "ruff-0.1.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10cdb302f519664d5e2cf954562ac86c9d20ca05855e5b5c2f9d542228f45da4"},
+ {file = "ruff-0.1.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:f89ebcbe57a1eab7d7b4ceb57ddf0af9ed13eae24e443a7c1dc078000bd8cc6b"},
+ {file = "ruff-0.1.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7344eaca057d4c32373c9c3a7afb7274f56040c225b6193dd495fcf69453b436"},
+ {file = "ruff-0.1.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dffa25f6e03c4950b6ac6f216bc0f98a4be9719cb0c5260c8e88d1bac36f1683"},
+ {file = "ruff-0.1.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42ddaea52cb7ba7c785e8593a7532866c193bc774fe570f0e4b1ccedd95b83c5"},
+ {file = "ruff-0.1.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a8533efda625bbec0bf27da2886bd641dae0c209104f6c39abc4be5b7b22de2a"},
+ {file = "ruff-0.1.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:b0b1b82221ba7c50e03b7a86b983157b5d3f4d8d4f16728132bdf02c6d651f77"},
+ {file = "ruff-0.1.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:6c1362eb9288f8cc95535294cb03bd4665c8cef86ec32745476a4e5c6817034c"},
+ {file = "ruff-0.1.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:ffa7ef5ded0563329a35bd5a1cfdae40f05a75c0cc2dd30f00b1320b1fb461fc"},
+ {file = "ruff-0.1.2-py3-none-win32.whl", hash = "sha256:6e8073f85e47072256e2e1909f1ae515cf61ff5a4d24730a63b8b4ac24b6704a"},
+ {file = "ruff-0.1.2-py3-none-win_amd64.whl", hash = "sha256:b836ddff662a45385948ee0878b0a04c3a260949905ad861a37b931d6ee1c210"},
+ {file = "ruff-0.1.2-py3-none-win_arm64.whl", hash = "sha256:b0c42d00db5639dbd5f7f9923c63648682dd197bf5de1151b595160c96172691"},
+ {file = "ruff-0.1.2.tar.gz", hash = "sha256:afd4785ae060ce6edcd52436d0c197628a918d6d09e3107a892a1bad6a4c6608"},
+]
+
+[[package]]
+name = "setuptools"
+version = "68.2.2"
+description = "Easily download, build, install, upgrade, and uninstall Python packages"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "setuptools-68.2.2-py3-none-any.whl", hash = "sha256:b454a35605876da60632df1a60f736524eb73cc47bbc9f3f1ef1b644de74fd2a"},
+ {file = "setuptools-68.2.2.tar.gz", hash = "sha256:4ac1475276d2f1c48684874089fefcd83bd7162ddaafb81fac866ba0db282a87"},
+]
+
+[package.extras]
+docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
+testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
+testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.1)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"]
+
+[[package]]
+name = "text-unidecode"
+version = "1.3"
+description = "The most basic Text::Unidecode port"
+optional = false
+python-versions = "*"
+files = [
+ {file = "text-unidecode-1.3.tar.gz", hash = "sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93"},
+ {file = "text_unidecode-1.3-py2.py3-none-any.whl", hash = "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8"},
+]
+
+[[package]]
+name = "types-python-slugify"
+version = "8.0.0.3"
+description = "Typing stubs for python-slugify"
+optional = false
+python-versions = "*"
+files = [
+ {file = "types-python-slugify-8.0.0.3.tar.gz", hash = "sha256:868e6610ab9a01c01b2ccc1b982363e694d6bbb4fcf32e0d82688c89dceb4e2c"},
+ {file = "types_python_slugify-8.0.0.3-py3-none-any.whl", hash = "sha256:2353c161c79ab6cce955b50720c6cd03586ec297558122236d130e4a19f21209"},
+]
+
+[[package]]
+name = "typing-extensions"
+version = "4.7.1"
+description = "Backported and Experimental Type Hints for Python 3.7+"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "typing_extensions-4.7.1-py3-none-any.whl", hash = "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36"},
+ {file = "typing_extensions-4.7.1.tar.gz", hash = "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2"},
+]
+
+[metadata]
+lock-version = "2.0"
+python-versions = "^3.11"
+content-hash = "7387371b3507348467a83780d62e7e3e0cf33bd0e6f8346c9cfa03e6e86a46ab"
diff --git a/libs/hash-graph-types/python/poetry.toml b/libs/hash-graph-types/python/poetry.toml
new file mode 100644
index 0000000..a5f04e6
--- /dev/null
+++ b/libs/hash-graph-types/python/poetry.toml
@@ -0,0 +1,3 @@
+[virtualenvs]
+name = ".venv"
+in-project = true
diff --git a/libs/hash-graph-types/python/pyproject.toml b/libs/hash-graph-types/python/pyproject.toml
new file mode 100644
index 0000000..4438efd
--- /dev/null
+++ b/libs/hash-graph-types/python/pyproject.toml
@@ -0,0 +1,82 @@
+[tool.poetry]
+name = "hash-graph-types"
+version = "0.0.0"
+description = "Type definitions for the type system and entity validation based on schemas"
+license = "AGPL-3.0-only"
+authors = ["HASH"]
+readme = "README.md"
+packages = [{ include = "graph_types" }]
+
+[tool.poetry.dependencies]
+python = "^3.11"
+
+pydantic = "^2.4.2"
+pydantic_core = "^2.10.1"
+python-slugify = "^8.0.1"
+types-python-slugify = "^8.0"
+
+[tool.poetry.group.dev.dependencies]
+setuptools = "^68.2.2"
+
+[tool.poetry.group.lint-tools.dependencies]
+black = "^23.10.1"
+ruff = "^0.1.2"
+mypy = "^1.6.1"
+
+
+[tool.black]
+target-version = ['py311']
+preview = true
+
+[tool.ruff]
+select = ["ALL"]
+ignore = [
+ "D203", # this conflicts with `D211`
+ "D213", # this conflicts with `D212`
+ "D401", # Relates to PEP-257 but also conflicts with Google Python Style Guide, generally gets in the way
+ "ANN101", # most type checkers infer the type of `self` automatically
+ "ANN102", # most type checkers infer the type of `cls` automatically
+ "FIX002", # TODOs should be fixed, not removed
+ "TD002", # We don't add authors to TODO strings
+ "PLC0414", # Conflicts with `no_implicit_reexport = true`
+]
+
+target-version = "py311"
+
+[tool.mypy]
+plugins = "pydantic.mypy"
+
+disallow_untyped_defs = true
+disallow_any_unimported = true
+no_implicit_optional = true
+check_untyped_defs = true
+no_implicit_reexport = true
+strict_optional = true
+show_error_codes = true
+show_traceback = true
+strict_equality = true
+
+# warnings
+warn_unused_configs = true
+warn_return_any = true
+warn_incomplete_stub = true
+warn_unreachable = true
+warn_redundant_casts = true
+warn_unused_ignores = true
+
+# error messages
+pretty = true
+color_output = true
+error_summary = true
+incremental = true
+
+[tool.pydantic-mypy]
+init_forbid_extra = true
+init_typed = true
+warn_required_dynamic_aliases = true
+warn_untyped_fields = true
+
+
+[build-system]
+requires = ["poetry-core"]
+build-backend = "poetry.core.masonry.api"
diff --git a/libs/hash-graph-types/python/turbo.json b/libs/hash-graph-types/python/turbo.json
new file mode 100644
index 0000000..8ff639a
--- /dev/null
+++ b/libs/hash-graph-types/python/turbo.json
@@ -0,0 +1,41 @@
+{
+ "extends": ["//"],
+ "pipeline": {
+ "build": {
+ "dependsOn": ["^build", "codegen"],
+ "inputs": ["./**/*.py", "pyproject.toml", "poetry.lock", "LICENSE*"],
+ "outputs": ["dist/**"]
+ },
+ "poetry:venv": {
+ "cache": false
+ },
+ "poetry:install": {
+ "cache": false
+ },
+ "poetry:install-production": {
+ "cache": false
+ },
+ "fix:black": {
+ "inputs": ["./**/*.py", "pyproject.toml"]
+ },
+ "fix:ruff": {
+ "inputs": ["./**/*.py", "pyproject.toml"]
+ },
+ "fix:lock-files": {
+ "inputs": ["poetry.lock", "pyproject.toml"],
+ "outputs": ["poetry.lock"]
+ },
+ "lint:black": {
+ "inputs": ["./**/*.py", "pyproject.toml"]
+ },
+ "lint:lock-files": {
+ "inputs": ["poetry.lock", "pyproject.toml"]
+ },
+ "lint:ruff": {
+ "inputs": ["./**/*.py", "pyproject.toml"]
+ },
+ "lint:mypy": {
+ "inputs": ["./**/*.py", "pyproject.toml"]
+ }
+ }
+}