diff --git a/.github/workflows/typecheck.yml b/.github/workflows/typecheck.yml index f4ae01cd..23c300ec 100644 --- a/.github/workflows/typecheck.yml +++ b/.github/workflows/typecheck.yml @@ -1,4 +1,4 @@ -name: MyPy Type Check +name: Pyright Type Check on: push: @@ -7,7 +7,7 @@ on: branches: ["main"] jobs: - mypy: + pyright: runs-on: depot-ubuntu-22.04 environment: development steps: @@ -22,26 +22,18 @@ jobs: with: version: "latest" - name: Install dependencies - run: uv sync --extra dev - - name: Run MyPy for graphiti-core + run: uv sync --all-extras + - name: Run Pyright for graphiti-core shell: bash run: | - set -o pipefail - uv run mypy ./graphiti_core --show-column-numbers --show-error-codes | sed -E ' - s/^(.*):([0-9]+):([0-9]+): (error|warning): (.+) \[(.+)\]/::error file=\1,line=\2,endLine=\2,col=\3,title=\6::\5/; - s/^(.*):([0-9]+):([0-9]+): note: (.+)/::notice file=\1,line=\2,endLine=\2,col=\3,title=Note::\4/; - ' + uv run pyright ./graphiti_core - name: Install graph-service dependencies shell: bash run: | cd server - uv sync --extra dev - - name: Run MyPy for graph-service + uv sync --all-extras + - name: Run Pyright for graph-service shell: bash run: | cd server - set -o pipefail - uv run mypy . --show-column-numbers --show-error-codes | sed -E ' - s/^(.*):([0-9]+):([0-9]+): (error|warning): (.+) \[(.+)\]/::error file=\1,line=\2,endLine=\2,col=\3,title=\6::\5/; - s/^(.*):([0-9]+):([0-9]+): note: (.+)/::notice file=\1,line=\2,endLine=\2,col=\3,title=Note::\4/; - ' + uv run pyright . diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index ffd04351..a1cca03b 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -30,7 +30,7 @@ jobs: - name: Install redis-cli for FalkorDB health check run: sudo apt-get update && sudo apt-get install -y redis-tools - name: Install dependencies - run: uv sync --extra dev + run: uv sync --all-extras - name: Run non-integration tests env: PYTHONPATH: ${{ github.workspace }} diff --git a/Makefile b/Makefile index 7a292c3b..de6e6f53 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ PYTHON = python3 UV = uv PYTEST = $(UV) run pytest RUFF = $(UV) run ruff -MYPY = $(UV) run mypy +PYRIGHT = $(UV) run pyright # Default target all: format lint test @@ -22,7 +22,7 @@ format: # Lint code lint: $(RUFF) check - $(MYPY) ./graphiti_core --show-column-numbers --show-error-codes --pretty + $(PYRIGHT) ./graphiti_core # Run tests test: diff --git a/graphiti_core/driver/__init__.py b/graphiti_core/driver/__init__.py index 05f19311..86d74079 100644 --- a/graphiti_core/driver/__init__.py +++ b/graphiti_core/driver/__init__.py @@ -14,4 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. """ -__all__ = ['GraphDriver', 'Neo4jDriver', 'FalkorDriver'] +from falkordb import FalkorDB +from neo4j import Neo4jDriver + +__all__ = ['Neo4jDriver', 'FalkorDB'] diff --git a/graphiti_core/driver/falkordb_driver.py b/graphiti_core/driver/falkordb_driver.py index c0fd08df..1ff50b3e 100644 --- a/graphiti_core/driver/falkordb_driver.py +++ b/graphiti_core/driver/falkordb_driver.py @@ -51,11 +51,11 @@ class FalkorDriverSession(GraphDriverSession): if isinstance(query, list): for cypher, params in query: params = convert_datetimes_to_strings(params) - await self.graph.query(str(cypher), params) + await self.graph.query(str(cypher), params) # type: ignore[reportUnknownArgumentType] else: params = dict(kwargs) params = convert_datetimes_to_strings(params) - await self.graph.query(str(query), params) + await self.graph.query(str(query), params) # type: ignore[reportUnknownArgumentType] # Assuming `graph.query` is async (ideal); otherwise, wrap in executor return None @@ -99,7 +99,7 @@ class FalkorDriver(GraphDriver): params = convert_datetimes_to_strings(dict(kwargs)) try: - result = await graph.query(cypher_query_, params) + result = await graph.query(cypher_query_, params) # type: ignore[reportUnknownArgumentType] except Exception as e: if 'already indexed' in str(e): # check if index already exists diff --git a/graphiti_core/driver/neo4j_driver.py b/graphiti_core/driver/neo4j_driver.py index 9dd48fbf..060f0a6a 100644 --- a/graphiti_core/driver/neo4j_driver.py +++ b/graphiti_core/driver/neo4j_driver.py @@ -18,7 +18,7 @@ import logging from collections.abc import Coroutine from typing import Any -from neo4j import AsyncGraphDatabase +from neo4j import AsyncGraphDatabase, EagerResult from typing_extensions import LiteralString from graphiti_core.driver.driver import GraphDriver, GraphDriverSession @@ -42,7 +42,7 @@ class Neo4jDriver(GraphDriver): auth=(user or '', password or ''), ) - async def execute_query(self, cypher_query_: LiteralString, **kwargs: Any) -> Coroutine: + async def execute_query(self, cypher_query_: LiteralString, **kwargs: Any) -> EagerResult: params = kwargs.pop('params', None) result = await self.client.execute_query(cypher_query_, parameters_=params, **kwargs) @@ -54,7 +54,9 @@ class Neo4jDriver(GraphDriver): async def close(self) -> None: return await self.client.close() - def delete_all_indexes(self, database_: str = DEFAULT_DATABASE) -> Coroutine: + def delete_all_indexes( + self, database_: str = DEFAULT_DATABASE + ) -> Coroutine[Any, Any, EagerResult]: return self.client.execute_query( 'CALL db.indexes() YIELD name DROP INDEX name', database_=database_, diff --git a/graphiti_core/embedder/voyage.py b/graphiti_core/embedder/voyage.py index 6a22e2e2..e602f9a0 100644 --- a/graphiti_core/embedder/voyage.py +++ b/graphiti_core/embedder/voyage.py @@ -38,7 +38,7 @@ class VoyageAIEmbedder(EmbedderClient): if config is None: config = VoyageAIEmbedderConfig() self.config = config - self.client = voyageai.AsyncClient(api_key=config.api_key) + self.client = voyageai.AsyncClient(api_key=config.api_key) # type: ignore[reportUnknownMemberType] async def create( self, input_data: str | list[str] | Iterable[int] | Iterable[Iterable[int]] diff --git a/graphiti_core/helpers.py b/graphiti_core/helpers.py index 9fb54846..bfd73fe6 100644 --- a/graphiti_core/helpers.py +++ b/graphiti_core/helpers.py @@ -19,6 +19,7 @@ import os import re from collections.abc import Coroutine from datetime import datetime +from typing import Any import numpy as np from dotenv import load_dotenv @@ -99,7 +100,7 @@ def normalize_l2(embedding: list[float]) -> NDArray: async def semaphore_gather( *coroutines: Coroutine, max_coroutines: int | None = None, -): +) -> list[Any]: semaphore = asyncio.Semaphore(max_coroutines or SEMAPHORE_LIMIT) async def _wrap_coroutine(coroutine): diff --git a/graphiti_core/search/search_filters.py b/graphiti_core/search/search_filters.py index 8f93a513..f0286471 100644 --- a/graphiti_core/search/search_filters.py +++ b/graphiti_core/search/search_filters.py @@ -19,7 +19,6 @@ from enum import Enum from typing import Any from pydantic import BaseModel, Field -from typing_extensions import LiteralString class ComparisonOperator(Enum): @@ -53,8 +52,8 @@ class SearchFilters(BaseModel): def node_search_filter_query_constructor( filters: SearchFilters, -) -> tuple[LiteralString, dict[str, Any]]: - filter_query: LiteralString = '' +) -> tuple[str, dict[str, Any]]: + filter_query: str = '' filter_params: dict[str, Any] = {} if filters.node_labels is not None: @@ -67,8 +66,8 @@ def node_search_filter_query_constructor( def edge_search_filter_query_constructor( filters: SearchFilters, -) -> tuple[LiteralString, dict[str, Any]]: - filter_query: LiteralString = '' +) -> tuple[str, dict[str, Any]]: + filter_query: str = '' filter_params: dict[str, Any] = {} if filters.edge_types is not None: diff --git a/graphiti_core/utils/maintenance/community_operations.py b/graphiti_core/utils/maintenance/community_operations.py index 049444f0..b508f347 100644 --- a/graphiti_core/utils/maintenance/community_operations.py +++ b/graphiti_core/utils/maintenance/community_operations.py @@ -40,7 +40,7 @@ async def get_community_clusters( database_=DEFAULT_DATABASE, ) - group_ids = group_id_values[0]['group_ids'] + group_ids = group_id_values[0]['group_ids'] if group_id_values else [] for group_id in group_ids: projection: dict[str, list[Neighbor]] = {} diff --git a/graphiti_core/utils/maintenance/edge_operations.py b/graphiti_core/utils/maintenance/edge_operations.py index 78c2eae4..e26f2b55 100644 --- a/graphiti_core/utils/maintenance/edge_operations.py +++ b/graphiti_core/utils/maintenance/edge_operations.py @@ -297,7 +297,7 @@ async def resolve_extracted_edges( embedder = clients.embedder await create_entity_edge_embeddings(embedder, extracted_edges) - search_results: tuple[list[list[EntityEdge]], list[list[EntityEdge]]] = await semaphore_gather( + search_results = await semaphore_gather( get_relevant_edges(driver, extracted_edges, SearchFilters()), get_edge_invalidation_candidates(driver, extracted_edges, SearchFilters(), 0.2), ) diff --git a/pyproject.toml b/pyproject.toml index 07d1cf3f..3e6815f9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,7 +31,7 @@ groq = ["groq>=0.2.0"] google-genai = ["google-genai>=1.8.0"] falkord-db = ["falkordb>=1.1.2,<2.0.0"] dev = [ - "mypy>=1.11.1", + "pyright>=1.1.380", "groq>=0.2.0", "anthropic>=0.49.0", "google-genai>=1.8.0", @@ -83,13 +83,11 @@ quote-style = "single" indent-style = "space" docstring-code-format = true -[tool.mypy] -packages = ["graphiti_core"] +[tool.pyright] +include = ["graphiti_core"] +pythonVersion = "3.10" +typeCheckingMode = "basic" -[[tool.mypy.overrides]] -module = "falkordb" -ignore_missing_imports = true - -[[tool.mypy.overrides]] -module = "falkordb.asyncio" -ignore_missing_imports = true +[[tool.pyright.overrides]] +include = ["**/falkordb*"] +reportMissingImports = false diff --git a/server/Makefile b/server/Makefile index 1ada45d1..abb13c22 100644 --- a/server/Makefile +++ b/server/Makefile @@ -5,7 +5,7 @@ PYTHON = python3 UV = uv PYTEST = $(UV) run pytest RUFF = $(UV) run ruff -MYPY = $(UV) run mypy +PYRIGHT = $(UV) run pyright # Default target all: format lint test @@ -22,7 +22,7 @@ format: # Lint code lint: $(RUFF) check - $(MYPY) . --show-column-numbers --show-error-codes --pretty + $(PYRIGHT) . # Run tests test: diff --git a/server/graph_service/config.py b/server/graph_service/config.py index af4eca84..d3edb220 100644 --- a/server/graph_service/config.py +++ b/server/graph_service/config.py @@ -20,7 +20,7 @@ class Settings(BaseSettings): @lru_cache def get_settings(): - return Settings() + return Settings() # type: ignore[call-arg] ZepEnvDep = Annotated[Settings, Depends(get_settings)] diff --git a/server/pyproject.toml b/server/pyproject.toml index 2f590b30..960e0bc1 100644 --- a/server/pyproject.toml +++ b/server/pyproject.toml @@ -18,7 +18,7 @@ dependencies = [ [project.optional-dependencies] dev = [ "pydantic>=2.8.2", - "mypy>=1.11.1", + "pyright>=1.1.380", "pytest>=8.3.2", "python-dotenv>=1.0.1", "pytest-asyncio>=0.24.0", @@ -61,3 +61,8 @@ ignore = ["E501"] quote-style = "single" indent-style = "space" docstring-code-format = true + +[tool.pyright] +include = ["."] +pythonVersion = "3.10" +typeCheckingMode = "standard" diff --git a/server/uv.lock b/server/uv.lock index 2fd30027..94bc8247 100644 --- a/server/uv.lock +++ b/server/uv.lock @@ -137,8 +137,8 @@ dependencies = [ [package.optional-dependencies] dev = [ { name = "fastapi-cli" }, - { name = "mypy" }, { name = "pydantic" }, + { name = "pyright" }, { name = "pytest" }, { name = "pytest-asyncio" }, { name = "pytest-xdist" }, @@ -152,9 +152,9 @@ requires-dist = [ { name = "fastapi-cli", marker = "extra == 'dev'", specifier = ">=0.0.5" }, { name = "graphiti-core" }, { name = "httpx", specifier = ">=0.28.1" }, - { name = "mypy", marker = "extra == 'dev'", specifier = ">=1.11.1" }, { name = "pydantic", marker = "extra == 'dev'", specifier = ">=2.8.2" }, { name = "pydantic-settings", specifier = ">=2.4.0" }, + { name = "pyright", marker = "extra == 'dev'", specifier = ">=1.1.380" }, { name = "pytest", marker = "extra == 'dev'", specifier = ">=8.3.2" }, { name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.24.0" }, { name = "pytest-xdist", marker = "extra == 'dev'", specifier = ">=3.6.1" }, @@ -365,54 +365,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 }, ] -[[package]] -name = "mypy" -version = "1.16.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mypy-extensions" }, - { name = "pathspec" }, - { name = "tomli", marker = "python_full_version < '3.11'" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/81/69/92c7fa98112e4d9eb075a239caa4ef4649ad7d441545ccffbd5e34607cbb/mypy-1.16.1.tar.gz", hash = "sha256:6bd00a0a2094841c5e47e7374bb42b83d64c527a502e3334e1173a0c24437bab", size = 3324747 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8e/12/2bf23a80fcef5edb75de9a1e295d778e0f46ea89eb8b115818b663eff42b/mypy-1.16.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b4f0fed1022a63c6fec38f28b7fc77fca47fd490445c69d0a66266c59dd0b88a", size = 10958644 }, - { url = "https://files.pythonhosted.org/packages/08/50/bfe47b3b278eacf348291742fd5e6613bbc4b3434b72ce9361896417cfe5/mypy-1.16.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:86042bbf9f5a05ea000d3203cf87aa9d0ccf9a01f73f71c58979eb9249f46d72", size = 10087033 }, - { url = "https://files.pythonhosted.org/packages/21/de/40307c12fe25675a0776aaa2cdd2879cf30d99eec91b898de00228dc3ab5/mypy-1.16.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ea7469ee5902c95542bea7ee545f7006508c65c8c54b06dc2c92676ce526f3ea", size = 11875645 }, - { url = "https://files.pythonhosted.org/packages/a6/d8/85bdb59e4a98b7a31495bd8f1a4445d8ffc86cde4ab1f8c11d247c11aedc/mypy-1.16.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:352025753ef6a83cb9e7f2427319bb7875d1fdda8439d1e23de12ab164179574", size = 12616986 }, - { url = "https://files.pythonhosted.org/packages/0e/d0/bb25731158fa8f8ee9e068d3e94fcceb4971fedf1424248496292512afe9/mypy-1.16.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ff9fa5b16e4c1364eb89a4d16bcda9987f05d39604e1e6c35378a2987c1aac2d", size = 12878632 }, - { url = "https://files.pythonhosted.org/packages/2d/11/822a9beb7a2b825c0cb06132ca0a5183f8327a5e23ef89717c9474ba0bc6/mypy-1.16.1-cp310-cp310-win_amd64.whl", hash = "sha256:1256688e284632382f8f3b9e2123df7d279f603c561f099758e66dd6ed4e8bd6", size = 9484391 }, - { url = "https://files.pythonhosted.org/packages/9a/61/ec1245aa1c325cb7a6c0f8570a2eee3bfc40fa90d19b1267f8e50b5c8645/mypy-1.16.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:472e4e4c100062488ec643f6162dd0d5208e33e2f34544e1fc931372e806c0cc", size = 10890557 }, - { url = "https://files.pythonhosted.org/packages/6b/bb/6eccc0ba0aa0c7a87df24e73f0ad34170514abd8162eb0c75fd7128171fb/mypy-1.16.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ea16e2a7d2714277e349e24d19a782a663a34ed60864006e8585db08f8ad1782", size = 10012921 }, - { url = "https://files.pythonhosted.org/packages/5f/80/b337a12e2006715f99f529e732c5f6a8c143bb58c92bb142d5ab380963a5/mypy-1.16.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:08e850ea22adc4d8a4014651575567b0318ede51e8e9fe7a68f25391af699507", size = 11802887 }, - { url = "https://files.pythonhosted.org/packages/d9/59/f7af072d09793d581a745a25737c7c0a945760036b16aeb620f658a017af/mypy-1.16.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22d76a63a42619bfb90122889b903519149879ddbf2ba4251834727944c8baca", size = 12531658 }, - { url = "https://files.pythonhosted.org/packages/82/c4/607672f2d6c0254b94a646cfc45ad589dd71b04aa1f3d642b840f7cce06c/mypy-1.16.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:2c7ce0662b6b9dc8f4ed86eb7a5d505ee3298c04b40ec13b30e572c0e5ae17c4", size = 12732486 }, - { url = "https://files.pythonhosted.org/packages/b6/5e/136555ec1d80df877a707cebf9081bd3a9f397dedc1ab9750518d87489ec/mypy-1.16.1-cp311-cp311-win_amd64.whl", hash = "sha256:211287e98e05352a2e1d4e8759c5490925a7c784ddc84207f4714822f8cf99b6", size = 9479482 }, - { url = "https://files.pythonhosted.org/packages/b4/d6/39482e5fcc724c15bf6280ff5806548c7185e0c090712a3736ed4d07e8b7/mypy-1.16.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:af4792433f09575d9eeca5c63d7d90ca4aeceda9d8355e136f80f8967639183d", size = 11066493 }, - { url = "https://files.pythonhosted.org/packages/e6/e5/26c347890efc6b757f4d5bb83f4a0cf5958b8cf49c938ac99b8b72b420a6/mypy-1.16.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:66df38405fd8466ce3517eda1f6640611a0b8e70895e2a9462d1d4323c5eb4b9", size = 10081687 }, - { url = "https://files.pythonhosted.org/packages/44/c7/b5cb264c97b86914487d6a24bd8688c0172e37ec0f43e93b9691cae9468b/mypy-1.16.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:44e7acddb3c48bd2713994d098729494117803616e116032af192871aed80b79", size = 11839723 }, - { url = "https://files.pythonhosted.org/packages/15/f8/491997a9b8a554204f834ed4816bda813aefda31cf873bb099deee3c9a99/mypy-1.16.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0ab5eca37b50188163fa7c1b73c685ac66c4e9bdee4a85c9adac0e91d8895e15", size = 12722980 }, - { url = "https://files.pythonhosted.org/packages/df/f0/2bd41e174b5fd93bc9de9a28e4fb673113633b8a7f3a607fa4a73595e468/mypy-1.16.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb6229b2c9086247e21a83c309754b9058b438704ad2f6807f0d8227f6ebdd", size = 12903328 }, - { url = "https://files.pythonhosted.org/packages/61/81/5572108a7bec2c46b8aff7e9b524f371fe6ab5efb534d38d6b37b5490da8/mypy-1.16.1-cp312-cp312-win_amd64.whl", hash = "sha256:1f0435cf920e287ff68af3d10a118a73f212deb2ce087619eb4e648116d1fe9b", size = 9562321 }, - { url = "https://files.pythonhosted.org/packages/28/e3/96964af4a75a949e67df4b95318fe2b7427ac8189bbc3ef28f92a1c5bc56/mypy-1.16.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ddc91eb318c8751c69ddb200a5937f1232ee8efb4e64e9f4bc475a33719de438", size = 11063480 }, - { url = "https://files.pythonhosted.org/packages/f5/4d/cd1a42b8e5be278fab7010fb289d9307a63e07153f0ae1510a3d7b703193/mypy-1.16.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:87ff2c13d58bdc4bbe7dc0dedfe622c0f04e2cb2a492269f3b418df2de05c536", size = 10090538 }, - { url = "https://files.pythonhosted.org/packages/c9/4f/c3c6b4b66374b5f68bab07c8cabd63a049ff69796b844bc759a0ca99bb2a/mypy-1.16.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a7cfb0fe29fe5a9841b7c8ee6dffb52382c45acdf68f032145b75620acfbd6f", size = 11836839 }, - { url = "https://files.pythonhosted.org/packages/b4/7e/81ca3b074021ad9775e5cb97ebe0089c0f13684b066a750b7dc208438403/mypy-1.16.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:051e1677689c9d9578b9c7f4d206d763f9bbd95723cd1416fad50db49d52f359", size = 12715634 }, - { url = "https://files.pythonhosted.org/packages/e9/95/bdd40c8be346fa4c70edb4081d727a54d0a05382d84966869738cfa8a497/mypy-1.16.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d5d2309511cc56c021b4b4e462907c2b12f669b2dbeb68300110ec27723971be", size = 12895584 }, - { url = "https://files.pythonhosted.org/packages/5a/fd/d486a0827a1c597b3b48b1bdef47228a6e9ee8102ab8c28f944cb83b65dc/mypy-1.16.1-cp313-cp313-win_amd64.whl", hash = "sha256:4f58ac32771341e38a853c5d0ec0dfe27e18e27da9cdb8bbc882d2249c71a3ee", size = 9573886 }, - { url = "https://files.pythonhosted.org/packages/cf/d3/53e684e78e07c1a2bf7105715e5edd09ce951fc3f47cf9ed095ec1b7a037/mypy-1.16.1-py3-none-any.whl", hash = "sha256:5fc2ac4027d0ef28d6ba69a0343737a23c4d1b83672bf38d1fe237bdc0643b37", size = 2265923 }, -] - -[[package]] -name = "mypy-extensions" -version = "1.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963 }, -] - [[package]] name = "neo4j" version = "5.28.1" @@ -425,6 +377,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6a/57/94225fe5e9dabdc0ff60c88cbfcedf11277f4b34e7ab1373d3e62dbdd207/neo4j-5.28.1-py3-none-any.whl", hash = "sha256:6755ef9e5f4e14b403aef1138fb6315b120631a0075c138b5ddb2a06b87b09fd", size = 312258 }, ] +[[package]] +name = "nodeenv" +version = "1.9.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314 }, +] + [[package]] name = "numpy" version = "2.2.6" @@ -515,15 +476,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469 }, ] -[[package]] -name = "pathspec" -version = "0.12.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191 }, -] - [[package]] name = "pluggy" version = "1.6.0" @@ -658,6 +610,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217 }, ] +[[package]] +name = "pyright" +version = "1.1.402" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nodeenv" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/aa/04/ce0c132d00e20f2d2fb3b3e7c125264ca8b909e693841210534b1ea1752f/pyright-1.1.402.tar.gz", hash = "sha256:85a33c2d40cd4439c66aa946fd4ce71ab2f3f5b8c22ce36a623f59ac22937683", size = 3888207 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fe/37/1a1c62d955e82adae588be8e374c7f77b165b6cb4203f7d581269959abbc/pyright-1.1.402-py3-none-any.whl", hash = "sha256:2c721f11869baac1884e846232800fe021c33f1b4acb3929cff321f7ea4e2982", size = 5624004 }, +] + [[package]] name = "pytest" version = "8.4.1" diff --git a/tests/test_entity_exclusion_int.py b/tests/test_entity_exclusion_int.py index d5dec06e..715e5ada 100644 --- a/tests/test_entity_exclusion_int.py +++ b/tests/test_entity_exclusion_int.py @@ -38,7 +38,7 @@ NEO4J_PASSWORD = os.getenv('NEO4J_PASSWORD') # Test entity type definitions class Person(BaseModel): """A human person mentioned in the conversation.""" - + first_name: str | None = Field(None, description='First name of the person') last_name: str | None = Field(None, description='Last name of the person') occupation: str | None = Field(None, description='Job or profession of the person') @@ -46,15 +46,21 @@ class Person(BaseModel): class Organization(BaseModel): """A company, institution, or organized group.""" - - organization_type: str | None = Field(None, description='Type of organization (company, NGO, etc.)') - industry: str | None = Field(None, description='Industry or sector the organization operates in') + + organization_type: str | None = Field( + None, description='Type of organization (company, NGO, etc.)' + ) + industry: str | None = Field( + None, description='Industry or sector the organization operates in' + ) class Location(BaseModel): """A geographic location, place, or address.""" - - location_type: str | None = Field(None, description='Type of location (city, country, building, etc.)') + + location_type: str | None = Field( + None, description='Type of location (city, country, building, etc.)' + ) coordinates: str | None = Field(None, description='Geographic coordinates if available') @@ -62,49 +68,51 @@ class Location(BaseModel): async def test_exclude_default_entity_type(): """Test excluding the default 'Entity' type while keeping custom types.""" graphiti = Graphiti(NEO4J_URI, NEO4J_USER, NEO4J_PASSWORD) - + try: await graphiti.build_indices_and_constraints() - + # Define entity types but exclude the default 'Entity' type entity_types = { 'Person': Person, 'Organization': Organization, } - + # Add an episode that would normally create both Entity and custom type entities - episode_content = "John Smith works at Acme Corporation in New York. The weather is nice today." - + episode_content = ( + 'John Smith works at Acme Corporation in New York. The weather is nice today.' + ) + result = await graphiti.add_episode( - name="Business Meeting", + name='Business Meeting', episode_body=episode_content, - source_description="Meeting notes", + source_description='Meeting notes', reference_time=datetime.now(timezone.utc), entity_types=entity_types, excluded_entity_types=['Entity'], # Exclude default type - group_id='test_exclude_default' + group_id='test_exclude_default', ) - + # Verify that nodes were created (custom types should still work) assert result is not None - + # Search for nodes to verify only custom types were created search_results = await graphiti.search_( - query="John Smith Acme Corporation", - group_ids=['test_exclude_default'] + query='John Smith Acme Corporation', group_ids=['test_exclude_default'] ) - + # Check that entities were created but with specific types, not default 'Entity' found_nodes = search_results.nodes for node in found_nodes: assert 'Entity' in node.labels # All nodes should have Entity label # But they should also have specific type labels - assert any(label in ['Person', 'Organization'] for label in node.labels), \ - f"Node {node.name} should have a specific type label, got: {node.labels}" - + assert any(label in ['Person', 'Organization'] for label in node.labels), ( + f'Node {node.name} should have a specific type label, got: {node.labels}' + ) + # Clean up await _cleanup_test_nodes(graphiti, 'test_exclude_default') - + finally: await graphiti.close() @@ -113,54 +121,57 @@ async def test_exclude_default_entity_type(): async def test_exclude_specific_custom_types(): """Test excluding specific custom entity types while keeping others.""" graphiti = Graphiti(NEO4J_URI, NEO4J_USER, NEO4J_PASSWORD) - + try: await graphiti.build_indices_and_constraints() - + # Define multiple entity types entity_types = { 'Person': Person, 'Organization': Organization, 'Location': Location, } - + # Add an episode with content that would create all types - episode_content = "Sarah Johnson from Google visited the San Francisco office to discuss the new project." - + episode_content = ( + 'Sarah Johnson from Google visited the San Francisco office to discuss the new project.' + ) + result = await graphiti.add_episode( - name="Office Visit", + name='Office Visit', episode_body=episode_content, - source_description="Visit report", + source_description='Visit report', reference_time=datetime.now(timezone.utc), entity_types=entity_types, excluded_entity_types=['Organization', 'Location'], # Exclude these types - group_id='test_exclude_custom' + group_id='test_exclude_custom', ) - + assert result is not None - + # Search for nodes to verify only Person and Entity types were created search_results = await graphiti.search_( - query="Sarah Johnson Google San Francisco", - group_ids=['test_exclude_custom'] + query='Sarah Johnson Google San Francisco', group_ids=['test_exclude_custom'] ) - + found_nodes = search_results.nodes - + # Should have Person and Entity type nodes, but no Organization or Location for node in found_nodes: assert 'Entity' in node.labels # Should not have excluded types - assert 'Organization' not in node.labels, f"Found excluded Organization in node: {node.name}" - assert 'Location' not in node.labels, f"Found excluded Location in node: {node.name}" - + assert 'Organization' not in node.labels, ( + f'Found excluded Organization in node: {node.name}' + ) + assert 'Location' not in node.labels, f'Found excluded Location in node: {node.name}' + # Should find at least one Person entity (Sarah Johnson) person_nodes = [n for n in found_nodes if 'Person' in n.labels] - assert len(person_nodes) > 0, "Should have found at least one Person entity" - + assert len(person_nodes) > 0, 'Should have found at least one Person entity' + # Clean up await _cleanup_test_nodes(graphiti, 'test_exclude_custom') - + finally: await graphiti.close() @@ -169,41 +180,42 @@ async def test_exclude_specific_custom_types(): async def test_exclude_all_types(): """Test excluding all entity types (edge case).""" graphiti = Graphiti(NEO4J_URI, NEO4J_USER, NEO4J_PASSWORD) - + try: await graphiti.build_indices_and_constraints() - + entity_types = { 'Person': Person, 'Organization': Organization, } - + # Exclude all types result = await graphiti.add_episode( - name="No Entities", - episode_body="This text mentions John and Microsoft but no entities should be created.", - source_description="Test content", + name='No Entities', + episode_body='This text mentions John and Microsoft but no entities should be created.', + source_description='Test content', reference_time=datetime.now(timezone.utc), entity_types=entity_types, excluded_entity_types=['Entity', 'Person', 'Organization'], # Exclude everything - group_id='test_exclude_all' + group_id='test_exclude_all', ) - + assert result is not None - + # Search for nodes - should find very few or none from this episode search_results = await graphiti.search_( - query="John Microsoft", - group_ids=['test_exclude_all'] + query='John Microsoft', group_ids=['test_exclude_all'] ) - + # There should be minimal to no entities created found_nodes = search_results.nodes - assert len(found_nodes) == 0, f"Expected no entities, but found: {[n.name for n in found_nodes]}" - + assert len(found_nodes) == 0, ( + f'Expected no entities, but found: {[n.name for n in found_nodes]}' + ) + # Clean up await _cleanup_test_nodes(graphiti, 'test_exclude_all') - + finally: await graphiti.close() @@ -212,47 +224,46 @@ async def test_exclude_all_types(): async def test_exclude_no_types(): """Test normal behavior when no types are excluded (baseline test).""" graphiti = Graphiti(NEO4J_URI, NEO4J_USER, NEO4J_PASSWORD) - + try: await graphiti.build_indices_and_constraints() - + entity_types = { 'Person': Person, 'Organization': Organization, } - + # Don't exclude any types result = await graphiti.add_episode( - name="Normal Behavior", - episode_body="Alice Smith works at TechCorp.", - source_description="Normal test", + name='Normal Behavior', + episode_body='Alice Smith works at TechCorp.', + source_description='Normal test', reference_time=datetime.now(timezone.utc), entity_types=entity_types, excluded_entity_types=None, # No exclusions - group_id='test_exclude_none' + group_id='test_exclude_none', ) - + assert result is not None - + # Search for nodes - should find entities of all types search_results = await graphiti.search_( - query="Alice Smith TechCorp", - group_ids=['test_exclude_none'] + query='Alice Smith TechCorp', group_ids=['test_exclude_none'] ) - + found_nodes = search_results.nodes - assert len(found_nodes) > 0, "Should have found some entities" - + assert len(found_nodes) > 0, 'Should have found some entities' + # Should have both Person and Organization entities person_nodes = [n for n in found_nodes if 'Person' in n.labels] org_nodes = [n for n in found_nodes if 'Organization' in n.labels] - - assert len(person_nodes) > 0, "Should have found Person entities" - assert len(org_nodes) > 0, "Should have found Organization entities" - + + assert len(person_nodes) > 0, 'Should have found Person entities' + assert len(org_nodes) > 0, 'Should have found Organization entities' + # Clean up await _cleanup_test_nodes(graphiti, 'test_exclude_none') - + finally: await graphiti.close() @@ -263,7 +274,7 @@ def test_validation_valid_excluded_types(): 'Person': Person, 'Organization': Organization, } - + # Valid exclusions assert validate_excluded_entity_types(['Entity'], entity_types) is True assert validate_excluded_entity_types(['Person'], entity_types) is True @@ -278,12 +289,12 @@ def test_validation_invalid_excluded_types(): 'Person': Person, 'Organization': Organization, } - + # Invalid exclusions should raise ValueError - with pytest.raises(ValueError, match="Invalid excluded entity types"): + with pytest.raises(ValueError, match='Invalid excluded entity types'): validate_excluded_entity_types(['InvalidType'], entity_types) - - with pytest.raises(ValueError, match="Invalid excluded entity types"): + + with pytest.raises(ValueError, match='Invalid excluded entity types'): validate_excluded_entity_types(['Person', 'NonExistentType'], entity_types) @@ -291,24 +302,24 @@ def test_validation_invalid_excluded_types(): async def test_excluded_types_parameter_validation_in_add_episode(): """Test that add_episode validates excluded_entity_types parameter.""" graphiti = Graphiti(NEO4J_URI, NEO4J_USER, NEO4J_PASSWORD) - + try: entity_types = { 'Person': Person, } - + # Should raise ValueError for invalid excluded type - with pytest.raises(ValueError, match="Invalid excluded entity types"): + with pytest.raises(ValueError, match='Invalid excluded entity types'): await graphiti.add_episode( - name="Invalid Test", - episode_body="Test content", - source_description="Test", + name='Invalid Test', + episode_body='Test content', + source_description='Test', reference_time=datetime.now(timezone.utc), entity_types=entity_types, excluded_entity_types=['NonExistentType'], - group_id='test_validation' + group_id='test_validation', ) - + finally: await graphiti.close() @@ -317,15 +328,12 @@ async def _cleanup_test_nodes(graphiti: Graphiti, group_id: str): """Helper function to clean up test nodes.""" try: # Get all nodes for this group - search_results = await graphiti.search_( - query="*", - group_ids=[group_id] - ) - + search_results = await graphiti.search_(query='*', group_ids=[group_id]) + # Delete all found nodes for node in search_results.nodes: await node.delete(graphiti.driver) - + except Exception as e: # Log but don't fail the test if cleanup fails - print(f"Warning: Failed to clean up test nodes for group {group_id}: {e}") \ No newline at end of file + print(f'Warning: Failed to clean up test nodes for group {group_id}: {e}') diff --git a/uv.lock b/uv.lock index 73c3fceb..77abb7c2 100644 --- a/uv.lock +++ b/uv.lock @@ -266,6 +266,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2", size = 10182537 }, ] +[[package]] +name = "backoff" +version = "2.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/47/d7/5bbeb12c44d7c4f2fb5b56abce497eb5ed9f34d85701de869acedd602619/backoff-2.2.1.tar.gz", hash = "sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba", size = 17001 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/73/b6e24bd22e6720ca8ee9a85a0c4a2971af8497d8f3193fa05390cbd46e09/backoff-2.2.1-py3-none-any.whl", hash = "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8", size = 15148 }, +] + [[package]] name = "beautifulsoup4" version = "4.13.4" @@ -531,7 +540,7 @@ name = "exceptiongroup" version = "1.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, + { name = "typing-extensions", marker = "python_full_version < '3.12'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/0b/9f/a65090624ecf468cdca03533906e7c69ed7588582240cfe7cc9e770b50eb/exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88", size = 29749 } wheels = [ @@ -729,13 +738,14 @@ wheels = [ [[package]] name = "graphiti-core" -version = "0.13.2" +version = "0.14.0" source = { editable = "." } dependencies = [ { name = "diskcache" }, { name = "neo4j" }, { name = "numpy" }, { name = "openai" }, + { name = "posthog" }, { name = "pydantic" }, { name = "python-dotenv" }, { name = "tenacity" }, @@ -756,7 +766,7 @@ dev = [ { name = "langchain-openai" }, { name = "langgraph" }, { name = "langsmith" }, - { name = "mypy" }, + { name = "pyright" }, { name = "pytest" }, { name = "pytest-asyncio" }, { name = "pytest-xdist" }, @@ -792,11 +802,12 @@ requires-dist = [ { name = "langchain-openai", marker = "extra == 'dev'", specifier = ">=0.2.6" }, { name = "langgraph", marker = "extra == 'dev'", specifier = ">=0.2.15" }, { name = "langsmith", marker = "extra == 'dev'", specifier = ">=0.1.108" }, - { name = "mypy", marker = "extra == 'dev'", specifier = ">=1.11.1" }, { name = "neo4j", specifier = ">=5.26.0" }, { name = "numpy", specifier = ">=1.0.0" }, { name = "openai", specifier = ">=1.91.0" }, + { name = "posthog", specifier = ">=3.0.0" }, { name = "pydantic", specifier = ">=2.11.5" }, + { name = "pyright", marker = "extra == 'dev'", specifier = ">=1.1.380" }, { name = "pytest", marker = "extra == 'dev'", specifier = ">=8.3.3" }, { name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.24.0" }, { name = "pytest-xdist", marker = "extra == 'dev'", specifier = ">=3.6.1" }, @@ -1605,54 +1616,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/07/9f/d4719ce55a1d8bf6619e8bb92f1e2e7399026ea85ae0c324ec77ee06c050/multidict-6.5.1-py3-none-any.whl", hash = "sha256:895354f4a38f53a1df2cc3fa2223fa714cff2b079a9f018a76cad35e7f0f044c", size = 12185 }, ] -[[package]] -name = "mypy" -version = "1.16.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mypy-extensions" }, - { name = "pathspec" }, - { name = "tomli", marker = "python_full_version < '3.11'" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/81/69/92c7fa98112e4d9eb075a239caa4ef4649ad7d441545ccffbd5e34607cbb/mypy-1.16.1.tar.gz", hash = "sha256:6bd00a0a2094841c5e47e7374bb42b83d64c527a502e3334e1173a0c24437bab", size = 3324747 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8e/12/2bf23a80fcef5edb75de9a1e295d778e0f46ea89eb8b115818b663eff42b/mypy-1.16.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b4f0fed1022a63c6fec38f28b7fc77fca47fd490445c69d0a66266c59dd0b88a", size = 10958644 }, - { url = "https://files.pythonhosted.org/packages/08/50/bfe47b3b278eacf348291742fd5e6613bbc4b3434b72ce9361896417cfe5/mypy-1.16.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:86042bbf9f5a05ea000d3203cf87aa9d0ccf9a01f73f71c58979eb9249f46d72", size = 10087033 }, - { url = "https://files.pythonhosted.org/packages/21/de/40307c12fe25675a0776aaa2cdd2879cf30d99eec91b898de00228dc3ab5/mypy-1.16.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ea7469ee5902c95542bea7ee545f7006508c65c8c54b06dc2c92676ce526f3ea", size = 11875645 }, - { url = "https://files.pythonhosted.org/packages/a6/d8/85bdb59e4a98b7a31495bd8f1a4445d8ffc86cde4ab1f8c11d247c11aedc/mypy-1.16.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:352025753ef6a83cb9e7f2427319bb7875d1fdda8439d1e23de12ab164179574", size = 12616986 }, - { url = "https://files.pythonhosted.org/packages/0e/d0/bb25731158fa8f8ee9e068d3e94fcceb4971fedf1424248496292512afe9/mypy-1.16.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ff9fa5b16e4c1364eb89a4d16bcda9987f05d39604e1e6c35378a2987c1aac2d", size = 12878632 }, - { url = "https://files.pythonhosted.org/packages/2d/11/822a9beb7a2b825c0cb06132ca0a5183f8327a5e23ef89717c9474ba0bc6/mypy-1.16.1-cp310-cp310-win_amd64.whl", hash = "sha256:1256688e284632382f8f3b9e2123df7d279f603c561f099758e66dd6ed4e8bd6", size = 9484391 }, - { url = "https://files.pythonhosted.org/packages/9a/61/ec1245aa1c325cb7a6c0f8570a2eee3bfc40fa90d19b1267f8e50b5c8645/mypy-1.16.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:472e4e4c100062488ec643f6162dd0d5208e33e2f34544e1fc931372e806c0cc", size = 10890557 }, - { url = "https://files.pythonhosted.org/packages/6b/bb/6eccc0ba0aa0c7a87df24e73f0ad34170514abd8162eb0c75fd7128171fb/mypy-1.16.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ea16e2a7d2714277e349e24d19a782a663a34ed60864006e8585db08f8ad1782", size = 10012921 }, - { url = "https://files.pythonhosted.org/packages/5f/80/b337a12e2006715f99f529e732c5f6a8c143bb58c92bb142d5ab380963a5/mypy-1.16.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:08e850ea22adc4d8a4014651575567b0318ede51e8e9fe7a68f25391af699507", size = 11802887 }, - { url = "https://files.pythonhosted.org/packages/d9/59/f7af072d09793d581a745a25737c7c0a945760036b16aeb620f658a017af/mypy-1.16.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22d76a63a42619bfb90122889b903519149879ddbf2ba4251834727944c8baca", size = 12531658 }, - { url = "https://files.pythonhosted.org/packages/82/c4/607672f2d6c0254b94a646cfc45ad589dd71b04aa1f3d642b840f7cce06c/mypy-1.16.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:2c7ce0662b6b9dc8f4ed86eb7a5d505ee3298c04b40ec13b30e572c0e5ae17c4", size = 12732486 }, - { url = "https://files.pythonhosted.org/packages/b6/5e/136555ec1d80df877a707cebf9081bd3a9f397dedc1ab9750518d87489ec/mypy-1.16.1-cp311-cp311-win_amd64.whl", hash = "sha256:211287e98e05352a2e1d4e8759c5490925a7c784ddc84207f4714822f8cf99b6", size = 9479482 }, - { url = "https://files.pythonhosted.org/packages/b4/d6/39482e5fcc724c15bf6280ff5806548c7185e0c090712a3736ed4d07e8b7/mypy-1.16.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:af4792433f09575d9eeca5c63d7d90ca4aeceda9d8355e136f80f8967639183d", size = 11066493 }, - { url = "https://files.pythonhosted.org/packages/e6/e5/26c347890efc6b757f4d5bb83f4a0cf5958b8cf49c938ac99b8b72b420a6/mypy-1.16.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:66df38405fd8466ce3517eda1f6640611a0b8e70895e2a9462d1d4323c5eb4b9", size = 10081687 }, - { url = "https://files.pythonhosted.org/packages/44/c7/b5cb264c97b86914487d6a24bd8688c0172e37ec0f43e93b9691cae9468b/mypy-1.16.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:44e7acddb3c48bd2713994d098729494117803616e116032af192871aed80b79", size = 11839723 }, - { url = "https://files.pythonhosted.org/packages/15/f8/491997a9b8a554204f834ed4816bda813aefda31cf873bb099deee3c9a99/mypy-1.16.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0ab5eca37b50188163fa7c1b73c685ac66c4e9bdee4a85c9adac0e91d8895e15", size = 12722980 }, - { url = "https://files.pythonhosted.org/packages/df/f0/2bd41e174b5fd93bc9de9a28e4fb673113633b8a7f3a607fa4a73595e468/mypy-1.16.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb6229b2c9086247e21a83c309754b9058b438704ad2f6807f0d8227f6ebdd", size = 12903328 }, - { url = "https://files.pythonhosted.org/packages/61/81/5572108a7bec2c46b8aff7e9b524f371fe6ab5efb534d38d6b37b5490da8/mypy-1.16.1-cp312-cp312-win_amd64.whl", hash = "sha256:1f0435cf920e287ff68af3d10a118a73f212deb2ce087619eb4e648116d1fe9b", size = 9562321 }, - { url = "https://files.pythonhosted.org/packages/28/e3/96964af4a75a949e67df4b95318fe2b7427ac8189bbc3ef28f92a1c5bc56/mypy-1.16.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ddc91eb318c8751c69ddb200a5937f1232ee8efb4e64e9f4bc475a33719de438", size = 11063480 }, - { url = "https://files.pythonhosted.org/packages/f5/4d/cd1a42b8e5be278fab7010fb289d9307a63e07153f0ae1510a3d7b703193/mypy-1.16.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:87ff2c13d58bdc4bbe7dc0dedfe622c0f04e2cb2a492269f3b418df2de05c536", size = 10090538 }, - { url = "https://files.pythonhosted.org/packages/c9/4f/c3c6b4b66374b5f68bab07c8cabd63a049ff69796b844bc759a0ca99bb2a/mypy-1.16.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a7cfb0fe29fe5a9841b7c8ee6dffb52382c45acdf68f032145b75620acfbd6f", size = 11836839 }, - { url = "https://files.pythonhosted.org/packages/b4/7e/81ca3b074021ad9775e5cb97ebe0089c0f13684b066a750b7dc208438403/mypy-1.16.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:051e1677689c9d9578b9c7f4d206d763f9bbd95723cd1416fad50db49d52f359", size = 12715634 }, - { url = "https://files.pythonhosted.org/packages/e9/95/bdd40c8be346fa4c70edb4081d727a54d0a05382d84966869738cfa8a497/mypy-1.16.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d5d2309511cc56c021b4b4e462907c2b12f669b2dbeb68300110ec27723971be", size = 12895584 }, - { url = "https://files.pythonhosted.org/packages/5a/fd/d486a0827a1c597b3b48b1bdef47228a6e9ee8102ab8c28f944cb83b65dc/mypy-1.16.1-cp313-cp313-win_amd64.whl", hash = "sha256:4f58ac32771341e38a853c5d0ec0dfe27e18e27da9cdb8bbc882d2249c71a3ee", size = 9573886 }, - { url = "https://files.pythonhosted.org/packages/cf/d3/53e684e78e07c1a2bf7105715e5edd09ce951fc3f47cf9ed095ec1b7a037/mypy-1.16.1-py3-none-any.whl", hash = "sha256:5fc2ac4027d0ef28d6ba69a0343737a23c4d1b83672bf38d1fe237bdc0643b37", size = 2265923 }, -] - -[[package]] -name = "mypy-extensions" -version = "1.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963 }, -] - [[package]] name = "nbclient" version = "0.10.2" @@ -1738,6 +1701,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b9/54/dd730b32ea14ea797530a4479b2ed46a6fb250f682a9cfb997e968bf0261/networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f", size = 1723263 }, ] +[[package]] +name = "nodeenv" +version = "1.9.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314 }, +] + [[package]] name = "notebook-shim" version = "0.2.4" @@ -2127,15 +2099,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c6/ac/dac4a63f978e4dcb3c6d3a78c4d8e0192a113d288502a1216950c41b1027/parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18", size = 103650 }, ] -[[package]] -name = "pathspec" -version = "0.12.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191 }, -] - [[package]] name = "pexpect" version = "4.9.0" @@ -2243,6 +2206,23 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538 }, ] +[[package]] +name = "posthog" +version = "6.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "backoff" }, + { name = "distro" }, + { name = "python-dateutil" }, + { name = "requests" }, + { name = "six" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f3/c3/c83883af8cc5e3b45d1bee85edce546a4db369fb8dc8eb6339fad764178b/posthog-6.0.0.tar.gz", hash = "sha256:b7bfa0da03bd9240891885d3e44b747e62192ac9ee6da280f45320f4ad3479e0", size = 88066 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ab/ec/7a44533c9fe7046ffcfe48ca0e7472ada2633854f474be633f4afed7b044/posthog-6.0.0-py3-none-any.whl", hash = "sha256:01f5d11046a6267d4384f552e819f0f4a7dc885eb19f606c36d44d662df9ff89", size = 104945 }, +] + [[package]] name = "prometheus-client" version = "0.22.1" @@ -2536,6 +2516,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/79/84/0fdf9b18ba31d69877bd39c9cd6052b47f3761e9910c15de788e519f079f/PyJWT-2.9.0-py3-none-any.whl", hash = "sha256:3b02fb0f44517787776cf48f2ae25d8e14f300e6d7545a4315cee571a415e850", size = 22344 }, ] +[[package]] +name = "pyright" +version = "1.1.402" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nodeenv" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/aa/04/ce0c132d00e20f2d2fb3b3e7c125264ca8b909e693841210534b1ea1752f/pyright-1.1.402.tar.gz", hash = "sha256:85a33c2d40cd4439c66aa946fd4ce71ab2f3f5b8c22ce36a623f59ac22937683", size = 3888207 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fe/37/1a1c62d955e82adae588be8e374c7f77b165b6cb4203f7d581269959abbc/pyright-1.1.402-py3-none-any.whl", hash = "sha256:2c721f11869baac1884e846232800fe021c33f1b4acb3929cff321f7ea4e2982", size = 5624004 }, +] + [[package]] name = "pytest" version = "8.4.1"