diff --git a/.hooks/generate_docs.py b/.hooks/generate_docs.py index 1753520..f82044c 100644 --- a/.hooks/generate_docs.py +++ b/.hooks/generate_docs.py @@ -3,8 +3,8 @@ import typing as t from pathlib import Path -from markdown import Markdown # type: ignore[import-untyped] -from markdownify import MarkdownConverter # type: ignore[import-untyped] +from markdown import Markdown # type: ignore [import-untyped] +from markdownify import MarkdownConverter # type: ignore [import-untyped] from markupsafe import Markup from mkdocstrings_handlers.python._internal.config import PythonConfig from mkdocstrings_handlers.python._internal.handler import ( @@ -14,7 +14,7 @@ # ruff: noqa: T201 -class CustomMarkdownConverter(MarkdownConverter): # type: ignore[misc] +class CustomMarkdownConverter(MarkdownConverter): # type: ignore [misc] # Strip extra whitespace from code blocks def convert_pre(self, el: t.Any, text: str, parent_tags: t.Any) -> t.Any: return super().convert_pre(el, text.strip(), parent_tags) diff --git a/docs/api/data.mdx b/docs/api/data.mdx index 23e6b37..49c1234 100644 --- a/docs/api/data.mdx +++ b/docs/api/data.mdx @@ -58,7 +58,7 @@ chat\_id column. ```python -def chats_to_df(chats: Chat | t.Sequence[Chat]) -> pd.DataFrame: +def chats_to_df(chats: Chat | t.Sequence[Chat]) -> "pd.DataFrame": """ Convert a Chat or list of Chat objects into a pandas DataFrame. @@ -73,6 +73,13 @@ def chats_to_df(chats: Chat | t.Sequence[Chat]) -> pd.DataFrame: A pandas DataFrame containing the chat data. """ + try: + import pandas as pd + except ImportError as e: + raise ImportError( + "Pandas is not available. Please install `pandas` or use `rigging[data]`.", + ) from e + chats = [chats] if isinstance(chats, Chat) else chats flattened = flatten_chats(chats) @@ -176,10 +183,10 @@ async def chats_to_elastic( The indexed count from the bulk operation """ try: - import elasticsearch.helpers + import elasticsearch.helpers # type: ignore [import-not-found, unused-ignore] except ImportError as e: raise ImportError( - "Elasticsearch is not available. Please install `elasticsearch` or use `rigging[extra]`.", + "Elasticsearch is not available. Please install `elasticsearch` or use `rigging[data]`.", ) from e es_data = chats_to_elastic_data(chats, index, op_type=op_type) @@ -190,7 +197,7 @@ async def chats_to_elastic( await client.indices.put_mapping(index=index, properties=ElasticMapping["properties"]) results = await elasticsearch.helpers.async_bulk(client, es_data, **kwargs) - return results[0] # Return modified count + return results[0] # type: ignore [no-any-return, unused-ignore] ``` @@ -286,7 +293,7 @@ generated by the `chats_to_df` function. ```python -def df_to_chats(df: pd.DataFrame) -> list[Chat]: +def df_to_chats(df: "pd.DataFrame") -> list[Chat]: """ Convert a pandas DataFrame into a list of Chat objects. @@ -301,6 +308,7 @@ def df_to_chats(df: pd.DataFrame) -> list[Chat]: A list of Chat objects. """ + chats = [] for chat_id, chat_group in df.groupby("chat_id"): chat_data = chat_group.iloc[0] @@ -564,7 +572,7 @@ Determine if an S3 bucket exists. ```python -async def s3_bucket_exists(client: S3Client, bucket: str) -> bool: +async def s3_bucket_exists(client: "S3Client", bucket: str) -> bool: """ Determine if an S3 bucket exists. @@ -618,7 +626,7 @@ Determine if an S3 object exists. ```python -async def s3_object_exists(client: S3Client, bucket: str, key: str) -> bool: +async def s3_object_exists(client: "S3Client", bucket: str, key: str) -> bool: """ Determine if an S3 object exists. diff --git a/docs/api/logging.mdx b/docs/api/logging.mdx index 8c5cc87..f1f074f 100644 --- a/docs/api/logging.mdx +++ b/docs/api/logging.mdx @@ -32,7 +32,7 @@ configure\_logging ```python configure_logging( - log_level: LogLevelLiteral, + log_level: LogLevelLiteral = "info", log_file: Path | None = None, log_file_level: LogLevelLiteral = "debug", ) -> None @@ -43,7 +43,9 @@ Configures common loguru handlers. **Parameters:** * **`log_level`** - (`LogLevelLiteral`) + (`LogLevelLiteral`, default: + `'info'` + ) –The desired log level. * **`log_file`** (`Path | None`, default: @@ -60,7 +62,7 @@ Configures common loguru handlers. ```python def configure_logging( - log_level: LogLevelLiteral, + log_level: LogLevelLiteral = "info", log_file: pathlib.Path | None = None, log_file_level: LogLevelLiteral = "debug", ) -> None: diff --git a/docs/api/tools.mdx b/docs/api/tools.mdx index 8105c4d..554b97d 100644 --- a/docs/api/tools.mdx +++ b/docs/api/tools.mdx @@ -41,10 +41,9 @@ Base class for representing a tool to a generator. ### catch ```python -catch: bool | set[type[Exception]] = { - JSONDecodeError, - ValidationError, -} +catch: bool | set[type[Exception]] = set( + DEFAULT_CATCH_EXCEPTIONS +) ``` Whether to catch exceptions and return them as messages. @@ -207,6 +206,8 @@ async def handle_tool_call( # noqa: PLR0912 kwargs = json.loads(tool_call.function.arguments) if self._type_adapter is not None: kwargs = self._type_adapter.validate_python(kwargs) + kwargs = kwargs or {} + dn.log_inputs(**kwargs) # Call the function @@ -367,7 +368,51 @@ def with_( ToolMethod ---------- -A Tool wrapping a class method. +```python +ToolMethod( + fget: Callable[..., Any], + name: str, + description: str, + parameters_schema: dict[str, Any], + catch: bool | Iterable[type[Exception]] | None, + truncate: int | None, + signature: Signature, + type_adapter: TypeAdapter[Any], +) +``` + +A descriptor that acts as a factory for creating bound Tool instances. + +It inherits from `property` to be ignored by pydantic's `ModelMetaclass` +during field inspection. This prevents validation errors which would +otherwise treat the descriptor as a field and stop tool\_method decorators +from being applied in BaseModel classes. + + +```python +def __init__( + self, + fget: t.Callable[..., t.Any], + name: str, + description: str, + parameters_schema: dict[str, t.Any], + catch: bool | t.Iterable[type[Exception]] | None, + truncate: int | None, + signature: inspect.Signature, + type_adapter: TypeAdapter[t.Any], +): + super().__init__(fget) + self.tool_name = name + self.tool_description = description + self.tool_parameters_schema = parameters_schema + self.tool_catch = catch + self.tool_truncate = truncate + self._tool_signature = signature + self._tool_type_adapter = type_adapter +``` + + + tool ---- @@ -621,41 +666,38 @@ def tool_method( ~~~ """ - def make_tool(func: t.Callable[..., t.Any]) -> ToolMethod[P, R]: - # TODO: Improve consistency of detection here before enabling this warning - # if not _is_unbound_method(func): - # warnings.warn( - # "Passing a regular function to @tool_method improperly handles the 'self' argument, use @tool instead.", - # SyntaxWarning, - # stacklevel=3, - # ) + def make_tool(f: t.Callable[t.Concatenate[t.Any, P], R]) -> ToolMethod[P, R]: + # This logic is specialized from `Tool.from_callable` to correctly + # handle the `self` parameter in method signatures. - # Strip the `self` argument from the function signature so - # our schema generation doesn't include it under the hood. + signature = inspect.signature(f) + params_without_self = [p for p_name, p in signature.parameters.items() if p_name != "self"] + schema_signature = signature.replace(parameters=params_without_self) - @functools.wraps(func) - def wrapper(self: t.Any, *args: P.args, **kwargs: P.kwargs) -> R: - return func(self, *args, **kwargs) # type: ignore [no-any-return] + @functools.wraps(f) + def empty_func(*_: t.Any, **kwargs: t.Any) -> t.Any: + return kwargs - wrapper.__signature__ = inspect.signature(func).replace( # type: ignore [attr-defined] - parameters=tuple( - param - for param in inspect.signature(func).parameters.values() - if param.name != "self" - ), - ) + empty_func.__signature__ = schema_signature # type: ignore [attr-defined] + type_adapter: TypeAdapter[t.Any] = TypeAdapter(empty_func) + schema = deref_json(type_adapter.json_schema(), is_json_schema=True) - return ToolMethod.from_callable( - wrapper, # type: ignore [arg-type] - name=name, - description=description, + tool_name = name or f.__name__ + tool_description = inspect.cleandoc(description or f.__doc__ or "") + + return ToolMethod( + fget=f, + name=tool_name, + description=tool_description, + parameters_schema=schema, catch=catch, truncate=truncate, + signature=schema_signature, + type_adapter=type_adapter, ) if func is not None: return make_tool(func) - return make_tool ``` @@ -677,7 +719,7 @@ A client for communicating with MCP servers. ```python -def __init__(self, transport: Transport, connection: StdioConnection | SSEConnection) -> None: +def __init__(self, transport: Transport, connection: "StdioConnection | SSEConnection") -> None: self.transport = transport self.connection = connection self.tools = [] @@ -793,6 +835,9 @@ def as_mcp( ) ~~~ """ + from mcp.server.fastmcp import FastMCP + from mcp.server.fastmcp.tools import Tool as FastMCPTool + rigging_tools: list[Tool[..., t.Any]] = [] for tool in flatten_list(list(tools)): interior_tools = [ diff --git a/docs/api/watchers.mdx b/docs/api/watchers.mdx index 77123b6..d2ed61a 100644 --- a/docs/api/watchers.mdx +++ b/docs/api/watchers.mdx @@ -343,7 +343,7 @@ Create a watcher to write each chat to an Amazon S3 bucket. ```python def write_chats_to_s3( - client: S3Client, + client: "S3Client", bucket: str, key: str, *, diff --git a/poetry.lock b/poetry.lock index 3620549..ede92a9 100644 --- a/poetry.lock +++ b/poetry.lock @@ -7,7 +7,7 @@ description = "Accelerate" optional = true python-versions = ">=3.8.0" groups = ["main"] -markers = "extra == \"all\"" +markers = "extra == \"llm\" or extra == \"all\"" files = [ {file = "accelerate-0.30.1-py3-none-any.whl", hash = "sha256:8dd4edd532a4dac72558c5fe6fe8cb70d0c8ec9e8733f48db97d51ee41cbe763"}, {file = "accelerate-0.30.1.tar.gz", hash = "sha256:96779c618889646b86dc928c9e55e86e50a7ccab59e1692e22096481977ae682"}, @@ -320,7 +320,7 @@ description = "LTS Port of Python audioop" optional = true python-versions = ">=3.13" groups = ["main"] -markers = "extra == \"all\" and python_version >= \"3.13\"" +markers = "(extra == \"llm\" or extra == \"all\") and python_version >= \"3.13\"" files = [ {file = "audioop_lts-0.2.1-cp313-abi3-macosx_10_13_universal2.whl", hash = "sha256:fd1345ae99e17e6910f47ce7d52673c6a1a70820d78b67de1b7abb3af29c426a"}, {file = "audioop_lts-0.2.1-cp313-abi3-macosx_10_13_x86_64.whl", hash = "sha256:e175350da05d2087e12cea8e72a70a1a8b14a17e92ed2022952a4419689ede5e"}, @@ -364,7 +364,7 @@ description = "Multi-library, cross-platform audio decoding." optional = true python-versions = ">=3.6" groups = ["main"] -markers = "extra == \"all\"" +markers = "extra == \"llm\" or extra == \"all\"" files = [ {file = "audioread-3.0.1-py3-none-any.whl", hash = "sha256:4cdce70b8adc0da0a3c9e0d85fb10b3ace30fbdf8d1670fd443929b61d117c33"}, {file = "audioread-3.0.1.tar.gz", hash = "sha256:ac5460a5498c48bdf2e8e767402583a4dcd13f4414d286f42ce4379e8b35066d"}, @@ -409,33 +409,13 @@ charset-normalizer = ["charset-normalizer"] html5lib = ["html5lib"] lxml = ["lxml"] -[[package]] -name = "boto3" -version = "1.40.0" -description = "The AWS SDK for Python" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "boto3-1.40.0-py3-none-any.whl", hash = "sha256:959443055d2af676c336cc6033b3f870a8a924384b70d0b2905081d649378179"}, - {file = "boto3-1.40.0.tar.gz", hash = "sha256:fc1b3ca3baf3d8820c6faddf47cbba8ad3cd16f8e8d7e2f76d304bf995932eb7"}, -] - -[package.dependencies] -botocore = ">=1.40.0,<1.41.0" -jmespath = ">=0.7.1,<2.0.0" -s3transfer = ">=0.13.0,<0.14.0" - -[package.extras] -crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] - [[package]] name = "boto3-stubs" version = "1.39.14" description = "Type annotations for boto3 1.39.14 generated with mypy-boto3-builder 8.11.0" optional = false python-versions = ">=3.8" -groups = ["main"] +groups = ["dev"] files = [ {file = "boto3_stubs-1.39.14-py3-none-any.whl", hash = "sha256:28270e752243742493295777f62afca7c7f18ae6ff71ad1feb3ff927e79c95ef"}, {file = "boto3_stubs-1.39.14.tar.gz", hash = "sha256:34d89022be097442f02bd4e9a1fe75803cb6b5a3f6b4763bb6a5ebd7dd6d7dc9"}, @@ -891,7 +871,7 @@ version = "1.38.19" description = "Type annotations and code completion for botocore" optional = false python-versions = ">=3.8" -groups = ["main"] +groups = ["dev"] files = [ {file = "botocore_stubs-1.38.19-py3-none-any.whl", hash = "sha256:66fd7d231c21134a12acbe313ef7a6b152cbf9bfd7bfa12a62f8c33e94737e26"}, {file = "botocore_stubs-1.38.19.tar.gz", hash = "sha256:84f67a42bb240a8ea0c5fe4f05d497cc411177db600bc7012182e499ac24bf19"}, @@ -991,7 +971,7 @@ files = [ {file = "cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662"}, {file = "cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824"}, ] -markers = {main = "(extra == \"examples\" or extra == \"all\") and platform_python_implementation != \"PyPy\" or extra == \"all\"", dev = "implementation_name == \"pypy\""} +markers = {main = "(extra == \"examples\" or extra == \"all\") and platform_python_implementation != \"PyPy\" or extra == \"llm\" or extra == \"all\"", dev = "implementation_name == \"pypy\""} [package.dependencies] pycparser = "*" @@ -1120,7 +1100,7 @@ description = "Pickler class to extend the standard pickle.Pickler functionality optional = true python-versions = ">=3.8" groups = ["main"] -markers = "extra == \"all\"" +markers = "extra == \"llm\" or extra == \"all\"" files = [ {file = "cloudpickle-3.1.1-py3-none-any.whl", hash = "sha256:c8c5a44295039331ee9dad40ba100a9c7297b6f988e50e87ccdf3765a668350e"}, {file = "cloudpickle-3.1.1.tar.gz", hash = "sha256:b216fa8ae4019d5482a8ac3c95d8f6346115d8835911fd4aefd1a445e4242c64"}, @@ -1332,14 +1312,14 @@ test-randomorder = ["pytest-randomly"] [[package]] name = "cyclopts" -version = "3.22.3" +version = "3.22.5" description = "Intuitive, easy CLIs based on type hints." optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "cyclopts-3.22.3-py3-none-any.whl", hash = "sha256:771ae584868c8beeac74184a96e9fad3726c787b17e47a6f0d5f42cece1df57a"}, - {file = "cyclopts-3.22.3.tar.gz", hash = "sha256:7df1d05e4b56b07079e13880b457b78522101531e2947af1a68f182e89742b34"}, + {file = "cyclopts-3.22.5-py3-none-any.whl", hash = "sha256:92efb4a094d9812718d7efe0bffa319a19cb661f230dbf24406c18cd8809fb82"}, + {file = "cyclopts-3.22.5.tar.gz", hash = "sha256:fa2450b9840abc41c6aa37af5eaeafc7a1264e08054e3a2fe39d49aa154f592a"}, ] [package.dependencies] @@ -1361,7 +1341,7 @@ description = "HuggingFace community-driven open-source library of datasets" optional = true python-versions = ">=3.9.0" groups = ["main"] -markers = "extra == \"all\"" +markers = "extra == \"llm\" or extra == \"all\"" files = [ {file = "datasets-3.6.0-py3-none-any.whl", hash = "sha256:25000c4a2c0873a710df127d08a202a06eab7bf42441a6bc278b499c2f72cd1b"}, {file = "datasets-3.6.0.tar.gz", hash = "sha256:1b2bf43b19776e2787e181cfd329cb0ca1a358ea014780c3581e0f276375e041"}, @@ -1445,7 +1425,7 @@ files = [ {file = "decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a"}, {file = "decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360"}, ] -markers = {main = "extra == \"all\""} +markers = {main = "extra == \"llm\" or extra == \"all\""} [[package]] name = "dill" @@ -1454,7 +1434,7 @@ description = "serialize all of Python" optional = true python-versions = ">=3.8" groups = ["main"] -markers = "extra == \"all\"" +markers = "extra == \"llm\" or extra == \"all\"" files = [ {file = "dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"}, {file = "dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca"}, @@ -1471,7 +1451,7 @@ description = "Disk Cache -- Disk and file backed persistent cache." optional = true python-versions = ">=3" groups = ["main"] -markers = "extra == \"all\"" +markers = "extra == \"llm\" or extra == \"all\"" files = [ {file = "diskcache-5.6.3-py3-none-any.whl", hash = "sha256:5e31b2d5fbad117cc363ebaf6b689474db18a1f6438bc82358b024abd4c2ca19"}, {file = "diskcache-5.6.3.tar.gz", hash = "sha256:2c3a3fa2743d8535d832ec61c2054a1641f41775aa7c556758a109941e33e4fc"}, @@ -1520,14 +1500,14 @@ files = [ [[package]] name = "dreadnode" -version = "1.13.2" +version = "1.13.4" description = "Dreadnode SDK" optional = false python-versions = "<3.14,>=3.10" groups = ["main"] files = [ - {file = "dreadnode-1.13.2-py3-none-any.whl", hash = "sha256:dbd7b15e5f7948fbcbbac4c1f1b7de34ae942bf9b7a874514eeb06427aeabad7"}, - {file = "dreadnode-1.13.2.tar.gz", hash = "sha256:489ca59f60aeeeb202d05e7f0949263ca5a7dc08ecbde1cd761f2d8ee388e3a2"}, + {file = "dreadnode-1.13.4-py3-none-any.whl", hash = "sha256:89df7ad89c6a8f68c8c32fc845bf4ffd686ce96db65a33acbd770dc61dc52544"}, + {file = "dreadnode-1.13.4.tar.gz", hash = "sha256:e59b3673f72fdb30508a4fbc8034cb08a9a1abe6eb0be4583bed804d2e443091"}, ] [package.dependencies] @@ -1552,7 +1532,7 @@ description = "Transport classes and utilities shared among Python Elastic clien optional = true python-versions = ">=3.8" groups = ["main"] -markers = "extra == \"all\"" +markers = "extra == \"data\" or extra == \"all\"" files = [ {file = "elastic_transport-8.17.1-py3-none-any.whl", hash = "sha256:192718f498f1d10c5e9aa8b9cf32aed405e469a7f0e9d6a8923431dbb2c59fb8"}, {file = "elastic_transport-8.17.1.tar.gz", hash = "sha256:5edef32ac864dca8e2f0a613ef63491ee8d6b8cfb52881fa7313ba9290cac6d2"}, @@ -1572,7 +1552,7 @@ description = "Python client for Elasticsearch" optional = true python-versions = ">=3.8" groups = ["main"] -markers = "extra == \"all\"" +markers = "extra == \"data\" or extra == \"all\"" files = [ {file = "elasticsearch-8.19.0-py3-none-any.whl", hash = "sha256:33eee4ec4244ef217952eeee0adff64a409120a1401c3bd35790e22145727e3f"}, {file = "elasticsearch-8.19.0.tar.gz", hash = "sha256:66fe755e55127afc47c68eca6858006932d9d128b68834a15e7e9894517cfb67"}, @@ -1633,7 +1613,7 @@ description = "FastAPI framework, high performance, easy to learn, fast to code, optional = true python-versions = ">=3.8" groups = ["main"] -markers = "extra == \"all\"" +markers = "extra == \"llm\" or extra == \"all\"" files = [ {file = "fastapi-0.115.12-py3-none-any.whl", hash = "sha256:e94613d6c05e27be7ffebdd6ea5f388112e5e430c8f7d6494a9d1d88d43e814d"}, {file = "fastapi-0.115.12.tar.gz", hash = "sha256:1e2c2a2646905f9e83d32f04a3f86aff4a286669c6c950ca95b5fd68c2602681"}, @@ -1830,7 +1810,7 @@ description = "Read and write ML models in GGUF for GGML" optional = true python-versions = ">=3.8" groups = ["main"] -markers = "extra == \"all\"" +markers = "extra == \"llm\" or extra == \"all\"" files = [ {file = "gguf-0.9.1-py3-none-any.whl", hash = "sha256:3c2e921e59c022584de201726f8bf3cabe8801fe0e53d931774cd88d16702bbc"}, {file = "gguf-0.9.1.tar.gz", hash = "sha256:f5e70987e15b19c545f6a9f7533b4033fb306330eb1f3c5c95fd28b14c3fd33b"}, @@ -1954,7 +1934,7 @@ description = "A collection of framework independent HTTP protocol utils." optional = true python-versions = ">=3.8.0" groups = ["main"] -markers = "extra == \"all\"" +markers = "extra == \"llm\" or extra == \"all\"" files = [ {file = "httptools-0.6.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3c73ce323711a6ffb0d247dcd5a550b8babf0f757e86a52558fe5b86d6fefcc0"}, {file = "httptools-0.6.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:345c288418f0944a6fe67be8e6afa9262b18c7626c3ef3c28adc5eabc06a68da"}, @@ -2138,7 +2118,7 @@ description = "a regex intersection checker" optional = true python-versions = ">=3.7" groups = ["main"] -markers = "extra == \"all\"" +markers = "extra == \"llm\" or extra == \"all\"" files = [ {file = "interegular-0.3.3-py37-none-any.whl", hash = "sha256:b0c07007d48c89d6d19f7204972d369b2a77222722e126b6aa63aa721dc3b19c"}, {file = "interegular-0.3.3.tar.gz", hash = "sha256:d9b697b21b34884711399ba0f0376914b81899ce670032486d0d048344a76600"}, @@ -2434,7 +2414,7 @@ description = "Lightweight pipelining with Python functions" optional = true python-versions = ">=3.9" groups = ["main"] -markers = "extra == \"all\"" +markers = "extra == \"llm\" or extra == \"all\"" files = [ {file = "joblib-1.5.1-py3-none-any.whl", hash = "sha256:4719a31f054c7d766948dcd83e9613686b27114f190f717cec7eaa2084f8a74a"}, {file = "joblib-1.5.1.tar.gz", hash = "sha256:f4f86e351f39fe3d0d32a9f2c3d8af1ee4cec285aafcb27003dda5205576b444"}, @@ -2568,7 +2548,7 @@ description = "a modern parsing library" optional = true python-versions = ">=3.8" groups = ["main"] -markers = "extra == \"all\"" +markers = "extra == \"llm\" or extra == \"all\"" files = [ {file = "lark-1.2.2-py3-none-any.whl", hash = "sha256:c2276486b02f0f1b90be155f2c8ba4a8e194d42775786db622faccd652d8e80c"}, {file = "lark-1.2.2.tar.gz", hash = "sha256:ca807d0162cd16cef15a8feecb862d7319e7a09bdb13aef927968e45040fed80"}, @@ -2587,7 +2567,7 @@ description = "Makes it easy to load subpackages and functions on demand." optional = true python-versions = ">=3.7" groups = ["main"] -markers = "extra == \"all\"" +markers = "extra == \"llm\" or extra == \"all\"" files = [ {file = "lazy_loader-0.4-py3-none-any.whl", hash = "sha256:342aa8e14d543a154047afb4ba8ef17f5563baad3fc610d7b15b213b0f119efc"}, {file = "lazy_loader-0.4.tar.gz", hash = "sha256:47c75182589b91a4e1a85a136c074285a5ad4d9f39c63e0d7fb76391c4574cd1"}, @@ -2608,7 +2588,7 @@ description = "Python module for audio and music processing" optional = true python-versions = ">=3.8" groups = ["main"] -markers = "extra == \"all\"" +markers = "extra == \"llm\" or extra == \"all\"" files = [ {file = "librosa-0.11.0-py3-none-any.whl", hash = "sha256:0b6415c4fd68bff4c29288abe67c6d80b587e0e1e2cfb0aad23e4559504a7fa1"}, {file = "librosa-0.11.0.tar.gz", hash = "sha256:f5ed951ca189b375bbe2e33b2abd7e040ceeee302b9bbaeeffdfddb8d0ace908"}, @@ -2675,7 +2655,7 @@ description = "lightweight wrapper around basic LLVM functionality" optional = true python-versions = ">=3.10" groups = ["main"] -markers = "extra == \"all\"" +markers = "extra == \"llm\" or extra == \"all\"" files = [ {file = "llvmlite-0.44.0-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:9fbadbfba8422123bab5535b293da1cf72f9f478a65645ecd73e781f962ca614"}, {file = "llvmlite-0.44.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cccf8eb28f24840f2689fb1a45f9c0f7e582dd24e088dcf96e424834af11f791"}, @@ -2707,7 +2687,7 @@ description = "Enforce the output format (JSON Schema, Regex etc) of a language optional = true python-versions = "<4.0,>=3.8" groups = ["main"] -markers = "extra == \"all\"" +markers = "extra == \"llm\" or extra == \"all\"" files = [ {file = "lm_format_enforcer-0.10.6-py3-none-any.whl", hash = "sha256:cad5a0cfbc9708332b57586a43d2945612fbcf40f9098f3ba95740d1a467a896"}, {file = "lm_format_enforcer-0.10.6.tar.gz", hash = "sha256:ca2f061e9cf22fbe9f5ada2bc774c2bd369e8649b806fb5d9080eee37449f4a6"}, @@ -3097,7 +3077,7 @@ description = "Python library for arbitrary-precision floating-point arithmetic" optional = true python-versions = "*" groups = ["main"] -markers = "extra == \"all\"" +markers = "extra == \"llm\" or extra == \"all\"" files = [ {file = "mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c"}, {file = "mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f"}, @@ -3116,7 +3096,7 @@ description = "MessagePack serializer" optional = true python-versions = ">=3.8" groups = ["main"] -markers = "extra == \"all\"" +markers = "extra == \"llm\" or extra == \"all\"" files = [ {file = "msgpack-1.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7ad442d527a7e358a469faf43fda45aaf4ac3249c8310a82f0ccff9164e5dccd"}, {file = "msgpack-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:74bed8f63f8f14d75eec75cf3d04ad581da6b914001b474a5d3cd3372c8cc27d"}, @@ -3191,7 +3171,7 @@ description = "A fast serialization and validation library, with builtin support optional = true python-versions = ">=3.9" groups = ["main"] -markers = "extra == \"all\"" +markers = "extra == \"llm\" or extra == \"all\"" files = [ {file = "msgspec-0.19.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d8dd848ee7ca7c8153462557655570156c2be94e79acec3561cf379581343259"}, {file = "msgspec-0.19.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0553bbc77662e5708fe66aa75e7bd3e4b0f209709c48b299afd791d711a93c36"}, @@ -3362,7 +3342,7 @@ description = "better multiprocessing and multithreading in Python" optional = true python-versions = ">=3.8" groups = ["main"] -markers = "extra == \"all\"" +markers = "extra == \"llm\" or extra == \"all\"" files = [ {file = "multiprocess-0.70.16-pp310-pypy310_pp73-macosx_10_13_x86_64.whl", hash = "sha256:476887be10e2f59ff183c006af746cb6f1fd0eadcfd4ef49e605cbe2659920ee"}, {file = "multiprocess-0.70.16-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d951bed82c8f73929ac82c61f01a7b5ce8f3e5ef40f5b52553b4f547ce2b08ec"}, @@ -3442,7 +3422,7 @@ version = "1.39.2" description = "Type annotations for boto3 S3 1.39.2 service generated with mypy-boto3-builder 8.11.0" optional = false python-versions = ">=3.8" -groups = ["main"] +groups = ["dev"] files = [ {file = "mypy_boto3_s3-1.39.2-py3-none-any.whl", hash = "sha256:aa110de8944cc533babe5ae2dfae89a8fa13b961a7affa6a1c43c383228060e9"}, {file = "mypy_boto3_s3-1.39.2.tar.gz", hash = "sha256:e1977ee93ad57a2d9533a397e6255a41ca1b206018762c1cf4614271e6fede89"}, @@ -3474,7 +3454,7 @@ files = [ {file = "nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c"}, {file = "nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe"}, ] -markers = {main = "extra == \"all\""} +markers = {main = "extra == \"llm\" or extra == \"all\""} [[package]] name = "networkx" @@ -3483,7 +3463,7 @@ description = "Python package for creating and manipulating graphs and networks" optional = true python-versions = ">=3.10" groups = ["main"] -markers = "extra == \"all\"" +markers = "extra == \"llm\" or extra == \"all\"" files = [ {file = "networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f"}, {file = "networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1"}, @@ -3504,7 +3484,7 @@ description = "compiling Python code using LLVM" optional = true python-versions = ">=3.10" groups = ["main"] -markers = "extra == \"all\"" +markers = "extra == \"llm\" or extra == \"all\"" files = [ {file = "numba-0.61.2-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:cf9f9fc00d6eca0c23fc840817ce9f439b9f03c8f03d6246c0e7f0cb15b7162a"}, {file = "numba-0.61.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ea0247617edcb5dd61f6106a56255baab031acc4257bddaeddb3a1003b4ca3fd"}, @@ -3586,7 +3566,7 @@ description = "CUBLAS native runtime libraries" optional = true python-versions = ">=3" groups = ["main"] -markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and extra == \"all\"" +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and (extra == \"llm\" or extra == \"all\")" files = [ {file = "nvidia_cublas_cu12-12.1.3.1-py3-none-manylinux1_x86_64.whl", hash = "sha256:ee53ccca76a6fc08fb9701aa95b6ceb242cdaab118c3bb152af4e579af792728"}, {file = "nvidia_cublas_cu12-12.1.3.1-py3-none-win_amd64.whl", hash = "sha256:2b964d60e8cf11b5e1073d179d85fa340c120e99b3067558f3cf98dd69d02906"}, @@ -3599,7 +3579,7 @@ description = "CUDA profiling tools runtime libs." optional = true python-versions = ">=3" groups = ["main"] -markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and extra == \"all\"" +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and (extra == \"llm\" or extra == \"all\")" files = [ {file = "nvidia_cuda_cupti_cu12-12.1.105-py3-none-manylinux1_x86_64.whl", hash = "sha256:e54fde3983165c624cb79254ae9818a456eb6e87a7fd4d56a2352c24ee542d7e"}, {file = "nvidia_cuda_cupti_cu12-12.1.105-py3-none-win_amd64.whl", hash = "sha256:bea8236d13a0ac7190bd2919c3e8e6ce1e402104276e6f9694479e48bb0eb2a4"}, @@ -3612,7 +3592,7 @@ description = "NVRTC native runtime libraries" optional = true python-versions = ">=3" groups = ["main"] -markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and extra == \"all\"" +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and (extra == \"llm\" or extra == \"all\")" files = [ {file = "nvidia_cuda_nvrtc_cu12-12.1.105-py3-none-manylinux1_x86_64.whl", hash = "sha256:339b385f50c309763ca65456ec75e17bbefcbbf2893f462cb8b90584cd27a1c2"}, {file = "nvidia_cuda_nvrtc_cu12-12.1.105-py3-none-win_amd64.whl", hash = "sha256:0a98a522d9ff138b96c010a65e145dc1b4850e9ecb75a0172371793752fd46ed"}, @@ -3625,7 +3605,7 @@ description = "CUDA Runtime native Libraries" optional = true python-versions = ">=3" groups = ["main"] -markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and extra == \"all\"" +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and (extra == \"llm\" or extra == \"all\")" files = [ {file = "nvidia_cuda_runtime_cu12-12.1.105-py3-none-manylinux1_x86_64.whl", hash = "sha256:6e258468ddf5796e25f1dc591a31029fa317d97a0a94ed93468fc86301d61e40"}, {file = "nvidia_cuda_runtime_cu12-12.1.105-py3-none-win_amd64.whl", hash = "sha256:dfb46ef84d73fababab44cf03e3b83f80700d27ca300e537f85f636fac474344"}, @@ -3638,7 +3618,7 @@ description = "cuDNN runtime libraries" optional = true python-versions = ">=3" groups = ["main"] -markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and extra == \"all\"" +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and (extra == \"llm\" or extra == \"all\")" files = [ {file = "nvidia_cudnn_cu12-9.1.0.70-py3-none-manylinux2014_x86_64.whl", hash = "sha256:165764f44ef8c61fcdfdfdbe769d687e06374059fbb388b6c89ecb0e28793a6f"}, {file = "nvidia_cudnn_cu12-9.1.0.70-py3-none-win_amd64.whl", hash = "sha256:6278562929433d68365a07a4a1546c237ba2849852c0d4b2262a486e805b977a"}, @@ -3654,7 +3634,7 @@ description = "CUFFT native runtime libraries" optional = true python-versions = ">=3" groups = ["main"] -markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and extra == \"all\"" +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and (extra == \"llm\" or extra == \"all\")" files = [ {file = "nvidia_cufft_cu12-11.0.2.54-py3-none-manylinux1_x86_64.whl", hash = "sha256:794e3948a1aa71fd817c3775866943936774d1c14e7628c74f6f7417224cdf56"}, {file = "nvidia_cufft_cu12-11.0.2.54-py3-none-win_amd64.whl", hash = "sha256:d9ac353f78ff89951da4af698f80870b1534ed69993f10a4cf1d96f21357e253"}, @@ -3667,7 +3647,7 @@ description = "CURAND native runtime libraries" optional = true python-versions = ">=3" groups = ["main"] -markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and extra == \"all\"" +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and (extra == \"llm\" or extra == \"all\")" files = [ {file = "nvidia_curand_cu12-10.3.2.106-py3-none-manylinux1_x86_64.whl", hash = "sha256:9d264c5036dde4e64f1de8c50ae753237c12e0b1348738169cd0f8a536c0e1e0"}, {file = "nvidia_curand_cu12-10.3.2.106-py3-none-win_amd64.whl", hash = "sha256:75b6b0c574c0037839121317e17fd01f8a69fd2ef8e25853d826fec30bdba74a"}, @@ -3680,7 +3660,7 @@ description = "CUDA solver native runtime libraries" optional = true python-versions = ">=3" groups = ["main"] -markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and extra == \"all\"" +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and (extra == \"llm\" or extra == \"all\")" files = [ {file = "nvidia_cusolver_cu12-11.4.5.107-py3-none-manylinux1_x86_64.whl", hash = "sha256:8a7ec542f0412294b15072fa7dab71d31334014a69f953004ea7a118206fe0dd"}, {file = "nvidia_cusolver_cu12-11.4.5.107-py3-none-win_amd64.whl", hash = "sha256:74e0c3a24c78612192a74fcd90dd117f1cf21dea4822e66d89e8ea80e3cd2da5"}, @@ -3698,7 +3678,7 @@ description = "CUSPARSE native runtime libraries" optional = true python-versions = ">=3" groups = ["main"] -markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and extra == \"all\"" +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and (extra == \"llm\" or extra == \"all\")" files = [ {file = "nvidia_cusparse_cu12-12.1.0.106-py3-none-manylinux1_x86_64.whl", hash = "sha256:f3b50f42cf363f86ab21f720998517a659a48131e8d538dc02f8768237bd884c"}, {file = "nvidia_cusparse_cu12-12.1.0.106-py3-none-win_amd64.whl", hash = "sha256:b798237e81b9719373e8fae8d4f091b70a0cf09d9d85c95a557e11df2d8e9a5a"}, @@ -3714,7 +3694,7 @@ description = "Python Bindings for the NVIDIA Management Library" optional = true python-versions = "*" groups = ["main"] -markers = "extra == \"all\"" +markers = "extra == \"llm\" or extra == \"all\"" files = [ {file = "nvidia_ml_py-12.575.51-py3-none-any.whl", hash = "sha256:eb8641800d98ce40a22f479873f34b482e214a7e80349c63be51c3919845446e"}, {file = "nvidia_ml_py-12.575.51.tar.gz", hash = "sha256:6490e93fea99eb4e966327ae18c6eec6256194c921f23459c8767aee28c54581"}, @@ -3727,7 +3707,7 @@ description = "NVIDIA Collective Communication Library (NCCL) Runtime" optional = true python-versions = ">=3" groups = ["main"] -markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and extra == \"all\"" +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and (extra == \"llm\" or extra == \"all\")" files = [ {file = "nvidia_nccl_cu12-2.20.5-py3-none-manylinux2014_aarch64.whl", hash = "sha256:1fc150d5c3250b170b29410ba682384b14581db722b2531b0d8d33c595f33d01"}, {file = "nvidia_nccl_cu12-2.20.5-py3-none-manylinux2014_x86_64.whl", hash = "sha256:057f6bf9685f75215d0c53bf3ac4a10b3e6578351de307abad9e18a99182af56"}, @@ -3740,7 +3720,7 @@ description = "Nvidia JIT LTO Library" optional = true python-versions = ">=3" groups = ["main"] -markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and extra == \"all\"" +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and (extra == \"llm\" or extra == \"all\")" files = [ {file = "nvidia_nvjitlink_cu12-12.9.41-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:c3a2cd87cecf3f0ca5e5df97115ede3a81efec1d4b7e2ec89d13f66834042930"}, {file = "nvidia_nvjitlink_cu12-12.9.41-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:631270891e78de08ebc669bb9ba4418b7899da9efb927fcf6fdff85c9507f54f"}, @@ -3754,7 +3734,7 @@ description = "NVIDIA Tools Extension" optional = true python-versions = ">=3" groups = ["main"] -markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and extra == \"all\"" +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and (extra == \"llm\" or extra == \"all\")" files = [ {file = "nvidia_nvtx_cu12-12.1.105-py3-none-manylinux1_x86_64.whl", hash = "sha256:dc21cf308ca5691e7c04d962e213f8a4aa9bbfa23d95412f452254c2caeb09e5"}, {file = "nvidia_nvtx_cu12-12.1.105-py3-none-win_amd64.whl", hash = "sha256:65f4d98982b31b60026e0e6de73fbdfc09d08a96f4656dd3665ca616a11e1e82"}, @@ -3912,7 +3892,7 @@ description = "Probabilistic Generative Model Programming" optional = true python-versions = ">=3.8" groups = ["main"] -markers = "extra == \"all\"" +markers = "extra == \"llm\" or extra == \"all\"" files = [ {file = "outlines-0.0.46-py3-none-any.whl", hash = "sha256:fea7b2ccd2dd82ddf11849a55ef14c4d7b72f12136a67d1b105b81639c8ca2b0"}, {file = "outlines-0.0.46.tar.gz", hash = "sha256:cd272418a268e0a25b7189180dfdcf9fe1b99f50ac1dfb9ffd83c896c5b3fa3c"}, @@ -4107,7 +4087,7 @@ description = "Python Imaging Library (Fork)" optional = true python-versions = ">=3.9" groups = ["main"] -markers = "extra == \"all\"" +markers = "extra == \"llm\" or extra == \"all\"" files = [ {file = "pillow-11.2.1-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:d57a75d53922fc20c165016a20d9c44f73305e67c351bbc60d1adaf662e74047"}, {file = "pillow-11.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:127bf6ac4a5b58b3d32fc8289656f77f80567d65660bc46f72c0d77e6600cc95"}, @@ -4212,7 +4192,7 @@ files = [ {file = "platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4"}, {file = "platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc"}, ] -markers = {main = "extra == \"all\""} +markers = {main = "extra == \"llm\" or extra == \"all\""} [package.extras] docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"] @@ -4254,7 +4234,7 @@ description = "A friend to fetch your data files" optional = true python-versions = ">=3.7" groups = ["main"] -markers = "extra == \"all\"" +markers = "extra == \"llm\" or extra == \"all\"" files = [ {file = "pooch-1.8.2-py3-none-any.whl", hash = "sha256:3529a57096f7198778a5ceefd5ac3ef0e4d06a6ddaf9fc2d609b806f25302c47"}, {file = "pooch-1.8.2.tar.gz", hash = "sha256:76561f0de68a01da4df6af38e9955c4c9d1a5c90da73f7e40276a5728ec83d10"}, @@ -4277,7 +4257,7 @@ description = "Python client for the Prometheus monitoring system." optional = true python-versions = ">=3.9" groups = ["main"] -markers = "extra == \"all\"" +markers = "extra == \"llm\" or extra == \"all\"" files = [ {file = "prometheus_client-0.22.0-py3-none-any.whl", hash = "sha256:c8951bbe64e62b96cd8e8f5d917279d1b9b91ab766793f33d4dce6c228558713"}, {file = "prometheus_client-0.22.0.tar.gz", hash = "sha256:18da1d2241ac2d10c8d2110f13eedcd5c7c0c8af18c926e8731f04fc10cd575c"}, @@ -4293,7 +4273,7 @@ description = "Instrument your FastAPI app with Prometheus metrics" optional = true python-versions = ">=3.8" groups = ["main"] -markers = "extra == \"all\"" +markers = "extra == \"llm\" or extra == \"all\"" files = [ {file = "prometheus_fastapi_instrumentator-7.1.0-py3-none-any.whl", hash = "sha256:978130f3c0bb7b8ebcc90d35516a6fe13e02d2eb358c8f83887cdef7020c31e9"}, {file = "prometheus_fastapi_instrumentator-7.1.0.tar.gz", hash = "sha256:be7cd61eeea4e5912aeccb4261c6631b3f227d8924542d79eaf5af3f439cbe5e"}, @@ -4466,7 +4446,7 @@ files = [ {file = "psutil-7.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:4cf3d4eb1aa9b348dec30105c55cd9b7d4629285735a102beb4441e38db90553"}, {file = "psutil-7.0.0.tar.gz", hash = "sha256:7be9c3eba38beccb6495ea33afd982a44074b78f28c434a1f51cc07fd315c456"}, ] -markers = {main = "extra == \"all\""} +markers = {main = "extra == \"llm\" or extra == \"all\""} [package.extras] dev = ["abi3audit", "black (==24.10.0)", "check-manifest", "coverage", "packaging", "pylint", "pyperf", "pypinfo", "pytest", "pytest-cov", "pytest-xdist", "requests", "rstcheck", "ruff", "setuptools", "sphinx", "sphinx_rtd_theme", "toml-sort", "twine", "virtualenv", "vulture", "wheel"] @@ -4507,7 +4487,7 @@ description = "Get CPU info with pure Python" optional = true python-versions = "*" groups = ["main"] -markers = "extra == \"all\"" +markers = "extra == \"llm\" or extra == \"all\"" files = [ {file = "py-cpuinfo-9.0.0.tar.gz", hash = "sha256:3cdbbf3fac90dc6f118bfd64384f309edeadd902d7c8fb17f02ffa1fc3f49690"}, {file = "py_cpuinfo-9.0.0-py3-none-any.whl", hash = "sha256:859625bc251f64e21f077d099d4162689c762b5d6a4c3c97553d56241c9674d5"}, @@ -4520,7 +4500,7 @@ description = "Airport and other locations database" optional = true python-versions = "*" groups = ["main"] -markers = "extra == \"all\"" +markers = "extra == \"llm\" or extra == \"all\"" files = [ {file = "pyairports-2.1.1-py3-none-any.whl", hash = "sha256:3dfa0cc3e47696692ade92feccdc6b046968f2a75f5e30f65735d6db7251cb26"}, {file = "pyairports-2.1.1.tar.gz", hash = "sha256:3d60a727fce4da81b9c6393ea8ae0b33d67b37ece344dffc863f749e3ad62bcd"}, @@ -4533,7 +4513,7 @@ description = "Python library for Apache Arrow" optional = true python-versions = ">=3.9" groups = ["main"] -markers = "extra == \"all\"" +markers = "extra == \"llm\" or extra == \"all\"" files = [ {file = "pyarrow-20.0.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:c7dd06fd7d7b410ca5dc839cc9d485d2bc4ae5240851bcd45d85105cc90a47d7"}, {file = "pyarrow-20.0.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:d5382de8dc34c943249b01c19110783d0d64b207167c728461add1ecc2db88e4"}, @@ -4602,7 +4582,7 @@ description = "ISO country, subdivision, language, currency and script definitio optional = true python-versions = ">=3.8" groups = ["main"] -markers = "extra == \"all\"" +markers = "extra == \"llm\" or extra == \"all\"" files = [ {file = "pycountry-24.6.1-py3-none-any.whl", hash = "sha256:f1a4fb391cd7214f8eefd39556d740adcc233c778a27f8942c8dca351d6ce06f"}, {file = "pycountry-24.6.1.tar.gz", hash = "sha256:b61b3faccea67f87d10c1f2b0fc0be714409e8fcdcc1315613174f6466c10221"}, @@ -4619,7 +4599,7 @@ files = [ {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, ] -markers = {main = "(extra == \"examples\" or extra == \"all\") and platform_python_implementation != \"PyPy\" or extra == \"all\"", dev = "implementation_name == \"pypy\""} +markers = {main = "(extra == \"examples\" or extra == \"all\") and platform_python_implementation != \"PyPy\" or extra == \"llm\" or extra == \"all\"", dev = "implementation_name == \"pypy\""} [[package]] name = "pydantic" @@ -5153,7 +5133,7 @@ files = [ {file = "pyzmq-26.4.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:49b6ca2e625b46f499fb081aaf7819a177f41eeb555acb05758aa97f4f95d147"}, {file = "pyzmq-26.4.0.tar.gz", hash = "sha256:4bd13f85f80962f91a651a7356fe0472791a5f7a92f227822b5acf44795c626d"}, ] -markers = {main = "extra == \"all\""} +markers = {main = "extra == \"llm\" or extra == \"all\""} [package.dependencies] cffi = {version = "*", markers = "implementation_name == \"pypy\""} @@ -5165,7 +5145,7 @@ description = "Ray provides a simple, universal API for building distributed app optional = true python-versions = ">=3.9" groups = ["main"] -markers = "extra == \"all\"" +markers = "extra == \"llm\" or extra == \"all\"" files = [ {file = "ray-2.46.0-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:719244b84df79502e5f09497f256618d94d78d66fbaf229422008a0568d3a0ff"}, {file = "ray-2.46.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4378a86919e6643238a1094f711b87fa8dc1a18b998d4190f69ab33c64a22a8c"}, @@ -5366,20 +5346,19 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "rich" -version = "14.0.0" +version = "14.1.0" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" optional = false python-versions = ">=3.8.0" groups = ["main"] files = [ - {file = "rich-14.0.0-py3-none-any.whl", hash = "sha256:1c9491e1951aac09caffd42f448ee3d04e58923ffe14993f6e83068dc395d7e0"}, - {file = "rich-14.0.0.tar.gz", hash = "sha256:82f1bc23a6a21ebca4ae0c45af9bdbc492ed20231dcb63f297d6d1021a9d5725"}, + {file = "rich-14.1.0-py3-none-any.whl", hash = "sha256:536f5f1785986d6dbdea3c75205c473f970777b4a0d6c6dd1b696aa05a3fa04f"}, + {file = "rich-14.1.0.tar.gz", hash = "sha256:e497a48b844b0320d45007cdebfeaeed8db2a4f4bcf49f15e455cfc4af11eaa8"}, ] [package.dependencies] markdown-it-py = ">=2.2.0" pygments = ">=2.13.0,<3.0.0" -typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.11\""} [package.extras] jupyter = ["ipywidgets (>=7.5.1,<9)"] @@ -5561,7 +5540,6 @@ files = [ {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f66efbc1caa63c088dead1c4170d148eabc9b80d95fb75b6c92ac0aad2437d76"}, {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:22353049ba4181685023b25b5b51a574bce33e7f51c759371a7422dcae5402a6"}, {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:932205970b9f9991b34f55136be327501903f7c66830e9760a8ffb15b07f05cd"}, - {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a52d48f4e7bf9005e8f0a89209bf9a73f7190ddf0489eee5eb51377385f59f2a"}, {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-win32.whl", hash = "sha256:3eac5a91891ceb88138c113f9db04f3cebdae277f5d44eaa3651a4f573e6a5da"}, {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-win_amd64.whl", hash = "sha256:ab007f2f5a87bd08ab1499bdf96f3d5c6ad4dcfa364884cb4549aa0154b13a28"}, {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:4a6679521a58256a90b0d89e03992c15144c5f3858f40d7c18886023d7943db6"}, @@ -5570,7 +5548,6 @@ files = [ {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:811ea1594b8a0fb466172c384267a4e5e367298af6b228931f273b111f17ef52"}, {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:cf12567a7b565cbf65d438dec6cfbe2917d3c1bdddfce84a9930b7d35ea59642"}, {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7dd5adc8b930b12c8fc5b99e2d535a09889941aa0d0bd06f4749e9a9397c71d2"}, - {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1492a6051dab8d912fc2adeef0e8c72216b24d57bd896ea607cb90bb0c4981d3"}, {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-win32.whl", hash = "sha256:bd0a08f0bab19093c54e18a14a10b4322e1eacc5217056f3c063bd2f59853ce4"}, {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-win_amd64.whl", hash = "sha256:a274fb2cb086c7a3dea4322ec27f4cb5cc4b6298adb583ab0e211a4682f241eb"}, {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:20b0f8dc160ba83b6dcc0e256846e1a02d044e13f7ea74a3d1d56ede4e48c632"}, @@ -5579,7 +5556,6 @@ files = [ {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:749c16fcc4a2b09f28843cda5a193e0283e47454b63ec4b81eaa2242f50e4ccd"}, {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bf165fef1f223beae7333275156ab2022cffe255dcc51c27f066b4370da81e31"}, {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:32621c177bbf782ca5a18ba4d7af0f1082a3f6e517ac2a18b3974d4edf349680"}, - {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b82a7c94a498853aa0b272fd5bc67f29008da798d4f93a2f9f289feb8426a58d"}, {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-win32.whl", hash = "sha256:e8c4ebfcfd57177b572e2040777b8abc537cdef58a2120e830124946aa9b42c5"}, {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-win_amd64.whl", hash = "sha256:0467c5965282c62203273b838ae77c0d29d7638c8a4e3a1c8bdd3602c10904e4"}, {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:4c8c5d82f50bb53986a5e02d1b3092b03622c02c2eb78e29bec33fd9593bae1a"}, @@ -5588,7 +5564,6 @@ files = [ {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96777d473c05ee3e5e3c3e999f5d23c6f4ec5b0c38c098b3a5229085f74236c6"}, {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:3bc2a80e6420ca8b7d3590791e2dfc709c88ab9152c00eeb511c9875ce5778bf"}, {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:e188d2699864c11c36cdfdada94d781fd5d6b0071cd9c427bceb08ad3d7c70e1"}, - {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4f6f3eac23941b32afccc23081e1f50612bdbe4e982012ef4f5797986828cd01"}, {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-win32.whl", hash = "sha256:6442cb36270b3afb1b4951f060eccca1ce49f3d087ca1ca4563a6eb479cb3de6"}, {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-win_amd64.whl", hash = "sha256:e5b8daf27af0b90da7bb903a876477a9e6d7270be6146906b276605997c7e9a3"}, {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:fc4b630cd3fa2cf7fce38afa91d7cfe844a9f75d7f0f36393fa98815e911d987"}, @@ -5597,7 +5572,6 @@ files = [ {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2f1c3765db32be59d18ab3953f43ab62a761327aafc1594a2a1fbe038b8b8a7"}, {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d85252669dc32f98ebcd5d36768f5d4faeaeaa2d655ac0473be490ecdae3c285"}, {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e143ada795c341b56de9418c58d028989093ee611aa27ffb9b7f609c00d813ed"}, - {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2c59aa6170b990d8d2719323e628aaf36f3bfbc1c26279c0eeeb24d05d2d11c7"}, {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-win32.whl", hash = "sha256:beffaed67936fbbeffd10966a4eb53c402fafd3d6833770516bf7314bc6ffa12"}, {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-win_amd64.whl", hash = "sha256:040ae85536960525ea62868b642bdb0c2cc6021c9f9d507810c0c604e66f5a7b"}, {file = "ruamel.yaml.clib-0.2.12.tar.gz", hash = "sha256:6c8fbb13ec503f99a91901ab46e0b07ae7941cd527393187039aec586fdfd36f"}, @@ -5647,24 +5621,6 @@ files = [ botocore = ">=1.12.91" fsspec = ">=0.6.0" -[[package]] -name = "s3transfer" -version = "0.13.0" -description = "An Amazon S3 Transfer Manager" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "s3transfer-0.13.0-py3-none-any.whl", hash = "sha256:0148ef34d6dd964d0d8cf4311b2b21c474693e57c2e069ec708ce043d2b527be"}, - {file = "s3transfer-0.13.0.tar.gz", hash = "sha256:f5e6db74eb7776a37208001113ea7aa97695368242b364d73e91c981ac522177"}, -] - -[package.dependencies] -botocore = ">=1.37.4,<2.0a.0" - -[package.extras] -crt = ["botocore[crt] (>=1.37.4,<2.0a.0)"] - [[package]] name = "safetensors" version = "0.5.3" @@ -5672,7 +5628,7 @@ description = "" optional = true python-versions = ">=3.7" groups = ["main"] -markers = "extra == \"all\"" +markers = "extra == \"llm\" or extra == \"all\"" files = [ {file = "safetensors-0.5.3-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:bd20eb133db8ed15b40110b7c00c6df51655a2998132193de2f75f72d99c7073"}, {file = "safetensors-0.5.3-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:21d01c14ff6c415c485616b8b0bf961c46b3b343ca59110d38d744e577f9cce7"}, @@ -5711,7 +5667,7 @@ description = "A set of python modules for machine learning and data mining" optional = true python-versions = ">=3.9" groups = ["main"] -markers = "extra == \"all\"" +markers = "extra == \"llm\" or extra == \"all\"" files = [ {file = "scikit_learn-1.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d056391530ccd1e501056160e3c9673b4da4805eb67eb2bdf4e983e1f9c9204e"}, {file = "scikit_learn-1.6.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:0c8d036eb937dbb568c6242fa598d551d88fb4399c0344d95c001980ec1c7d36"}, @@ -5767,7 +5723,7 @@ description = "Fundamental algorithms for scientific computing in Python" optional = true python-versions = ">=3.10" groups = ["main"] -markers = "extra == \"all\"" +markers = "extra == \"llm\" or extra == \"all\"" files = [ {file = "scipy-1.15.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:a345928c86d535060c9c2b25e71e87c39ab2f22fc96e9636bd74d1dbf9de448c"}, {file = "scipy-1.15.3-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:ad3432cb0f9ed87477a8d97f03b763fd1d57709f1bbde3c9369b1dff5503b253"}, @@ -5832,7 +5788,7 @@ description = "SentencePiece python wrapper" optional = true python-versions = "*" groups = ["main"] -markers = "extra == \"all\"" +markers = "extra == \"llm\" or extra == \"all\"" files = [ {file = "sentencepiece-0.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:188779e1298a1c8b8253c7d3ad729cb0a9891e5cef5e5d07ce4592c54869e227"}, {file = "sentencepiece-0.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bed9cf85b296fa2b76fc2547b9cbb691a523864cebaee86304c43a7b4cb1b452"}, @@ -5920,7 +5876,7 @@ description = "An audio library based on libsndfile, CFFI and NumPy" optional = true python-versions = "*" groups = ["main"] -markers = "extra == \"all\"" +markers = "extra == \"llm\" or extra == \"all\"" files = [ {file = "soundfile-0.13.1-py2.py3-none-any.whl", hash = "sha256:a23c717560da2cf4c7b5ae1142514e0fd82d6bbd9dfc93a50423447142f2c445"}, {file = "soundfile-0.13.1-py2.py3-none-macosx_10_9_x86_64.whl", hash = "sha256:82dc664d19831933fe59adad199bf3945ad06d84bc111a5b4c0d3089a5b9ec33"}, @@ -5955,7 +5911,7 @@ description = "High quality, one-dimensional sample-rate conversion library" optional = true python-versions = ">=3.9" groups = ["main"] -markers = "extra == \"all\"" +markers = "extra == \"llm\" or extra == \"all\"" files = [ {file = "soxr-0.5.0.post1-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:7406d782d85f8cf64e66b65e6b7721973de8a1dc50b9e88bc2288c343a987484"}, {file = "soxr-0.5.0.post1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fa0a382fb8d8e2afed2c1642723b2d2d1b9a6728ff89f77f3524034c8885b8c9"}, @@ -6034,7 +5990,7 @@ description = "Standard library aifc redistribution. \"dead battery\"." optional = true python-versions = "*" groups = ["main"] -markers = "extra == \"all\" and python_version >= \"3.13\"" +markers = "(extra == \"llm\" or extra == \"all\") and python_version >= \"3.13\"" files = [ {file = "standard_aifc-3.13.0-py3-none-any.whl", hash = "sha256:f7ae09cc57de1224a0dd8e3eb8f73830be7c3d0bc485de4c1f82b4a7f645ac66"}, {file = "standard_aifc-3.13.0.tar.gz", hash = "sha256:64e249c7cb4b3daf2fdba4e95721f811bde8bdfc43ad9f936589b7bb2fae2e43"}, @@ -6051,7 +6007,7 @@ description = "Standard library chunk redistribution. \"dead battery\"." optional = true python-versions = "*" groups = ["main"] -markers = "extra == \"all\" and python_version >= \"3.13\"" +markers = "(extra == \"llm\" or extra == \"all\") and python_version >= \"3.13\"" files = [ {file = "standard_chunk-3.13.0-py3-none-any.whl", hash = "sha256:17880a26c285189c644bd5bd8f8ed2bdb795d216e3293e6dbe55bbd848e2982c"}, {file = "standard_chunk-3.13.0.tar.gz", hash = "sha256:4ac345d37d7e686d2755e01836b8d98eda0d1a3ee90375e597ae43aaf064d654"}, @@ -6064,7 +6020,7 @@ description = "Standard library sunau redistribution. \"dead battery\"." optional = true python-versions = "*" groups = ["main"] -markers = "extra == \"all\" and python_version >= \"3.13\"" +markers = "(extra == \"llm\" or extra == \"all\") and python_version >= \"3.13\"" files = [ {file = "standard_sunau-3.13.0-py3-none-any.whl", hash = "sha256:53af624a9529c41062f4c2fd33837f297f3baa196b0cfceffea6555654602622"}, {file = "standard_sunau-3.13.0.tar.gz", hash = "sha256:b319a1ac95a09a2378a8442f403c66f4fd4b36616d6df6ae82b8e536ee790908"}, @@ -6098,7 +6054,7 @@ description = "Computer algebra system (CAS) in Python" optional = true python-versions = ">=3.9" groups = ["main"] -markers = "extra == \"all\"" +markers = "extra == \"llm\" or extra == \"all\"" files = [ {file = "sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5"}, {file = "sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517"}, @@ -6117,7 +6073,7 @@ description = "threadpoolctl" optional = true python-versions = ">=3.9" groups = ["main"] -markers = "extra == \"all\"" +markers = "extra == \"llm\" or extra == \"all\"" files = [ {file = "threadpoolctl-3.6.0-py3-none-any.whl", hash = "sha256:43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb"}, {file = "threadpoolctl-3.6.0.tar.gz", hash = "sha256:8ab8b4aa3491d812b623328249fab5302a68d2d71745c8a4c719a2fcaba9f44e"}, @@ -6254,7 +6210,7 @@ description = "Tensors and Dynamic neural networks in Python with strong GPU acc optional = true python-versions = ">=3.8.0" groups = ["main"] -markers = "extra == \"all\"" +markers = "extra == \"llm\" or extra == \"all\"" files = [ {file = "torch-2.4.0-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:4ed94583e244af51d6a8d28701ca5a9e02d1219e782f5a01dd401f90af17d8ac"}, {file = "torch-2.4.0-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:c4ca297b7bd58b506bfd6e78ffd14eb97c0e7797dcd7965df62f50bb575d8954"}, @@ -6309,7 +6265,7 @@ description = "image and video datasets and models for torch deep learning" optional = true python-versions = ">=3.8" groups = ["main"] -markers = "extra == \"all\"" +markers = "extra == \"llm\" or extra == \"all\"" files = [ {file = "torchvision-0.19.0-1-cp310-cp310-win_amd64.whl", hash = "sha256:6ed066aae5c50465d7c4761357aefe5dbd2eb7075a33ab8c14b352fc2353ad4c"}, {file = "torchvision-0.19.0-1-cp311-cp311-win_amd64.whl", hash = "sha256:6b1bce2e4c003d890a18f14ff289528707d918e38539ff890ef02aa31dae1b56"}, @@ -6414,7 +6370,7 @@ description = "State-of-the-art Machine Learning for JAX, PyTorch and TensorFlow optional = true python-versions = ">=3.9.0" groups = ["main"] -markers = "extra == \"all\"" +markers = "extra == \"llm\" or extra == \"all\"" files = [ {file = "transformers-4.52.3-py3-none-any.whl", hash = "sha256:cd04059da50e7cf2a617ce3143ba8beffbf119f8c25a0717c3454fd9d0f19609"}, {file = "transformers-4.52.3.tar.gz", hash = "sha256:2e1de29374f27920aaf6d589d4e6339f33def2fb08809e1a1d792e040e9fbce7"}, @@ -6487,7 +6443,7 @@ description = "A language and compiler for custom Deep Learning operations" optional = true python-versions = "*" groups = ["main"] -markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version < \"3.13\" and extra == \"all\"" +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version < \"3.13\" and (extra == \"llm\" or extra == \"all\")" files = [ {file = "triton-3.0.0-1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e1efef76935b2febc365bfadf74bcb65a6f959a9872e5bddf44cc9e0adce1e1a"}, {file = "triton-3.0.0-1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5ce8520437c602fb633f1324cc3871c47bee3b67acf9756c1a66309b60e3216c"}, @@ -6510,7 +6466,7 @@ version = "0.27.2" description = "Type annotations and code completion for awscrt" optional = false python-versions = ">=3.8" -groups = ["main"] +groups = ["dev"] files = [ {file = "types_awscrt-0.27.2-py3-none-any.whl", hash = "sha256:49a045f25bbd5ad2865f314512afced933aed35ddbafc252e2268efa8a787e4e"}, {file = "types_awscrt-0.27.2.tar.gz", hash = "sha256:acd04f57119eb15626ab0ba9157fc24672421de56e7bd7b9f61681fedee44e91"}, @@ -6561,7 +6517,7 @@ version = "0.12.0" description = "Type annotations and code completion for s3transfer" optional = false python-versions = ">=3.8" -groups = ["main"] +groups = ["dev"] files = [ {file = "types_s3transfer-0.12.0-py3-none-any.whl", hash = "sha256:101bbc5b7f00b71512374df881f480fc6bf63c948b5098ab024bf3370fbfb0e8"}, {file = "types_s3transfer-0.12.0.tar.gz", hash = "sha256:f8f59201481e904362873bf0be3267f259d60ad946ebdfcb847d092a1fa26f98"}, @@ -6631,7 +6587,7 @@ description = "The lightning-fast ASGI server." optional = false python-versions = ">=3.9" groups = ["main"] -markers = "sys_platform != \"emscripten\" or extra == \"all\"" +markers = "sys_platform != \"emscripten\" or extra == \"llm\" or extra == \"all\"" files = [ {file = "uvicorn-0.34.2-py3-none-any.whl", hash = "sha256:deb49af569084536d269fe0a6d67e3754f104cf03aba7c11c40f01aadf33c403"}, {file = "uvicorn-0.34.2.tar.gz", hash = "sha256:0e929828f6186353a80b58ea719861d2629d766293b6d19baf086ba31d4f3328"}, @@ -6659,7 +6615,7 @@ description = "Fast implementation of asyncio event loop on top of libuv" optional = true python-versions = ">=3.8.0" groups = ["main"] -markers = "sys_platform != \"win32\" and sys_platform != \"cygwin\" and platform_python_implementation != \"PyPy\" and extra == \"all\"" +markers = "sys_platform != \"win32\" and sys_platform != \"cygwin\" and platform_python_implementation != \"PyPy\" and (extra == \"llm\" or extra == \"all\")" files = [ {file = "uvloop-0.21.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ec7e6b09a6fdded42403182ab6b832b71f4edaf7f37a9a0e371a01db5f0cb45f"}, {file = "uvloop-0.21.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:196274f2adb9689a289ad7d65700d37df0c0930fd8e4e743fa4834e850d7719d"}, @@ -6712,7 +6668,7 @@ description = "A high-throughput and memory-efficient inference and serving engi optional = true python-versions = ">=3.8" groups = ["main"] -markers = "extra == \"all\"" +markers = "extra == \"llm\" or extra == \"all\"" files = [ {file = "vllm-0.5.5-cp38-abi3-manylinux1_x86_64.whl", hash = "sha256:64fb5b8a3300af3006a3b9ba4eacdf5f4bd2a85887a695e6587684447c49eb8e"}, {file = "vllm-0.5.5.tar.gz", hash = "sha256:14abe33243939e18cdc437abfc78543aac62e11a43f66d36ec855a3e40c797dd"}, @@ -6764,7 +6720,7 @@ description = "Forward-only flash-attn" optional = true python-versions = ">=3.8" groups = ["main"] -markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and extra == \"all\"" +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and (extra == \"llm\" or extra == \"all\")" files = [ {file = "vllm_flash_attn-2.6.1-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:f4570a3ab09178877e00d530a5019d8b3b56b7eabe2ec6b86b696dcc33c3bacb"}, {file = "vllm_flash_attn-2.6.1-cp311-cp311-manylinux1_x86_64.whl", hash = "sha256:2ce0a1ca09bc07dca4b59a56f0db98f5f6a93f8392377fbfd2979e639bc91e88"}, @@ -6826,7 +6782,7 @@ description = "Simple, modern and high performance file watching and code reload optional = true python-versions = ">=3.9" groups = ["main"] -markers = "extra == \"all\"" +markers = "extra == \"llm\" or extra == \"all\"" files = [ {file = "watchfiles-1.0.5-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:5c40fe7dd9e5f81e0847b1ea64e1f5dd79dd61afbedb57759df06767ac719b40"}, {file = "watchfiles-1.0.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8c0db396e6003d99bb2d7232c957b5f0b5634bbd1b24e381a5afcc880f7373fb"}, @@ -6923,7 +6879,7 @@ description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" optional = true python-versions = ">=3.8" groups = ["main"] -markers = "extra == \"all\" or extra == \"examples\"" +markers = "extra == \"llm\" or extra == \"all\" or extra == \"examples\"" files = [ {file = "websockets-13.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f48c749857f8fb598fb890a75f540e3221d0976ed0bf879cf3c7eef34151acee"}, {file = "websockets-13.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c7e72ce6bda6fb9409cc1e8164dd41d7c91466fb599eb047cfda72fe758a34a7"}, @@ -7137,7 +7093,7 @@ description = "XFormers: A collection of composable Transformer building blocks. optional = true python-versions = ">=3.7" groups = ["main"] -markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and extra == \"all\"" +markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and (extra == \"llm\" or extra == \"all\")" files = [ {file = "xformers-0.0.27.post2-cp310-cp310-manylinux2014_x86_64.whl", hash = "sha256:7d4bd4876559fdc3cccbcaea717d3a28ca7f2fb09ab84cac3d859449ba247830"}, {file = "xformers-0.0.27.post2-cp310-cp310-win_amd64.whl", hash = "sha256:cd476c23fea18aa7298753c031c4827a544d919ee542cec771c01bc824d0127d"}, @@ -7175,7 +7131,7 @@ description = "Python binding for xxHash" optional = true python-versions = ">=3.7" groups = ["main"] -markers = "extra == \"all\"" +markers = "extra == \"llm\" or extra == \"all\"" files = [ {file = "xxhash-3.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ece616532c499ee9afbb83078b1b952beffef121d989841f7f4b3dc5ac0fd212"}, {file = "xxhash-3.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3171f693dbc2cef6477054a665dc255d996646b4023fe56cb4db80e26f4cc520"}, @@ -7442,11 +7398,12 @@ test = ["big-O", "importlib_resources ; python_version < \"3.9\"", "jaraco.funct type = ["pytest-mypy"] [extras] -all = ["accelerate", "aiodocker", "asyncssh", "click", "elasticsearch", "httpx", "transformers", "vllm", "websockets"] +all = ["accelerate", "aiodocker", "asyncssh", "click", "elasticsearch", "httpx", "pandas", "transformers", "vllm", "websockets"] +data = ["elasticsearch", "pandas"] examples = ["aiodocker", "asyncssh", "click", "httpx", "websockets"] -tracing = [] +llm = ["accelerate", "transformers", "vllm"] [metadata] lock-version = "2.1" python-versions = ">=3.10,<3.14" -content-hash = "f89b57179a89ba18d1d0cf3cbc8785348925eabc3b921147c0bbf80326f987f7" +content-hash = "d2448a7259469d91fdff7927ce8ef0a19cfeb4be2ca72689aa4cdf311cd9d65d" diff --git a/pyproject.toml b/pyproject.toml index 16dbbee..f6da2c7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,11 +16,8 @@ pydantic = "^2.7.3" pydantic-xml = "^2.11.0" loguru = "^0.7.2" litellm = "^1.67.2" -pandas = "^2.2.2" xmltodict = "^0.13.0" colorama = "^0.4.6" -boto3 = "^1.35.0" -boto3-stubs = { extras = ["s3"], version = "^1.35.0" } jsonpath-ng = "^1.7.0" ruamel-yaml = "^0.18.10" jsonref = "^1.1.0" @@ -30,7 +27,6 @@ dreadnode = ">=1.12.0" vllm = { version = "^0.5.0", optional = true } transformers = { version = "^4.41.0", optional = true } accelerate = { version = "^0.30.1", optional = true } -elasticsearch = { version = "^8.13.2", optional = true } asyncssh = { version = "^2.14.2", optional = true } click = { version = "^8.1.7", optional = true } @@ -38,10 +34,13 @@ httpx = { version = "^0.28.0", optional = true } aiodocker = { version = "^0.22.2", optional = true } websockets = { version = "^13.0", optional = true } +elasticsearch = { version = "^8.13.2", optional = true } +pandas = { version = "^2.2.2", optional = true } [tool.poetry.extras] -tracing = ["logfire"] +data = ["pandas", "elasticsearch"] examples = ["asyncssh", "click", "httpx", "aiodocker", "websockets"] +llm = ["vllm", "transformers", "accelerate"] all = [ "vllm", "transformers", @@ -53,6 +52,7 @@ all = [ "websockets", "logfire", "elasticsearch", + "pandas", ] [tool.poetry.group.dev.dependencies] @@ -70,6 +70,7 @@ beautifulsoup4 = "^4.13.4" mkdocstrings = {extras = ["python"], version = "^0.29.1"} markdown = "^3.8" markdownify = "^1.1.0" +boto3-stubs = { extras = ["s3"], version = "^1.35.0" } # Build diff --git a/rigging/chat.py b/rigging/chat.py index 49e739b..d00fddb 100644 --- a/rigging/chat.py +++ b/rigging/chat.py @@ -60,7 +60,7 @@ if t.TYPE_CHECKING: from dreadnode.metric import Scorer, ScorerCallable from dreadnode.scorers.rigging import ChatFilterFunction, ChatFilterMode - from elasticsearch import AsyncElasticsearch + from elasticsearch import AsyncElasticsearch # type: ignore [import-not-found, unused-ignore] from rigging.data import ElasticOpType from rigging.prompt import Prompt diff --git a/rigging/data.py b/rigging/data.py index 75d9068..bc7fdf9 100644 --- a/rigging/data.py +++ b/rigging/data.py @@ -6,15 +6,16 @@ import json import typing as t -import pandas as pd -from mypy_boto3_s3 import S3Client - from rigging.chat import Chat from rigging.message import Message if t.TYPE_CHECKING: - import elasticsearch - from elastic_transport import ObjectApiResponse + import elasticsearch # type: ignore [import-not-found, unused-ignore] + import pandas as pd + from elastic_transport import ( # type: ignore [import-not-found, unused-ignore] + ObjectApiResponse, + ) + from mypy_boto3_s3 import S3Client def flatten_chats(chats: Chat | t.Sequence[Chat]) -> list[dict[t.Any, t.Any]]: @@ -126,7 +127,7 @@ def by_chat_id(message: dict[t.Any, t.Any]) -> t.Any: # Pandas -def chats_to_df(chats: Chat | t.Sequence[Chat]) -> pd.DataFrame: +def chats_to_df(chats: Chat | t.Sequence[Chat]) -> "pd.DataFrame": """ Convert a Chat or list of Chat objects into a pandas DataFrame. @@ -141,6 +142,13 @@ def chats_to_df(chats: Chat | t.Sequence[Chat]) -> pd.DataFrame: A pandas DataFrame containing the chat data. """ + try: + import pandas as pd + except ImportError as e: + raise ImportError( + "Pandas is not available. Please install `pandas` or use `rigging[data]`.", + ) from e + chats = [chats] if isinstance(chats, Chat) else chats flattened = flatten_chats(chats) @@ -165,7 +173,7 @@ def chats_to_df(chats: Chat | t.Sequence[Chat]) -> pd.DataFrame: ) -def df_to_chats(df: pd.DataFrame) -> list[Chat]: +def df_to_chats(df: "pd.DataFrame") -> list[Chat]: """ Convert a pandas DataFrame into a list of Chat objects. @@ -180,6 +188,7 @@ def df_to_chats(df: pd.DataFrame) -> list[Chat]: A list of Chat objects. """ + chats = [] for chat_id, chat_group in df.groupby("chat_id"): chat_data = chat_group.iloc[0] @@ -279,10 +288,10 @@ async def chats_to_elastic( The indexed count from the bulk operation """ try: - import elasticsearch.helpers + import elasticsearch.helpers # type: ignore [import-not-found, unused-ignore] except ImportError as e: raise ImportError( - "Elasticsearch is not available. Please install `elasticsearch` or use `rigging[extra]`.", + "Elasticsearch is not available. Please install `elasticsearch` or use `rigging[data]`.", ) from e es_data = chats_to_elastic_data(chats, index, op_type=op_type) @@ -293,7 +302,7 @@ async def chats_to_elastic( await client.indices.put_mapping(index=index, properties=ElasticMapping["properties"]) results = await elasticsearch.helpers.async_bulk(client, es_data, **kwargs) - return results[0] # Return modified count + return results[0] # type: ignore [no-any-return, unused-ignore] def elastic_data_to_chats( @@ -352,7 +361,7 @@ async def elastic_to_chats( return elastic_data_to_chats(t.cast("dict[str, t.Any]", data)) -async def s3_bucket_exists(client: S3Client, bucket: str) -> bool: +async def s3_bucket_exists(client: "S3Client", bucket: str) -> bool: """ Determine if an S3 bucket exists. @@ -373,7 +382,7 @@ async def s3_bucket_exists(client: S3Client, bucket: str) -> bool: return True -async def s3_object_exists(client: S3Client, bucket: str, key: str) -> bool: +async def s3_object_exists(client: "S3Client", bucket: str, key: str) -> bool: """ Determine if an S3 object exists. diff --git a/rigging/generator/__init__.py b/rigging/generator/__init__.py index 14ce49e..55616fc 100644 --- a/rigging/generator/__init__.py +++ b/rigging/generator/__init__.py @@ -33,7 +33,7 @@ def get_vllm_lazy() -> type[Generator]: from rigging.generator.vllm_ import VLLMGenerator except ImportError as e: raise ImportError( - "VLLMGenerator is not available. Please install `vllm` or use `rigging[extra]`.", + "VLLMGenerator is not available. Please install `vllm` or use `rigging[llm]`.", ) from e return VLLMGenerator @@ -47,7 +47,7 @@ def get_transformers_lazy() -> type[Generator]: from rigging.generator.transformers_ import TransformersGenerator except ImportError as e: raise ImportError( - "TransformersGenerator is not available. Please install `transformers` or use `rigging[extra]`.", + "TransformersGenerator is not available. Please install `transformers` or use `rigging[llm]`.", ) from e return TransformersGenerator diff --git a/rigging/generator/litellm_.py b/rigging/generator/litellm_.py index 9f0d2b0..f59a829 100644 --- a/rigging/generator/litellm_.py +++ b/rigging/generator/litellm_.py @@ -4,8 +4,6 @@ import re import typing as t -import litellm -import litellm.types.utils from loguru import logger from rigging.generator.base import ( @@ -21,14 +19,8 @@ from rigging.message import ContentAudioInput, ContentImageUrl, ContentText, Message from rigging.tools.base import FunctionDefinition, ToolDefinition -# We should probably let people configure -# this independently, but for now we'll -# fix it to prevent confusion -litellm.drop_params = True - -# Prevent the small debug statements -# from being printed to the console -litellm.suppress_debug_info = True +if t.TYPE_CHECKING: + from litellm.types.utils import ModelResponse, TextCompletionResponse class OpenAIToolsWithImageURLsFixup(Fixup): @@ -151,6 +143,18 @@ class LiteLLMGenerator(Generator): _last_request_time: datetime.datetime | None = None _supports_function_calling: bool | None = None + def __post_model_init__(self, _: t.Any) -> None: + import litellm + + # We should probably let people configure + # this independently, but for now we'll + # fix it to prevent confusion + litellm.drop_params = True + + # Prevent the small debug statements + # from being printed to the console + litellm.suppress_debug_info = True + @property def semaphore(self) -> asyncio.Semaphore: if self._semaphore is None: @@ -161,6 +165,7 @@ def semaphore(self) -> asyncio.Semaphore: async def supports_function_calling(self) -> bool | None: import dreadnode as dn + import litellm.utils if self._supports_function_calling is not None: return self._supports_function_calling @@ -233,8 +238,10 @@ async def _ensure_delay_between_requests(self) -> None: def _parse_model_response( self, - response: litellm.types.utils.ModelResponse, + response: "ModelResponse", ) -> GeneratedMessage: + import litellm.types.utils + choice = response.choices[-1] usage = None if getattr(response, "usage", None) is not None: @@ -312,7 +319,7 @@ def _parse_model_response( def _parse_text_completion_response( self, - response: litellm.types.utils.TextCompletionResponse, + response: "TextCompletionResponse", ) -> GeneratedText: choice = response.choices[-1] usage = None @@ -333,6 +340,8 @@ async def _generate_message( messages: t.Sequence[Message], params: GenerateParams, ) -> GeneratedMessage: + import litellm + async with self.semaphore: # if params.max_tokens is None: # params.max_tokens = get_max_tokens_for_model(self.model) @@ -353,6 +362,8 @@ async def _generate_message( return self._parse_model_response(response) async def _generate_text(self, text: str, params: GenerateParams) -> GeneratedText: + import litellm + async with self.semaphore: # if params.max_tokens is None: # params.max_tokens = get_max_tokens_for_model(self.model) @@ -419,6 +430,8 @@ def get_max_tokens_for_model(model: str) -> int | None: Returns: The maximum number of tokens. """ + import litellm + while model not in litellm.model_cost: if "/" not in model: return None diff --git a/rigging/generator/transformers_.py b/rigging/generator/transformers_.py index 40276f8..15d5d8a 100644 --- a/rigging/generator/transformers_.py +++ b/rigging/generator/transformers_.py @@ -1,9 +1,9 @@ import gc import typing as t -import torch -import transformers # type: ignore [import-untyped, unused-ignore] -from transformers import ( # type: ignore [attr-defined] +import torch # type: ignore [import-not-found, import-untyped, unused-ignore] +import transformers # type: ignore [import-not-found, import-untyped, unused-ignore] +from transformers import ( # type: ignore [import-not-found, attr-defined, unused-ignore] AutoModelForCausalLM, AutoTokenizer, TextGenerationPipeline, @@ -80,7 +80,7 @@ def llm(self) -> AutoModelForCausalLM: "load_in_4bit", }, ) - self._llm = AutoModelForCausalLM.from_pretrained(self.model, **llm_kwargs) # type: ignore [no-untyped-call] # nosec + self._llm = AutoModelForCausalLM.from_pretrained(self.model, **llm_kwargs) # type: ignore [no-untyped-call, unused-ignore] # nosec return self._llm @property @@ -94,13 +94,13 @@ def tokenizer(self) -> AutoTokenizer: def pipeline(self) -> TextGenerationPipeline: """The underlying `TextGenerationPipeline` instance.""" if self._pipeline is None: - self._pipeline = transformers.pipeline( # type: ignore [attr-defined, assignment] + self._pipeline = transformers.pipeline( # type: ignore [attr-defined, assignment, unused-ignore] "text-generation", return_full_text=False, - model=self.llm, # type: ignore [arg-type] - tokenizer=self.tokenizer, # type: ignore [arg-type] + model=self.llm, # type: ignore [arg-type, unused-ignore] + tokenizer=self.tokenizer, # type: ignore [arg-type, unused-ignore] ) - return self._pipeline # type: ignore [return-value] + return self._pipeline # type: ignore [return-value, unused-ignore] @classmethod def from_obj( diff --git a/rigging/generator/vllm_.py b/rigging/generator/vllm_.py index 12674b5..9d2a2e5 100644 --- a/rigging/generator/vllm_.py +++ b/rigging/generator/vllm_.py @@ -2,8 +2,8 @@ import inspect import typing as t -import torch -import vllm +import torch # type: ignore [import-not-found, import-untyped, unused-ignore] +import vllm # type: ignore [import-not-found,import-untyped, unused-ignore] from rigging.generator.base import ( GeneratedMessage, diff --git a/rigging/logging.py b/rigging/logging.py index 6fe8468..91626d4 100644 --- a/rigging/logging.py +++ b/rigging/logging.py @@ -18,7 +18,7 @@ def configure_logging( - log_level: LogLevelLiteral, + log_level: LogLevelLiteral = "info", log_file: pathlib.Path | None = None, log_file_level: LogLevelLiteral = "debug", ) -> None: diff --git a/rigging/tokenizer/__init__.py b/rigging/tokenizer/__init__.py index a8c2b02..005612a 100644 --- a/rigging/tokenizer/__init__.py +++ b/rigging/tokenizer/__init__.py @@ -18,7 +18,7 @@ def get_transformers_lazy() -> type[Tokenizer]: from rigging.tokenizer.transformers_ import TransformersTokenizer except ImportError as e: raise ImportError( - "TransformersTokenizer is not available. Please install `transformers` or use `rigging[extra]`.", + "TransformersTokenizer is not available. Please install `transformers` or use `rigging[llm]`.", ) from e return TransformersTokenizer diff --git a/rigging/tokenizer/transformers_.py b/rigging/tokenizer/transformers_.py index 8f2616c..5762da4 100644 --- a/rigging/tokenizer/transformers_.py +++ b/rigging/tokenizer/transformers_.py @@ -1,12 +1,14 @@ import typing as t from pydantic import Field -from transformers import AutoTokenizer +from transformers import AutoTokenizer # type: ignore [import-not-found, unused-ignore] from rigging.tokenizer.base import Tokenizer if t.TYPE_CHECKING: - from transformers.tokenization_utils import PreTrainedTokenizer + from transformers.tokenization_utils import ( # type: ignore [import-not-found, unused-ignore] + PreTrainedTokenizer, + ) from rigging.chat import Chat @@ -67,7 +69,7 @@ def decode(self, tokens: list[int]) -> str: "clean_up_tokenization_spaces": False, **self.decode_kwargs, } - return self.tokenizer.decode(tokens, **decode_kwargs) + return self.tokenizer.decode(tokens, **decode_kwargs) # type: ignore [no-any-return, unused-ignore] def format_chat(self, chat: "Chat") -> str: messages = [m.to_openai(compatibility_flags={"content_as_str"}) for m in chat.all] @@ -85,7 +87,7 @@ def format_chat(self, chat: "Chat") -> str: return str( self.tokenizer.apply_chat_template( messages, - tools=tools, # type: ignore [arg-type] + tools=tools, # type: ignore [arg-type, unused-ignore] **apply_chat_template_kwargs, ), ) diff --git a/rigging/tools/base.py b/rigging/tools/base.py index 94af3c7..27fd3b0 100644 --- a/rigging/tools/base.py +++ b/rigging/tools/base.py @@ -140,6 +140,9 @@ def _is_unbound_method(func: t.Any) -> bool: return is_method is not hasattr(func, "__self__") +DEFAULT_CATCH_EXCEPTIONS: set[type[Exception]] = {json.JSONDecodeError, ValidationError} + + class Tool(BaseModel, t.Generic[P, R]): """Base class for representing a tool to a generator.""" @@ -154,7 +157,7 @@ class Tool(BaseModel, t.Generic[P, R]): exclude=True, ) """The function to call.""" - catch: bool | set[type[Exception]] = {json.JSONDecodeError, ValidationError} + catch: bool | set[type[Exception]] = set(DEFAULT_CATCH_EXCEPTIONS) """ Whether to catch exceptions and return them as messages. @@ -283,7 +286,7 @@ def empty_func(*args, **kwargs): # type: ignore [no-untyped-def] # noqa: ARG001 description=description, parameters_schema=schema, fn=fn, - catch=catch or {json.JSONDecodeError, ValidationError}, + catch=catch or DEFAULT_CATCH_EXCEPTIONS, truncate=truncate, ) @@ -378,6 +381,8 @@ async def handle_tool_call( # noqa: PLR0912 kwargs = json.loads(tool_call.function.arguments) if self._type_adapter is not None: kwargs = self._type_adapter.validate_python(kwargs) + kwargs = kwargs or {} + dn.log_inputs(**kwargs) # Call the function @@ -583,27 +588,75 @@ def make_tool(func: t.Callable[..., t.Any]) -> Tool[P, R]: # Special code for handling tool decorators on class methods -class ToolMethod(Tool[P, R]): - """A Tool wrapping a class method.""" +class ToolMethod(property, t.Generic[P, R]): + """ + A descriptor that acts as a factory for creating bound Tool instances. + + It inherits from `property` to be ignored by pydantic's `ModelMetaclass` + during field inspection. This prevents validation errors which would + otherwise treat the descriptor as a field and stop tool_method decorators + from being applied in BaseModel classes. + """ - def __get__(self, instance: t.Any, owner: t.Any) -> "Tool[P, R]": + def __init__( + self, + fget: t.Callable[..., t.Any], + name: str, + description: str, + parameters_schema: dict[str, t.Any], + catch: bool | t.Iterable[type[Exception]] | None, + truncate: int | None, + signature: inspect.Signature, + type_adapter: TypeAdapter[t.Any], + ): + super().__init__(fget) + self.tool_name = name + self.tool_description = description + self.tool_parameters_schema = parameters_schema + self.tool_catch = catch + self.tool_truncate = truncate + self._tool_signature = signature + self._tool_type_adapter = type_adapter + + @te.overload # type: ignore [override] + def __get__(self, instance: None, owner: type[object] | None = None) -> Tool[P, R]: ... + + @te.overload + def __get__(self, instance: object, owner: type[object] | None = None) -> Tool[P, R]: ... + + @te.override + def __get__(self, instance: object | None, owner: type[object] | None = None) -> Tool[P, R]: + if self.fget is None: + raise AttributeError( + f"Tool '{self.tool_name}' is not defined on instance of {owner.__name__ if owner else 'unknown'}.", + ) + + # Class access: return an unbound Tool for inspection. if instance is None: - return self + tool = Tool[P, R]( + fn=self.fget, + name=self.tool_name, + description=self.tool_description, + parameters_schema=self.tool_parameters_schema, + catch=self.tool_catch or DEFAULT_CATCH_EXCEPTIONS, + truncate=self.tool_truncate, + ) + tool._signature = self._tool_signature # noqa: SLF001 + tool._type_adapter = self._tool_type_adapter # noqa: SLF001 + return tool - bound_method = self.fn.__get__(instance, owner) + # Instance access: return a new Tool bound to the instance. + bound_method = self.fget.__get__(instance, owner) bound_tool = Tool[P, R]( - name=self.name, - description=self.description, - parameters_schema=self.parameters_schema, fn=bound_method, - catch=self.catch, + name=self.tool_name, + description=self.tool_description, + parameters_schema=self.tool_parameters_schema, + catch=self.tool_catch or DEFAULT_CATCH_EXCEPTIONS, + truncate=self.tool_truncate, ) - - bound_tool.__signature__ = self.__signature__ # type: ignore [misc] - bound_tool._signature = self._signature # noqa: SLF001 - bound_tool._type_adapter = self._type_adapter # noqa: SLF001 - bound_tool._model = self._model # noqa: SLF001 - + bound_tool._signature = self._tool_signature # noqa: SLF001 + bound_tool._type_adapter = self._tool_type_adapter # noqa: SLF001 return bound_tool @@ -663,39 +716,36 @@ def static_add(x: int, y: int) -> int: ``` """ - def make_tool(func: t.Callable[..., t.Any]) -> ToolMethod[P, R]: - # TODO: Improve consistency of detection here before enabling this warning - # if not _is_unbound_method(func): - # warnings.warn( - # "Passing a regular function to @tool_method improperly handles the 'self' argument, use @tool instead.", - # SyntaxWarning, - # stacklevel=3, - # ) + def make_tool(f: t.Callable[t.Concatenate[t.Any, P], R]) -> ToolMethod[P, R]: + # This logic is specialized from `Tool.from_callable` to correctly + # handle the `self` parameter in method signatures. - # Strip the `self` argument from the function signature so - # our schema generation doesn't include it under the hood. + signature = inspect.signature(f) + params_without_self = [p for p_name, p in signature.parameters.items() if p_name != "self"] + schema_signature = signature.replace(parameters=params_without_self) - @functools.wraps(func) - def wrapper(self: t.Any, *args: P.args, **kwargs: P.kwargs) -> R: - return func(self, *args, **kwargs) # type: ignore [no-any-return] + @functools.wraps(f) + def empty_func(*_: t.Any, **kwargs: t.Any) -> t.Any: + return kwargs - wrapper.__signature__ = inspect.signature(func).replace( # type: ignore [attr-defined] - parameters=tuple( - param - for param in inspect.signature(func).parameters.values() - if param.name != "self" - ), - ) + empty_func.__signature__ = schema_signature # type: ignore [attr-defined] + type_adapter: TypeAdapter[t.Any] = TypeAdapter(empty_func) + schema = deref_json(type_adapter.json_schema(), is_json_schema=True) - return ToolMethod.from_callable( - wrapper, # type: ignore [arg-type] - name=name, - description=description, + tool_name = name or f.__name__ + tool_description = inspect.cleandoc(description or f.__doc__ or "") + + return ToolMethod( + fget=f, + name=tool_name, + description=tool_description, + parameters_schema=schema, catch=catch, truncate=truncate, + signature=schema_signature, + type_adapter=type_adapter, ) if func is not None: return make_tool(func) - return make_tool diff --git a/rigging/tools/mcp.py b/rigging/tools/mcp.py index c566158..696e2b3 100644 --- a/rigging/tools/mcp.py +++ b/rigging/tools/mcp.py @@ -13,18 +13,16 @@ from types import TracebackType import typing_extensions as te -from mcp import ClientSession, StdioServerParameters -from mcp.client.sse import sse_client -from mcp.client.stdio import stdio_client -from mcp.server.fastmcp import FastMCP, Image -from mcp.server.fastmcp.tools import Tool as FastMCPTool -from mcp.types import CallToolResult, TextResourceContents from rigging.error import Stop from rigging.tools.base import Tool from rigging.util import flatten_list if t.TYPE_CHECKING: + from mcp import ClientSession + from mcp.server.fastmcp import FastMCP + from mcp.types import CallToolResult + from rigging.message import Content INITIALIZE_TIMEOUT = 5 @@ -51,7 +49,9 @@ class SSEConnection(te.TypedDict): sse_read_timeout: float -def _convert_mcp_result_to_message_parts(result: CallToolResult) -> list[t.Any]: +def _convert_mcp_result_to_message_parts(result: "CallToolResult") -> list[t.Any]: + from mcp.types import TextResourceContents + from rigging.message import ContentImageUrl, ContentText parts: list[Content] = [] @@ -82,6 +82,8 @@ def _convert_rigging_return_to_mcp(result: t.Any) -> t.Any: Returns: A value compatible with MCP serialization (JSON types, mcp.Image, etc.). """ + from mcp.server.fastmcp import Image + from rigging.message import ContentImageUrl, ContentText, Message if isinstance(result, Stop): @@ -134,7 +136,7 @@ class MCPClient: tools: list[Tool[..., t.Any]] """A list of tools available on the server""" - def __init__(self, transport: Transport, connection: StdioConnection | SSEConnection) -> None: + def __init__(self, transport: Transport, connection: "StdioConnection | SSEConnection") -> None: self.transport = transport self.connection = connection self.tools = [] @@ -142,7 +144,7 @@ def __init__(self, transport: Transport, connection: StdioConnection | SSEConnec self._session: ClientSession | None = None @property - def session(self) -> ClientSession: + def session(self) -> "ClientSession": if self._session is None: raise RuntimeError("Session not initialized") return self._session @@ -169,12 +171,18 @@ async def _load_tools(self) -> None: for tool in mcp_tool_result.tools ] - async def _connect_via_stdio(self, connection: StdioConnection) -> ClientSession: + async def _connect_via_stdio(self, connection: "StdioConnection") -> "ClientSession": + from mcp import ClientSession, StdioServerParameters + from mcp.client.stdio import stdio_client + server_params = StdioServerParameters(**connection) read, write = await self._exit_stack.enter_async_context(stdio_client(server_params)) return await self._exit_stack.enter_async_context(ClientSession(read, write)) - async def _connect_via_sse(self, connection: SSEConnection) -> ClientSession: + async def _connect_via_sse(self, connection: "SSEConnection") -> "ClientSession": + from mcp import ClientSession + from mcp.client.sse import sse_client + client = sse_client(**connection) read, write = await self._exit_stack.enter_async_context(client) return await self._exit_stack.enter_async_context(ClientSession(read, write)) @@ -320,6 +328,9 @@ def add_numbers(a: int, b: int) -> int: ) ``` """ + from mcp.server.fastmcp import FastMCP + from mcp.server.fastmcp.tools import Tool as FastMCPTool + rigging_tools: list[Tool[..., t.Any]] = [] for tool in flatten_list(list(tools)): interior_tools = [ diff --git a/rigging/watchers.py b/rigging/watchers.py index c2b9edc..6a0752b 100644 --- a/rigging/watchers.py +++ b/rigging/watchers.py @@ -7,7 +7,6 @@ from pathlib import Path from loguru import logger -from mypy_boto3_s3 import S3Client from rigging.data import chats_to_elastic, flatten_chats, s3_object_exists from rigging.logging import LogLevelLiteral @@ -15,7 +14,8 @@ from rigging.util import shorten_text if t.TYPE_CHECKING: - from elasticsearch import AsyncElasticsearch + from elasticsearch import AsyncElasticsearch # type: ignore [import-not-found, unused-ignore] + from mypy_boto3_s3 import S3Client from rigging.chat import Chat, WatchChatCallback @@ -111,7 +111,7 @@ async def _write_chats_to_elastic(chats: "list[Chat]") -> None: def write_chats_to_s3( - client: S3Client, + client: "S3Client", bucket: str, key: str, *, diff --git a/tests/test_tool.py b/tests/test_tool.py index dfcdb50..f612f69 100644 --- a/tests/test_tool.py +++ b/tests/test_tool.py @@ -286,3 +286,98 @@ def faulty_function(x: int) -> int: assert stop is False assert "This is a test error" in message.content + + +class PydanticTool(BaseModel): + prefix: str + + @rg.tool_method(description="A custom method description.") + def my_method(self, text: str) -> str: + """Applies a prefix to the text.""" + return f"{self.prefix}: {text}" + + @rg.tool_method(name="custom_named_tool") + def another_method(self, num: int) -> int: + """A method with a custom name.""" + return num * 2 + + +def test_pydantic_model_instantiation() -> None: + """Ensure a Pydantic model with a @tool_method can be instantiated without raising an error.""" + + instance = PydanticTool(prefix="test") + assert isinstance(instance, BaseModel) + assert instance.prefix == "test" + + +def test_pydantic_instance_access_returns_bound_tool() -> None: + """Accessing the method on an INSTANCE returns a bound Tool whose function can access `self`.""" + instance = PydanticTool(prefix="instance_prefix") + bound_tool = instance.my_method + + assert isinstance(bound_tool, Tool) + assert bound_tool.name == "my_method" + assert bound_tool.description == "A custom method description." + + # Crucially, test that the function is bound to the instance + result = bound_tool.fn("world") + assert result == "instance_prefix: world" + + +def test_pydantic_class_access_returns_unbound_tool() -> None: + """Accessing the method on the base model class returns an unbound Tool suitable for inspection.""" + + unbound_tool = PydanticTool.my_method + + assert isinstance(unbound_tool, Tool) + assert unbound_tool.name == "my_method" + + # Calling the unbound function directly should fail + with pytest.raises(TypeError, match="missing 1 required positional argument"): + unbound_tool.fn("world") + + +def test_pydantic_decorator_arguments_are_respected() -> None: + """That arguments passed to the @tool_method decorator (like `name`) are correctly applied.""" + + instance = PydanticTool(prefix="test") + tool = instance.another_method + assert tool.name == "custom_named_tool" + assert tool.description == "A method with a custom name." # Docstring is the fallback + + +@pytest.mark.asyncio +async def test_pydantic_handle_tool_call_on_bound_tool() -> None: + """The full end-to-end functionality of a bound tool.""" + + instance = PydanticTool(prefix="end-to-end") + bound_tool = instance.my_method + + assert bound_tool("works") == "end-to-end: works" + + tool_call = ToolCall( + id="call456", + function=FunctionCall( + name="my_method", + arguments=json.dumps({"text": "works"}), + ), + ) + + message, stop = await bound_tool.handle_tool_call(tool_call) + + assert stop is False + assert message.role == "tool" + assert message.tool_call_id == "call456" + assert message.content == "end-to-end: works" + + +def test_pydantic_type_hinting_and_static_analysis() -> None: + """Confirms that the types inferred from accessing the decorated method are correct.""" + + # Static analysis for instance access + instance_tool = PydanticTool(prefix="").my_method + assert isinstance(instance_tool, Tool) + + # Static analysis for class access + class_tool = PydanticTool.my_method + assert isinstance(class_tool, Tool)