diff --git a/.gitignore b/.gitignore index 7fcd246..b72678e 100644 --- a/.gitignore +++ b/.gitignore @@ -181,3 +181,6 @@ cython_debug/ # VSCode configuration .vscode/ + +/.quarto/ +**/*.quarto_ipynb diff --git a/_quarto.yml b/_quarto.yml new file mode 100644 index 0000000..63a5941 --- /dev/null +++ b/_quarto.yml @@ -0,0 +1,107 @@ +project: + type: website + render: + - index.qmd + - docs/**.qmd + - classifai/**.py + - DEMO/**.ipynb + - DEMO/**.py + - DEMO/**.qmd + - README.md + - DEMO/README.md + +website: + title: "ClassifAI" + page-navigation: true + navbar: + background: light + search: true + left: + - file: docs/index.qmd + text: "Documentation" + right: + - icon: github + href: https://github.com/datasciencecampus/classifai + + sidebar: + - id: index + - title: "Documentation" + - style: "docked" + contents: + - section: "Overview" + contents: + - docs/index.qmd + - section: "Vectorisers" + contents: + - section: "Vectorisers Overview" + contents: + - docs/vectorisers.base.VectoriserBase.qmd + - docs/vectorisers.base.VectoriserBase.transform.qmd + - section: "Specific Vectorisers" + contents: + - docs/vectorisers.huggingface.HuggingFaceVectoriser.qmd + - docs/vectorisers.ollama.OllamaVectoriser.qmd + - docs/vectorisers.gcp.GcpVectoriser.qmd + - section: "Indexers" + contents: + - docs/indexers.VectorStore.qmd + - docs/indexers.VectorStore.embed.qmd + - docs/indexers.VectorStore.search.qmd + - docs/indexers.VectorStore.reverse_search.qmd + - docs/indexers.VectorStore.from_filespace.qmd + - section: "Servers" + contents: + - docs/servers.start_api.qmd + - section: "DEMO" + contents: + - file: DEMO/README.md + - file: DEMO/general_workflow_demo.ipynb + - file: DEMO/custom_vectoriser.ipynb + +interlinks: + sources: + python: + url: https://docs.python.org/3/ + +format: + html: + theme: cosmo + css: styles.css + toc: true + grid: + sidebar-width: 400px + body-width: 900px + margin-width: 200px + gutter-width: 1.0rem + +quartodoc: + style: pkgdown + dir: docs + renderer: + style: _renderer.py + show_signature_annotations: false + # renderer: + # style: markdown + package: classifai + parser: google + sections: + - title: Vectorisers + desc: "Utilities to project text into numerical representation in a semantic vector space" + contents: + - vectorisers.base.VectoriserBase + - vectorisers.base.VectoriserBase.transform + - vectorisers.huggingface.HuggingFaceVectoriser + - vectorisers.ollama.OllamaVectoriser + - vectorisers.gcp.GcpVectoriser + - title: Indexers + desc: "Creation of Vector Stores for efficient similarity search and retrieval" + contents: + - indexers.VectorStore + - indexers.VectorStore.embed + - indexers.VectorStore.search + - indexers.VectorStore.reverse_search + - indexers.VectorStore.from_filespace + - title: Servers + desc: "Expose ClassifAI functionality via Fast-API endpoints" + contents: + - servers.start_api diff --git a/_renderer.py b/_renderer.py new file mode 100644 index 0000000..f5c4bfa --- /dev/null +++ b/_renderer.py @@ -0,0 +1,63 @@ +from __future__ import annotations + +from numpydoc.docscrape import NumpyDocString +from plum import dispatch +from quartodoc import MdRenderer +from quartodoc import ast as qast + + +class Renderer(MdRenderer): + style = "siuba" + + @dispatch + def render(self, el): + """General render method. + Note: overloading of `render` enabled via plum.dispatch to allow different + rendering behaviour for some elements. + """ + prev_obj = getattr(self, "crnt_obj", None) + self.crnt_obj = el + res = super().render(el) + self.crnt_obj = prev_obj + + return res + + @dispatch + def render(self, el: qast.DocstringSectionSeeAlso): # noqa: F811 + """Numpy Docstring style render method. + Note: overloading of `render` enabled via plum.dispatch to allow different + rendering behaviour for some elements. + """ + lines = el.value.split("\n") + + # each entry in result has form: ([('func1', '), ...], ) + parsed = NumpyDocString("")._parse_see_also(lines) + + result = [] + for funcs, description in parsed: + links = [f"[{name}](`{self._name_to_target(name)}`)" for name, role in funcs] + + str_links = ", ".join(links) + + if description: + str_description = "
".join(description) + result.append(f"{str_links}: {str_description}") + else: + result.append(str_links) + + return "*\n".join(result) + + def _name_to_target(self, name: str): + """Helper method to convert a function/class name to a full target path, + used for Numpy Docstring style render method. + """ + crnt_path = getattr(self.crnt_obj, "path", None) + parent = crnt_path.rsplit(".", 1)[0] + "." + pkg = "classifai." + + if crnt_path and not (name.startswith(pkg) or name.startswith(parent)): + return f"{parent}{name}" + elif not name.startswith(pkg): + return f"{pkg}{name}" + + return name diff --git a/index.qmd b/index.qmd new file mode 100644 index 0000000..e54d850 --- /dev/null +++ b/index.qmd @@ -0,0 +1,3 @@ +# Package Overview {.unnumbered} + +{{< include README.md >}} \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index ac465c1..4bad582 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,6 +28,8 @@ dependencies = [ "pyarrow>=20.0.0", "tqdm>=4.67.1", "scikit-learn>=1.7.2", + "numpydoc>=1.10.0", + "quartodoc==0.11.1", ] classifiers = [ "Programming Language :: Python :: 3", diff --git a/src/classifai/_optional.py b/src/classifai/_optional.py index 7b4e574..d9efa9e 100644 --- a/src/classifai/_optional.py +++ b/src/classifai/_optional.py @@ -23,7 +23,7 @@ def check_deps(reqs: list[str], extra: str | None = None) -> None: Args: reqs (list[str]): A list of package names to check. - extra (str, optional): The name of the extra installation group. Defaults to None. + extra (str): [optional] The name of the extra installation group. Defaults to None. Raises: OptionalDependencyError: If any of the required packages are not installed. diff --git a/src/classifai/indexers/main.py b/src/classifai/indexers/main.py index a1d95c0..6b5d6f1 100644 --- a/src/classifai/indexers/main.py +++ b/src/classifai/indexers/main.py @@ -32,8 +32,10 @@ import shutil import time import uuid +from typing import Optional, Self, Union # noqa: F401 import numpy as np +import pandas as pd # for type hinting import polars as pl from tqdm.autonotebook import tqdm @@ -52,7 +54,7 @@ class VectorStore: data_type (str): the data type of the original file (curently only csv supported) vectoriser (object): A Vectoriser object from the corresponding ClassifAI Pacakge module batch_size (int): the batch size to pass to the vectoriser when embedding - meta_data (dict[str:type]): key-value pairs of metadata to extract from the input file and their correpsonding types + meta_data (dict): key-value pairs of metadata to extract from the input file and their correpsonding types output_dir (str): the path to the output directory where the VectorStore will be saved vectors (np.array): a numpy array of vectors for the vector DB vector_shape (int): the dimension of the vectors @@ -78,28 +80,28 @@ def __init__( # noqa: PLR0913 data_type (str): The type of input data (currently supports only "csv"). vectoriser (object): The vectoriser object used to transform text into vector embeddings. - batch_size (int, optional): The batch size for processing the input file and batching to + batch_size (int): [optional] The batch size for processing the input file and batching to vectoriser. Defaults to 8. - meta_data (dict, optional): key,value pair metadata column names to extract from the input file and their types. + meta_data (dict): key,value pair metadata column names to extract from the input file and their types. Defaults to None. - output_dir (str, optional): The directory where the vector store will be saved. + output_dir (str): [optional] The directory where the vector store will be saved. Defaults to None, where input file name will be used. - overwrite (bool, optional): If True, allows overwriting existing folders with the same name. Defaults to false to prevent accidental overwrites. + overwrite (bool): [optional] If True, allows overwriting existing folders with the same name. Defaults to false to prevent accidental overwrites. Raises: ValueError: If the data type is not supported or if the folder name conflicts with an existing folder. """ - self.file_name = file_name + self.file_name: str = file_name self.data_type = data_type self.vectoriser = vectoriser - self.batch_size = batch_size - self.meta_data = meta_data if meta_data is not None else {} - self.vectors = None - self.vector_shape = None - self.num_vectors = None - self.vectoriser_class = vectoriser.__class__.__name__ - self.output_dir = output_dir + self.batch_size: str = batch_size + self.meta_data: dict = meta_data if meta_data is not None else {} + self.vectors: np.array = None + self.vector_shape: tuple | list | None = None + self.num_vectors: int = None + self.vectoriser_class: str = vectoriser.__class__.__name__ + self.output_dir: str = output_dir if self.data_type not in ["csv"]: raise ValueError("Data type must be one of ['csv'].") @@ -143,7 +145,7 @@ def __init__( # noqa: PLR0913 logging.info("Vector Store created - files saved to %s", self.output_dir) - def _save_metadata(self, path): + def _save_metadata(self, path: str): """Saves metadata about the vector store to a JSON file. Args: @@ -225,35 +227,40 @@ def validate(self): # This method is a placeholder for future validation logic. # Currently, it does not perform any validation. - def embed(self, text): + def embed(self, text: str | list[str] | None) -> np.ndarray: """Converts text into vector embeddings using the vectoriser. Args: - text (str or list): The text or list of text to be converted into vector embeddings. + text (str,list): The text or list of text to be converted into vector embeddings. Returns: np.ndarray: The vector embeddings generated by the vectoriser. """ return self.vectoriser.transform(text) - def reverse_search(self, query, ids=None, n_results=100): + def reverse_search( + self, query: str | list[str], ids: int | list[int] | None = None, n_results: int = 100 + ) -> pd.DataFrame: """Reverse searches the vector store using a input code or list of codes and returns matched results. In batches, converts users text queries into vector embeddings, computes cosine similarity with stored document vectors, and retrieves the top results. Args: - query (str or list): The text query or list of queries to search for. - ids (list, optional): List of query IDs. Defaults to None. - n_results (int, optional): Number of top results to return for each query. Default 100. + query (str,list): The text query or list of queries to search for. + ids (int,list): List of query IDs. Defaults to None. + n_results (int): Number of top results to return for each query. Default 100. Returns: pd.DataFrame: DataFrame containing search results with columns for query ID, matching document ID, document text and metadata. """ - # if the query is a string, convert it to a list + # if the query/ids is a string/int, convert it to a list if isinstance(query, str): query = [query] + if isinstance(ids, int): + ids = [ids] + # pair query ids with input ids query_ids = ids if ids else list(range(0, len(query))) paired_query = pl.DataFrame({"query_id": query_ids, "id": query}) @@ -276,16 +283,18 @@ def reverse_search(self, query, ids=None, n_results=100): return final_table.to_pandas() - def search(self, query, ids=None, n_results=10, batch_size=8): + def search( + self, query: str | list[str], ids: int | list[int] | None = None, n_results: int = 10, batch_size: int = 8 + ) -> pd.DataFrame: """Searches the vector store using a text query or list of queries and returns ranked results. In batches, converts users text queries into vector embeddings, computes cosine similarity with stored document vectors, and retrieves the top results. Args: - query (str or list): The text query or list of queries to search for. - ids (list, optional): List of query IDs. Defaults to None. - n_results (int, optional): Number of top results to return for each query. Default 10. - batch_size (int, optional): The batch size for processing queries. Default 8. + query (str,list): The text query or list of queries to search for. + ids (int,list): List of query IDs. Defaults to None. + n_results (int): Number of top results to return for each query. Default 10. + batch_size (int): The batch size for processing queries. Default 8. Returns: pd.DataFrame: DataFrame containing search results with columns for query ID, query text, @@ -294,9 +303,11 @@ def search(self, query, ids=None, n_results=10, batch_size=8): Raises: ValueError: Raised if invalid arguments are passed. """ - # if the query is a string, convert it to a list + # if the query/ids is a string/int, convert it to a list if isinstance(query, str): query = [query] + if isinstance(ids, int): + ids = [ids] if (ids is not None) and not all( [ @@ -380,7 +391,7 @@ def search(self, query, ids=None, n_results=10, batch_size=8): return reordered_df.to_pandas() @classmethod - def from_filespace(cls, folder_path, vectoriser): + def from_filespace(cls, folder_path: str, vectoriser) -> Self: """Creates a `VectorStore` instance from stored metadata and Parquet files. This method reads the metadata and vectors from the specified folder, validates the contents, and initializes a `VectorStore` object with the diff --git a/src/classifai/servers/main.py b/src/classifai/servers/main.py index 310a3d6..298bc3c 100644 --- a/src/classifai/servers/main.py +++ b/src/classifai/servers/main.py @@ -39,7 +39,7 @@ def start_api(vector_stores, endpoint_names, port=8000): vector_stores (list): A list of vector store objects, each responsible for handling embedding and search operations for a specific endpoint. endpoint_names (list): A list of endpoint names corresponding to the vector stores. - port (int, optional): The port on which the API server will run. Defaults to 8000. + port (int): [optional] The port on which the API server will run. Defaults to 8000. """ diff --git a/src/classifai/vectorisers/gcp.py b/src/classifai/vectorisers/gcp.py index 02c959f..4dcb20e 100644 --- a/src/classifai/vectorisers/gcp.py +++ b/src/classifai/vectorisers/gcp.py @@ -25,18 +25,18 @@ class GcpVectoriser(VectoriserBase): def __init__( self, - project_id, - location="europe-west2", - model_name="text-embedding-004", - task_type="RETRIEVAL_DOCUMENT", + project_id: str, + location: str = "europe-west2", + model_name: str = "text-embedding-004", + task_type: str = "RETRIEVAL_DOCUMENT", ): """Initializes the GcpVectoriser with the specified project ID, location, and model name. Args: project_id (str): The Google Cloud project ID. - location (str, optional): The location of the GenAI API. Defaults to 'europe-west2'. - model_name (str, optional): The name of the embedding model. Defaults to "text-embedding-004". - task_type (str, optional): The embedding task. Defaults to "CLASSIFICATION". + location (str): [optional] The location of the GenAI API. Defaults to 'europe-west2'. + model_name (str): [optional] The name of the embedding model. Defaults to "text-embedding-004". + task_type (str): [optional] The embedding task. Defaults to "CLASSIFICATION". See https://cloud.google.com/vertex-ai/generative-ai/docs/embeddings/task-types for other options. @@ -58,11 +58,11 @@ def __init__( except Exception as e: raise RuntimeError(f"Failed to initialize GCP Vectoriser through ganai.Client API: {e}") from e - def transform(self, texts): + def transform(self, texts: str | list[str]) -> np.ndarray: """Transforms input text(s) into embeddings using the GenAI API. Args: - texts (str or list of str): The input text(s) to embed. Can be a single string or a list of strings. + texts (str,list[str]): The input text(s) to embed. Can be a single string or a list of strings. Returns: numpy.ndarray: A 2D array of embeddings, where each row corresponds to an input text. diff --git a/src/classifai/vectorisers/huggingface.py b/src/classifai/vectorisers/huggingface.py index 567d82c..f9178f1 100644 --- a/src/classifai/vectorisers/huggingface.py +++ b/src/classifai/vectorisers/huggingface.py @@ -1,5 +1,7 @@ """A module that provides a wrapper for Huggingface Transformers models to generate text embeddings.""" +from numpy import ndarray as np_ndarray + from classifai._optional import check_deps from .base import VectoriserBase @@ -15,13 +17,13 @@ class HuggingFaceVectoriser(VectoriserBase): device (torch.device): The device (CPU or GPU) on which the model is loaded. """ - def __init__(self, model_name, device=None, model_revision="main"): + def __init__(self, model_name: str, device=None, model_revision: str = "main"): """Initializes the HuggingfaceVectoriser with the specified model name and device. Args: model_name (str): The name of the Huggingface model to use. - device (torch.device, optional): The device to use for computation. Defaults to GPU if available, otherwise CPU. - model_revision (str, optional): The specific model revision to use. Defaults to "main". + device (torch.device): [optional] The device to use for computation. Defaults to GPU if available, otherwise CPU. + model_revision (str): [optional] The specific model revision to use. Defaults to "main". """ check_deps(["transformers", "torch"], extra="huggingface") import torch # type: ignore @@ -40,11 +42,11 @@ def __init__(self, model_name, device=None, model_revision="main"): self.model.to(self.device) self.model.eval() - def transform(self, texts): + def transform(self, texts: str | list[str]) -> np_ndarray: """Transforms input text(s) into embeddings using the Huggingface model. Args: - texts (str or list of str): The input text(s) to embed. Can be a single string or a list of strings. + texts (str,list[str]): The input text(s) to embed. Can be a single string or a list of strings. Returns: numpy.ndarray: A 2D array of embeddings, where each row corresponds to an input text. diff --git a/src/classifai/vectorisers/ollama.py b/src/classifai/vectorisers/ollama.py index ed57725..bcaeefe 100644 --- a/src/classifai/vectorisers/ollama.py +++ b/src/classifai/vectorisers/ollama.py @@ -26,11 +26,11 @@ def __init__(self, model_name: str): check_deps(["ollama"], extra="ollama") self.model_name = model_name - def transform(self, texts): + def transform(self, texts: str | list[str]) -> np.ndarray: """Transforms input text(s) into embeddings using the Huggingface model. Args: - texts (str or list of str): The input text(s) to embed. Can be a single string or a list of strings. + texts (str ,list [str]): The input text(s) to embed. Can be a single string or a list of strings. Returns: numpy.ndarray: A 2D array of embeddings, where each row corresponds to an input text. diff --git a/styles.css b/styles.css new file mode 100644 index 0000000..7a640c4 --- /dev/null +++ b/styles.css @@ -0,0 +1,202 @@ +/* css styles */ +@import url('https://rsms.me/inter/inter.css'); +html { font-family: 'Inter', sans-serif; } +@supports (font-variation-settings: normal) { + html { font-family: 'Inter var', sans-serif; } +} + +:root { + --bs-body-font-family: inter; + --bs-body-font-size: 16px; +} + +:root { + --site-color-primary: #F9F4D8;/*#ebfddd;*/ + --site-color-primary: #EE8320; + --site-color-primary: #C29A68; +} + +#quarto-header { + border: 1px solid #dee2e6; + background-color: #FFFFFF; +} + +#quarto-header nav { + max-width: 1300px; + margin: auto; +} + +#quarto-header .nav-link { + padding-left: 5.4px !important; + padding-right: 5.4px !important; + margin-left: 30px !important; + margin-right: 5.4px !important; + font-size: 18px; +} + +#quarto-header .nav-link .bi { + font-size: 20px; +} + +.sidebar a.nav-link { + font-size: 14.4px; + font-weight: 400; +} + +.nav-item .nav-link.active { + /*background-color: #EE8320;*/ + border-bottom: black 2px solid; +} + +.navbar { + /*background-color: var(--site-color-primary);*/ + background-color: #FFFFFF; + /*var(--site-color-primary);*/ + padding-top: 0px !important; + padding-bottom: 0px !important; + /*border-bottom: black 2px solid;*/ +} + +.navbar-title { + color: black !important; + font-weight: 800; + font-size: 32px; +} + +.navbar-nav .nav-link { + color: black !important; +} + +.navbar #quarto-search.type-overlay .aa-Autocomplete svg.aa-SubmitIcon { + color: black; +} + +div.sidebar-item-container .active { + /*color: #0047ab;*/ + color: #6699ff; + color: #5dba17; + color: #4D9089 !important; + font-weight: 600; +} + +.sidebar nav[role=doc-toc] ul>li>a.active, .sidebar nav[role=doc-toc] ul>li>ul>li>a.active +{ + color: black !important; + /*color: #4D9089 !important;*/ + border-left: 2px solid #4D9089 !important; +} + +.sidebar nav[role=doc-toc]>ul a { + border-left: 2px solid white; +} + +/* +div.sidebar-item-container .active { + color: black; + background-color: var(--site-color-secondary); +} + +.sidebar nav[role=doc-toc] ul>li>a.active { + border-left: 1px solid var(--site-color-secondary); + color: var(--site-color-secondary) !important; +} +*/ + +.sidebar-item-container .text-start { + font-weight: 600; + font-size: 14.4px !important; +} + +.sidebar-item-text { + /*color: rgba(60, 60, 60, 0.7);*/ + font-weight: 500; + font-size: 14px; + line-height: 22px; +} + +.sidebar-item-section { + padding-top: 16px; +} + +.sidebar-section { + padding-left: 0px !important; +} + +.dataframe { + font-size: 14px; +} + +.dataframe, .dataframe table { + width: inherit; +} + +/* +.dataframe tbody tr th:only-of-type { + vertical-align: middle; +} + +.dataframe tbody tr th { + vertical-align: top; +} + +.dataframe thead th { + text-align: right; +} +*/ + + + + +/* INDEX PAGE ------------------------------------------ */ + + + + +/* + * Buttons +*/ + +.btn-action { + min-width: 165px; + border-radius: 30px; + border: none; +} + +/* content block */ + +.content-block { + padding-top: 20px; + padding-bottom: 10px; + margin-left: 30px; + margin-right: 30px; +} + + +@media(min-width: 900px) { +.content-block { + margin-left: 50px; + margin-right: 50px; +} +} + +@media (min-width: 1200px) { +.content-block { + max-width: 1100px; + margin-left: auto; + margin-right: auto; +} +} + +.feature-blocks .grid { + padding-top: 20px; + padding-bottom: 20px; +} + +.hero-github-links { + padding-top: 30px; +} + +.hero-title { + font-size: 64px; + background-color: #FFFFFF77; +} diff --git a/uv.lock b/uv.lock index cc36110..d27223d 100644 --- a/uv.lock +++ b/uv.lock @@ -8,6 +8,15 @@ resolution-markers = [ "python_full_version < '3.11'", ] +[[package]] +name = "alabaster" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a6/f8/d9c74d0daf3f742840fd818d69cfae176fa332022fd44e3469487d5a9420/alabaster-1.0.0.tar.gz", hash = "sha256:c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e", size = 24210, upload-time = "2024-07-26T18:15:03.762Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/b3/6b4067be973ae96ba0d615946e314c5ae35f9f993eca561b356540bb0c2b/alabaster-1.0.0-py3-none-any.whl", hash = "sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b", size = 13929, upload-time = "2024-07-26T18:15:02.05Z" }, +] + [[package]] name = "annotated-types" version = "0.7.0" @@ -109,6 +118,59 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/48/ca/ba5f909b40ea12ec542d5d7bdd13ee31c4d65f3beed20211ef81c18fa1f3/bandit-1.8.6-py3-none-any.whl", hash = "sha256:3348e934d736fcdb68b6aa4030487097e23a501adf3e7827b63658df464dddd0", size = 133808, upload-time = "2025-07-06T03:10:49.134Z" }, ] +[[package]] +name = "beartype" +version = "0.22.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/94/1009e248bbfbab11397abca7193bea6626806be9a327d399810d523a07cb/beartype-0.22.9.tar.gz", hash = "sha256:8f82b54aa723a2848a56008d18875f91c1db02c32ef6a62319a002e3e25a975f", size = 1608866, upload-time = "2025-12-13T06:50:30.72Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/71/cc/18245721fa7747065ab478316c7fea7c74777d07f37ae60db2e84f8172e8/beartype-0.22.9-py3-none-any.whl", hash = "sha256:d16c9bbc61ea14637596c5f6fbff2ee99cbe3573e46a716401734ef50c3060c2", size = 1333658, upload-time = "2025-12-13T06:50:28.266Z" }, +] + +[[package]] +name = "black" +version = "25.12.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "mypy-extensions" }, + { name = "packaging" }, + { name = "pathspec" }, + { name = "platformdirs" }, + { name = "pytokens" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c4/d9/07b458a3f1c525ac392b5edc6b191ff140b596f9d77092429417a54e249d/black-25.12.0.tar.gz", hash = "sha256:8d3dd9cea14bff7ddc0eb243c811cdb1a011ebb4800a5f0335a01a68654796a7", size = 659264, upload-time = "2025-12-08T01:40:52.501Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/37/d5/8d3145999d380e5d09bb00b0f7024bf0a8ccb5c07b5648e9295f02ec1d98/black-25.12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f85ba1ad15d446756b4ab5f3044731bf68b777f8f9ac9cdabd2425b97cd9c4e8", size = 1895720, upload-time = "2025-12-08T01:46:58.197Z" }, + { url = "https://files.pythonhosted.org/packages/06/97/7acc85c4add41098f4f076b21e3e4e383ad6ed0a3da26b2c89627241fc11/black-25.12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:546eecfe9a3a6b46f9d69d8a642585a6eaf348bcbbc4d87a19635570e02d9f4a", size = 1727193, upload-time = "2025-12-08T01:52:26.674Z" }, + { url = "https://files.pythonhosted.org/packages/24/f0/fdf0eb8ba907ddeb62255227d29d349e8256ef03558fbcadfbc26ecfe3b2/black-25.12.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:17dcc893da8d73d8f74a596f64b7c98ef5239c2cd2b053c0f25912c4494bf9ea", size = 1774506, upload-time = "2025-12-08T01:46:25.721Z" }, + { url = "https://files.pythonhosted.org/packages/e4/f5/9203a78efe00d13336786b133c6180a9303d46908a9aa72d1104ca214222/black-25.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:09524b0e6af8ba7a3ffabdfc7a9922fb9adef60fed008c7cd2fc01f3048e6e6f", size = 1416085, upload-time = "2025-12-08T01:46:06.073Z" }, + { url = "https://files.pythonhosted.org/packages/ba/cc/7a6090e6b081c3316282c05c546e76affdce7bf7a3b7d2c3a2a69438bd01/black-25.12.0-cp310-cp310-win_arm64.whl", hash = "sha256:b162653ed89eb942758efeb29d5e333ca5bb90e5130216f8369857db5955a7da", size = 1226038, upload-time = "2025-12-08T01:45:29.388Z" }, + { url = "https://files.pythonhosted.org/packages/60/ad/7ac0d0e1e0612788dbc48e62aef8a8e8feffac7eb3d787db4e43b8462fa8/black-25.12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d0cfa263e85caea2cff57d8f917f9f51adae8e20b610e2b23de35b5b11ce691a", size = 1877003, upload-time = "2025-12-08T01:43:29.967Z" }, + { url = "https://files.pythonhosted.org/packages/e8/dd/a237e9f565f3617a88b49284b59cbca2a4f56ebe68676c1aad0ce36a54a7/black-25.12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1a2f578ae20c19c50a382286ba78bfbeafdf788579b053d8e4980afb079ab9be", size = 1712639, upload-time = "2025-12-08T01:52:46.756Z" }, + { url = "https://files.pythonhosted.org/packages/12/80/e187079df1ea4c12a0c63282ddd8b81d5107db6d642f7d7b75a6bcd6fc21/black-25.12.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d3e1b65634b0e471d07ff86ec338819e2ef860689859ef4501ab7ac290431f9b", size = 1758143, upload-time = "2025-12-08T01:45:29.137Z" }, + { url = "https://files.pythonhosted.org/packages/93/b5/3096ccee4f29dc2c3aac57274326c4d2d929a77e629f695f544e159bfae4/black-25.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:a3fa71e3b8dd9f7c6ac4d818345237dfb4175ed3bf37cd5a581dbc4c034f1ec5", size = 1420698, upload-time = "2025-12-08T01:45:53.379Z" }, + { url = "https://files.pythonhosted.org/packages/7e/39/f81c0ffbc25ffbe61c7d0385bf277e62ffc3e52f5ee668d7369d9854fadf/black-25.12.0-cp311-cp311-win_arm64.whl", hash = "sha256:51e267458f7e650afed8445dc7edb3187143003d52a1b710c7321aef22aa9655", size = 1229317, upload-time = "2025-12-08T01:46:35.606Z" }, + { url = "https://files.pythonhosted.org/packages/d1/bd/26083f805115db17fda9877b3c7321d08c647df39d0df4c4ca8f8450593e/black-25.12.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:31f96b7c98c1ddaeb07dc0f56c652e25bdedaac76d5b68a059d998b57c55594a", size = 1924178, upload-time = "2025-12-08T01:49:51.048Z" }, + { url = "https://files.pythonhosted.org/packages/89/6b/ea00d6651561e2bdd9231c4177f4f2ae19cc13a0b0574f47602a7519b6ca/black-25.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:05dd459a19e218078a1f98178c13f861fe6a9a5f88fc969ca4d9b49eb1809783", size = 1742643, upload-time = "2025-12-08T01:49:59.09Z" }, + { url = "https://files.pythonhosted.org/packages/6d/f3/360fa4182e36e9875fabcf3a9717db9d27a8d11870f21cff97725c54f35b/black-25.12.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c1f68c5eff61f226934be6b5b80296cf6939e5d2f0c2f7d543ea08b204bfaf59", size = 1800158, upload-time = "2025-12-08T01:44:27.301Z" }, + { url = "https://files.pythonhosted.org/packages/f8/08/2c64830cb6616278067e040acca21d4f79727b23077633953081c9445d61/black-25.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:274f940c147ddab4442d316b27f9e332ca586d39c85ecf59ebdea82cc9ee8892", size = 1426197, upload-time = "2025-12-08T01:45:51.198Z" }, + { url = "https://files.pythonhosted.org/packages/d4/60/a93f55fd9b9816b7432cf6842f0e3000fdd5b7869492a04b9011a133ee37/black-25.12.0-cp312-cp312-win_arm64.whl", hash = "sha256:169506ba91ef21e2e0591563deda7f00030cb466e747c4b09cb0a9dae5db2f43", size = 1237266, upload-time = "2025-12-08T01:45:10.556Z" }, + { url = "https://files.pythonhosted.org/packages/c8/52/c551e36bc95495d2aa1a37d50566267aa47608c81a53f91daa809e03293f/black-25.12.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a05ddeb656534c3e27a05a29196c962877c83fa5503db89e68857d1161ad08a5", size = 1923809, upload-time = "2025-12-08T01:46:55.126Z" }, + { url = "https://files.pythonhosted.org/packages/a0/f7/aac9b014140ee56d247e707af8db0aae2e9efc28d4a8aba92d0abd7ae9d1/black-25.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9ec77439ef3e34896995503865a85732c94396edcc739f302c5673a2315e1e7f", size = 1742384, upload-time = "2025-12-08T01:49:37.022Z" }, + { url = "https://files.pythonhosted.org/packages/74/98/38aaa018b2ab06a863974c12b14a6266badc192b20603a81b738c47e902e/black-25.12.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e509c858adf63aa61d908061b52e580c40eae0dfa72415fa47ac01b12e29baf", size = 1798761, upload-time = "2025-12-08T01:46:05.386Z" }, + { url = "https://files.pythonhosted.org/packages/16/3a/a8ac542125f61574a3f015b521ca83b47321ed19bb63fe6d7560f348bfe1/black-25.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:252678f07f5bac4ff0d0e9b261fbb029fa530cfa206d0a636a34ab445ef8ca9d", size = 1429180, upload-time = "2025-12-08T01:45:34.903Z" }, + { url = "https://files.pythonhosted.org/packages/e6/2d/bdc466a3db9145e946762d52cd55b1385509d9f9004fec1c97bdc8debbfb/black-25.12.0-cp313-cp313-win_arm64.whl", hash = "sha256:bc5b1c09fe3c931ddd20ee548511c64ebf964ada7e6f0763d443947fd1c603ce", size = 1239350, upload-time = "2025-12-08T01:46:09.458Z" }, + { url = "https://files.pythonhosted.org/packages/35/46/1d8f2542210c502e2ae1060b2e09e47af6a5e5963cb78e22ec1a11170b28/black-25.12.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:0a0953b134f9335c2434864a643c842c44fba562155c738a2a37a4d61f00cad5", size = 1917015, upload-time = "2025-12-08T01:53:27.987Z" }, + { url = "https://files.pythonhosted.org/packages/41/37/68accadf977672beb8e2c64e080f568c74159c1aaa6414b4cd2aef2d7906/black-25.12.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2355bbb6c3b76062870942d8cc450d4f8ac71f9c93c40122762c8784df49543f", size = 1741830, upload-time = "2025-12-08T01:54:36.861Z" }, + { url = "https://files.pythonhosted.org/packages/ac/76/03608a9d8f0faad47a3af3a3c8c53af3367f6c0dd2d23a84710456c7ac56/black-25.12.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9678bd991cc793e81d19aeeae57966ee02909877cb65838ccffef24c3ebac08f", size = 1791450, upload-time = "2025-12-08T01:44:52.581Z" }, + { url = "https://files.pythonhosted.org/packages/06/99/b2a4bd7dfaea7964974f947e1c76d6886d65fe5d24f687df2d85406b2609/black-25.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:97596189949a8aad13ad12fcbb4ae89330039b96ad6742e6f6b45e75ad5cfd83", size = 1452042, upload-time = "2025-12-08T01:46:13.188Z" }, + { url = "https://files.pythonhosted.org/packages/b2/7c/d9825de75ae5dd7795d007681b752275ea85a1c5d83269b4b9c754c2aaab/black-25.12.0-cp314-cp314-win_arm64.whl", hash = "sha256:778285d9ea197f34704e3791ea9404cd6d07595745907dd2ce3da7a13627b29b", size = 1267446, upload-time = "2025-12-08T01:46:14.497Z" }, + { url = "https://files.pythonhosted.org/packages/68/11/21331aed19145a952ad28fca2756a1433ee9308079bd03bd898e903a2e53/black-25.12.0-py3-none-any.whl", hash = "sha256:48ceb36c16dbc84062740049eef990bb2ce07598272e673c17d1a7720c71c828", size = 206191, upload-time = "2025-12-08T01:40:50.963Z" }, +] + [[package]] name = "cachetools" version = "5.5.2" @@ -292,6 +354,7 @@ dependencies = [ { name = "fastparquet" }, { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "numpy", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "numpydoc" }, { name = "opentelemetry-instrumentation-fastapi" }, { name = "pandas" }, { name = "polars" }, @@ -300,6 +363,7 @@ dependencies = [ { name = "pydantic-core" }, { name = "pydantic-settings" }, { name = "python-dotenv" }, + { name = "quartodoc" }, { name = "requests" }, { name = "requests-oauthlib" }, { name = "requests-toolbelt" }, @@ -411,6 +475,7 @@ requires-dist = [ { name = "googleapis-common-protos", marker = "extra == 'gcp'", specifier = ">=1.69.2" }, { name = "grpc-google-iam-v1", marker = "extra == 'gcp'", specifier = ">=0.14.2" }, { name = "numpy", specifier = ">=2.2.4" }, + { name = "numpydoc", specifier = ">=1.10.0" }, { name = "ollama", marker = "extra == 'ollama'", specifier = ">=0.5.1" }, { name = "opentelemetry-instrumentation-fastapi", specifier = ">=0.52b1" }, { name = "pandas", specifier = ">=2.3.0" }, @@ -420,6 +485,7 @@ requires-dist = [ { name = "pydantic-core", specifier = ">=2.23.4" }, { name = "pydantic-settings", specifier = ">=2.8.1" }, { name = "python-dotenv", specifier = ">=1.0.1" }, + { name = "quartodoc", specifier = "==0.11.1" }, { name = "requests", specifier = ">=2.32.3" }, { name = "requests-oauthlib", specifier = ">=2.0.0" }, { name = "requests-toolbelt", specifier = ">=1.0.0" }, @@ -759,6 +825,32 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/55/e2/2537ebcff11c1ee1ff17d8d0b6f4db75873e3b0fb32c2d4a2ee31ecb310a/docstring_parser-0.17.0-py3-none-any.whl", hash = "sha256:cf2569abd23dce8099b300f9b4fa8191e9582dda731fd533daf54c4551658708", size = 36896, upload-time = "2025-07-21T07:35:00.684Z" }, ] +[[package]] +name = "docutils" +version = "0.21.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] +sdist = { url = "https://files.pythonhosted.org/packages/ae/ed/aefcc8cd0ba62a0560c3c18c33925362d46c6075480bfa4df87b28e169a9/docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f", size = 2204444, upload-time = "2024-04-23T18:57:18.24Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8f/d7/9322c609343d929e75e7e5e6255e614fcc67572cfd083959cdef3b7aad79/docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2", size = 587408, upload-time = "2024-04-23T18:57:14.835Z" }, +] + +[[package]] +name = "docutils" +version = "0.22.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.13'", + "python_full_version == '3.12.*'", + "python_full_version == '3.11.*'", +] +sdist = { url = "https://files.pythonhosted.org/packages/d9/02/111134bfeb6e6c7ac4c74594e39a59f6c0195dc4846afbeac3cba60f1927/docutils-0.22.3.tar.gz", hash = "sha256:21486ae730e4ca9f622677b1412b879af1791efcfba517e4c6f60be543fc8cdd", size = 2290153, upload-time = "2025-11-06T02:35:55.655Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/11/a8/c6a4b901d17399c77cd81fb001ce8961e9f5e04d3daf27e8925cb012e163/docutils-0.22.3-py3-none-any.whl", hash = "sha256:bd772e4aca73aff037958d44f2be5229ded4c09927fcf8690c577b66234d6ceb", size = 633032, upload-time = "2025-11-06T02:35:52.391Z" }, +] + [[package]] name = "email-validator" version = "2.3.0" @@ -1513,6 +1605,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" }, ] +[[package]] +name = "imagesize" +version = "1.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a7/84/62473fb57d61e31fef6e36d64a179c8781605429fd927b5dd608c997be31/imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a", size = 1280026, upload-time = "2022-07-01T12:21:05.687Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ff/62/85c4c919272577931d407be5ba5d71c20f0b616d31a0befe0ae45bb79abd/imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b", size = 8769, upload-time = "2022-07-01T12:21:02.467Z" }, +] + [[package]] name = "importlib-metadata" version = "8.7.0" @@ -1525,6 +1626,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/20/b0/36bd937216ec521246249be3bf9855081de4c5e06a0c9b4219dbeda50373/importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd", size = 27656, upload-time = "2025-04-27T15:29:00.214Z" }, ] +[[package]] +name = "importlib-resources" +version = "6.5.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cf/8c/f834fbf984f691b4f7ff60f50b514cc3de5cc08abfc3295564dd89c5e2e7/importlib_resources-6.5.2.tar.gz", hash = "sha256:185f87adef5bcc288449d98fb4fba07cea78bc036455dd44c5fc4a2fe78fed2c", size = 44693, upload-time = "2025-01-03T18:51:56.698Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/ed/1f1afb2e9e7f38a545d628f864d562a5ae64fe6f7a10e28ffb9b185b4e89/importlib_resources-6.5.2-py3-none-any.whl", hash = "sha256:789cfdc3ed28c78b67a06acb8126751ced69a3d5f79c095a98298cd8a760ccec", size = 37461, upload-time = "2025-01-03T18:51:54.306Z" }, +] + [[package]] name = "iniconfig" version = "2.1.0" @@ -2205,6 +2315,20 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/af/11/0cc63f9f321ccf63886ac203336777140011fb669e739da36d8db3c53b98/numpy-2.3.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:2e267c7da5bf7309670523896df97f93f6e469fb931161f483cd6882b3b1a5dc", size = 12971844, upload-time = "2025-09-09T15:58:57.359Z" }, ] +[[package]] +name = "numpydoc" +version = "1.10.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "sphinx", version = "9.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e9/3c/dfccc9e7dee357fb2aa13c3890d952a370dd0ed071e0f7ed62ed0df567c1/numpydoc-1.10.0.tar.gz", hash = "sha256:3f7970f6eee30912260a6b31ac72bba2432830cd6722569ec17ee8d3ef5ffa01", size = 94027, upload-time = "2025-12-02T16:39:12.937Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/5e/3a6a3e90f35cea3853c45e5d5fb9b7192ce4384616f932cf7591298ab6e1/numpydoc-1.10.0-py3-none-any.whl", hash = "sha256:3149da9874af890bcc2a82ef7aae5484e5aa81cb2778f08e3c307ba6d963721b", size = 69255, upload-time = "2025-12-02T16:39:11.561Z" }, +] + [[package]] name = "nvidia-cublas-cu12" version = "12.8.4.1" @@ -2550,6 +2674,20 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, ] +[[package]] +name = "plum-dispatch" +version = "2.6.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "beartype" }, + { name = "rich" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9b/49/1da3299aceee66bb48e8f89b85d4a5af95ac863df39c2c295a1a238c91fc/plum_dispatch-2.6.0.tar.gz", hash = "sha256:09367134541a05f965e3f58c191f4f45b91ef1d87613835171790617bb87ce6d", size = 35394, upload-time = "2025-10-28T13:05:58.358Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4f/6a/f435b9d12f34e03548949a51c7475775feda4c3e5b5373e180d70fd7fbe4/plum_dispatch-2.6.0-py3-none-any.whl", hash = "sha256:8e9b8f20c5119f944720fa5b93f84338a9f604329f016a5132e419e4894cddf1", size = 42251, upload-time = "2025-10-28T13:05:56.874Z" }, +] + [[package]] name = "polars" version = "1.33.1" @@ -2939,6 +3077,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/45/58/38b5afbc1a800eeea951b9285d3912613f2603bdf897a4ab0f4bd7f405fc/python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104", size = 24546, upload-time = "2024-12-16T19:45:44.423Z" }, ] +[[package]] +name = "pytokens" +version = "0.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4e/8d/a762be14dae1c3bf280202ba3172020b2b0b4c537f94427435f19c413b72/pytokens-0.3.0.tar.gz", hash = "sha256:2f932b14ed08de5fcf0b391ace2642f858f1394c0857202959000b68ed7a458a", size = 17644, upload-time = "2025-11-05T13:36:35.34Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/84/25/d9db8be44e205a124f6c98bc0324b2bb149b7431c53877fc6d1038dddaf5/pytokens-0.3.0-py3-none-any.whl", hash = "sha256:95b2b5eaf832e469d141a378872480ede3f251a5a5041b8ec6e581d3ac71bbf3", size = 12195, upload-time = "2025-11-05T13:36:33.183Z" }, +] + [[package]] name = "pytz" version = "2025.2" @@ -3119,6 +3266,30 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/01/1b/5dbe84eefc86f48473947e2f41711aded97eecef1231f4558f1f02713c12/pyzmq-27.1.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:c9f7f6e13dff2e44a6afeaf2cf54cee5929ad64afaf4d40b50f93c58fc687355", size = 544862, upload-time = "2025-09-08T23:09:56.509Z" }, ] +[[package]] +name = "quartodoc" +version = "0.11.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "black" }, + { name = "click" }, + { name = "griffe" }, + { name = "importlib-metadata" }, + { name = "importlib-resources" }, + { name = "plum-dispatch" }, + { name = "pydantic" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "sphobjinv" }, + { name = "tabulate" }, + { name = "typing-extensions" }, + { name = "watchdog" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ff/b9/0bb44cc62a5d63728d04fd6b1e14bcb64945fabad4aa8c03b9d70315fb06/quartodoc-0.11.1.tar.gz", hash = "sha256:c121626e1a36392d168631f33c4d3e7fd48d185de178859f8eafbda14fbfe92f", size = 778611, upload-time = "2025-06-10T14:50:08.185Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/75/d9/0b48d4184f9ca6a996c4fac46897a968698c9d1e0f0e43a6906746201323/quartodoc-0.11.1-py3-none-any.whl", hash = "sha256:0776eb8e53d89385e2c9a8ae0ec08e8c307c1410dd1bd78bb28e8b1823dbb6ad", size = 88053, upload-time = "2025-06-10T14:50:06.443Z" }, +] + [[package]] name = "referencing" version = "0.36.2" @@ -3401,6 +3572,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/84/9c/3881ad34f01942af0cf713e25e476bf851e04e389cc3ff146c3b459ab861/rignore-0.6.4-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:7e6c425603db2c147eace4f752ca3cd4551e7568c9d332175d586c68bcbe3d8d", size = 1122433, upload-time = "2025-07-19T19:24:43.973Z" }, ] +[[package]] +name = "roman-numerals" +version = "3.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/57/5b/1bcda2c6a8acec5b310dd70f732400827b96f05d815834f0f112b91b3539/roman_numerals-3.1.0.tar.gz", hash = "sha256:384e36fc1e8d4bd361bdb3672841faae7a345b3f708aae9895d074c878332551", size = 9069, upload-time = "2025-03-12T00:41:08.837Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/82/1d/7356f115a0e5faf8dc59894a3e9fc8b1821ab949163458b0072db0a12a68/roman_numerals-3.1.0-py3-none-any.whl", hash = "sha256:842ae5fd12912d62720c9aad8cab706e8c692556d01a38443e051ee6cc158d90", size = 7709, upload-time = "2025-03-12T00:41:07.626Z" }, +] + [[package]] name = "rpds-py" version = "0.27.1" @@ -3894,6 +4074,147 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, ] +[[package]] +name = "snowballstemmer" +version = "3.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/75/a7/9810d872919697c9d01295633f5d574fb416d47e535f258272ca1f01f447/snowballstemmer-3.0.1.tar.gz", hash = "sha256:6d5eeeec8e9f84d4d56b847692bacf79bc2c8e90c7f80ca4444ff8b6f2e52895", size = 105575, upload-time = "2025-05-09T16:34:51.843Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/78/3565d011c61f5a43488987ee32b6f3f656e7f107ac2782dd57bdd7d91d9a/snowballstemmer-3.0.1-py3-none-any.whl", hash = "sha256:6cd7b3897da8d6c9ffb968a6781fa6532dce9c3618a4b127d920dab764a19064", size = 103274, upload-time = "2025-05-09T16:34:50.371Z" }, +] + +[[package]] +name = "sphinx" +version = "8.1.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] +dependencies = [ + { name = "alabaster", marker = "python_full_version < '3.11'" }, + { name = "babel", marker = "python_full_version < '3.11'" }, + { name = "colorama", marker = "python_full_version < '3.11' and sys_platform == 'win32'" }, + { name = "docutils", version = "0.21.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "imagesize", marker = "python_full_version < '3.11'" }, + { name = "jinja2", marker = "python_full_version < '3.11'" }, + { name = "packaging", marker = "python_full_version < '3.11'" }, + { name = "pygments", marker = "python_full_version < '3.11'" }, + { name = "requests", marker = "python_full_version < '3.11'" }, + { name = "snowballstemmer", marker = "python_full_version < '3.11'" }, + { name = "sphinxcontrib-applehelp", marker = "python_full_version < '3.11'" }, + { name = "sphinxcontrib-devhelp", marker = "python_full_version < '3.11'" }, + { name = "sphinxcontrib-htmlhelp", marker = "python_full_version < '3.11'" }, + { name = "sphinxcontrib-jsmath", marker = "python_full_version < '3.11'" }, + { name = "sphinxcontrib-qthelp", marker = "python_full_version < '3.11'" }, + { name = "sphinxcontrib-serializinghtml", marker = "python_full_version < '3.11'" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/be0b61178fe2cdcb67e2a92fc9ebb488e3c51c4f74a36a7824c0adf23425/sphinx-8.1.3.tar.gz", hash = "sha256:43c1911eecb0d3e161ad78611bc905d1ad0e523e4ddc202a58a821773dc4c927", size = 8184611, upload-time = "2024-10-13T20:27:13.93Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/26/60/1ddff83a56d33aaf6f10ec8ce84b4c007d9368b21008876fceda7e7381ef/sphinx-8.1.3-py3-none-any.whl", hash = "sha256:09719015511837b76bf6e03e42eb7595ac8c2e41eeb9c29c5b755c6b677992a2", size = 3487125, upload-time = "2024-10-13T20:27:10.448Z" }, +] + +[[package]] +name = "sphinx" +version = "9.0.4" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.13'", + "python_full_version == '3.12.*'", + "python_full_version == '3.11.*'", +] +dependencies = [ + { name = "alabaster", marker = "python_full_version >= '3.11'" }, + { name = "babel", marker = "python_full_version >= '3.11'" }, + { name = "colorama", marker = "python_full_version >= '3.11' and sys_platform == 'win32'" }, + { name = "docutils", version = "0.22.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "imagesize", marker = "python_full_version >= '3.11'" }, + { name = "jinja2", marker = "python_full_version >= '3.11'" }, + { name = "packaging", marker = "python_full_version >= '3.11'" }, + { name = "pygments", marker = "python_full_version >= '3.11'" }, + { name = "requests", marker = "python_full_version >= '3.11'" }, + { name = "roman-numerals", marker = "python_full_version >= '3.11'" }, + { name = "snowballstemmer", marker = "python_full_version >= '3.11'" }, + { name = "sphinxcontrib-applehelp", marker = "python_full_version >= '3.11'" }, + { name = "sphinxcontrib-devhelp", marker = "python_full_version >= '3.11'" }, + { name = "sphinxcontrib-htmlhelp", marker = "python_full_version >= '3.11'" }, + { name = "sphinxcontrib-jsmath", marker = "python_full_version >= '3.11'" }, + { name = "sphinxcontrib-qthelp", marker = "python_full_version >= '3.11'" }, + { name = "sphinxcontrib-serializinghtml", marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/42/50/a8c6ccc36d5eacdfd7913ddccd15a9cee03ecafc5ee2bc40e1f168d85022/sphinx-9.0.4.tar.gz", hash = "sha256:594ef59d042972abbc581d8baa577404abe4e6c3b04ef61bd7fc2acbd51f3fa3", size = 8710502, upload-time = "2025-12-04T07:45:27.343Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c6/3f/4bbd76424c393caead2e1eb89777f575dee5c8653e2d4b6afd7a564f5974/sphinx-9.0.4-py3-none-any.whl", hash = "sha256:5bebc595a5e943ea248b99c13814c1c5e10b3ece718976824ffa7959ff95fffb", size = 3917713, upload-time = "2025-12-04T07:45:24.944Z" }, +] + +[[package]] +name = "sphinxcontrib-applehelp" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/6e/b837e84a1a704953c62ef8776d45c3e8d759876b4a84fe14eba2859106fe/sphinxcontrib_applehelp-2.0.0.tar.gz", hash = "sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1", size = 20053, upload-time = "2024-07-29T01:09:00.465Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5d/85/9ebeae2f76e9e77b952f4b274c27238156eae7979c5421fba91a28f4970d/sphinxcontrib_applehelp-2.0.0-py3-none-any.whl", hash = "sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5", size = 119300, upload-time = "2024-07-29T01:08:58.99Z" }, +] + +[[package]] +name = "sphinxcontrib-devhelp" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/d2/5beee64d3e4e747f316bae86b55943f51e82bb86ecd325883ef65741e7da/sphinxcontrib_devhelp-2.0.0.tar.gz", hash = "sha256:411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad", size = 12967, upload-time = "2024-07-29T01:09:23.417Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/35/7a/987e583882f985fe4d7323774889ec58049171828b58c2217e7f79cdf44e/sphinxcontrib_devhelp-2.0.0-py3-none-any.whl", hash = "sha256:aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2", size = 82530, upload-time = "2024-07-29T01:09:21.945Z" }, +] + +[[package]] +name = "sphinxcontrib-htmlhelp" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/93/983afd9aa001e5201eab16b5a444ed5b9b0a7a010541e0ddfbbfd0b2470c/sphinxcontrib_htmlhelp-2.1.0.tar.gz", hash = "sha256:c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9", size = 22617, upload-time = "2024-07-29T01:09:37.889Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0a/7b/18a8c0bcec9182c05a0b3ec2a776bba4ead82750a55ff798e8d406dae604/sphinxcontrib_htmlhelp-2.1.0-py3-none-any.whl", hash = "sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8", size = 98705, upload-time = "2024-07-29T01:09:36.407Z" }, +] + +[[package]] +name = "sphinxcontrib-jsmath" +version = "1.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/e8/9ed3830aeed71f17c026a07a5097edcf44b692850ef215b161b8ad875729/sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8", size = 5787, upload-time = "2019-01-21T16:10:16.347Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c2/42/4c8646762ee83602e3fb3fbe774c2fac12f317deb0b5dbeeedd2d3ba4b77/sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178", size = 5071, upload-time = "2019-01-21T16:10:14.333Z" }, +] + +[[package]] +name = "sphinxcontrib-qthelp" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/68/bc/9104308fc285eb3e0b31b67688235db556cd5b0ef31d96f30e45f2e51cae/sphinxcontrib_qthelp-2.0.0.tar.gz", hash = "sha256:4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab", size = 17165, upload-time = "2024-07-29T01:09:56.435Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/27/83/859ecdd180cacc13b1f7e857abf8582a64552ea7a061057a6c716e790fce/sphinxcontrib_qthelp-2.0.0-py3-none-any.whl", hash = "sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb", size = 88743, upload-time = "2024-07-29T01:09:54.885Z" }, +] + +[[package]] +name = "sphinxcontrib-serializinghtml" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3b/44/6716b257b0aa6bfd51a1b31665d1c205fb12cb5ad56de752dfa15657de2f/sphinxcontrib_serializinghtml-2.0.0.tar.gz", hash = "sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d", size = 16080, upload-time = "2024-07-29T01:10:09.332Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/52/a7/d2782e4e3f77c8450f727ba74a8f12756d5ba823d81b941f1b04da9d033a/sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331", size = 92072, upload-time = "2024-07-29T01:10:08.203Z" }, +] + +[[package]] +name = "sphobjinv" +version = "2.3.1.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "certifi" }, + { name = "jsonschema" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/77/9a/4887ebe7b46f4669a896dc286a3ac559101d2ceadbbea4614472960c2222/sphobjinv-2.3.1.3.tar.gz", hash = "sha256:a1d51e4cf3d968b9e0d3ed1cbccea0071e5e5795f24a2d7401a4e37d6bd75717", size = 268835, upload-time = "2025-05-26T15:18:16.994Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/63/f9/f48a8f489c8ae8930f12c558b4dd26da96791837747fca87e9da2643f12d/sphobjinv-2.3.1.3-py3-none-any.whl", hash = "sha256:41fc39f6f740a707cfe5b24c1a3a4a6e4ddbdd6429a59bf21f0b5ef1fddf932a", size = 50812, upload-time = "2025-05-26T15:18:10.636Z" }, +] + [[package]] name = "stack-data" version = "0.6.3" @@ -3942,6 +4263,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353, upload-time = "2025-04-27T18:04:59.103Z" }, ] +[[package]] +name = "tabulate" +version = "0.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ec/fe/802052aecb21e3797b8f7902564ab6ea0d60ff8ca23952079064155d1ae1/tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c", size = 81090, upload-time = "2022-10-06T17:21:48.54Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/40/44/4a5f08c96eb108af5cb50b41f76142f0afa346dfa99d5296fe7202a11854/tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f", size = 35252, upload-time = "2022-10-06T17:21:44.262Z" }, +] + [[package]] name = "tenacity" version = "9.1.2"