From ecbe16b813307b09f44769a6c1e8fd4aa7a66153 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Mart=C3=ADn=20Bl=C3=A1zquez?= Date: Tue, 6 Aug 2024 14:26:13 +0200 Subject: [PATCH 01/82] Bump version to `1.4.0` --- src/distilabel/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/distilabel/__init__.py b/src/distilabel/__init__.py index f275ee92d7..bafff914bf 100644 --- a/src/distilabel/__init__.py +++ b/src/distilabel/__init__.py @@ -14,6 +14,6 @@ from rich import traceback as rich_traceback -__version__ = "1.3.0" +__version__ = "1.4.0" rich_traceback.install(show_locals=True) From 2ded30f95853867addb0778035998b24664c1fb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Mart=C3=ADn=20Bl=C3=A1zquez?= Date: Thu, 8 Aug 2024 12:54:11 +0200 Subject: [PATCH 02/82] Make `ClientvLLM.model_name` a `cached_property` (#862) * Update `ClientvLLM.model_name` to `cached_property` * Fix unit test --- src/distilabel/llms/vllm.py | 5 +++-- tests/unit/llms/test_vllm.py | 5 ++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/distilabel/llms/vllm.py b/src/distilabel/llms/vllm.py index 636beb0986..c6351b16bb 100644 --- a/src/distilabel/llms/vllm.py +++ b/src/distilabel/llms/vllm.py @@ -13,6 +13,7 @@ # limitations under the License. import json +from functools import cached_property from typing import ( TYPE_CHECKING, Any, @@ -497,8 +498,8 @@ def load(self) -> None: self.tokenizer, revision=self.tokenizer_revision ) - @property - def model_name(self) -> str: + @cached_property + def model_name(self) -> str: # type: ignore """Returns the name of the model served with vLLM server.""" models = self._client.models.list() return models.data[0].id diff --git a/tests/unit/llms/test_vllm.py b/tests/unit/llms/test_vllm.py index ddc7efb482..5b1e25d187 100644 --- a/tests/unit/llms/test_vllm.py +++ b/tests/unit/llms/test_vllm.py @@ -179,15 +179,14 @@ def test_prepare_batches_and_sort_back( @mock.patch("openai.AsyncOpenAI") class TestClientvLLM: def test_clientvllm_model_name( - self, _openai_mock: mock.MagicMock, _async_openai_mock: mock.MagicMock + self, _: mock.MagicMock, openai_mock: mock.MagicMock ) -> None: llm = ClientvLLM( base_url="http://localhost:8000/v1", tokenizer="google-bert/bert-base-uncased", ) - llm.load() - + llm._client = mock.MagicMock() llm._client.models.list.return_value = SyncPage[Model]( # type: ignore data=[Model(id="llama", created=1234, object="model", owned_by="")], object="model", From 314b759c8e3c7813062632d79d16ce0ee57d221b Mon Sep 17 00:00:00 2001 From: Agus Date: Thu, 8 Aug 2024 16:18:59 +0200 Subject: [PATCH 03/82] Pass dataset to dry_run method (#863) --- src/distilabel/pipeline/base.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/distilabel/pipeline/base.py b/src/distilabel/pipeline/base.py index 2cf65d2b38..5d59c6a729 100644 --- a/src/distilabel/pipeline/base.py +++ b/src/distilabel/pipeline/base.py @@ -400,6 +400,7 @@ def dry_run( self, parameters: Optional[Dict[str, Dict[str, Any]]] = None, batch_size: int = 1, + dataset: Optional["InputDataset"] = None, ) -> "Distiset": """Do a dry run to test the pipeline runs as expected. @@ -412,6 +413,9 @@ def dry_run( the runtime parameters for the step as the value. Defaults to `None`. batch_size: The batch size of the unique batch generated by the generators steps of the pipeline. Defaults to `1`. + dataset: If given, it will be used to create a `GeneratorStep` and put it as the + root step. Convenient method when you have already processed the dataset in + your script and just want to pass it already processed. Defaults to `None`. Returns: Will return the `Distiset` as the main run method would do. @@ -426,7 +430,7 @@ def dry_run( parameters = {} parameters[step_name] = {"batch_size": batch_size} - distiset = self.run(parameters=parameters, use_cache=False) + distiset = self.run(parameters=parameters, use_cache=False, dataset=dataset) self._dry_run = False return distiset From 5e5e7c3ffb1e19d044ad45633116f1c6743c9664 Mon Sep 17 00:00:00 2001 From: Agus Date: Fri, 9 Aug 2024 12:48:50 +0200 Subject: [PATCH 04/82] Add default structured output for `GenerateSentencePair` task (#868) * Add default structured output for GenerateSentencePair task * Move default behavior to base class * Add docstrings to the methods and move json schemas to the class method * Add tests for default structured outputs in sentence transformers task * Add control for parsing errors on JSON data * Refactor code per code review, to simplify just creating the default schemas * Add extra check to avoid setting the structured output if the method wasn't overriden --- src/distilabel/steps/tasks/base.py | 38 ++++++++++ .../steps/tasks/sentence_transformers.py | 69 +++++++++++++++++++ tests/unit/conftest.py | 18 +++++ .../steps/tasks/test_sentence_transformers.py | 69 ++++++++++++++++++- 4 files changed, 191 insertions(+), 3 deletions(-) diff --git a/src/distilabel/steps/tasks/base.py b/src/distilabel/steps/tasks/base.py index 7281d6dd7e..42c0868fea 100644 --- a/src/distilabel/steps/tasks/base.py +++ b/src/distilabel/steps/tasks/base.py @@ -63,10 +63,12 @@ class _Task(_Step, ABC): num_generations: RuntimeParameter[int] = Field( default=1, description="The number of generations to be produced per input." ) + use_default_structured_output: bool = True def load(self) -> None: """Loads the LLM via the `LLM.load()` method.""" super().load() + self._set_default_structured_output() self.llm.load() @override @@ -152,6 +154,42 @@ def _maybe_add_raw_output( output[DISTILABEL_METADATA_KEY] = meta return output + def _set_default_structured_output(self) -> None: + """Prepares the structured output to be set in the selected `LLM`. + + If the method `get_structured_output` returns None (the default), there's no need + to set anything, as it doesn't apply. + If the `use_default_structured_output` and there's no previous structured output + set by hand, then decide the type of structured output to select depending on the + `LLM` provider. + """ + schema = self.get_structured_output() + if not schema: + return + + if self.use_default_structured_output and not self.llm.structured_output: + # In case the default structured output is required, we have to set it before + # the LLM is loaded + from distilabel.llms import InferenceEndpointsLLM + from distilabel.llms.base import AsyncLLM + + structured_output = {"schema": schema} + # To determine instructor or outlines format + if not ( + isinstance(self.llm, AsyncLLM) + and not isinstance(self.llm, InferenceEndpointsLLM) + ): + structured_output.update({"format": "json"}) + + self.llm.structured_output = structured_output + + def get_structured_output(self) -> Union[Dict[str, Any], None]: + """Returns the structured output for a task that implements one by default, + must be overriden by subclasses of `Task`. When implemented, should be a json + schema that enforces the response from the LLM so that it's easier to parse. + """ + return None + class Task(_Task, Step): """Task is a class that implements the `_Task` abstract class and adds the `Step` diff --git a/src/distilabel/steps/tasks/sentence_transformers.py b/src/distilabel/steps/tasks/sentence_transformers.py index 666c89243f..59c504200d 100644 --- a/src/distilabel/steps/tasks/sentence_transformers.py +++ b/src/distilabel/steps/tasks/sentence_transformers.py @@ -16,7 +16,9 @@ import sys from typing import TYPE_CHECKING, Any, Dict, Final, List, Literal, Optional, Union +import orjson from jinja2 import Template +from typing_extensions import override from distilabel.steps.tasks.base import Task @@ -232,6 +234,28 @@ class GenerateSentencePair(Task): result = generate_sentence_pair.process([{"anchor": "I want to generate queries for my LLM."}]) ``` + Generating structured data with default schema (**applies to every action**): + + ```python + from distilabel.steps.tasks import GenerateSentencePair + from distilabel.llms import InferenceEndpointsLLM + + generate_sentence_pair = GenerateSentencePair( + triplet=True, # `False` to generate only positive + action="query", + context="Argilla is an open-source data curation platform for LLMs.", + hard_negative=True, + llm=InferenceEndpointsLLM( + model_id="meta-llama/Meta-Llama-3.1-70B-Instruct", + ), + input_batch_size=10, + use_default_structured_output=True + ) + + generate_sentence_pair.load() + + result = generate_sentence_pair.process([{"anchor": "I want to generate queries for my LLM."}]) + ``` """ triplet: bool = False @@ -320,6 +344,9 @@ def format_output( if output is None: return {"positive": None, "negative": None} + if self.use_default_structured_output: + return self._format_structured_output(output) + match = POSITIVE_NEGATIVE_PAIR_REGEX.match(output) if match is None: formatted_output = {"positive": None} @@ -337,3 +364,45 @@ def format_output( } return {"positive": groups[0].strip()} + + @override + def get_structured_output(self) -> Dict[str, Any]: + """Creates the json schema to be passed to the LLM, to enforce generating + a dictionary with the output which can be directly parsed as a python dictionary. + + Returns: + JSON Schema of the response to enforce. + """ + if self.triplet: + return { + "properties": { + "positive": {"title": "Positive", "type": "string"}, + "negative": {"title": "Negative", "type": "string"}, + }, + "required": ["positive", "negative"], + "title": "Schema", + "type": "object", + } + return { + "properties": {"positive": {"title": "Positive", "type": "string"}}, + "required": ["positive"], + "title": "Schema", + "type": "object", + } + + def _format_structured_output(self, output: str) -> Dict[str, str]: + """Parses the structured response, which should correspond to a dictionary + with either `positive`, or `positive` and `negative` keys. + + Args: + output: The output from the `LLM`. + + Returns: + Formatted output. + """ + try: + return orjson.loads(output) + except orjson.JSONDecodeError: + if self.triplet: + return {"positive": None, "negative": None} + return {"positive": None} diff --git a/tests/unit/conftest.py b/tests/unit/conftest.py index adcd690276..8d27e66c87 100644 --- a/tests/unit/conftest.py +++ b/tests/unit/conftest.py @@ -25,6 +25,8 @@ # Defined here too, so that the serde still works class DummyLLM(AsyncLLM): + structured_output: Any = None + def load(self) -> None: pass @@ -38,6 +40,22 @@ async def agenerate( return ["output" for _ in range(num_generations)] +class DummySyncLLM(AsyncLLM): + structured_output: Any = None + + def load(self) -> None: + pass + + @property + def model_name(self) -> str: + return "test" + + def generate( + self, input: "FormattedInput", num_generations: int = 1 + ) -> "GenerateOutput": + return ["output" for _ in range(num_generations)] + + class DummyMagpieLLM(LLM, MagpieChatTemplateMixin): def load(self) -> None: pass diff --git a/tests/unit/steps/tasks/test_sentence_transformers.py b/tests/unit/steps/tasks/test_sentence_transformers.py index 099de2ff09..6e4223f079 100644 --- a/tests/unit/steps/tasks/test_sentence_transformers.py +++ b/tests/unit/steps/tasks/test_sentence_transformers.py @@ -26,6 +26,27 @@ from tests.unit.conftest import DummyLLM +# from distilabel.llms.base import LLM, AsyncLLM + +# if TYPE_CHECKING: +# from distilabel.llms.typing import GenerateOutput +# from distilabel.steps.tasks.typing import FormattedInput + +# # Defined here too, so that the serde still works +# class DummyStructuredLLM(LLM): +# structured_output: Any = None +# def load(self) -> None: +# pass + +# @property +# def model_name(self) -> str: +# return "test" + +# def generate( +# self, input: "FormattedInput", num_generations: int = 1 +# ) -> "GenerateOutput": +# return ['{ \n "negative": "negative",\n "positive": "positive"\n}' for _ in range(num_generations)] + class TestGenerateSentencePair: @pytest.mark.parametrize( @@ -300,11 +321,12 @@ def test_format_input_with_context( ] @pytest.mark.parametrize( - "output,triplet,expected", + "output,triplet,use_default_structured_output,expected", [ ( "## Positive\n\nThis is a paraphrase\n## Negative\n\nThis is not a paraphrase", True, + False, { "positive": "This is a paraphrase", "negative": "This is not a paraphrase", @@ -313,25 +335,66 @@ def test_format_input_with_context( ( "## Positive\n\nThis is a paraphrase", True, + False, {"positive": "This is a paraphrase", "negative": None}, ), ( "## Positive\n\nThis is a paraphrase", False, + False, {"positive": "This is a paraphrase"}, ), ( "random", False, + False, {"positive": None}, ), + ( + '{ \n "negative": "This is not a paraphrase",\n "positive": "This is a paraphrase"\n}', + True, + True, + { + "positive": "This is a paraphrase", + "negative": "This is not a paraphrase", + }, + ), + ( + '{ \n "positive": "This is a paraphrase"\n}', + True, + True, + { + "positive": "This is a paraphrase", + }, + ), + ( + "{ \n random\n}", + False, + True, + { + "positive": None, + }, + ), + ( + "{ \n random\n}", + True, + True, + {"positive": None, "negative": None}, + ), ], ) def test_format_output( - self, output: str, triplet: bool, expected: Dict[str, Any] + self, + output: str, + triplet: bool, + use_default_structured_output: bool, + expected: Dict[str, Any], ) -> None: task = GenerateSentencePair( - llm=DummyLLM(), action="paraphrase", triplet=triplet + llm=DummyLLM(), + action="paraphrase", + triplet=triplet, + use_default_structured_output=use_default_structured_output, ) task.load() From 7702e247bf0734a4e187be74ed6b60c4ef01fc8c Mon Sep 17 00:00:00 2001 From: Agus Date: Fri, 9 Aug 2024 12:51:32 +0200 Subject: [PATCH 05/82] Complexity scorer default structured output (#870) * Add default structured output for GenerateSentencePair task * Move default behavior to base class * Add docstrings to the methods and move json schemas to the class method * Add tests for default structured outputs in sentence transformers task * Add control for parsing errors on JSON data * Add default structured output for ComplexityScorer task * Refactor code per code review, to simplify just creating the default schemas * Add extra check to avoid setting the structured output if the method wasn't overriden * Refactor get_structured_output to return just the schema * Add reference for the JSON schema --- .../steps/tasks/complexity_scorer.py | 78 +++++++++++++++++++ .../steps/tasks/test_complexity_scorer.py | 24 ++++-- 2 files changed, 97 insertions(+), 5 deletions(-) diff --git a/src/distilabel/steps/tasks/complexity_scorer.py b/src/distilabel/steps/tasks/complexity_scorer.py index 170d75e13e..d7ddc23623 100644 --- a/src/distilabel/steps/tasks/complexity_scorer.py +++ b/src/distilabel/steps/tasks/complexity_scorer.py @@ -22,8 +22,10 @@ from typing import TYPE_CHECKING, Any, Dict, List, Union +import orjson from jinja2 import Template from pydantic import PrivateAttr +from typing_extensions import override from distilabel.steps.tasks.base import Task @@ -86,6 +88,31 @@ class ComplexityScorer(Task): # [{'instructions': ['plain instruction', 'highly complex instruction'], 'model_name': 'test', 'scores': [1, 5], 'distilabel_metadata': {'raw_output_complexity_scorer_0': 'output'}}] ``` + Generate structured output with default schema: + + ```python + from distilabel.steps.tasks import ComplexityScorer + from distilabel.llms.huggingface import InferenceEndpointsLLM + + # Consider this as a placeholder for your actual LLM. + scorer = ComplexityScorer( + llm=InferenceEndpointsLLM( + model_id="mistralai/Mistral-7B-Instruct-v0.2", + ), + use_default_structured_output=use_default_structured_output + ) + + scorer.load() + + result = next( + scorer.process( + [{"instructions": ["plain instruction", "highly complex instruction"]}] + ) + ) + # result + # [{'instructions': ['plain instruction', 'highly complex instruction'], 'model_name': 'test', 'scores': [1, 2], 'distilabel_metadata': {'raw_output_complexity_scorer_0': '{ \n "scores": [\n 1, \n 2\n ]\n}'}}] + ``` + Citations: ``` @@ -153,6 +180,9 @@ def format_output( if output is None: return {"scores": [None] * len(input["instructions"])} + if self.use_default_structured_output: + return self._format_structured_output(output, input) + scores = [] score_lines = output.split("\n") for i, line in enumerate(score_lines): @@ -162,3 +192,51 @@ def format_output( if i == len(input["instructions"]) - 1: break return {"scores": scores} + + @override + def get_structured_output(self) -> Dict[str, Any]: + """Creates the json schema to be passed to the LLM, to enforce generating + a dictionary with the output which can be directly parsed as a python dictionary. + + The schema corresponds to the following: + + ```python + from pydantic import BaseModel + from typing import List + + class SchemaComplexityScorer(BaseModel): + scores: List[int] + ``` + + Returns: + JSON Schema of the response to enforce. + """ + return { + "properties": { + "scores": { + "items": {"type": "integer"}, + "title": "Scores", + "type": "array", + } + }, + "required": ["scores"], + "title": "SchemaComplexityScorer", + "type": "object", + } + + def _format_structured_output( + self, output: str, input: Dict[str, Any] + ) -> Dict[str, str]: + """Parses the structured response, which should correspond to a dictionary + with either `positive`, or `positive` and `negative` keys. + + Args: + output: The output from the `LLM`. + + Returns: + Formatted output. + """ + try: + return orjson.loads(output) + except orjson.JSONDecodeError: + return {"scores": [None] * len(input["instructions"])} diff --git a/tests/unit/steps/tasks/test_complexity_scorer.py b/tests/unit/steps/tasks/test_complexity_scorer.py index ec0575d745..50c601155a 100644 --- a/tests/unit/steps/tasks/test_complexity_scorer.py +++ b/tests/unit/steps/tasks/test_complexity_scorer.py @@ -42,29 +42,43 @@ def test_format_input(self) -> None: ] @pytest.mark.parametrize( - "output, expected", + "output, use_default_structured_output, expected", [ ( "[1] Score: 1\n[2] Score: 2\n[3] Score: 3\n", + False, {"scores": [1.0, 2.0, 3.0]}, ), ( "[1] Score: 1\n[2] Score: 2\n[3] Score: 3\njfjfjfjjfjfjf this is noise from the llm\nlallalalala more noise\nand more noise", + False, {"scores": [1.0, 2.0, 3.0]}, ), ( None, + False, + {"scores": [None, None, None]}, + ), + ( + '{"scores":[1,2,3]}', + True, + {"scores": [1.0, 2.0, 3.0]}, + ), + ( + "wrong", + True, {"scores": [None, None, None]}, ), ], ) def test_format_output( - self, output: Union[str, None], expected: Dict[str, Any] + self, + output: Union[str, None], + use_default_structured_output: bool, + expected: Dict[str, Any], ) -> None: task = ComplexityScorer( - name="complexity_scorer", - llm=DummyLLM(), - pipeline=Pipeline(name="unit-test-pipeline"), + llm=DummyLLM(), use_default_structured_output=use_default_structured_output ) task.load() From aa616a1ce1b21639c73d11efd4780965f091d95e Mon Sep 17 00:00:00 2001 From: Agus Date: Fri, 9 Aug 2024 12:52:49 +0200 Subject: [PATCH 06/82] Quality scorer default structured output (#873) * Add default structured output for GenerateSentencePair task * Move default behavior to base class * Add docstrings to the methods and move json schemas to the class method * Add tests for default structured outputs in sentence transformers task * Add control for parsing errors on JSON data * Add default structured output for ComplexityScorer task * Add default structured output for QualityScorer task * Add example to the docstrings * Refactor code per code review, to simplify just creating the default schemas * Add extra check to avoid setting the structured output if the method wasn't overriden * Refactor get_structured_output to return just the schema * Add reference for the JSON schema * Refactor get_structured_output to return just the schema --- src/distilabel/steps/tasks/quality_scorer.py | 87 +++++++++++++++++++ tests/unit/steps/tasks/test_quality_scorer.py | 24 +++-- 2 files changed, 106 insertions(+), 5 deletions(-) diff --git a/src/distilabel/steps/tasks/quality_scorer.py b/src/distilabel/steps/tasks/quality_scorer.py index 5b905fd097..3e8857c1b6 100644 --- a/src/distilabel/steps/tasks/quality_scorer.py +++ b/src/distilabel/steps/tasks/quality_scorer.py @@ -22,8 +22,10 @@ from typing import Any, Dict, List, Union +import orjson from jinja2 import Template from pydantic import PrivateAttr +from typing_extensions import override from distilabel.steps.tasks.base import Task from distilabel.steps.tasks.typing import ChatType @@ -97,6 +99,40 @@ class QualityScorer(Task): ] ``` + Generate structured output with default schema: + + ```python + from distilabel.steps.tasks import QualityScorer + from distilabel.llms.huggingface import InferenceEndpointsLLM + + scorer = QualityScorer( + llm=InferenceEndpointsLLM( + model_id="meta-llama/Meta-Llama-3.1-70B-Instruct", + ), + use_default_structured_output=True + ) + + scorer.load() + + result = next( + scorer.process( + [ + { + "instruction": "instruction", + "responses": ["good response", "weird response", "bad response"] + } + ] + ) + ) + + # result + [{'instruction': 'instruction', + 'responses': ['good response', 'weird response', 'bad response'], + 'scores': [1, 2, 3], + 'distilabel_metadata': {'raw_output_quality_scorer_0': '{ "scores": [1, 2, 3] }'}, + 'model_name': 'meta-llama/Meta-Llama-3.1-70B-Instruct'}] + ``` + Citations: ``` @@ -166,6 +202,9 @@ def format_output( if output is None: return {"scores": [None] * len(input["responses"])} + if self.use_default_structured_output: + return self._format_structured_output(output, input) + scores = [] score_lines = output.split("\n") @@ -176,3 +215,51 @@ def format_output( if i == len(input["responses"]) - 1: break return {"scores": scores} + + @override + def get_structured_output(self) -> Dict[str, Any]: + """Creates the json schema to be passed to the LLM, to enforce generating + a dictionary with the output which can be directly parsed as a python dictionary. + + The schema corresponds to the following: + + ```python + from pydantic import BaseModel + from typing import List + + class SchemaQualityScorer(BaseModel): + scores: List[int] + ``` + + Returns: + JSON Schema of the response to enforce. + """ + return { + "properties": { + "scores": { + "items": {"type": "integer"}, + "title": "Scores", + "type": "array", + } + }, + "required": ["scores"], + "title": "SchemaQualityScorer", + "type": "object", + } + + def _format_structured_output( + self, output: str, input: Dict[str, Any] + ) -> Dict[str, str]: + """Parses the structured response, which should correspond to a dictionary + with the scores, and a list with them. + + Args: + output: The output from the `LLM`. + + Returns: + Formatted output. + """ + try: + return orjson.loads(output) + except orjson.JSONDecodeError: + return {"scores": [None] * len(input["responses"])} diff --git a/tests/unit/steps/tasks/test_quality_scorer.py b/tests/unit/steps/tasks/test_quality_scorer.py index 608631e9a2..f636dc94ca 100644 --- a/tests/unit/steps/tasks/test_quality_scorer.py +++ b/tests/unit/steps/tasks/test_quality_scorer.py @@ -45,29 +45,43 @@ def test_format_input(self) -> None: ] @pytest.mark.parametrize( - "output, expected", + "output, use_default_structured_output, expected", [ ( "[1] Score: 1\n[2] Score: 2\n[3] Score: 3\n", + False, {"scores": [1.0, 2.0, 3.0]}, ), ( "[1] Score: 1\n[2] Score: 2\n[3] Score: 3\njfjfjfjjfjfjf this is noise from the llm\nlallalalala more noise\nand more noise", + False, {"scores": [1.0, 2.0, 3.0]}, ), ( None, + False, + {"scores": [None, None, None]}, + ), + ( + '{"scores":[1,2,3]}', + True, + {"scores": [1.0, 2.0, 3.0]}, + ), + ( + "wrong", + True, {"scores": [None, None, None]}, ), ], ) def test_format_output( - self, output: Union[str, None], expected: Dict[str, Any] + self, + output: Union[str, None], + use_default_structured_output: bool, + expected: Dict[str, Any], ) -> None: task = QualityScorer( - name="quality_score", - llm=DummyLLM(), - pipeline=Pipeline(name="unit-test-pipeline"), + llm=DummyLLM(), use_default_structured_output=use_default_structured_output ) task.load() From c006ddcf05c663dbee93c03a15ef27530286133d Mon Sep 17 00:00:00 2001 From: Agus Date: Fri, 9 Aug 2024 12:54:10 +0200 Subject: [PATCH 07/82] Ultrafeedback default structured output (#876) * Add default structured output for GenerateSentencePair task * Move default behavior to base class * Add docstrings to the methods and move json schemas to the class method * Add tests for default structured outputs in sentence transformers task * Add control for parsing errors on JSON data * Add default structured output for ComplexityScorer task * Add default structured output for QualityScorer task * Add example to the docstrings * Refactor code per code review, to simplify just creating the default schemas * Add extra check to avoid setting the structured output if the method wasn't overriden * Refactor get_structured_output to return just the schema * Add reference for the JSON schema * Refactor get_structured_output to return just the schema * Add default structured output for UltraFeedback task --- src/distilabel/steps/tasks/ultrafeedback.py | 195 ++++++++++++++++++- tests/unit/steps/tasks/test_ultrafeedback.py | 83 ++++++-- 2 files changed, 264 insertions(+), 14 deletions(-) diff --git a/src/distilabel/steps/tasks/ultrafeedback.py b/src/distilabel/steps/tasks/ultrafeedback.py index e8e98759ec..eec232aabd 100644 --- a/src/distilabel/steps/tasks/ultrafeedback.py +++ b/src/distilabel/steps/tasks/ultrafeedback.py @@ -22,8 +22,10 @@ from typing import Any, Dict, List, Literal, Optional, Union +import orjson from jinja2 import Template from pydantic import PrivateAttr +from typing_extensions import override from distilabel.steps.tasks.base import Task from distilabel.steps.tasks.typing import ChatType @@ -74,13 +76,14 @@ class UltraFeedback(Task): ultrafeedback = UltraFeedback( llm=InferenceEndpointsLLM( model_id="mistralai/Mistral-7B-Instruct-v0.2", - ) + ), + use_default_structured_output=False ) ultrafeedback.load() result = next( - chat.process( + ultrafeedback.process( [ { "instruction": "How much is 2+2?", @@ -101,6 +104,82 @@ class UltraFeedback(Task): # ] ``` + Rate generations from different LLMs based on the honesty, using the default structured output: + + ```python + from distilabel.steps.tasks import UltraFeedback + from distilabel.llms.huggingface import InferenceEndpointsLLM + + # Consider this as a placeholder for your actual LLM. + ultrafeedback = UltraFeedback( + llm=InferenceEndpointsLLM( + model_id="meta-llama/Meta-Llama-3.1-70B-Instruct", + ), + aspect="honesty" + ) + + ultrafeedback.load() + + result = next( + ultrafeedback.process( + [ + { + "instruction": "How much is 2+2?", + "generations": ["4", "and a car"], + } + ] + ) + ) + # result + # [{'instruction': 'How much is 2+2?', + # 'generations': ['4', 'and a car'], + # 'ratings': [5, 1], + # 'rationales': ['The response is correct and confident, as it directly answers the question without expressing any uncertainty or doubt.', + # "The response is confidently incorrect, as it provides unrelated information ('a car') and does not address the question. The model shows no uncertainty or indication that it does not know the answer."], + # 'distilabel_metadata': {'raw_output_ultra_feedback_0': '{"ratings": [\n 5,\n 1\n] \n\n,"rationales": [\n "The response is correct and confident, as it directly answers the question without expressing any uncertainty or doubt.",\n "The response is confidently incorrect, as it provides unrelated information (\'a car\') and does not address the question. The model shows no uncertainty or indication that it does not know the answer."\n] }'}, + # 'model_name': 'meta-llama/Meta-Llama-3.1-70B-Instruct'}] + ``` + + Rate generations from different LLMs based on the helpfulness, using the default structured output: + + ```python + from distilabel.steps.tasks import UltraFeedback + from distilabel.llms.huggingface import InferenceEndpointsLLM + + # Consider this as a placeholder for your actual LLM. + ultrafeedback = UltraFeedback( + llm=InferenceEndpointsLLM( + model_id="meta-llama/Meta-Llama-3.1-70B-Instruct", + generation_kwargs={"max_new_tokens": 512}, + ), + aspect="helpfulness" + ) + + ultrafeedback.load() + + result = next( + ultrafeedback.process( + [ + { + "instruction": "How much is 2+2?", + "generations": ["4", "and a car"], + } + ] + ) + ) + # result + # [{'instruction': 'How much is 2+2?', + # 'generations': ['4', 'and a car'], + # 'ratings': [1, 5], + # 'rationales': ['Text 1 is clear and relevant, providing the correct answer to the question. It is also not lengthy and does not contain repetition. However, it lacks comprehensive information or detailed description.', + # 'Text 2 is neither clear nor relevant to the task. It does not provide any useful information and seems unrelated to the question.'], + # 'rationales_for_rating': ['Text 1 is rated as Correct (3) because it provides the accurate answer to the question, but lacks comprehensive information or detailed description.', + # 'Text 2 is rated as Severely Incorrect (1) because it does not provide any relevant information and seems unrelated to the question.'], + # 'types': [1, 3, 1], + # 'distilabel_metadata': {'raw_output_ultra_feedback_0': '{ \n "ratings": [\n 1,\n 5\n ]\n ,\n "rationales": [\n "Text 1 is clear and relevant, providing the correct answer to the question. It is also not lengthy and does not contain repetition. However, it lacks comprehensive information or detailed description.",\n "Text 2 is neither clear nor relevant to the task. It does not provide any useful information and seems unrelated to the question."\n ]\n ,\n "rationales_for_rating": [\n "Text 1 is rated as Correct (3) because it provides the accurate answer to the question, but lacks comprehensive information or detailed description.",\n "Text 2 is rated as Severely Incorrect (1) because it does not provide any relevant information and seems unrelated to the question."\n ]\n ,\n "types": [\n 1, 3,\n 1\n ]\n }'}, + # 'model_name': 'meta-llama/Meta-Llama-3.1-70B-Instruct'}] + ``` + Citations: ``` @@ -220,6 +299,9 @@ def _format_ratings_rationales_output( "rationales": [None] * len(input["generations"]), } + if self.use_default_structured_output: + return self._format_structured_output(output, input) + pattern = r"Rating: (.+?)\nRationale: (.+)" sections = output.split("\n\n") @@ -254,6 +336,9 @@ def _format_types_ratings_rationales_output( "rationales-for-ratings": [None] * len(input["generations"]), } + if self.use_default_structured_output: + return self._format_structured_output(output, input) + pattern = r"Type: (.+?)\nRationale: (.+?)\nRating: (.+?)\nRationale: (.+)" sections = output.split("\n\n") @@ -287,3 +372,109 @@ def _format_types_ratings_rationales_output( } ) return group_dicts(*formatted_outputs) + + @override + def get_structured_output(self) -> Dict[str, Any]: + """Creates the json schema to be passed to the LLM, to enforce generating + a dictionary with the output which can be directly parsed as a python dictionary. + + The schema corresponds to the following: + + ```python + from pydantic import BaseModel + from typing import List + + class SchemaUltraFeedback(BaseModel): + ratings: List[int] + rationales: List[str] + + class SchemaUltraFeedbackWithType(BaseModel): + types: List[Optional[int]] + ratings: List[int] + rationales: List[str] + rationales_for_rating: List[str] + ``` + + Returns: + JSON Schema of the response to enforce. + """ + if self.aspect in [ + "honesty", + "instruction-following", + "overall-rating", + ]: + return { + "properties": { + "ratings": { + "items": {"type": "integer"}, + "title": "Ratings", + "type": "array", + }, + "rationales": { + "items": {"type": "string"}, + "title": "Rationales", + "type": "array", + }, + }, + "required": ["ratings", "rationales"], + "title": "SchemaUltraFeedback", + "type": "object", + } + return { + "properties": { + "types": { + "items": {"anyOf": [{"type": "integer"}, {"type": "null"}]}, + "title": "Types", + "type": "array", + }, + "ratings": { + "items": {"type": "integer"}, + "title": "Ratings", + "type": "array", + }, + "rationales": { + "items": {"type": "string"}, + "title": "Rationales", + "type": "array", + }, + "rationales_for_rating": { + "items": {"type": "string"}, + "title": "Rationales For Rating", + "type": "array", + }, + }, + "required": ["types", "ratings", "rationales", "rationales_for_rating"], + "title": "SchemaUltraFeedbackWithType", + "type": "object", + } + + def _format_structured_output( + self, output: str, input: Dict[str, Any] + ) -> Dict[str, str]: + """Parses the structured response, which should correspond to a dictionary + with either `positive`, or `positive` and `negative` keys. + + Args: + output: The output from the `LLM`. + + Returns: + Formatted output. + """ + try: + return orjson.loads(output) + except orjson.JSONDecodeError: + if self.aspect in [ + "honesty", + "instruction-following", + "overall-rating", + ]: + return { + "ratings": [None] * len(input["generations"]), + "rationales": [None] * len(input["generations"]), + } + return { + "ratings": [None] * len(input["generations"]), + "rationales": [None] * len(input["generations"]), + "types": [None] * len(input["generations"]), + "rationales-for-ratings": [None] * len(input["generations"]), + } diff --git a/tests/unit/steps/tasks/test_ultrafeedback.py b/tests/unit/steps/tasks/test_ultrafeedback.py index fa72ff9442..d9e272959e 100644 --- a/tests/unit/steps/tasks/test_ultrafeedback.py +++ b/tests/unit/steps/tasks/test_ultrafeedback.py @@ -12,16 +12,18 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Any, List +from typing import Any, Dict, List, Union +import pytest from distilabel.llms.base import LLM from distilabel.llms.typing import GenerateOutput -from distilabel.pipeline.local import Pipeline from distilabel.steps.tasks.typing import ChatType from distilabel.steps.tasks.ultrafeedback import UltraFeedback class UltraFeedbackLLM(LLM): + structured_output: Any = None + def load(self) -> None: pass @@ -43,14 +45,11 @@ def generate( class TestUltraFeedback: def test_process_with_simple_aspect(self) -> None: - pipeline = Pipeline(name="unit-test-pipeline") - llm = UltraFeedbackLLM() - task = UltraFeedback( name="ultrafeedback", aspect="instruction-following", - llm=llm, - pipeline=pipeline, + llm=UltraFeedbackLLM(), + use_default_structured_output=False, ) task.load() @@ -70,14 +69,11 @@ def test_process_with_simple_aspect(self) -> None: ] def test_process_with_complex_aspect(self) -> None: - pipeline = Pipeline(name="unit-test-pipeline") - llm = UltraFeedbackLLM() - task = UltraFeedback( name="ultrafeedback", aspect="truthfulness", - llm=llm, - pipeline=pipeline, + llm=UltraFeedbackLLM(), + use_default_structured_output=False, ) task.load() @@ -97,3 +93,66 @@ def test_process_with_complex_aspect(self) -> None: }, } ] + + @pytest.mark.parametrize( + "output, use_default_structured_output, aspect, expected", + [ + ( + "{ \n random\n}", + True, + "honesty", + {"ratings": [None, None], "rationales": [None, None]}, + ), + ( + '{ \n "ratings": [\n 1,\n 5\n ]\n ,\n "rationales": [\n "rationale1",\n "rationale2"\n ]}', + True, + "honesty", + {"ratings": [1, 5], "rationales": ["rationale1", "rationale2"]}, + ), + ( + "{ \n random\n}", + True, + "helpfulness", + { + "ratings": [None, None], + "rationales": [None, None], + "rationales-for-ratings": [None, None], + "types": [None, None], + }, + ), + ( + '{ \n "ratings": [\n 1,\n 5\n ]\n ,\n "rationales": [\n "rationale1",\n "rationale2"\n ], "rationales-for-ratings": [\n "rationale1",\n "rationale2"\n ], "types": [\n 1,\n 2\n ]}', + True, + "helpfulness", + { + "ratings": [1, 5], + "rationales": ["rationale1", "rationale2"], + "rationales-for-ratings": ["rationale1", "rationale2"], + "types": [1, 2], + }, + ), + ], + ) + def test_format_output( + self, + output: Union[str, None], + use_default_structured_output: bool, + aspect: str, + expected: Dict[str, Any], + ) -> None: + task = UltraFeedback( + llm=UltraFeedbackLLM(), + aspect=aspect, + use_default_structured_output=use_default_structured_output, + ) + task.load() + + result = task.format_output( + output=output, + input={ + "instruction": "How much is 2+2?", + "generations": ["4", "something weird"], + }, + ) + + assert result == expected From bbe04fd86499d5406c5ed9ed9d1b6f5f9b592ef5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Mart=C3=ADn=20Bl=C3=A1zquez?= Date: Mon, 12 Aug 2024 17:52:20 +0200 Subject: [PATCH 08/82] Remove use of `default_chat_template` (#888) --- src/distilabel/llms/chat_templates.py | 15 --------------- src/distilabel/llms/huggingface/transformers.py | 9 +++------ src/distilabel/llms/vllm.py | 9 +++------ 3 files changed, 6 insertions(+), 27 deletions(-) delete mode 100644 src/distilabel/llms/chat_templates.py diff --git a/src/distilabel/llms/chat_templates.py b/src/distilabel/llms/chat_templates.py deleted file mode 100644 index 7edba0132c..0000000000 --- a/src/distilabel/llms/chat_templates.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright 2023-present, Argilla, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -CHATML_TEMPLATE = "{% for message in messages %}{{'<|im_start|>' + message['role'] + '\n' + message[\"content\"] + '<|im_end|>' + '\n'}}{% endfor %}{% if add_generation_prompt %}{{ '<|im_start|>assistant\n' }}{% endif %}" diff --git a/src/distilabel/llms/huggingface/transformers.py b/src/distilabel/llms/huggingface/transformers.py index 6b8ad25e2f..455e6e898b 100644 --- a/src/distilabel/llms/huggingface/transformers.py +++ b/src/distilabel/llms/huggingface/transformers.py @@ -18,7 +18,6 @@ from pydantic import Field, PrivateAttr, SecretStr, validate_call from distilabel.llms.base import LLM -from distilabel.llms.chat_templates import CHATML_TEMPLATE from distilabel.llms.mixins.cuda_device_placement import CudaDevicePlacementMixin from distilabel.llms.mixins.magpie import MagpieChatTemplateMixin from distilabel.llms.typing import GenerateOutput @@ -145,11 +144,6 @@ def load(self) -> None: if self.chat_template is not None: self._pipeline.tokenizer.chat_template = self.chat_template # type: ignore - elif ( - self._pipeline.tokenizer.chat_template is None # type: ignore - and self._pipeline.tokenizer.default_chat_template is None # type: ignore - ): - self._pipeline.tokenizer.chat_template = CHATML_TEMPLATE # type: ignore if self.structured_output: self._prefix_allowed_tokens_fn = self._prepare_structured_output( @@ -178,6 +172,9 @@ def prepare_input(self, input: "StandardInput") -> str: Returns: The prompt to send to the LLM. """ + if self._pipeline.tokenizer.chat_template: # type: ignore + return input[0]["content"] + prompt: str = ( self._pipeline.tokenizer.apply_chat_template( # type: ignore input, # type: ignore diff --git a/src/distilabel/llms/vllm.py b/src/distilabel/llms/vllm.py index c6351b16bb..4ff30c07f4 100644 --- a/src/distilabel/llms/vllm.py +++ b/src/distilabel/llms/vllm.py @@ -30,7 +30,6 @@ from pydantic import Field, PrivateAttr, SecretStr, validate_call from distilabel.llms.base import LLM -from distilabel.llms.chat_templates import CHATML_TEMPLATE from distilabel.llms.mixins.cuda_device_placement import CudaDevicePlacementMixin from distilabel.llms.mixins.magpie import MagpieChatTemplateMixin from distilabel.llms.openai import OpenAILLM @@ -204,11 +203,6 @@ def load(self) -> None: self._tokenizer = self._model.get_tokenizer() # type: ignore if self.chat_template is not None: self._tokenizer.chat_template = self.chat_template # type: ignore - elif ( - self._tokenizer.chat_template is None # type: ignore - and self._tokenizer.default_chat_template is None # type: ignore - ): - self._tokenizer.chat_template = CHATML_TEMPLATE if self.structured_output: self._logits_processor = self._prepare_structured_output( @@ -235,6 +229,9 @@ def prepare_input(self, input: "StandardInput") -> str: Returns: The prompt to send to the LLM. """ + if self._tokenizer.chat_template is None: + return input[0]["content"] + prompt: str = ( self._tokenizer.apply_chat_template( input, # type: ignore From 1198d24d77be5fa781bb69239d862fa6a251eb41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Mart=C3=ADn=20Bl=C3=A1zquez?= Date: Tue, 13 Aug 2024 10:21:12 +0200 Subject: [PATCH 09/82] Temporary (using `pip`) fix for installing `llama-cpp-python` in CI (#886) --- scripts/install_dependencies.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/install_dependencies.sh b/scripts/install_dependencies.sh index 7deb35b778..21f0f3b52a 100755 --- a/scripts/install_dependencies.sh +++ b/scripts/install_dependencies.sh @@ -6,7 +6,8 @@ python_version=$(python -c "import sys; print(sys.version_info[:2])") python -m pip install uv -uv pip install --system -e ".[dev,tests,anthropic,argilla,cohere,groq,hf-inference-endpoints,hf-transformers,litellm,llama-cpp,ollama,openai,outlines,vertexai,mistralai,instructor,sentence-transformers,faiss-cpu]" +uv pip install --system -e ".[dev,tests,anthropic,argilla,cohere,groq,hf-inference-endpoints,hf-transformers,litellm,ollama,openai,outlines,vertexai,mistralai,instructor,sentence-transformers,faiss-cpu]" +pip install llama-cpp-python if [ "${python_version}" != "(3, 12)" ]; then uv pip install --system -e .[ray] From 8916ff260f6f70a662b4a9278ae61e82dc50569e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Mart=C3=ADn=20Bl=C3=A1zquez?= Date: Tue, 13 Aug 2024 12:23:41 +0200 Subject: [PATCH 10/82] Fix unit tests after release of `transformers==4.44.0` (#891) * Update unit tests so they work with `transformers>=4.44.0` * fix more unit tests --- .../llms/huggingface/transformers.py | 3 ++ .../huggingface/test_inference_endpoints.py | 2 +- .../llms/huggingface/test_transformers.py | 12 +++++--- tests/unit/llms/test_vllm.py | 28 ++++++++++++++----- .../tasks/structured_outputs/test_outlines.py | 2 +- .../steps/tasks/test_generate_embeddings.py | 5 ++-- tests/unit/steps/test_reward_model.py | 12 +++++--- 7 files changed, 44 insertions(+), 20 deletions(-) diff --git a/src/distilabel/llms/huggingface/transformers.py b/src/distilabel/llms/huggingface/transformers.py index 455e6e898b..d3d16f6ac9 100644 --- a/src/distilabel/llms/huggingface/transformers.py +++ b/src/distilabel/llms/huggingface/transformers.py @@ -145,6 +145,9 @@ def load(self) -> None: if self.chat_template is not None: self._pipeline.tokenizer.chat_template = self.chat_template # type: ignore + if self._pipeline.tokenizer.pad_token is None: # type: ignore + self._pipeline.tokenizer.pad_token = self._pipeline.tokenizer.eos_token # type: ignore + if self.structured_output: self._prefix_allowed_tokens_fn = self._prepare_structured_output( self.structured_output diff --git a/tests/unit/llms/huggingface/test_inference_endpoints.py b/tests/unit/llms/huggingface/test_inference_endpoints.py index b4d32b26ae..bdf7cef71f 100644 --- a/tests/unit/llms/huggingface/test_inference_endpoints.py +++ b/tests/unit/llms/huggingface/test_inference_endpoints.py @@ -276,7 +276,7 @@ async def test_agenerate_with_structured_output( ) == [" Aenean hendrerit aliquam velit. ..."] kwargs = { - "prompt": "[INST] Lorem ipsum dolor sit amet, consectetur adipiscing elit. [/INST]", + "prompt": " [INST] Lorem ipsum dolor sit amet, consectetur adipiscing elit. [/INST]", "max_new_tokens": 128, "do_sample": False, "typical_p": None, diff --git a/tests/unit/llms/huggingface/test_transformers.py b/tests/unit/llms/huggingface/test_transformers.py index 081b6fae78..3b1fa539b9 100644 --- a/tests/unit/llms/huggingface/test_transformers.py +++ b/tests/unit/llms/huggingface/test_transformers.py @@ -22,9 +22,10 @@ @pytest.fixture(scope="module") def transformers_llm() -> Generator[TransformersLLM, None, None]: llm = TransformersLLM( - model="sentence-transformers/all-MiniLM-L6-v2", + model="distilabel-internal-testing/tiny-random-mistral", model_kwargs={"is_decoder": True}, cuda_devices=[], + torch_dtype="float16", ) llm.load() @@ -33,7 +34,10 @@ def transformers_llm() -> Generator[TransformersLLM, None, None]: class TestTransformersLLM: def test_model_name(self, transformers_llm: TransformersLLM) -> None: - assert transformers_llm.model_name == "sentence-transformers/all-MiniLM-L6-v2" + assert ( + transformers_llm.model_name + == "distilabel-internal-testing/tiny-random-mistral" + ) def test_generate(self, transformers_llm: TransformersLLM) -> None: responses = transformers_llm.generate( @@ -59,5 +63,5 @@ def test_get_last_hidden_states(self, transformers_llm: TransformersLLM) -> None ] last_hidden_states = transformers_llm.get_last_hidden_states(inputs) # type: ignore - assert last_hidden_states[0].shape == (31, 384) - assert last_hidden_states[1].shape == (34, 384) + assert last_hidden_states[0].shape == (7, 128) + assert last_hidden_states[1].shape == (10, 128) diff --git a/tests/unit/llms/test_vllm.py b/tests/unit/llms/test_vllm.py index 5b1e25d187..748b580abd 100644 --- a/tests/unit/llms/test_vllm.py +++ b/tests/unit/llms/test_vllm.py @@ -43,42 +43,54 @@ class Animal(BaseModel): SAMPLE_DATA = [ [ { - "instruction": "Generate a character from a RPG game.", + "instruction": [ + {"role": "user", "content": "Generate a character from a RPG game."} + ], "structured_output": { "format": "json", "schema": Character.model_json_schema(), }, }, { - "instruction": "Generate an animal from a zoo.", + "instruction": [ + { + "role": "user", + "content": "Generate an animal from a zoo.", + } + ], "structured_output": { "format": "json", "schema": Animal.model_json_schema(), }, }, { - "instruction": "Repeated character", + "instruction": [{"role": "user", "content": "Repeated character"}], "structured_output": { "format": "json", "schema": Character.model_json_schema(), }, }, { - "instruction": "What's the weather like today in Seattle in Celsius degrees?", + "instruction": [ + { + "role": "user", + "content": "What's the weather like today in Seattle in Celsius degrees?", + } + ], "structured_output": { "format": "regex", "schema": "(\\d{1,2})°C", }, }, { - "instruction": "Other character", + "instruction": [{"role": "user", "content": "Other character"}], "structured_output": { "format": "json", "schema": Character.model_json_schema(), }, }, { - "instruction": "repeated regex", + "instruction": [{"role": "user", "content": "repeated regex"}], "structured_output": { "format": "regex", "schema": "(\\d{1,2})°C", @@ -90,6 +102,8 @@ class Animal(BaseModel): # Just a mock to avoid loading the model class DummyTokenizer: + chat_template = None + def __init__(self) -> None: pass @@ -200,7 +214,7 @@ async def test_agenerate( ) -> None: llm = ClientvLLM( base_url="http://localhost:8000/v1", - tokenizer="google-bert/bert-base-uncased", + tokenizer="distilabel-internal-testing/tiny-random-mistral", ) llm.load() diff --git a/tests/unit/steps/tasks/structured_outputs/test_outlines.py b/tests/unit/steps/tasks/structured_outputs/test_outlines.py index b940cee321..943fbad38c 100644 --- a/tests/unit/steps/tasks/structured_outputs/test_outlines.py +++ b/tests/unit/steps/tasks/structured_outputs/test_outlines.py @@ -119,7 +119,7 @@ def test_generation( self, format: str, schema: Union[str, Type[BaseModel]], prompt: str ) -> None: llm = TransformersLLM( - model="openaccess-ai-collective/tiny-mistral", + model="distilabel-internal-testing/tiny-random-mistral", structured_output=OutlinesStructuredOutputType( format=format, schema=schema ), diff --git a/tests/unit/steps/tasks/test_generate_embeddings.py b/tests/unit/steps/tasks/test_generate_embeddings.py index 73e2edfc38..4d8f3447c3 100644 --- a/tests/unit/steps/tasks/test_generate_embeddings.py +++ b/tests/unit/steps/tasks/test_generate_embeddings.py @@ -23,8 +23,7 @@ @pytest.fixture(scope="module") def transformers_llm() -> Generator[TransformersLLM, None, None]: llm = TransformersLLM( - model="TaylorAI/bge-micro-v2", - model_kwargs={"is_decoder": True}, + model="distilabel-internal-testing/tiny-random-mistral", cuda_devices=[], ) llm.load() @@ -42,4 +41,4 @@ def test_process(self, transformers_llm: TransformersLLM) -> None: result = next(task.process([{"text": "Hello, how are you?"}])) assert "embedding" in result[0] - assert len(result[0]["embedding"]) == 384 + assert len(result[0]["embedding"]) == 128 diff --git a/tests/unit/steps/test_reward_model.py b/tests/unit/steps/test_reward_model.py index cb6db69624..80f37765ca 100644 --- a/tests/unit/steps/test_reward_model.py +++ b/tests/unit/steps/test_reward_model.py @@ -24,6 +24,8 @@ def test_process(self) -> None: step.load() + step._tokenizer.chat_template = "{% for message in messages %}{% if message['role'] == 'user' %}{{ ' ' }}{% endif %}{{ message['content'] }}{% if not loop.last %}{{ ' ' }}{% endif %}{% endfor %}{{ eos_token }}" + result = next( step.process( inputs=[ @@ -40,12 +42,12 @@ def test_process(self) -> None: { "instruction": "How much is 2+2?", "response": "The output of 2+2 is 4", - "score": pytest.approx(-0.5738837122917175, abs=1e-6), + "score": pytest.approx(0.28374260663986206, abs=1e-5), }, { "instruction": "How much is 2+2?", "response": "4", - "score": pytest.approx(-0.6376492977142334, abs=1e-6), + "score": pytest.approx(-4.194192409515381, abs=1e-5), }, ] @@ -56,6 +58,8 @@ def test_process_with_conversation(self) -> None: step.load() + step._tokenizer.chat_template = "{% for message in messages %}{% if message['role'] == 'user' %}{{ ' ' }}{% endif %}{{ message['content'] }}{% if not loop.last %}{{ ' ' }}{% endif %}{% endfor %}{{ eos_token }}" + result = next( step.process( inputs=[ @@ -81,13 +85,13 @@ def test_process_with_conversation(self) -> None: {"role": "user", "content": "How much is 2+2?"}, {"role": "assistant", "content": "The output of 2+2 is 4"}, ], - "score": pytest.approx(-0.5738837122917175, abs=1e-6), + "score": pytest.approx(0.28374260663986206, abs=1e-5), }, { "conversation": [ {"role": "user", "content": "How much is 2+2?"}, {"role": "assistant", "content": "4"}, ], - "score": pytest.approx(-0.6376492977142334, abs=1e-6), + "score": pytest.approx(-4.194192409515381, abs=1e-5), }, ] From 75baf64024433f232a351cda6db2c74152adcdc1 Mon Sep 17 00:00:00 2001 From: Agus Date: Tue, 13 Aug 2024 12:43:05 +0200 Subject: [PATCH 11/82] Fix default structured output (#892) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add check for dependencies for structured outputs and change default value of structured outputs * Update tests with serialized default structured output --------- Co-authored-by: Gabriel Martín Blázquez --- src/distilabel/steps/tasks/base.py | 13 ++++++++++++- tests/unit/steps/tasks/evol_instruct/test_base.py | 2 ++ .../steps/tasks/evol_instruct/test_generator.py | 2 ++ tests/unit/steps/tasks/evol_quality/test_base.py | 2 ++ tests/unit/steps/tasks/magpie/test_base.py | 1 + tests/unit/steps/tasks/magpie/test_generator.py | 1 + tests/unit/steps/tasks/test_base.py | 2 ++ 7 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/distilabel/steps/tasks/base.py b/src/distilabel/steps/tasks/base.py index 42c0868fea..78910abb79 100644 --- a/src/distilabel/steps/tasks/base.py +++ b/src/distilabel/steps/tasks/base.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import importlib from abc import ABC, abstractmethod from typing import TYPE_CHECKING, Any, Dict, List, Union @@ -63,7 +64,7 @@ class _Task(_Step, ABC): num_generations: RuntimeParameter[int] = Field( default=1, description="The number of generations to be produced per input." ) - use_default_structured_output: bool = True + use_default_structured_output: bool = False def load(self) -> None: """Loads the LLM via the `LLM.load()` method.""" @@ -173,14 +174,24 @@ def _set_default_structured_output(self) -> None: from distilabel.llms import InferenceEndpointsLLM from distilabel.llms.base import AsyncLLM + def check_dependency(module_name: str) -> None: + if not importlib.util.find_spec(module_name): + raise ImportError( + f"`{module_name}` is not installed and is needed for the structured generation with this LLM." + f" Please install it using `pip install {module_name}`." + ) + + dependency = "outlines" structured_output = {"schema": schema} # To determine instructor or outlines format if not ( isinstance(self.llm, AsyncLLM) and not isinstance(self.llm, InferenceEndpointsLLM) ): + dependency = "instructor" structured_output.update({"format": "json"}) + check_dependency(dependency) self.llm.structured_output = structured_output def get_structured_output(self) -> Union[Dict[str, Any], None]: diff --git a/tests/unit/steps/tasks/evol_instruct/test_base.py b/tests/unit/steps/tasks/evol_instruct/test_base.py index 59ca4fae96..f98b5ae7f9 100644 --- a/tests/unit/steps/tasks/evol_instruct/test_base.py +++ b/tests/unit/steps/tasks/evol_instruct/test_base.py @@ -134,6 +134,7 @@ def test_serialization(self, dummy_llm: LLM) -> None: "input_batch_size": task.input_batch_size, "llm": { "generation_kwargs": {}, + "structured_output": None, "type_info": { "module": task.llm.__module__, "name": task.llm.__class__.__name__, @@ -152,6 +153,7 @@ def test_serialization(self, dummy_llm: LLM) -> None: "INCREASED_REASONING_STEPS": "I want you act as a Prompt Rewriter.\n\nYour objective is to rewrite a given prompt into a more complex version to make those famous AI systems (e.g., chatgpt and GPT4) a bit harder to handle.\n\nBut the rewritten prompt must be reasonable and must be understood and responded by humans.\n\nYour rewriting cannot omit the non-text parts such as the table and code in #The Given Prompt#:. Also, please do not omit the input in #The Given Prompt#.\n\nYou SHOULD complicate the given prompt using the following method: \nIf #The Given Prompt# can be solved with just a few simple thinking processes, you can rewrite it to explicitly request multiple-step reasoning.\n\nYou should try your best not to make the #Rewritten Prompt# become verbose, #Rewritten Prompt# can only add 10 to 20 words into #The Given Prompt#.\n\n'#The Given Prompt#', '#Rewritten Prompt#', 'given prompt' and 'rewritten prompt' are not allowed to appear in #Rewritten Prompt#\n\n#The Given Prompt#:\n\n#Rewritten Prompt#:\n\n", "BREADTH": "I want you act as a Prompt Creator.\n\nYour goal is to draw inspiration from the #Given Prompt# to create a brand new prompt.\n\nThis new prompt should belong to the same domain as the #Given Prompt# but be even more rare.\n\nThe LENGTH and complexity of the #Created Prompt# should be similar to that of the #Given Prompt#.\n\nThe #Created Prompt# must be reasonable and must be understood and responded by humans.\n\n'#Given Prompt#', '#Created Prompt#', 'given prompt' and 'created prompt' are not allowed to appear in #Created Prompt#\n\n#Given Prompt#:\n\n#Created Prompt#:\n\n", }, + "use_default_structured_output": False, "seed": task.seed, "runtime_parameters_info": [ { diff --git a/tests/unit/steps/tasks/evol_instruct/test_generator.py b/tests/unit/steps/tasks/evol_instruct/test_generator.py index 9f9612148d..d20f631fd4 100644 --- a/tests/unit/steps/tasks/evol_instruct/test_generator.py +++ b/tests/unit/steps/tasks/evol_instruct/test_generator.py @@ -117,6 +117,7 @@ def test_serialization(self, dummy_llm: LLM) -> None: "name": "task", "llm": { "generation_kwargs": {}, + "structured_output": None, "type_info": { "module": task.llm.__class__.__module__, "name": task.llm.__class__.__name__, @@ -148,6 +149,7 @@ def test_serialization(self, dummy_llm: LLM) -> None: "min_length": task.min_length, "max_length": task.max_length, "seed": task.seed, + "use_default_structured_output": False, "runtime_parameters_info": [ { "name": "resources", diff --git a/tests/unit/steps/tasks/evol_quality/test_base.py b/tests/unit/steps/tasks/evol_quality/test_base.py index 0e41ba29f1..9b45b3d3ef 100644 --- a/tests/unit/steps/tasks/evol_quality/test_base.py +++ b/tests/unit/steps/tasks/evol_quality/test_base.py @@ -105,6 +105,7 @@ def test_serialization(self, dummy_llm: LLM) -> None: "input_batch_size": task.input_batch_size, "llm": { "generation_kwargs": {}, + "structured_output": None, "type_info": { "module": task.llm.__module__, "name": task.llm.__class__.__name__, @@ -117,6 +118,7 @@ def test_serialization(self, dummy_llm: LLM) -> None: "group_generations": task.group_generations, "include_original_response": task.include_original_response, "seed": task.seed, + "use_default_structured_output": False, "runtime_parameters_info": [ { "name": "resources", diff --git a/tests/unit/steps/tasks/magpie/test_base.py b/tests/unit/steps/tasks/magpie/test_base.py index aa9a8cbab5..208dc817a8 100644 --- a/tests/unit/steps/tasks/magpie/test_base.py +++ b/tests/unit/steps/tasks/magpie/test_base.py @@ -423,6 +423,7 @@ def test_serialization(self) -> None: "group_generations": False, "add_raw_output": True, "num_generations": 1, + "use_default_structured_output": False, "runtime_parameters_info": [ { "name": "llm", diff --git a/tests/unit/steps/tasks/magpie/test_generator.py b/tests/unit/steps/tasks/magpie/test_generator.py index 8a85e2e26c..c3d35adcb6 100644 --- a/tests/unit/steps/tasks/magpie/test_generator.py +++ b/tests/unit/steps/tasks/magpie/test_generator.py @@ -80,6 +80,7 @@ def test_serialization(self) -> None: "add_raw_output": True, "num_generations": 1, "num_rows": None, + "use_default_structured_output": False, "runtime_parameters_info": [ { "name": "llm", diff --git a/tests/unit/steps/tasks/test_base.py b/tests/unit/steps/tasks/test_base.py index 5d4c9ddaaa..ac0d17de1d 100644 --- a/tests/unit/steps/tasks/test_base.py +++ b/tests/unit/steps/tasks/test_base.py @@ -320,6 +320,7 @@ def test_serialization(self) -> None: "input_batch_size": 50, "llm": { "generation_kwargs": {}, + "structured_output": None, "type_info": { "module": "tests.unit.conftest", "name": "DummyLLM", @@ -389,6 +390,7 @@ def test_serialization(self) -> None: "module": "tests.unit.steps.tasks.test_base", "name": "DummyTask", }, + "use_default_structured_output": False, } with Pipeline(name="unit-test-pipeline") as pipeline: From 7ff4d2060b858a6e46fe40718321126a1186c11e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Mart=C3=ADn=20Bl=C3=A1zquez?= Date: Tue, 13 Aug 2024 20:12:10 +0200 Subject: [PATCH 12/82] Send as many batches as possible to input queues (#895) * Update `_manage_batch_flow` to send as many batches as can be built * Fix load stages * Fix unit test * Fix `argilla` unit test after release `2.0.1` * Can fail --- src/distilabel/pipeline/base.py | 43 ++++++++++++------- tests/integration/test_load_stages.py | 10 ++--- tests/integration/test_multiple_replicas.py | 11 ++--- tests/unit/pipeline/test_base.py | 1 + tests/unit/steps/argilla/test_preference.py | 4 +- .../steps/argilla/test_text_generation.py | 4 +- 6 files changed, 43 insertions(+), 30 deletions(-) diff --git a/src/distilabel/pipeline/base.py b/src/distilabel/pipeline/base.py index 5d59c6a729..5f4f6afe97 100644 --- a/src/distilabel/pipeline/base.py +++ b/src/distilabel/pipeline/base.py @@ -782,7 +782,9 @@ def _should_continue_processing(self) -> bool: """ return self._batch_manager.can_generate() and not self._stop_called # type: ignore - def _process_batch(self, batch: "_Batch") -> None: + def _process_batch( + self, batch: "_Batch", send_last_batch_flag: bool = True + ) -> None: """Process a batch consumed from the `output_queue`. Args: @@ -798,18 +800,28 @@ def _process_batch(self, batch: "_Batch") -> None: self._write_buffer.add_batch(batch) # type: ignore if batch.last_batch: - _, stages_last_steps = self.dag.get_steps_load_stages() - stage_last_steps = stages_last_steps[self._current_stage] - if batch.step_name in stage_last_steps: - self._stages_last_batch[self._current_stage].append(batch.step_name) - self._stages_last_batch[self._current_stage].sort() + self._register_stages_last_batch(batch) # Make sure to send the `LAST_BATCH_SENT_FLAG` to the predecessors of the step # if the batch is the last one, so they stop their processing loop even if they # haven't received the last batch because of the routing function. - for step_name in self.dag.get_step_predecessors(batch.step_name): - if self._is_step_running(step_name): - self._send_last_batch_flag_to_step(step_name) + if send_last_batch_flag: + for step_name in self.dag.get_step_predecessors(batch.step_name): + if self._is_step_running(step_name): + self._send_last_batch_flag_to_step(step_name) + + def _register_stages_last_batch(self, batch: "_Batch") -> None: + """Registers the last batch received from a step in the `_stages_last_batch` + dictionary. + + Args: + batch: The last batch received from a step. + """ + _, stages_last_steps = self.dag.get_steps_load_stages() + stage_last_steps = stages_last_steps[self._current_stage] + if batch.step_name in stage_last_steps: + self._stages_last_batch[self._current_stage].append(batch.step_name) + self._stages_last_batch[self._current_stage].sort() def _update_stage(self) -> bool: """Checks if the steps of next stage should be loaded and updates `_current_stage` @@ -979,6 +991,9 @@ def _handle_stop(self) -> None: self._consume_output_queue() + if self._should_load_next_stage(): + self._current_stage += 1 + def _wait_step_input_queue_empty(self, step_name: str) -> Union["Queue[Any]", None]: """Waits for the input queue of a step to be empty. @@ -1101,10 +1116,7 @@ def _consume_output_queue(self) -> None: batch = self._output_queue.get() if batch is None: continue - - if batch.step_name in self.dag.leaf_steps: - self._write_buffer.add_batch(batch) # type: ignore - + self._process_batch(batch, send_last_batch_flag=False) self._handle_batch_on_stop(batch) def _manage_batch_flow(self, batch: "_Batch") -> None: @@ -1153,13 +1165,14 @@ def _manage_batch_flow(self, batch: "_Batch") -> None: # If successor step has enough data in its buffer to create a new batch, then # send the batch to the step. - if new_batch := self._batch_manager.get_batch(successor): + while new_batch := self._batch_manager.get_batch(successor): self._send_batch_to_step(new_batch) if not step.is_generator: # Step ("this", the one from which the batch was received) has enough data on its # buffers to create a new batch - if new_batch := self._batch_manager.get_batch(step.name): # type: ignore + while new_batch := self._batch_manager.get_batch(step.name): # type: ignore + # if new_batch := self._batch_manager.get_batch(step.name): # type: ignore self._send_batch_to_step(new_batch) else: self._request_more_batches_if_needed(step) diff --git a/tests/integration/test_load_stages.py b/tests/integration/test_load_stages.py index 2079f32ea1..9faa771d77 100644 --- a/tests/integration/test_load_stages.py +++ b/tests/integration/test_load_stages.py @@ -155,10 +155,12 @@ def test_load_stages_status_load_from_cache() -> None: original_process_batch = pipeline._process_batch - def _process_batch_wrapper(batch: "_Batch") -> None: + def _process_batch_wrapper( + batch: "_Batch", send_last_batch_flag: bool = True + ) -> None: if batch.step_name == group_1.name and batch.seq_no == 10: pipeline._stop_called = True - original_process_batch(batch) + original_process_batch(batch, send_last_batch_flag) # Run first time and stop the pipeline when specific batch received (simulate CTRL + C) with mock.patch.object(pipeline, "_process_batch", _process_batch_wrapper): @@ -167,7 +169,3 @@ def _process_batch_wrapper(batch: "_Batch") -> None: distiset = pipeline.run(use_cache=True) assert len(distiset["default"]["train"]) == 1000 - - -if __name__ == "__main__": - test_load_stages_status_load_from_cache() diff --git a/tests/integration/test_multiple_replicas.py b/tests/integration/test_multiple_replicas.py index 59950a4374..0f7226d0f3 100644 --- a/tests/integration/test_multiple_replicas.py +++ b/tests/integration/test_multiple_replicas.py @@ -14,20 +14,16 @@ import random import time -from typing import TYPE_CHECKING, List +from typing import TYPE_CHECKING -from distilabel.pipeline import Pipeline, routing_batch_function +import pytest +from distilabel.pipeline import Pipeline from distilabel.steps import LoadDataFromDicts, StepInput, StepResources, step if TYPE_CHECKING: from distilabel.steps.typing import StepOutput -@routing_batch_function() -def random_routing_batch(steps: List[str]) -> List[str]: - return random.sample(steps, 2) - - @step(outputs=["generation"]) def Generate(inputs: StepInput) -> "StepOutput": # random sleep to simulate processing time @@ -57,6 +53,7 @@ def CombineGenerations(*inputs: StepInput) -> "StepOutput": yield combined_list +@pytest.mark.xfail def test_multiple_replicas() -> None: with Pipeline(name="test") as pipeline: load_dataset = LoadDataFromDicts( diff --git a/tests/unit/pipeline/test_base.py b/tests/unit/pipeline/test_base.py index edd79d2efb..dc1bd99446 100644 --- a/tests/unit/pipeline/test_base.py +++ b/tests/unit/pipeline/test_base.py @@ -352,6 +352,7 @@ def test_handle_stop(self) -> None: pipeline._add_batches_back_to_batch_manager = mock.MagicMock() pipeline._wait_step_input_queue_empty = mock.MagicMock() pipeline._consume_output_queue = mock.MagicMock() + pipeline._stages_last_batch = [[]] pipeline._handle_stop() diff --git a/tests/unit/steps/argilla/test_preference.py b/tests/unit/steps/argilla/test_preference.py index 1de2c5a466..f893c8a4c9 100644 --- a/tests/unit/steps/argilla/test_preference.py +++ b/tests/unit/steps/argilla/test_preference.py @@ -13,6 +13,7 @@ # limitations under the License. import os +from unittest import mock from unittest.mock import patch import argilla as rg @@ -23,7 +24,8 @@ @pytest.fixture def mock_dataset() -> rg.Dataset: # type: ignore - client = rg.Argilla(api_url="", api_key="") + rg.Argilla._validate_connection = mock.MagicMock() # type: ignore + client = rg.Argilla(api_url="https://example.com", api_key="") return rg.Dataset( name="dataset", settings=rg.Settings( diff --git a/tests/unit/steps/argilla/test_text_generation.py b/tests/unit/steps/argilla/test_text_generation.py index bcf3511376..5ed9347291 100644 --- a/tests/unit/steps/argilla/test_text_generation.py +++ b/tests/unit/steps/argilla/test_text_generation.py @@ -13,6 +13,7 @@ # limitations under the License. import os +from unittest import mock from unittest.mock import patch import argilla as rg @@ -23,7 +24,8 @@ @pytest.fixture def mock_dataset() -> rg.Dataset: - client = rg.Argilla(api_url="", api_key="") + rg.Argilla._validate_connection = mock.MagicMock() # type: ignore + client = rg.Argilla(api_url="https://example.com", api_key="") return rg.Dataset( name="dataset", settings=rg.Settings( From 04d0bf0a8b3a27d07bfa3278b8ce2ecdf3fa1590 Mon Sep 17 00:00:00 2001 From: Agus Date: Tue, 13 Aug 2024 20:31:32 +0200 Subject: [PATCH 13/82] Exclude `repo_id` from `LoadDataFromFileSystem` (#898) * Exclude repo_id from LoadDataFromFileSystem generator class and update tests * Update code to be compatible with python 3.9 --- .../steps/generators/huggingface.py | 14 ++++++++--- .../unit/steps/generators/test_huggingface.py | 23 ++++++++++++------- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/src/distilabel/steps/generators/huggingface.py b/src/distilabel/steps/generators/huggingface.py index b31b9fbadc..b2add099b1 100644 --- a/src/distilabel/steps/generators/huggingface.py +++ b/src/distilabel/steps/generators/huggingface.py @@ -17,6 +17,7 @@ from pathlib import Path from typing import ( TYPE_CHECKING, + Annotated, Any, Dict, List, @@ -24,6 +25,7 @@ Optional, Sequence, Tuple, + TypeVar, Union, ) @@ -46,6 +48,13 @@ from distilabel.steps.typing import GeneratorStepOutput +T = TypeVar("T") + +# To avoid using repo_id in LoadDataFromFileSystem: +# https://github.com/pydantic/pydantic/discussions/7076#discussioncomment-6699138 +ExcludedField = Annotated[T, Field(exclude=True)] + + class LoadDataFromHub(GeneratorStep): """Loads a dataset from the Hugging Face Hub. @@ -334,6 +343,7 @@ class LoadDataFromFileSystem(LoadDataFromHub): default=None, description="The expected filetype. If not provided, it will be inferred from the file extension.", ) + repo_id: ExcludedField[Union[str, None]] = None def load(self) -> None: """Load the dataset from the file/s in disk.""" @@ -416,9 +426,7 @@ def outputs(self) -> List[str]: """ # We assume there are Dataset/IterableDataset, not it's ...Dict counterparts if self._dataset is None: - raise ValueError( - "Dataset not loaded yet, you must call `load` method first." - ) + self.load() return self._dataset.column_names diff --git a/tests/unit/steps/generators/test_huggingface.py b/tests/unit/steps/generators/test_huggingface.py index 280053c8cb..f1dd55a450 100644 --- a/tests/unit/steps/generators/test_huggingface.py +++ b/tests/unit/steps/generators/test_huggingface.py @@ -27,6 +27,8 @@ LoadDataFromHub, ) +from tests.unit.pipeline.utils import DummyStep1 + DISTILABEL_RUN_SLOW_TESTS = os.getenv("DISTILABEL_RUN_SLOW_TESTS", False) @@ -133,18 +135,23 @@ def test_read_from_jsonl_with_nested_folder( assert isinstance(generator_step_output[1], bool) assert len(generator_step_output[0]) == 22 - @pytest.mark.parametrize("load", [True, False]) - def test_outputs(self, load: bool) -> None: + def test_outputs(self) -> None: loader = LoadDataFromFileSystem( filetype="json", data_files=str(Path(__file__).parent / "sample_functions.jsonl"), ) - if load: - loader.load() - assert loader.outputs == ["type", "function"] - else: - with pytest.raises(ValueError): - loader.outputs # noqa: B018 + loader.load() + assert loader.outputs == ["type", "function"] + + def test_loading_in_pipeline(self): + with Pipeline(): + loader = LoadDataFromFileSystem( + filetype="json", + data_files=str(Path(__file__).parent / "sample_functions.jsonl"), + ) + dummy = DummyStep1(input_mappings={"instruction": "function"}) + loader >> dummy + assert loader.outputs == ["type", "function"] class TestLoadDataFromDisk: From f382f1ceb72096013a7dfa7d85d4376fff8b66c3 Mon Sep 17 00:00:00 2001 From: Agus Date: Wed, 14 Aug 2024 08:51:02 +0200 Subject: [PATCH 14/82] Fix loader to read from a glob pattern (#877) * Fix loader to read from a glob pattern * Fix to read from general UPath instead of Path * Update tests to use glob patterns * Refactor to simplify check for glob pattern --- .../steps/generators/huggingface.py | 24 +++++++++++++++++-- .../unit/steps/generators/test_huggingface.py | 4 ++-- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/distilabel/steps/generators/huggingface.py b/src/distilabel/steps/generators/huggingface.py index b2add099b1..243277fd49 100644 --- a/src/distilabel/steps/generators/huggingface.py +++ b/src/distilabel/steps/generators/huggingface.py @@ -333,6 +333,23 @@ class LoadDataFromFileSystem(LoadDataFromHub): # >>> result # ([{'type': 'function', 'function':...', False) ``` + + Load data passing a glob pattern: + + ```python + from distilabel.steps import LoadDataFromFileSystem + + loader = LoadDataFromFileSystem( + data_files="path/to/dataset/*.jsonl", + streaming=True + ) + loader.load() + + # Just like we saw with LoadDataFromDicts, the `process` method will yield batches. + result = next(loader.process()) + # >>> result + # ([{'type': 'function', 'function':...', False) + ``` """ data_files: RuntimeParameter[Union[str, Path]] = Field( @@ -376,7 +393,7 @@ def load(self) -> None: self.num_examples = len(self._dataset) @staticmethod - def _prepare_data_files( + def _prepare_data_files( # noqa: C901 data_path: UPath, ) -> Tuple[Union[str, Sequence[str], Mapping[str, Union[str, Sequence[str]]]], str]: """Prepare the loading process by setting the `data_files` attribute. @@ -394,9 +411,12 @@ def get_filetype(data_path: UPath) -> str: filetype = "json" return filetype - if data_path.is_file(): + if data_path.is_file() or ( + len(str(data_path.parent.glob(data_path.name))) >= 1 + ): filetype = get_filetype(data_path) data_files = str(data_path) + elif data_path.is_dir(): file_sequence = [] file_map = defaultdict(list) diff --git a/tests/unit/steps/generators/test_huggingface.py b/tests/unit/steps/generators/test_huggingface.py index f1dd55a450..281d5187e2 100644 --- a/tests/unit/steps/generators/test_huggingface.py +++ b/tests/unit/steps/generators/test_huggingface.py @@ -104,7 +104,7 @@ def test_read_from_jsonl_with_folder(self, filetype: Union[str, None]) -> None: loader = LoadDataFromFileSystem( filetype=filetype, - data_files=tmpdir, + data_files=str(Path(tmpdir) / "*.jsonl"), ) loader.load() generator_step_output = next(loader.process()) @@ -127,7 +127,7 @@ def test_read_from_jsonl_with_nested_folder( loader = LoadDataFromFileSystem( filetype=filetype, - data_files=tmpdir, + data_files=str(Path(tmpdir) / "**/*.jsonl"), ) loader.load() generator_step_output = next(loader.process()) From c8df5a99e064affa3465a335a408e7b4b0f01976 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Mart=C3=ADn=20Bl=C3=A1zquez?= Date: Wed, 14 Aug 2024 11:12:27 +0200 Subject: [PATCH 15/82] Add `save_artifact` method to `_Step` (#871) * Add `save_artifact` method * Upload pipeline generated artifacts * Fix log file was being saved in different cache * Update `save_to_disk` to also save artifacts * Render artifacts in card * Update unit tests * Add missing unit tests * Update src/distilabel/distiset.py Co-authored-by: Agus * Add section about saving artifacts * Add correct `edit_uri` --------- Co-authored-by: Agus --- .../saving_step_generated_artifacts.md | 123 ++++++++++++++++++ mkdocs.yml | 2 + src/distilabel/constants.py | 10 ++ src/distilabel/distiset.py | 123 +++++++++++++++--- src/distilabel/pipeline/base.py | 54 +++++--- src/distilabel/pipeline/constants.py | 22 ---- src/distilabel/steps/base.py | 75 ++++++++++- src/distilabel/steps/constants.py | 17 --- .../steps/embeddings/nearest_neighbour.py | 22 +++- .../utils/card/distilabel_template.md | 15 +++ tests/unit/pipeline/test_base.py | 23 +++- tests/unit/pipeline/test_dag.py | 2 +- tests/unit/pipeline/test_write_buffer.py | 36 +++-- tests/unit/steps/test_base.py | 46 ++++++- tests/unit/test_distiset.py | 95 ++++++++++++-- 15 files changed, 557 insertions(+), 108 deletions(-) create mode 100644 docs/sections/how_to_guides/advanced/saving_step_generated_artifacts.md delete mode 100644 src/distilabel/pipeline/constants.py delete mode 100644 src/distilabel/steps/constants.py diff --git a/docs/sections/how_to_guides/advanced/saving_step_generated_artifacts.md b/docs/sections/how_to_guides/advanced/saving_step_generated_artifacts.md new file mode 100644 index 0000000000..9e89f07491 --- /dev/null +++ b/docs/sections/how_to_guides/advanced/saving_step_generated_artifacts.md @@ -0,0 +1,123 @@ +# Saving step generated artifacts + +Some `Step`s might need to produce an auxiliary artifact that is not a result of the computation, but is needed for the computation. For example, the [`FaissNearestNeighbour`](/distilabel/components-gallery/steps/faissnearestneighbour/) needs to create a Faiss index to compute the output of the step which are the top `k` nearest neighbours for each input. Generating the Faiss index takes time and it could potentially be reused outside of the `distilabel` pipeline, so it would be a shame not saving it. + +For this reason, `Step`s have a method called `save_artifact` that allows saving artifacts that will be included along the outputs of the pipeline in the generated [`Distiset`][distilabel.distiset.Distiset]. The generated artifacts will be uploaded and saved when using `Distiset.push_to_hub` or `Distiset.save_to_disk` respectively. Let's see how to use it with a simple example. + +```python +from typing import List, TYPE_CHECKING +from distilabel.steps import GlobalStep, StepInput, StepOutput +import matplotlib.pyplot as plt + +if TYPE_CHECKING: + from distilabel.steps import StepOutput + + +class CountTextCharacters(GlobalStep): + @property + def inputs(self) -> List[str]: + return ["text"] + + @property + def outputs(self) -> List[str]: + return ["text_character_count"] + + def process(self, inputs: StepInput) -> "StepOutput": # type: ignore + character_counts = [] + + for input in inputs: + text_character_count = len(input["text"]) + input["text_character_count"] = text_character_count + character_counts.append(text_character_count) + + # Generate plot with the distribution of text character counts + plt.figure(figsize=(10, 6)) + plt.hist(character_counts, bins=30, edgecolor="black") + plt.title("Distribution of Text Character Counts") + plt.xlabel("Character Count") + plt.ylabel("Frequency") + + # Save the plot as an artifact of the step + self.save_artifact( + name="text_character_count_distribution", + write_function=lambda path: plt.savefig(path / "figure.png"), + metadata={"type": "image", "library": "matplotlib"}, + ) + + plt.close() + + yield inputs +``` + +As it can be seen in the example above, we have created a simple step that counts the number of characters in each input text and generates a histogram with the distribution of the character counts. We save the histogram as an artifact of the step using the `save_artifact` method. The method takes three arguments: + +- `name`: The name we want to give to the artifact. +- `write_function`: A function that writes the artifact to the desired path. The function will receive a `path` argument which is a `pathlib.Path` object pointing to the directory where the artifact should be saved. +- `metadata`: A dictionary with metadata about the artifact. This metadata will be saved along with the artifact. + +Let's execute the step with a simple pipeline and push the resulting `Distiset` to the Hugging Face Hub: + +??? "Example full code" + + ```python + from typing import TYPE_CHECKING, List + + import matplotlib.pyplot as plt + from datasets import load_dataset + from distilabel.pipeline import Pipeline + from distilabel.steps import GlobalStep, StepInput, StepOutput + + if TYPE_CHECKING: + from distilabel.steps import StepOutput + + + class CountTextCharacters(GlobalStep): + @property + def inputs(self) -> List[str]: + return ["text"] + + @property + def outputs(self) -> List[str]: + return ["text_character_count"] + + def process(self, inputs: StepInput) -> "StepOutput": # type: ignore + character_counts = [] + + for input in inputs: + text_character_count = len(input["text"]) + input["text_character_count"] = text_character_count + character_counts.append(text_character_count) + + # Generate plot with the distribution of text character counts + plt.figure(figsize=(10, 6)) + plt.hist(character_counts, bins=30, edgecolor="black") + plt.title("Distribution of Text Character Counts") + plt.xlabel("Character Count") + plt.ylabel("Frequency") + + # Save the plot as an artifact of the step + self.save_artifact( + name="text_character_count_distribution", + write_function=lambda path: plt.savefig(path / "figure.png"), + metadata={"type": "image", "library": "matplotlib"}, + ) + + plt.close() + + yield inputs + + + with Pipeline() as pipeline: + count_text_characters = CountTextCharacters() + + if __name__ == "__main__": + distiset = pipeline.run( + dataset=load_dataset( + "HuggingFaceH4/instruction-dataset", split="test" + ).rename_column("prompt", "text"), + ) + + distiset.push_to_hub("distilabel-internal-testing/distilabel-artifacts-example") + ``` + +The generated [distilabel-internal-testing/distilabel-artifacts-example](https://huggingface.co/datasets/distilabel-internal-testing/distilabel-artifacts-example) dataset repository has a section in its card [describing the artifacts generated by the pipeline](https://huggingface.co/datasets/distilabel-internal-testing/distilabel-artifacts-example#artifacts) and the generated plot can be seen [here](https://huggingface.co/datasets/distilabel-internal-testing/distilabel-artifacts-example/blob/main/artifacts/count_text_characters_0/text_character_count_distribution/figure.png). diff --git a/mkdocs.yml b/mkdocs.yml index 8808f27a46..082c7ef27a 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -7,6 +7,7 @@ site_description: Distilabel is an AI Feedback (AIF) framework for building data # Repository repo_name: argilla-io/distilabel repo_url: https://github.com/argilla-io/distilabel +edit_uri: edit/main/docs/ extra: version: @@ -179,6 +180,7 @@ nav: - Using CLI to explore and re-run existing Pipelines: "sections/how_to_guides/advanced/cli/index.md" - Using a file system to pass data of batches between steps: "sections/how_to_guides/advanced/fs_to_pass_data.md" - Assigning resources to a step: "sections/how_to_guides/advanced/assigning_resources_to_step.md" + - Saving step generated artifacts: "sections/how_to_guides/advanced/saving_step_generated_artifacts.md" - Serving an LLM for sharing it between several tasks: "sections/how_to_guides/advanced/serving_an_llm_for_reuse.md" - Scaling and distributing a pipeline with Ray: "sections/how_to_guides/advanced/scaling_with_ray.md" - Pipeline Samples: diff --git a/src/distilabel/constants.py b/src/distilabel/constants.py index a1400bcd03..bd636f8165 100644 --- a/src/distilabel/constants.py +++ b/src/distilabel/constants.py @@ -25,6 +25,16 @@ CONVERGENCE_STEP_ATTR_NAME: Final[str] = "convergence_step" LAST_BATCH_SENT_FLAG: Final[str] = "last_batch_sent" +# Data paths constants +STEPS_OUTPUTS_PATH = "steps_outputs" +STEPS_ARTIFACTS_PATH = "steps_artifacts" + +# Distiset related constants +DISTISET_CONFIG_FOLDER: Final[str] = "distiset_configs" +DISTISET_ARTIFACTS_FOLDER: Final[str] = "artifacts" +PIPELINE_CONFIG_FILENAME: Final[str] = "pipeline.yaml" +PIPELINE_LOG_FILENAME: Final[str] = "pipeline.log" + __all__ = [ "STEP_ATTR_NAME", diff --git a/src/distilabel/distiset.py b/src/distilabel/distiset.py index 92eedecfca..2263bc1553 100644 --- a/src/distilabel/distiset.py +++ b/src/distilabel/distiset.py @@ -12,24 +12,34 @@ # See the License for the specific language governing permissions and # limitations under the License. +import json import logging import os.path as posixpath import re import sys +from collections import defaultdict from os import PathLike from pathlib import Path -from typing import TYPE_CHECKING, Any, Dict, Final, List, Optional, Union +from typing import TYPE_CHECKING, Any, Dict, Generator, List, Optional, Union import fsspec import yaml from datasets import Dataset, load_dataset, load_from_disk from datasets.filesystems import is_remote_filesystem -from huggingface_hub import DatasetCardData, HfApi, upload_file +from huggingface_hub import DatasetCardData, HfApi, upload_file, upload_folder from huggingface_hub.file_download import hf_hub_download from pyarrow.lib import ArrowInvalid from typing_extensions import Self -from distilabel.constants import STEP_ATTR_NAME +from distilabel.constants import ( + DISTISET_ARTIFACTS_FOLDER, + DISTISET_CONFIG_FOLDER, + PIPELINE_CONFIG_FILENAME, + PIPELINE_LOG_FILENAME, + STEP_ATTR_NAME, + STEPS_ARTIFACTS_PATH, + STEPS_OUTPUTS_PATH, +) from distilabel.utils.card.dataset_card import ( DistilabelDatasetCard, size_categories_parser, @@ -42,11 +52,6 @@ from distilabel.pipeline._dag import DAG -DISTISET_CONFIG_FOLDER: Final[str] = "distiset_configs" -PIPELINE_CONFIG_FILENAME: Final[str] = "pipeline.yaml" -PIPELINE_LOG_FILENAME: Final[str] = "pipeline.log" - - class Distiset(dict): """Convenient wrapper around `datasets.Dataset` to push to the Hugging Face Hub. @@ -54,12 +59,18 @@ class Distiset(dict): `DAG` and the values are `datasets.Dataset`. Attributes: - pipeline_path: Optional path to the pipeline.yaml file that generated the dataset. - log_filename_path: Optional path to the pipeline.log file that generated was written by the - pipeline. + _pipeline_path: Optional path to the `pipeline.yaml` file that generated the dataset. + Defaults to `None`. + _artifacts_path: Optional path to the directory containing the generated artifacts + by the pipeline steps. Defaults to `None`. + _log_filename_path: Optional path to the `pipeline.log` file that generated was written + by the pipeline. Defaults to `None`. + _citations: Optional list containing citations that will be included in the dataset + card. Defaults to `None`. """ _pipeline_path: Optional[Path] = None + _artifacts_path: Optional[Path] = None _log_filename_path: Optional[Path] = None _citations: Optional[List[str]] = None @@ -121,6 +132,16 @@ def push_to_hub( **kwargs, ) + if self.artifacts_path: + upload_folder( + repo_id=repo_id, + folder_path=self.artifacts_path, + path_in_repo="artifacts", + token=token, + repo_type="dataset", + commit_message="Include pipeline artifacts", + ) + if include_script and script_path.exists(): upload_file( path_or_fileobj=script_path, @@ -128,7 +149,7 @@ def push_to_hub( repo_id=repo_id, repo_type="dataset", token=token, - commit_message="Include pipeline script.", + commit_message="Include pipeline script", ) if generate_card: @@ -185,11 +206,38 @@ def _get_card( sample_records=sample_records, include_script=include_script, filename_py=filename_py, + artifacts=self._get_artifacts_metadata(), references=self.citations, ) return card + def _get_artifacts_metadata(self) -> Dict[str, List[Dict[str, Any]]]: + """Gets a dictionary with the metadata of the artifacts generated by the pipeline steps. + + Returns: + A dictionary in which the key is the name of the step and the value is a list + of dictionaries, each of them containing the name and metadata of the step artifact. + """ + if not self.artifacts_path: + return {} + + def iterdir_ignore_hidden(path: Path) -> Generator[Path, None, None]: + return (f for f in Path(path).iterdir() if not f.name.startswith(".")) + + artifacts_metadata = defaultdict(list) + for step_artifacts_dir in iterdir_ignore_hidden(self.artifacts_path): + step_name = step_artifacts_dir.stem + for artifact_dir in iterdir_ignore_hidden(step_artifacts_dir): + artifact_name = artifact_dir.stem + metadata_path = artifact_dir / "metadata.json" + metadata = json.loads(metadata_path.read_text()) + artifacts_metadata[step_name].append( + {"name": artifact_name, "metadata": metadata} + ) + + return dict(artifacts_metadata) + def _extract_readme_metadata( self, repo_id: str, token: Optional[str] ) -> Dict[str, Any]: @@ -243,6 +291,7 @@ def _generate_card( repo_type="dataset", token=token, ) + if self.pipeline_path: # If the pipeline.yaml is available, upload it to the Hugging Face Hub as well. HfApi().upload_file( @@ -252,6 +301,7 @@ def _generate_card( repo_type="dataset", token=token, ) + if self.log_filename_path: # The same we had with "pipeline.yaml" but with the log file. HfApi().upload_file( @@ -360,6 +410,12 @@ def save_to_disk( ) fs.makedirs(distiset_config_folder, exist_ok=True) + if self.artifacts_path: + distiset_artifacts_folder = posixpath.join( + distiset_path, DISTISET_ARTIFACTS_FOLDER + ) + fs.copy(str(self.artifacts_path), distiset_artifacts_folder, recursive=True) + if save_card: # NOTE: Currently the card is not the same if we write to disk or push to the HF hub, # as we aren't generating the README copying/updating the data from the dataset repo. @@ -415,7 +471,7 @@ def load_from_disk( original_distiset_path = str(distiset_path) fs: fsspec.AbstractFileSystem - fs, _, [distiset_path] = fsspec.get_fs_token_paths( + fs, _, [distiset_path] = fsspec.get_fs_token_paths( # type: ignore original_distiset_path, storage_options=storage_options ) dest_distiset_path = distiset_path @@ -425,6 +481,7 @@ def load_from_disk( ), "`distiset_path` must be a `PathLike` object pointing to a folder or a URI of a remote filesystem." has_config = False + has_artifacts = False distiset = cls() if is_remote_filesystem(fs): @@ -432,19 +489,23 @@ def load_from_disk( if download_dir: dest_distiset_path = download_dir else: - dest_distiset_path = Dataset._build_local_temp_path(src_dataset_path) - fs.download(src_dataset_path, dest_distiset_path.as_posix(), recursive=True) + dest_distiset_path = Dataset._build_local_temp_path(src_dataset_path) # type: ignore + fs.download(src_dataset_path, dest_distiset_path.as_posix(), recursive=True) # type: ignore # Now we should have the distiset locally, so we can read those files for folder in Path(dest_distiset_path).iterdir(): if folder.stem == DISTISET_CONFIG_FOLDER: has_config = True continue + elif folder.stem == DISTISET_ARTIFACTS_FOLDER: + has_artifacts = True + continue distiset[folder.stem] = load_from_disk( str(folder), keep_in_memory=keep_in_memory, ) - # From the config folder we just need to point to the files. Once downloaded we set the path + + # From the config folder we just need to point to the files. Once downloaded we set the path to point to point to the files. Once downloaded we set the path # to wherever they are. if has_config: distiset_config_folder = posixpath.join( @@ -463,6 +524,11 @@ def load_from_disk( if Path(log_filename_path).exists(): distiset.log_filename_path = Path(log_filename_path) + if has_artifacts: + distiset.artifacts_path = Path( + posixpath.join(dest_distiset_path, DISTISET_ARTIFACTS_FOLDER) + ) + return distiset @property @@ -474,6 +540,16 @@ def pipeline_path(self) -> Union[Path, None]: def pipeline_path(self, path: PathLike) -> None: self._pipeline_path = Path(path) + @property + def artifacts_path(self) -> Union[Path, None]: + """Returns the path to the directory containing the artifacts generated by the steps + of the pipeline.""" + return self._artifacts_path + + @artifacts_path.setter + def artifacts_path(self, path: PathLike) -> None: + self._artifacts_path = Path(path) + @property def log_filename_path(self) -> Union[Path, None]: """Returns the path to the `pipeline.log` file that generated the `Pipeline`.""" @@ -540,10 +616,10 @@ def create_distiset( # noqa: C901 logger = logging.getLogger("distilabel.distiset") - data_dir = Path(data_dir) + steps_outputs_dir = data_dir / STEPS_OUTPUTS_PATH distiset = Distiset() - for file in data_dir.iterdir(): + for file in steps_outputs_dir.iterdir(): if file.is_file(): continue @@ -569,19 +645,26 @@ def create_distiset( # noqa: C901 if len(distiset.keys()) == 1: distiset["default"] = distiset.pop(list(distiset.keys())[0]) + # If there's any artifact set the `artifacts_path` so they can be uploaded + steps_artifacts_dir = data_dir / STEPS_ARTIFACTS_PATH + if any(steps_artifacts_dir.rglob("*")): + distiset.artifacts_path = steps_artifacts_dir + + # Include `pipeline.yaml` if exists if pipeline_path: distiset.pipeline_path = pipeline_path else: # If the pipeline path is not provided, try to find it in the parent directory # and assume that's the wanted file. - pipeline_path = data_dir.parent / "pipeline.yaml" + pipeline_path = steps_outputs_dir.parent / "pipeline.yaml" if pipeline_path.exists(): distiset.pipeline_path = pipeline_path + # Include `pipeline.log` if exists if log_filename_path: distiset.log_filename_path = log_filename_path else: - log_filename_path = data_dir.parent / "pipeline.log" + log_filename_path = steps_outputs_dir.parent / "pipeline.log" if log_filename_path.exists(): distiset.log_filename_path = log_filename_path diff --git a/src/distilabel/pipeline/base.py b/src/distilabel/pipeline/base.py index 5f4f6afe97..911273c2c9 100644 --- a/src/distilabel/pipeline/base.py +++ b/src/distilabel/pipeline/base.py @@ -46,6 +46,8 @@ RECEIVES_ROUTED_BATCHES_ATTR_NAME, ROUTING_BATCH_FUNCTION_ATTR_NAME, STEP_ATTR_NAME, + STEPS_ARTIFACTS_PATH, + STEPS_OUTPUTS_PATH, ) from distilabel.distiset import create_distiset from distilabel.mixins.requirements import RequirementsMixin @@ -128,6 +130,8 @@ def get_pipeline(cls) -> Union["BasePipeline", None]: _STEP_LOAD_FAILED_CODE = -666 _STEP_NOT_LOADED_CODE = -999 +_ATTRIBUTES_IGNORED_CACHE = ("disable_cuda_device_placement",) + class BasePipeline(ABC, RequirementsMixin, _Serializable): """Base class for a `distilabel` pipeline. @@ -257,14 +261,14 @@ def _create_signature(self) -> str: [ f"{str(k)}={str(v)}" for k, v in value.items() - if k not in ("disable_cuda_device_placement",) + if k not in _ATTRIBUTES_IGNORED_CACHE ] ) elif isinstance(value, (list, tuple)): # runtime_parameters_info step_info += "-".join([str(v) for v in value]) elif isinstance(value, (int, str, float, bool)): - if argument != "disable_cuda_device_placement": + if argument not in _ATTRIBUTES_IGNORED_CACHE: # batch_size/name step_info += str(value) else: @@ -340,17 +344,19 @@ def run( # cache when the pipeline is run, so it's important to do it first. self._set_runtime_parameters(parameters or {}) + if dataset is not None: + self._add_dataset_generator_step(dataset) + setup_logging( log_queue=self._log_queue, filename=str(self._cache_location["log_file"]) ) - if dataset is not None: - self._add_dataset_generator_step(dataset) - # Validate the pipeline DAG to check that all the steps are chainable, there are # no missing runtime parameters, batch sizes are correct, etc. self.dag.validate() + self._set_pipeline_artifacts_path_in_steps() + # Set the initial load status for all the steps self._init_steps_load_status() @@ -360,12 +366,8 @@ def run( # Load the `_BatchManager` from cache or create one from scratch self._load_batch_manager(use_cache) - if to_install := self.requirements_to_install(): - # Print the list of requirements like they would appear in a requirements.txt - to_install_list = "\n" + "\n".join(to_install) - msg = f"Please install the following requirements to run the pipeline: {to_install_list}" - self._logger.error(msg) - raise ModuleNotFoundError(msg) + # Check pipeline requirements are installed + self._check_requirements() # Setup the filesystem that will be used to pass the data of the `_Batch`es self._setup_fsspec(storage_parameters) @@ -383,7 +385,7 @@ def run( " Returning `Distiset` from cache data..." ) distiset = create_distiset( - self._cache_location["data"], + data_dir=self._cache_location["data"], pipeline_path=self._cache_location["pipeline"], log_filename_path=self._cache_location["log_file"], enable_metadata=self._enable_metadata, @@ -450,8 +452,9 @@ def _add_dataset_generator_step(self, dataset: "InputDataset") -> None: step = self.dag.get_step(step_name)[STEP_ATTR_NAME] if isinstance(step_name, GeneratorStep): raise ValueError( - "There is already a `GeneratorStep` in the pipeline, you can either pass a `dataset` to the " - f"run method, or create a `GeneratorStep` explictly. `GeneratorStep`: {step}" + "There is already a `GeneratorStep` in the pipeline, you can either" + " pass a `dataset` to the run method, or create a `GeneratorStep` explictly." + f" `GeneratorStep`: {step}" ) loader = make_generator_step(dataset) self.dag.add_root_step(loader) @@ -475,6 +478,27 @@ def _init_steps_load_status(self) -> None: for step_name in self.dag: self._steps_load_status[step_name] = _STEP_NOT_LOADED_CODE + def _set_pipeline_artifacts_path_in_steps(self) -> None: + """Sets the attribute `_pipeline_artifacts_path` in all the `Step`s of the pipeline, + so steps can use it to get the path to save the generated artifacts.""" + artifacts_path = self._cache_location["data"] / STEPS_ARTIFACTS_PATH + for name in self.dag: + step: "_Step" = self.dag.get_step(name)[STEP_ATTR_NAME] + step.set_pipeline_artifacts_path(path=artifacts_path) + + def _check_requirements(self) -> None: + """Checks if the dependencies required to run the pipeline are installed. + + Raises: + ModuleNotFoundError: if one or more requirements are missing. + """ + if to_install := self.requirements_to_install(): + # Print the list of requirements like they would appear in a requirements.txt + to_install_list = "\n" + "\n".join(to_install) + msg = f"Please install the following requirements to run the pipeline: {to_install_list}" + self._logger.error(msg) + raise ModuleNotFoundError(msg) + def _setup_fsspec( self, storage_parameters: Optional[Dict[str, Any]] = None ) -> None: @@ -696,7 +720,7 @@ def _setup_write_buffer(self) -> None: """Setups the `_WriteBuffer` that will store the data of the leaf steps of the pipeline while running, so the `Distiset` can be created at the end. """ - buffer_data_path = self._cache_location["data"] + buffer_data_path = self._cache_location["data"] / STEPS_OUTPUTS_PATH self._logger.info(f"📝 Pipeline data will be written to '{buffer_data_path}'") self._write_buffer = _WriteBuffer(buffer_data_path, self.dag.leaf_steps) diff --git a/src/distilabel/pipeline/constants.py b/src/distilabel/pipeline/constants.py deleted file mode 100644 index 3d400e4a1b..0000000000 --- a/src/distilabel/pipeline/constants.py +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright 2023-present, Argilla, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -from typing import Final - -STEP_ATTR_NAME: Final[str] = "step" -INPUT_QUEUE_ATTR_NAME: Final[str] = "input_queue" -RECEIVES_ROUTED_BATCHES_ATTR_NAME: Final[str] = "receives_routed_batches" -ROUTING_BATCH_FUNCTION_ATTR_NAME: Final[str] = "routing_batch_function" -CONVERGENCE_STEP_ATTR_NAME: Final[str] = "convergence_step" -LAST_BATCH_SENT_FLAG: Final[str] = "last_batch_sent" diff --git a/src/distilabel/steps/base.py b/src/distilabel/steps/base.py index 940b05d812..1aa6e8bb1d 100644 --- a/src/distilabel/steps/base.py +++ b/src/distilabel/steps/base.py @@ -17,7 +17,18 @@ import re from abc import ABC, abstractmethod from functools import cached_property -from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union, overload +from pathlib import Path +from typing import ( + TYPE_CHECKING, + Any, + Callable, + Dict, + List, + Optional, + Tuple, + Union, + overload, +) from pydantic import BaseModel, ConfigDict, Field, PositiveInt, PrivateAttr from typing_extensions import Annotated, Self @@ -27,7 +38,7 @@ RuntimeParameter, RuntimeParametersMixin, ) -from distilabel.utils.serialization import _Serializable +from distilabel.utils.serialization import _Serializable, write_json from distilabel.utils.typing_ import is_parameter_annotated_with if TYPE_CHECKING: @@ -182,6 +193,7 @@ def process(self, inputs: *StepInput) -> StepOutput: input_mappings: Dict[str, str] = {} output_mappings: Dict[str, str] = {} + _pipeline_artifacts_path: Path = PrivateAttr(None) _built_from_decorator: bool = PrivateAttr(default=False) _logger: "Logger" = PrivateAttr(None) @@ -485,6 +497,65 @@ def get_outputs(self) -> List[str]: """ return [self.output_mappings.get(output, output) for output in self.outputs] + def set_pipeline_artifacts_path(self, path: Path) -> None: + """Sets the `_pipeline_artifacts_path` attribute. This method is meant to be used + by the `Pipeline` once the cache location is known. + + Args: + path: the path where the artifacts generated by the pipeline steps should be + saved. + """ + self._pipeline_artifacts_path = path + + @property + def artifacts_directory(self) -> Union[Path, None]: + """Gets the path of the directory where the step should save its generated artifacts. + + Returns: + The path of the directory where the step should save the generated artifacts, + or `None` if `_pipeline_artifacts_path` is not set. + """ + if self._pipeline_artifacts_path is None: + return None + return self._pipeline_artifacts_path / self.name # type: ignore + + def save_artifact( + self, + name: str, + write_function: Callable[[Path], None], + metadata: Optional[Dict[str, Any]] = None, + ) -> None: + """Saves an artifact generated by the `Step`. + + Args: + name: the name of the artifact. + write_function: a function that will receive the path where the artifact should + be saved. + metadata: the artifact metadata. Defaults to `None`. + """ + if self.artifacts_directory is None: + self._logger.warning( + f"Cannot save artifact with '{name}' as `_pipeline_artifacts_path` is not" + " set. This is normal if the `Step` is being executed as a standalone component." + ) + return + + artifact_directory_path = self.artifacts_directory / name + artifact_directory_path.mkdir(parents=True, exist_ok=True) + + self._logger.info(f"🏺 Storing '{name}' generated artifact...") + + self._logger.debug( + f"Calling `write_function` to write artifact in '{artifact_directory_path}'..." + ) + write_function(artifact_directory_path) + + metadata_path = artifact_directory_path / "metadata.json" + self._logger.debug( + f"Calling `write_json` to write artifact metadata in '{metadata_path}'..." + ) + write_json(filename=metadata_path, data=metadata or {}) + def _model_dump(self, obj: Any, **kwargs: Any) -> Dict[str, Any]: dump = super()._model_dump(obj, **kwargs) dump["runtime_parameters_info"] = self.get_runtime_parameters_info() diff --git a/src/distilabel/steps/constants.py b/src/distilabel/steps/constants.py deleted file mode 100644 index 259780e9fd..0000000000 --- a/src/distilabel/steps/constants.py +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright 2023-present, Argilla, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from typing import Final - -DISTILABEL_METADATA_KEY: Final[str] = "distilabel_metadata" diff --git a/src/distilabel/steps/embeddings/nearest_neighbour.py b/src/distilabel/steps/embeddings/nearest_neighbour.py index cba8e293da..6d548bc948 100644 --- a/src/distilabel/steps/embeddings/nearest_neighbour.py +++ b/src/distilabel/steps/embeddings/nearest_neighbour.py @@ -186,6 +186,23 @@ def _build_index(self, inputs: List[Dict[str, Any]]) -> Dataset: ) return dataset + def _save_index(self, dataset: Dataset) -> None: + """Save the generated Faiss index as an artifact of the step. + + Args: + dataset: the dataset with the `faiss` index built. + """ + self.save_artifact( + name="faiss_index", + write_function=lambda path: dataset.save_faiss_index( + index_name="embedding", file=path / "index.faiss" + ), + metadata={ + "num_rows": len(dataset), + "embedding_dim": len(dataset[0]["embedding"]), + }, + ) + def _search(self, dataset: Dataset) -> Dataset: """Search the top `k` nearest neighbours for each row in the dataset. @@ -214,5 +231,6 @@ def add_search_results(examples: Dict[str, List[Any]]) -> Dict[str, List[Any]]: def process(self, inputs: StepInput) -> "StepOutput": # type: ignore dataset = self._build_index(inputs) - dataset = self._search(dataset) - yield dataset.to_list() + dataset_with_search_results = self._search(dataset) + self._save_index(dataset) + yield dataset_with_search_results.to_list() diff --git a/src/distilabel/utils/card/distilabel_template.md b/src/distilabel/utils/card/distilabel_template.md index c51e9a5953..38daa7f857 100644 --- a/src/distilabel/utils/card/distilabel_template.md +++ b/src/distilabel/utils/card/distilabel_template.md @@ -70,6 +70,21 @@ ds = load_dataset("{{ repo_id }}") {% endfor %} +{% if artifacts %} +## Artifacts + +{% for step_name, artifacts in artifacts.items() %} +* **Step**: `{{ step_name }}` + {% for artifact in artifacts %} + * **Artifact name**: `{{ artifact.name }}` + {% for name, value in artifact.metadata.items() %} + * `{{ name }}`: {{ value }} + {% endfor %} + {% endfor %} +{% endfor %} + +{% endif %} + {% if references %} ## References diff --git a/tests/unit/pipeline/test_base.py b/tests/unit/pipeline/test_base.py index dc1bd99446..a2a043f737 100644 --- a/tests/unit/pipeline/test_base.py +++ b/tests/unit/pipeline/test_base.py @@ -21,6 +21,11 @@ from unittest import mock import pytest +from distilabel.constants import ( + INPUT_QUEUE_ATTR_NAME, + LAST_BATCH_SENT_FLAG, + STEPS_ARTIFACTS_PATH, +) from distilabel.mixins.runtime_parameters import RuntimeParameter from distilabel.pipeline.base import ( _STEP_LOAD_FAILED_CODE, @@ -30,7 +35,6 @@ ) from distilabel.pipeline.batch import _Batch from distilabel.pipeline.batch_manager import _BatchManager -from distilabel.pipeline.constants import INPUT_QUEUE_ATTR_NAME, LAST_BATCH_SENT_FLAG from distilabel.pipeline.routing_batch_function import ( routing_batch_function, sample_n_steps, @@ -154,6 +158,23 @@ def test_setup_fsspec_raises_value_error(self) -> None: with pytest.raises(ValueError, match="The 'path' key must be present"): pipeline._setup_fsspec({"key": "random"}) + def test_set_pipeline_artifacts_path_in_steps(self) -> None: + with DummyPipeline(name="dummy") as pipeline: + generator = DummyGeneratorStep() + step = DummyStep1() + step2 = DummyStep1() + step3 = DummyStep2() + + generator >> [step, step2] >> step3 + + pipeline._set_pipeline_artifacts_path_in_steps() + + artifacts_directory = pipeline._cache_location["data"] / STEPS_ARTIFACTS_PATH + assert generator.artifacts_directory == artifacts_directory / generator.name # type: ignore + assert step.artifacts_directory == artifacts_directory / step.name # type: ignore + assert step2.artifacts_directory == artifacts_directory / step2.name # type: ignore + assert step3.artifacts_directory == artifacts_directory / step3.name # type: ignore + def test_init_steps_load_status(self) -> None: with DummyPipeline(name="dummy") as pipeline: generator = DummyGeneratorStep() diff --git a/tests/unit/pipeline/test_dag.py b/tests/unit/pipeline/test_dag.py index 6abfe51fd3..b566de2f1b 100644 --- a/tests/unit/pipeline/test_dag.py +++ b/tests/unit/pipeline/test_dag.py @@ -17,9 +17,9 @@ from typing import TYPE_CHECKING, Any, Callable, Dict, List import pytest +from distilabel.constants import STEP_ATTR_NAME from distilabel.mixins.runtime_parameters import RuntimeParameter from distilabel.pipeline._dag import DAG -from distilabel.pipeline.constants import STEP_ATTR_NAME from distilabel.pipeline.local import Pipeline from distilabel.pipeline.routing_batch_function import routing_batch_function from distilabel.steps.base import GeneratorStep, Step, StepInput, StepResources diff --git a/tests/unit/pipeline/test_write_buffer.py b/tests/unit/pipeline/test_write_buffer.py index a7ae64c91e..2fd552f17e 100644 --- a/tests/unit/pipeline/test_write_buffer.py +++ b/tests/unit/pipeline/test_write_buffer.py @@ -15,6 +15,7 @@ import tempfile from pathlib import Path +from distilabel.constants import STEPS_OUTPUTS_PATH from distilabel.distiset import Distiset, create_distiset from distilabel.pipeline.local import Pipeline from distilabel.pipeline.write_buffer import _WriteBuffer @@ -30,7 +31,8 @@ class TestWriteBuffer: def test_create(self) -> None: with tempfile.TemporaryDirectory() as tmpdirname: - folder = Path(tmpdirname) / "data" + folder = Path(tmpdirname) / "data" / STEPS_OUTPUTS_PATH + steps_outputs = folder / STEPS_OUTPUTS_PATH with Pipeline(name="unit-test-pipeline") as pipeline: dummy_generator_1 = DummyGeneratorStep(name="dummy_generator_step_1") dummy_generator_2 = DummyGeneratorStep(name="dummy_generator_step_2") @@ -43,7 +45,9 @@ def test_create(self) -> None: dummy_step_1.connect(dummy_step_2) dummy_step_1.connect(dummy_step_3) - write_buffer = _WriteBuffer(path=folder, leaf_steps=pipeline.dag.leaf_steps) + write_buffer = _WriteBuffer( + path=steps_outputs, leaf_steps=pipeline.dag.leaf_steps + ) assert write_buffer._buffers == {"dummy_step_2": [], "dummy_step_3": []} assert write_buffer._buffers_dump_batch_size == { @@ -59,6 +63,7 @@ def test_create(self) -> None: def test_write_buffer_one_leaf_step_and_create_dataset(self) -> None: with tempfile.TemporaryDirectory() as tmpdirname: folder = Path(tmpdirname) / "data" + steps_outputs = folder / STEPS_OUTPUTS_PATH with Pipeline(name="unit-test-pipeline") as pipeline: dummy_generator = DummyGeneratorStep(name="dummy_generator_step") dummy_step_1 = DummyStep1(name="dummy_step_1") @@ -67,7 +72,9 @@ def test_write_buffer_one_leaf_step_and_create_dataset(self) -> None: dummy_generator.connect(dummy_step_1) dummy_step_1.connect(dummy_step_2) - write_buffer = _WriteBuffer(path=folder, leaf_steps=pipeline.dag.leaf_steps) + write_buffer = _WriteBuffer( + path=steps_outputs, leaf_steps=pipeline.dag.leaf_steps + ) # Add one batch with 5 rows, shouldn't write anything 5 < 50 batch = batch_gen(dummy_step_2.name) # type: ignore @@ -78,14 +85,14 @@ def test_write_buffer_one_leaf_step_and_create_dataset(self) -> None: batch = batch_gen(dummy_step_2.name) # type: ignore write_buffer.add_batch(batch) - assert Path(folder, "dummy_step_2", "00001.parquet").exists() + assert Path(steps_outputs, "dummy_step_2", "00001.parquet").exists() # Add 50 more rows, we should have a new file for _ in range(10): batch = batch_gen(dummy_step_2.name) # type: ignore write_buffer.add_batch(batch) - assert Path(folder, "dummy_step_2", "00002.parquet").exists() + assert Path(steps_outputs, "dummy_step_2", "00002.parquet").exists() # Add more rows and close the write buffer, we should have a new file for _ in range(5): @@ -94,9 +101,9 @@ def test_write_buffer_one_leaf_step_and_create_dataset(self) -> None: write_buffer.close() - assert Path(folder, "dummy_step_2", "00003.parquet").exists() + assert Path(steps_outputs, "dummy_step_2", "00003.parquet").exists() - ds = create_distiset(write_buffer._path) + ds = create_distiset(folder) assert isinstance(ds, Distiset) assert len(ds.keys()) == 1 assert len(ds["default"]["train"]) == 125 @@ -104,6 +111,7 @@ def test_write_buffer_one_leaf_step_and_create_dataset(self) -> None: def test_write_buffer_multiple_leaf_steps_and_create_dataset(self) -> None: with tempfile.TemporaryDirectory() as tmpdirname: folder = Path(tmpdirname) / "data" + steps_outputs = folder / STEPS_OUTPUTS_PATH with Pipeline(name="unit-test-pipeline") as pipeline: dummy_generator_1 = DummyGeneratorStep(name="dummy_generator_step_1") dummy_generator_2 = DummyGeneratorStep(name="dummy_generator_step_2") @@ -116,19 +124,21 @@ def test_write_buffer_multiple_leaf_steps_and_create_dataset(self) -> None: dummy_step_1.connect(dummy_step_2) dummy_step_1.connect(dummy_step_3) - write_buffer = _WriteBuffer(path=folder, leaf_steps=pipeline.dag.leaf_steps) + write_buffer = _WriteBuffer( + path=steps_outputs, leaf_steps=pipeline.dag.leaf_steps + ) for _ in range(10): batch = batch_gen(dummy_step_2.name) # type: ignore write_buffer.add_batch(batch) - assert Path(folder, "dummy_step_2", "00001.parquet").exists() + assert Path(steps_outputs, "dummy_step_2", "00001.parquet").exists() for _ in range(10): batch = batch_gen(dummy_step_3.name) # type: ignore write_buffer.add_batch(batch) - assert Path(folder, "dummy_step_3", "00001.parquet").exists() + assert Path(steps_outputs, "dummy_step_3", "00001.parquet").exists() for _ in range(5): batch = batch_gen(dummy_step_2.name) # type: ignore @@ -140,10 +150,10 @@ def test_write_buffer_multiple_leaf_steps_and_create_dataset(self) -> None: write_buffer.close() - assert Path(folder, "dummy_step_2", "00002.parquet").exists() - assert Path(folder, "dummy_step_3", "00002.parquet").exists() + assert Path(steps_outputs, "dummy_step_2", "00002.parquet").exists() + assert Path(steps_outputs, "dummy_step_3", "00002.parquet").exists() - ds = create_distiset(write_buffer._path) + ds = create_distiset(folder) assert isinstance(ds, Distiset) assert len(ds.keys()) == 2 assert len(ds["dummy_step_2"]["train"]) == 75 diff --git a/tests/unit/steps/test_base.py b/tests/unit/steps/test_base.py index d66cb927eb..daf95bedb7 100644 --- a/tests/unit/steps/test_base.py +++ b/tests/unit/steps/test_base.py @@ -12,11 +12,13 @@ # See the License for the specific language governing permissions and # limitations under the License. +import tempfile +from pathlib import Path from typing import List, Optional import pytest +from distilabel.constants import ROUTING_BATCH_FUNCTION_ATTR_NAME from distilabel.mixins.runtime_parameters import RuntimeParameter -from distilabel.pipeline.constants import ROUTING_BATCH_FUNCTION_ATTR_NAME from distilabel.pipeline.local import Pipeline from distilabel.steps.base import GeneratorStep, GlobalStep, Step, StepInput from distilabel.steps.decorator import step @@ -259,6 +261,48 @@ def routing_batch_function(downstream_step_names: List[str]) -> List[str]: == routing_batch_function ) + def test_set_pipeline_artifacts_path(self) -> None: + step = DummyStep() + step.set_pipeline_artifacts_path(Path("/tmp")) + assert step.artifacts_directory == Path(f"/tmp/{step.name}") + + def test_save_artifact(self) -> None: + with tempfile.TemporaryDirectory() as tempdir: + pipeline_artifacts_path = Path(tempdir) + step = DummyStep() + step.load() + step.set_pipeline_artifacts_path(pipeline_artifacts_path) + step.save_artifact( + name="unit-test", + write_function=lambda path: Path(path / "file.txt").write_text( + "unit test" + ), + metadata={"unit-test": True}, + ) + + artifact_path = pipeline_artifacts_path / step.name / "unit-test" # type: ignore + + assert artifact_path.is_dir() + assert (artifact_path / "file.txt").read_text() == "unit test" + assert (artifact_path / "metadata.json").read_text() == '{"unit-test":true}' + + def test_save_artifact_without_setting_path(self) -> None: + with tempfile.TemporaryDirectory() as tempdir: + pipeline_artifacts_path = Path(tempdir) + step = DummyStep() + step.load() + step.save_artifact( + name="unit-test", + write_function=lambda path: Path(path / "file.txt").write_text( + "unit test" + ), + metadata={"unit-test": True}, + ) + + artifact_path = pipeline_artifacts_path / step.name / "unit-test" # type: ignore + + assert not artifact_path.exists() + class TestGeneratorStep: def test_is_generator(self) -> None: diff --git a/tests/unit/test_distiset.py b/tests/unit/test_distiset.py index 07e6549d7b..e492f218a1 100644 --- a/tests/unit/test_distiset.py +++ b/tests/unit/test_distiset.py @@ -22,11 +22,12 @@ import yaml from datasets import Dataset, DatasetDict from distilabel.distiset import Distiset +from distilabel.utils.serialization import write_json from upath import UPath @pytest.fixture(scope="function") -def distiset(): +def distiset() -> Distiset: return Distiset( { "leaf_step_1": Dataset.from_dict({"a": [1, 2, 3]}), @@ -42,14 +43,32 @@ def make_fake_file(filename: Path) -> None: def add_config_to_distiset(distiset: Distiset, folder: Path) -> Distiset: - from distilabel.distiset import DISTISET_CONFIG_FOLDER + from distilabel.constants import DISTISET_CONFIG_FOLDER pipeline_yaml = folder / DISTISET_CONFIG_FOLDER / "pipeline.yaml" pipeline_log = folder / DISTISET_CONFIG_FOLDER / "pipeline.log" make_fake_file(pipeline_yaml) make_fake_file(pipeline_log) distiset.pipeline_path = pipeline_yaml - distiset.pipeline_log_path = pipeline_log + distiset.log_filename_path = pipeline_log + return distiset + + +def add_artifacts_to_distiset(distiset: Distiset, folder: Path) -> Distiset: + from distilabel.constants import DISTISET_ARTIFACTS_FOLDER + + artifacts_folder = folder / DISTISET_ARTIFACTS_FOLDER + + for step in ("leaf_step_1", "leaf_step_2"): + step_artifacts_folder = artifacts_folder / step + step_artifacts_folder.mkdir(parents=True) + artifact_folder = step_artifacts_folder / "artifact" + artifact_folder.mkdir() + metadata_file = artifact_folder / "metadata.json" + write_json(metadata_file, {}) + + distiset.artifacts_path = artifacts_folder + return distiset @@ -63,54 +82,77 @@ def test_train_test_split(self, distiset: Distiset) -> None: @pytest.mark.parametrize("storage_options", [None, {"test": "option"}]) @pytest.mark.parametrize("with_config", [False, True]) + @pytest.mark.parametrize("with_artifacts", [False, True]) def test_save_to_disk( self, distiset: Distiset, with_config: bool, + with_artifacts: bool, storage_options: Optional[Dict[str, Any]], ) -> None: full_distiset = copy.deepcopy(distiset) # Distiset with Distiset with tempfile.TemporaryDirectory() as tmpdirname: folder = Path(tmpdirname) / "distiset_folder" + another_folder = Path(tmpdirname) / "another_distiset_folder" + if with_config: full_distiset = add_config_to_distiset(full_distiset, folder) + if with_artifacts: + full_distiset = add_artifacts_to_distiset(full_distiset, folder) + full_distiset.save_to_disk( - folder, + another_folder, save_card=with_config, save_pipeline_config=with_config, save_pipeline_log=with_config, storage_options=storage_options, ) - assert folder.is_dir() - assert len(list(folder.iterdir())) == 3 + assert another_folder.is_dir() + + if with_artifacts: + assert len(list(another_folder.iterdir())) == 4 + else: + assert len(list(another_folder.iterdir())) == 3 full_distiset = copy.deepcopy(distiset) # Distiset with DatasetDict distiset_with_dict = full_distiset.train_test_split(0.8) with tempfile.TemporaryDirectory() as tmpdirname: folder = Path(tmpdirname) / "distiset_folder" + another_folder = Path(tmpdirname) / "another_distiset_folder" + if with_config: distiset_with_dict = add_config_to_distiset(distiset_with_dict, folder) + if with_artifacts: + distiset_with_dict = add_artifacts_to_distiset( + distiset_with_dict, folder + ) + distiset_with_dict.save_to_disk( - folder, + another_folder, save_card=with_config, save_pipeline_config=with_config, save_pipeline_log=with_config, ) - assert folder.is_dir() - assert len(list(folder.iterdir())) == 3 + assert another_folder.is_dir() + if with_artifacts: + assert len(list(another_folder.iterdir())) == 4 + else: + assert len(list(another_folder.iterdir())) == 3 @pytest.mark.parametrize("pathlib_implementation", [Path, UPath]) @pytest.mark.parametrize("storage_options", [None, {"project": "experiments"}]) @pytest.mark.parametrize("with_config", [False, True]) + @pytest.mark.parametrize("with_artifacts", [False, True]) def test_load_from_disk( self, distiset: Distiset, with_config: bool, + with_artifacts: bool, storage_options: Optional[Dict[str, Any]], pathlib_implementation: type, ) -> None: @@ -120,17 +162,25 @@ def test_load_from_disk( # This way we can test also we work with UPath, using FilePath protocol, as it should # do the same as S3Path, GCSPath, etc. folder = pathlib_implementation(tmpdirname) / "distiset_folder" + another_folder = ( + pathlib_implementation(tmpdirname) / "another_distiset_folder" + ) + if with_config: full_distiset = add_config_to_distiset(full_distiset, folder) + + if with_artifacts: + full_distiset = add_artifacts_to_distiset(full_distiset, folder) + full_distiset.save_to_disk( - folder, + another_folder, save_card=with_config, save_pipeline_config=with_config, save_pipeline_log=with_config, storage_options=storage_options, ) ds = Distiset.load_from_disk( - folder, + another_folder, storage_options=storage_options, ) assert isinstance(ds, Distiset) @@ -140,24 +190,41 @@ def test_load_from_disk( assert ds.pipeline_path.exists() assert ds.log_filename_path.exists() + if with_artifacts: + assert ds.artifacts_path.exists() + full_distiset = copy.deepcopy(distiset) # Distiset with DatasetDict distiset_with_dict = full_distiset.train_test_split(0.8) with tempfile.TemporaryDirectory() as tmpdirname: folder = pathlib_implementation(tmpdirname) / "distiset_folder" + another_folder = ( + pathlib_implementation(tmpdirname) / "another_distiset_folder" + ) + if with_config: distiset_with_dict = add_config_to_distiset(distiset_with_dict, folder) - distiset_with_dict.save_to_disk(folder) - ds = Distiset.load_from_disk(folder, storage_options=storage_options) + if with_artifacts: + distiset_with_dict = add_artifacts_to_distiset( + distiset_with_dict, folder + ) - assert folder.is_dir() + distiset_with_dict.save_to_disk(another_folder) + ds = Distiset.load_from_disk( + another_folder, storage_options=storage_options + ) + + assert another_folder.is_dir() assert isinstance(ds["leaf_step_1"], DatasetDict) if with_config: assert ds.pipeline_path.exists() assert ds.log_filename_path.exists() + if with_artifacts: + assert ds.artifacts_path.exists() + def test_dataset_card(self, distiset: Distiset) -> None: # Test the the metadata we generate by default without extracting the already generated content from the HF hub. # We parse the content and check it's the same as the one we generate. From 3d772c56613887030c24a19b84947f3d3754d672 Mon Sep 17 00:00:00 2001 From: Agus Date: Wed, 14 Aug 2024 16:40:08 +0200 Subject: [PATCH 16/82] Add new `add_raw_input` argument to `_Task` so we can automatically include the formatted input (#903) * Add attribute to include raw formatted input to distilabel_metadata field * Update tests to take into account add_raw_input attribute of tasks * Add reference to add_raw_input in the documentation * Update tests to control for the add_raw_input of the _Task --- .../how_to_guides/basic/task/index.md | 26 +- src/distilabel/steps/tasks/base.py | 32 ++- .../steps/tasks/evol_instruct/test_base.py | 6 + .../tasks/evol_instruct/test_generator.py | 6 + .../steps/tasks/evol_quality/test_base.py | 6 + tests/unit/steps/tasks/magpie/test_base.py | 6 + .../unit/steps/tasks/magpie/test_generator.py | 6 + tests/unit/steps/tasks/test_base.py | 250 ++++++++++++++++-- .../tasks/test_improving_text_embeddings.py | 7 + .../tasks/test_instruction_backtranslation.py | 1 + .../steps/tasks/test_structured_generation.py | 4 +- .../unit/steps/tasks/test_text_generation.py | 8 +- tests/unit/steps/tasks/test_ultrafeedback.py | 2 + 13 files changed, 331 insertions(+), 29 deletions(-) diff --git a/docs/sections/how_to_guides/basic/task/index.md b/docs/sections/how_to_guides/basic/task/index.md index 70b118c3ea..c315fa1bb0 100644 --- a/docs/sections/how_to_guides/basic/task/index.md +++ b/docs/sections/how_to_guides/basic/task/index.md @@ -24,7 +24,12 @@ next(task.process([{"instruction": "What's the capital of Spain?"}])) # { # 'instruction': "What's the capital of Spain?", # 'generation': 'The capital of Spain is Madrid.', -# 'distilabel_metadata': {'raw_output_text-generation': 'The capital of Spain is Madrid.'}, +# 'distilabel_metadata': { +# 'raw_output_text-generation': 'The capital of Spain is Madrid.', +# 'raw_input_text-generation': [ +# {'role': 'user', 'content': "What's the capital of Spain?"} +# ] +# }, # 'model_name': 'meta-llama/Meta-Llama-3-70B-Instruct' # } # ] @@ -33,7 +38,24 @@ next(task.process([{"instruction": "What's the capital of Spain?"}])) !!! NOTE The `Step.load()` always needs to be executed when being used as a standalone. Within a pipeline, this will be done automatically during pipeline execution. -As shown above, the [`TextGeneration`][distilabel.steps.tasks.TextGeneration] task adds a `generation` based on the `instruction`. Additionally, it provides some metadata about the LLM call through `distilabel_metadata`. This can be disabled by setting the `add_raw_output` attribute to `False` when creating the task. +As shown above, the [`TextGeneration`][distilabel.steps.tasks.TextGeneration] task adds a `generation` based on the `instruction`. + +!!! Tip + Since version `1.2.0`, we provide some metadata about the LLM call through `distilabel_metadata`. This can be disabled by setting the `add_raw_output` attribute to `False` when creating the task. + + Additionally, since version `1.4.0`, the formatted input can also be included, which can be helpful when testing + custom templates (testing the pipeline using the [`dry_run`][distilabel.pipeline.local.Pipeline.dry_run] method). + + ```python title="disable raw input and output" + task = TextGeneration( + llm=InferenceEndpointsLLM( + model_id="meta-llama/Meta-Llama-3.1-70B-Instruct", + tokenizer_id="meta-llama/Meta-Llama-3.1-70B-Instruct", + ), + add_raw_output=False, + add_raw_input=False + ) + ``` ## Specifying the number of generations and grouping generations diff --git a/src/distilabel/steps/tasks/base.py b/src/distilabel/steps/tasks/base.py index 78910abb79..fd515a4c28 100644 --- a/src/distilabel/steps/tasks/base.py +++ b/src/distilabel/steps/tasks/base.py @@ -61,6 +61,13 @@ class _Task(_Step, ABC): " of the `distilabel_metadata` dictionary output column" ), ) + add_raw_input: RuntimeParameter[bool] = Field( + default=True, + description=( + "Whether to include the raw input of the LLM in the key `raw_input_`" + " of the `distilabel_metadata` dictionary column" + ), + ) num_generations: RuntimeParameter[int] = Field( default=1, description="The number of generations to be produced per input." ) @@ -113,10 +120,12 @@ def _format_outputs( for output, input in zip(outputs, inputs * len(outputs)): # type: ignore try: formatted_output = self.format_output(output, input) - formatted_output = self._maybe_add_raw_output( + formatted_output = self._maybe_add_raw_input_output( formatted_output, output, + input, add_raw_output=self.add_raw_output, # type: ignore + add_raw_input=self.add_raw_input, # type: ignore ) formatted_outputs.append(formatted_output) except Exception as e: @@ -135,24 +144,35 @@ def _output_on_failure( # Create a dictionary with the outputs of the task (every output set to None) outputs = {output: None for output in self.outputs} outputs["model_name"] = self.llm.model_name # type: ignore - outputs = self._maybe_add_raw_output( + outputs = self._maybe_add_raw_input_output( outputs, output, + input, add_raw_output=self.add_raw_output, # type: ignore + add_raw_input=self.add_raw_input, # type: ignore ) return outputs - def _maybe_add_raw_output( + def _maybe_add_raw_input_output( self, output: Dict[str, Any], raw_output: Union[str, None], + input: Union[str, None], add_raw_output: bool = True, - ) -> Dict[str, Any]: - """Adds the raw output of the LLM to the output dictionary if `add_raw_output` is True.""" + add_raw_input: bool = True, + ): + """Adds the raw output and or the formatted input of the LLM to the output dictionary + if `add_raw_output` is True or `add_raw_input` is True. + """ + meta = output.get(DISTILABEL_METADATA_KEY, {}) + if add_raw_output: - meta = output.get(DISTILABEL_METADATA_KEY, {}) meta[f"raw_output_{self.name}"] = raw_output + if add_raw_input: + meta[f"raw_input_{self.name}"] = self.format_input(input) + if meta: output[DISTILABEL_METADATA_KEY] = meta + return output def _set_default_structured_output(self) -> None: diff --git a/tests/unit/steps/tasks/evol_instruct/test_base.py b/tests/unit/steps/tasks/evol_instruct/test_base.py index f98b5ae7f9..d170ea3b53 100644 --- a/tests/unit/steps/tasks/evol_instruct/test_base.py +++ b/tests/unit/steps/tasks/evol_instruct/test_base.py @@ -122,6 +122,7 @@ def test_serialization(self, dummy_llm: LLM) -> None: assert task.dump() == { "name": "task", "add_raw_output": True, + "add_raw_input": True, "input_mappings": task.input_mappings, "output_mappings": task.output_mappings, "resources": { @@ -206,6 +207,11 @@ def test_serialization(self, dummy_llm: LLM) -> None: "name": "add_raw_output", "optional": True, }, + { + "description": "Whether to include the raw input of the LLM in the key `raw_input_` of the `distilabel_metadata` dictionary column", + "name": "add_raw_input", + "optional": True, + }, { "name": "num_generations", "optional": True, diff --git a/tests/unit/steps/tasks/evol_instruct/test_generator.py b/tests/unit/steps/tasks/evol_instruct/test_generator.py index d20f631fd4..d28520c8eb 100644 --- a/tests/unit/steps/tasks/evol_instruct/test_generator.py +++ b/tests/unit/steps/tasks/evol_instruct/test_generator.py @@ -124,6 +124,7 @@ def test_serialization(self, dummy_llm: LLM) -> None: }, }, "add_raw_output": True, + "add_raw_input": True, "input_mappings": task.input_mappings, "output_mappings": task.output_mappings, "resources": { @@ -201,6 +202,11 @@ def test_serialization(self, dummy_llm: LLM) -> None: "name": "add_raw_output", "optional": True, }, + { + "description": "Whether to include the raw input of the LLM in the key `raw_input_` of the `distilabel_metadata` dictionary column", + "name": "add_raw_input", + "optional": True, + }, { "name": "num_generations", "optional": True, diff --git a/tests/unit/steps/tasks/evol_quality/test_base.py b/tests/unit/steps/tasks/evol_quality/test_base.py index 9b45b3d3ef..a6d8775ad9 100644 --- a/tests/unit/steps/tasks/evol_quality/test_base.py +++ b/tests/unit/steps/tasks/evol_quality/test_base.py @@ -93,6 +93,7 @@ def test_serialization(self, dummy_llm: LLM) -> None: assert task.dump() == { "name": "task", "add_raw_output": True, + "add_raw_input": True, "input_mappings": task.input_mappings, "output_mappings": task.output_mappings, "resources": { @@ -170,6 +171,11 @@ def test_serialization(self, dummy_llm: LLM) -> None: "name": "add_raw_output", "optional": True, }, + { + "description": "Whether to include the raw input of the LLM in the key `raw_input_` of the `distilabel_metadata` dictionary column", + "name": "add_raw_input", + "optional": True, + }, { "name": "num_generations", "optional": True, diff --git a/tests/unit/steps/tasks/magpie/test_base.py b/tests/unit/steps/tasks/magpie/test_base.py index 208dc817a8..641c44b063 100644 --- a/tests/unit/steps/tasks/magpie/test_base.py +++ b/tests/unit/steps/tasks/magpie/test_base.py @@ -422,6 +422,7 @@ def test_serialization(self) -> None: "input_batch_size": 50, "group_generations": False, "add_raw_output": True, + "add_raw_input": True, "num_generations": 1, "use_default_structured_output": False, "runtime_parameters_info": [ @@ -500,6 +501,11 @@ def test_serialization(self) -> None: "optional": True, "description": "Whether to include the raw output of the LLM in the key `raw_output_` of the `distilabel_metadata` dictionary output column", }, + { + "description": "Whether to include the raw input of the LLM in the key `raw_input_` of the `distilabel_metadata` dictionary column", + "name": "add_raw_input", + "optional": True, + }, { "name": "num_generations", "optional": True, diff --git a/tests/unit/steps/tasks/magpie/test_generator.py b/tests/unit/steps/tasks/magpie/test_generator.py index c3d35adcb6..1255061d44 100644 --- a/tests/unit/steps/tasks/magpie/test_generator.py +++ b/tests/unit/steps/tasks/magpie/test_generator.py @@ -78,6 +78,7 @@ def test_serialization(self) -> None: "batch_size": 50, "group_generations": False, "add_raw_output": True, + "add_raw_input": True, "num_generations": 1, "num_rows": None, "use_default_structured_output": False, @@ -157,6 +158,11 @@ def test_serialization(self) -> None: "optional": True, "description": "Whether to include the raw output of the LLM in the key `raw_output_` of the `distilabel_metadata` dictionary output column", }, + { + "description": "Whether to include the raw input of the LLM in the key `raw_input_` of the `distilabel_metadata` dictionary column", + "name": "add_raw_input", + "optional": True, + }, { "name": "num_generations", "optional": True, diff --git a/tests/unit/steps/tasks/test_base.py b/tests/unit/steps/tasks/test_base.py index ac0d17de1d..f2ef1c9c84 100644 --- a/tests/unit/steps/tasks/test_base.py +++ b/tests/unit/steps/tasks/test_base.py @@ -107,7 +107,13 @@ def test_with_errors(self, caplog: pytest.LogCaptureFixture) -> None: "output": "output", "info_from_input": "additional_info_0", "model_name": "test", - "distilabel_metadata": {"raw_output_task": "output"}, + "distilabel_metadata": { + "raw_output_task": "output", + "raw_input_task": [ + {"content": "", "role": "system"}, + {"content": "test_0", "role": "user"}, + ], + }, }, { "instruction": "test_0", @@ -115,7 +121,13 @@ def test_with_errors(self, caplog: pytest.LogCaptureFixture) -> None: "output": "output", "info_from_input": "additional_info_0", "model_name": "test", - "distilabel_metadata": {"raw_output_task": "output"}, + "distilabel_metadata": { + "raw_output_task": "output", + "raw_input_task": [ + {"content": "", "role": "system"}, + {"content": "test_0", "role": "user"}, + ], + }, }, { "instruction": "test_0", @@ -123,7 +135,13 @@ def test_with_errors(self, caplog: pytest.LogCaptureFixture) -> None: "output": "output", "info_from_input": "additional_info_0", "model_name": "test", - "distilabel_metadata": {"raw_output_task": "output"}, + "distilabel_metadata": { + "raw_output_task": "output", + "raw_input_task": [ + {"content": "", "role": "system"}, + {"content": "test_0", "role": "user"}, + ], + }, }, { "instruction": "test_1", @@ -131,7 +149,13 @@ def test_with_errors(self, caplog: pytest.LogCaptureFixture) -> None: "output": "output", "info_from_input": "additional_info_1", "model_name": "test", - "distilabel_metadata": {"raw_output_task": "output"}, + "distilabel_metadata": { + "raw_output_task": "output", + "raw_input_task": [ + {"content": "", "role": "system"}, + {"content": "test_1", "role": "user"}, + ], + }, }, { "instruction": "test_1", @@ -139,7 +163,13 @@ def test_with_errors(self, caplog: pytest.LogCaptureFixture) -> None: "output": "output", "info_from_input": "additional_info_1", "model_name": "test", - "distilabel_metadata": {"raw_output_task": "output"}, + "distilabel_metadata": { + "raw_output_task": "output", + "raw_input_task": [ + {"content": "", "role": "system"}, + {"content": "test_1", "role": "user"}, + ], + }, }, { "instruction": "test_1", @@ -147,7 +177,13 @@ def test_with_errors(self, caplog: pytest.LogCaptureFixture) -> None: "output": "output", "info_from_input": "additional_info_1", "model_name": "test", - "distilabel_metadata": {"raw_output_task": "output"}, + "distilabel_metadata": { + "raw_output_task": "output", + "raw_input_task": [ + {"content": "", "role": "system"}, + {"content": "test_1", "role": "user"}, + ], + }, }, { "instruction": "test_2", @@ -155,7 +191,13 @@ def test_with_errors(self, caplog: pytest.LogCaptureFixture) -> None: "output": "output", "info_from_input": "additional_info_2", "model_name": "test", - "distilabel_metadata": {"raw_output_task": "output"}, + "distilabel_metadata": { + "raw_output_task": "output", + "raw_input_task": [ + {"content": "", "role": "system"}, + {"content": "test_2", "role": "user"}, + ], + }, }, { "instruction": "test_2", @@ -163,7 +205,13 @@ def test_with_errors(self, caplog: pytest.LogCaptureFixture) -> None: "output": "output", "info_from_input": "additional_info_2", "model_name": "test", - "distilabel_metadata": {"raw_output_task": "output"}, + "distilabel_metadata": { + "raw_output_task": "output", + "raw_input_task": [ + {"content": "", "role": "system"}, + {"content": "test_2", "role": "user"}, + ], + }, }, { "instruction": "test_2", @@ -171,7 +219,13 @@ def test_with_errors(self, caplog: pytest.LogCaptureFixture) -> None: "output": "output", "info_from_input": "additional_info_2", "model_name": "test", - "distilabel_metadata": {"raw_output_task": "output"}, + "distilabel_metadata": { + "raw_output_task": "output", + "raw_input_task": [ + {"content": "", "role": "system"}, + {"content": "test_2", "role": "user"}, + ], + }, }, ], ), @@ -194,9 +248,48 @@ def test_with_errors(self, caplog: pytest.LogCaptureFixture) -> None: ], "model_name": "test", "distilabel_metadata": [ - {"raw_output_task": "output"}, - {"raw_output_task": "output"}, - {"raw_output_task": "output"}, + { + "raw_output_task": "output", + "raw_input_task": [ + { + "content": "", + "role": "system", + }, + { + "content": "test_0", + "role": "user", + }, + ], + }, + { + "raw_output_task": "output", + "raw_input_task": [ + { + "content": "", + "role": "system", + }, + { + "content": "test_0", + "role": "user", + }, + ], + }, + { + "raw_output_task": "output", + "raw_input_task": [ + { + "content": "", + "role": "system", + }, + { + "content": "test_0", + "role": "user", + }, + ], + }, + # {"raw_output_task": "output"}, + # {"raw_output_task": "output"}, + # {"raw_output_task": "output"}, ], }, { @@ -210,9 +303,45 @@ def test_with_errors(self, caplog: pytest.LogCaptureFixture) -> None: ], "model_name": "test", "distilabel_metadata": [ - {"raw_output_task": "output"}, - {"raw_output_task": "output"}, - {"raw_output_task": "output"}, + { + "raw_output_task": "output", + "raw_input_task": [ + { + "content": "", + "role": "system", + }, + { + "content": "test_1", + "role": "user", + }, + ], + }, + { + "raw_output_task": "output", + "raw_input_task": [ + { + "content": "", + "role": "system", + }, + { + "content": "test_1", + "role": "user", + }, + ], + }, + { + "raw_output_task": "output", + "raw_input_task": [ + { + "content": "", + "role": "system", + }, + { + "content": "test_1", + "role": "user", + }, + ], + }, ], }, { @@ -226,9 +355,45 @@ def test_with_errors(self, caplog: pytest.LogCaptureFixture) -> None: ], "model_name": "test", "distilabel_metadata": [ - {"raw_output_task": "output"}, - {"raw_output_task": "output"}, - {"raw_output_task": "output"}, + { + "raw_output_task": "output", + "raw_input_task": [ + { + "content": "", + "role": "system", + }, + { + "content": "test_2", + "role": "user", + }, + ], + }, + { + "raw_output_task": "output", + "raw_input_task": [ + { + "content": "", + "role": "system", + }, + { + "content": "test_2", + "role": "user", + }, + ], + }, + { + "raw_output_task": "output", + "raw_input_task": [ + { + "content": "", + "role": "system", + }, + { + "content": "test_2", + "role": "user", + }, + ], + }, ], }, ], @@ -308,6 +473,7 @@ def test_serialization(self) -> None: assert task.dump() == { "name": "task", "add_raw_output": True, + "add_raw_input": True, "input_mappings": {}, "output_mappings": {}, "resources": { @@ -380,6 +546,11 @@ def test_serialization(self) -> None: "name": "add_raw_output", "optional": True, }, + { + "description": "Whether to include the raw input of the LLM in the key `raw_input_` of the `distilabel_metadata` dictionary column", + "name": "add_raw_input", + "optional": True, + }, { "name": "num_generations", "description": "The number of generations to be produced per input.", @@ -396,3 +567,46 @@ def test_serialization(self) -> None: with Pipeline(name="unit-test-pipeline") as pipeline: new_task = DummyTask.from_dict(task.dump()) assert isinstance(new_task, DummyTask) + + @pytest.mark.parametrize( + "add_raw_output, add_raw_input", + [ + (True, False), + (False, True), + (True, True), + (False, False), + ], + ) + def test_add_raw_input_and_or_output( + self, add_raw_output: bool, add_raw_input: bool + ) -> None: + task = DummyTask( + llm=DummyLLM(), + add_raw_output=add_raw_output, + add_raw_input=add_raw_input, + ) + assert task.add_raw_output is add_raw_output + assert task.add_raw_input is add_raw_input + task.load() + input = [ + {"instruction": "test_0", "additional_info": "additional_info_0"}, + {"instruction": "test_1", "additional_info": "additional_info_1"}, + {"instruction": "test_2", "additional_info": "additional_info_2"}, + ] + result = next(task.process(input)) + import pprint + + pprint.pprint(result) + + if add_raw_output or add_raw_input: + assert "distilabel_metadata" in result[0].keys() + if add_raw_output: + assert ( + "raw_output_dummy_task_0" in result[0]["distilabel_metadata"].keys() + ) + if add_raw_input: + assert ( + "raw_input_dummy_task_0" in result[0]["distilabel_metadata"].keys() + ) + else: + assert "distilabel_metadata" not in result[0].keys() diff --git a/tests/unit/steps/tasks/test_improving_text_embeddings.py b/tests/unit/steps/tasks/test_improving_text_embeddings.py index 8ab9b2fd51..a90247cc95 100644 --- a/tests/unit/steps/tasks/test_improving_text_embeddings.py +++ b/tests/unit/steps/tasks/test_improving_text_embeddings.py @@ -66,6 +66,7 @@ def test_process(self, category: str, flatten_tasks: bool) -> None: add_raw_output=False, llm=MockLLM(output="[ 'A', 'B', 'C' ]"), pipeline=Pipeline(name="unit-test-pipeline"), + add_raw_input=False, ) task.load() @@ -122,6 +123,7 @@ def test_process(self) -> None: add_raw_output=False, llm=MockLLM(output=json.dumps({"S1": "A", "S2": "B", "S3": "C"})), pipeline=Pipeline(name="unit-test-pipeline"), + add_raw_input=False, ) task.load() @@ -184,6 +186,7 @@ def test_process(self) -> None: add_raw_output=False, llm=MockLLM(output=json.dumps({"S1": "A", "S2": "B", "S3": "C"})), pipeline=Pipeline(name="unit-test-pipeline"), + add_raw_input=False, ) task.load() assert task.outputs == ["S1", "S2", "S3", "model_name"] @@ -230,6 +233,7 @@ def test_process(self) -> None: add_raw_output=False, llm=MockLLM(output=json.dumps({"input": "A", "positive_document": "B"})), pipeline=Pipeline(name="unit-test-pipeline"), + add_raw_input=False, ) task.load() @@ -261,6 +265,7 @@ def test_process(self) -> None: add_raw_output=False, llm=MockLLM(output=json.dumps({"input": "A", "positive_document": "B"})), pipeline=Pipeline(name="unit-test-pipeline"), + add_raw_input=False, ) task.load() assert task.outputs == ["input", "positive_document", "model_name"] @@ -316,6 +321,7 @@ def test_process(self) -> None: ) ), pipeline=Pipeline(name="unit-test-pipeline"), + add_raw_input=False, ) task.load() assert task.outputs == ["input_text", "label", "misleading_label", "model_name"] @@ -387,6 +393,7 @@ def test_process(self) -> None: ) ), pipeline=Pipeline(name="unit-test-pipeline"), + add_raw_input=False, ) task.load() assert task.outputs == [ diff --git a/tests/unit/steps/tasks/test_instruction_backtranslation.py b/tests/unit/steps/tasks/test_instruction_backtranslation.py index a6f2793285..1b2f9adffa 100644 --- a/tests/unit/steps/tasks/test_instruction_backtranslation.py +++ b/tests/unit/steps/tasks/test_instruction_backtranslation.py @@ -76,6 +76,7 @@ def test_process(self) -> None: name="instruction-backtranslation", llm=InstructionBacktranslationLLM(), pipeline=Pipeline(name="unit-test-pipeline"), + add_raw_input=False, ) task.load() diff --git a/tests/unit/steps/tasks/test_structured_generation.py b/tests/unit/steps/tasks/test_structured_generation.py index e2c230ef7e..9cd8f7a3af 100644 --- a/tests/unit/steps/tasks/test_structured_generation.py +++ b/tests/unit/steps/tasks/test_structured_generation.py @@ -86,7 +86,9 @@ def test_format_input_with_system_prompt(self) -> None: def test_process(self) -> None: pipeline = Pipeline(name="unit-test-pipeline") llm = DummyStructuredLLM() - task = StructuredGeneration(name="task", llm=llm, pipeline=pipeline) + task = StructuredGeneration( + name="task", llm=llm, pipeline=pipeline, add_raw_input=False + ) assert next( task.process( [ diff --git a/tests/unit/steps/tasks/test_text_generation.py b/tests/unit/steps/tasks/test_text_generation.py index c98adb00e5..dd43530cd9 100644 --- a/tests/unit/steps/tasks/test_text_generation.py +++ b/tests/unit/steps/tasks/test_text_generation.py @@ -75,7 +75,9 @@ def test_format_input_errors(self) -> None: def test_process(self) -> None: pipeline = Pipeline(name="unit-test-pipeline") llm = DummyLLM() - task = TextGeneration(name="task", llm=llm, pipeline=pipeline) + task = TextGeneration( + name="task", llm=llm, pipeline=pipeline, add_raw_input=False + ) assert next(task.process([{"instruction": "test"}])) == [ { @@ -125,7 +127,9 @@ def test_format_input_errors(self) -> None: def test_process(self) -> None: pipeline = Pipeline(name="unit-test-pipeline") llm = DummyLLM() - task = ChatGeneration(name="task", llm=llm, pipeline=pipeline) + task = ChatGeneration( + name="task", llm=llm, pipeline=pipeline, add_raw_input=False + ) assert next( task.process( diff --git a/tests/unit/steps/tasks/test_ultrafeedback.py b/tests/unit/steps/tasks/test_ultrafeedback.py index d9e272959e..152509f993 100644 --- a/tests/unit/steps/tasks/test_ultrafeedback.py +++ b/tests/unit/steps/tasks/test_ultrafeedback.py @@ -50,6 +50,7 @@ def test_process_with_simple_aspect(self) -> None: aspect="instruction-following", llm=UltraFeedbackLLM(), use_default_structured_output=False, + add_raw_input=False, ) task.load() @@ -74,6 +75,7 @@ def test_process_with_complex_aspect(self) -> None: aspect="truthfulness", llm=UltraFeedbackLLM(), use_default_structured_output=False, + add_raw_input=False, ) task.load() From 4740063d5ad45845bc7045d49b8f91920f582bd7 Mon Sep 17 00:00:00 2001 From: Agus Date: Wed, 14 Aug 2024 16:47:21 +0200 Subject: [PATCH 17/82] New `TruncateTextColumn` to truncate the length of texts using the number of tokens or characters (#902) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add new category for text manipulation and sort the dict aplhabetically * Redirect import * Add new TruncateRow step to truncate the text using the number of characters or tokens * Add tests for TruncateRow * Update tokenizer name to avoid errors accessing the repo in CI * Update src/distilabel/steps/__init__.py Co-authored-by: Gabriel Martín Blázquez * Update src/distilabel/steps/truncate.py Co-authored-by: Gabriel Martín Blázquez * Refactor tokenizer_name to tokenizer for consistency * Update test for the tokenizer refactor * Refactor TruncateRow to TruncateTextColumn --------- Co-authored-by: Gabriel Martín Blázquez --- src/distilabel/steps/__init__.py | 2 + src/distilabel/steps/truncate.py | 149 ++++++++++++++++++ .../utils/mkdocs/components_gallery.py | 13 +- tests/unit/steps/test_truncate.py | 46 ++++++ 4 files changed, 204 insertions(+), 6 deletions(-) create mode 100644 src/distilabel/steps/truncate.py create mode 100644 tests/unit/steps/test_truncate.py diff --git a/src/distilabel/steps/__init__.py b/src/distilabel/steps/__init__.py index bd8fde2251..420334bca9 100644 --- a/src/distilabel/steps/__init__.py +++ b/src/distilabel/steps/__init__.py @@ -47,6 +47,7 @@ from distilabel.steps.generators.utils import make_generator_step from distilabel.steps.globals.huggingface import PushToHub from distilabel.steps.reward_model import RewardModelScore +from distilabel.steps.truncate import TruncateTextColumn from distilabel.steps.typing import GeneratorStepOutput, StepOutput __all__ = [ @@ -77,6 +78,7 @@ "Step", "StepInput", "RewardModelScore", + "TruncateTextColumn", "GeneratorStepOutput", "StepOutput", "step", diff --git a/src/distilabel/steps/truncate.py b/src/distilabel/steps/truncate.py new file mode 100644 index 0000000000..bb1785b8a1 --- /dev/null +++ b/src/distilabel/steps/truncate.py @@ -0,0 +1,149 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import importlib +from typing import TYPE_CHECKING, Any, Callable, List, Optional + +from typing_extensions import override + +from distilabel.steps.base import Step, StepInput + +if TYPE_CHECKING: + from distilabel.steps.typing import StepOutput + + +class TruncateTextColumn(Step): + """Truncate a row using a tokenizer or the number of characters. + + `TruncateTextColumn` is a `Step` that truncates a row according to the max length. If + the `tokenizer` is provided, then the row will be truncated using the tokenizer, + and the `max_length` will be used as the maximum number of tokens, otherwise it will + be used as the maximum number of characters. The `TruncateTextColumn` step is useful when one + wants to truncate a row to a certain length, to avoid posterior errors in the model due + to the length. + + Attributes: + column: the column to truncate. Defaults to `"text"`. + max_length: the maximum length to use for truncation. + If a `tokenizer` is given, corresponds to the number of tokens, + otherwise corresponds to the number of characters. Defaults to `8192`. + tokenizer: the name of the tokenizer to use. If provided, the row will be + truncated using the tokenizer. Defaults to `None`. + + Input columns: + - dynamic (determined by `column` attribute): The columns to be truncated, defaults to "text". + + Output columns: + - dynamic (determined by `column` attribute): The truncated column. + + Categories: + - text-manipulation + + Examples: + + Truncating a row to a given number of tokens: + + ```python + from distilabel.steps import TruncateTextColumn + + trunc = TruncateTextColumn( + tokenizer="meta-llama/Meta-Llama-3.1-70B-Instruct", + max_length=4, + column="text" + ) + + trunc.load() + + result = next( + trunc.process( + [ + {"text": "This is a sample text that is longer than 10 characters"} + ] + ) + ) + # result + # [{'text': 'This is a sample'}] + ``` + + Truncating a row to a given number of characters: + + ```python + from distilabel.steps import TruncateTextColumn + + trunc = TruncateTextColumn(max_length=10) + + trunc.load() + + result = next( + trunc.process( + [ + {"text": "This is a sample text that is longer than 10 characters"} + ] + ) + ) + # result + # [{'text': 'This is a '}] + ``` + """ + + column: str = "text" + max_length: int = 8192 + tokenizer: Optional[str] = None + _truncator: Optional[Callable[[str], str]] = None + _tokenizer: Optional[Any] = None + + def load(self): + super().load() + if self.tokenizer: + if not importlib.util.find_spec("transformers"): + raise ImportError( + "`transformers` is needed to tokenize, but is not installed. " + "Please install it using `pip install transformers`." + ) + + from transformers import AutoTokenizer + + self._tokenizer = AutoTokenizer.from_pretrained(self.tokenizer) + self._truncator = self._truncate_with_tokenizer + else: + self._truncator = self._truncate_with_length + + @property + def inputs(self) -> List[str]: + return [self.column] + + @property + def outputs(self) -> List[str]: + return self.inputs + + def _truncate_with_length(self, text: str) -> str: + """Truncates the text according to the number of characters.""" + return text[: self.max_length] + + def _truncate_with_tokenizer(self, text: str) -> str: + """Truncates the text according to the number of characters using the tokenizer.""" + return self._tokenizer.decode( + self._tokenizer.encode( + text, + add_special_tokens=False, + max_length=self.max_length, + truncation=True, + ) + ) + + @override + def process(self, inputs: StepInput) -> "StepOutput": + for input in inputs: + input[self.column] = self._truncator(input[self.column]) + yield inputs diff --git a/src/distilabel/utils/mkdocs/components_gallery.py b/src/distilabel/utils/mkdocs/components_gallery.py index 2d28c55fb6..d1637d87cf 100644 --- a/src/distilabel/utils/mkdocs/components_gallery.py +++ b/src/distilabel/utils/mkdocs/components_gallery.py @@ -75,16 +75,17 @@ ) _STEPS_CATEGORY_TO_ICON = { - "text-generation": ":material-text-box-edit:", - "evol": ":material-dna:", - "preference": ":material-poll:", "critique": ":material-comment-edit:", - "scorer": ":octicons-number-16:", "embedding": ":material-vector-line:", - "format": ":material-format-list-bulleted:", + "evol": ":material-dna:", "filtering": ":material-filter:", - "save": ":material-content-save:", + "format": ":material-format-list-bulleted:", "load": ":material-file-download:", + "preference": ":material-poll:", + "save": ":material-content-save:", + "scorer": ":octicons-number-16:", + "text-generation": ":material-text-box-edit:", + "text-manipulation": ":material-receipt-text-edit:", } diff --git a/tests/unit/steps/test_truncate.py b/tests/unit/steps/test_truncate.py new file mode 100644 index 0000000000..b268080336 --- /dev/null +++ b/tests/unit/steps/test_truncate.py @@ -0,0 +1,46 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import Optional + +import pytest +from distilabel.steps.truncate import TruncateTextColumn + + +@pytest.mark.parametrize( + "max_length, text, tokenizer, expected", + [ + ( + 10, + "This is a sample text that is longer than 10 characters", + None, + "This is a ", + ), + ( + 4, + "This is a sample text that is longer than 10 characters", + "teknium/OpenHermes-2.5-Mistral-7B", + "This is a sample", + ), + ], +) +def test_truncate_row( + max_length: int, text: str, tokenizer: Optional[str], expected: str +) -> None: + trunc = TruncateTextColumn( + column="text", max_length=max_length, tokenizer=tokenizer + ) + trunc.load() + + assert next(trunc.process([{"text": text}])) == [{"text": expected}] From 409369995213d2b31b5927c017d695624bbd0171 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Mart=C3=ADn=20Bl=C3=A1zquez?= Date: Thu, 15 Aug 2024 08:00:49 +0200 Subject: [PATCH 18/82] Update `inputs` and `outputs` interface to allow returning dict indicating optionality (#883) * Use `CudaDevicePlacementMixin` in `RewardModelScore` step * Add `StepColumns` type * Update inputs and outputs validation * Update type hints * Update inputs checking * Add unit test for checking inputs/outputs with dict * Update type hints * Update `inputs` and `outputs` return * Add missing inputs and outputs in docstring * Update docs --- .../basic/step/generator_step.md | 31 +++++++---- .../how_to_guides/basic/step/global_step.md | 24 +++++--- .../how_to_guides/basic/step/index.md | 36 ++++++++---- .../how_to_guides/basic/task/index.md | 17 +++--- src/distilabel/pipeline/_dag.py | 5 +- src/distilabel/steps/argilla/base.py | 8 +-- src/distilabel/steps/base.py | 48 +++++++++++----- src/distilabel/steps/columns/expand.py | 6 +- src/distilabel/steps/columns/group.py | 6 +- src/distilabel/steps/columns/keep.py | 6 +- src/distilabel/steps/columns/merge.py | 6 +- src/distilabel/steps/decorator.py | 33 ++++++----- .../steps/embeddings/embedding_generation.py | 8 +-- .../steps/formatting/conversation.py | 8 +-- src/distilabel/steps/formatting/dpo.py | 18 ++++-- src/distilabel/steps/formatting/sft.py | 16 ++++-- src/distilabel/steps/reward_model.py | 6 +- .../steps/tasks/generate_embeddings.py | 8 +-- .../steps/tasks/improving_text_embeddings.py | 55 +++++++++++++++++++ src/distilabel/steps/tasks/magpie/base.py | 8 +-- .../steps/tasks/magpie/generator.py | 6 +- src/distilabel/steps/tasks/pair_rm.py | 8 +-- src/distilabel/steps/typing.py | 14 ++++- tests/unit/steps/test_base.py | 23 +++++++- 24 files changed, 275 insertions(+), 129 deletions(-) diff --git a/docs/sections/how_to_guides/basic/step/generator_step.md b/docs/sections/how_to_guides/basic/step/generator_step.md index c5b665a82e..0422644c36 100644 --- a/docs/sections/how_to_guides/basic/step/generator_step.md +++ b/docs/sections/how_to_guides/basic/step/generator_step.md @@ -3,17 +3,19 @@ The [`GeneratorStep`][distilabel.steps.GeneratorStep] is a subclass of [`Step`][distilabel.steps.Step] that is intended to be used as the first step within a [`Pipeline`][distilabel.pipeline.Pipeline], because it doesn't require input and generates data that can be used by other steps. Alternatively, it can also be used as a standalone. ```python -from typing import List +from typing import List, TYPE_CHECKING from typing_extensions import override from distilabel.steps import GeneratorStep -from distilabel.steps.typing import GeneratorStepOutput + +if TYPE_CHECKING: + from distilabel.steps.typing import StepColumns, GeneratorStepOutput class MyGeneratorStep(GeneratorStep): instructions: List[str] @override - def process(self, offset: int = 0) -> GeneratorStepOutput: + def process(self, offset: int = 0) -> "GeneratorStepOutput": if offset: self.instructions = self.instructions[offset:] @@ -30,7 +32,7 @@ class MyGeneratorStep(GeneratorStep): ) @property - def outputs(self) -> List[str]: + def outputs(self) -> "StepColumns": return ["instruction"] ``` @@ -57,7 +59,7 @@ next(step.process(offset=1)) We can define a custom generator step by creating a new subclass of the [`GeneratorStep`][distilabel.steps.GeneratorStep] and defining the following: -- `outputs`: is a property that returns a list of strings with the names of the output fields. +- `outputs`: is a property that returns a list of strings with the names of the output fields or a dictionary in which the keys are the names of the columns and the values are boolean indicating whether the column is required or not. - `process`: is a method that yields output data and a boolean flag indicating whether that's the last batch to be generated. @@ -73,21 +75,23 @@ We can define a custom generator step by creating a new subclass of the [`Genera ```python - from typing import List + from typing import List, TYPE_CHECKING from typing_extensions import override from distilabel.steps import GeneratorStep - from distilabel.steps.typing import GeneratorStepOutput + + if TYPE_CHECKING: + from distilabel.steps.typing import StepColumns, GeneratorStepOutput class MyGeneratorStep(GeneratorStep): instructions: List[str] @override - def process(self, offset: int = 0) -> GeneratorStepOutput: + def process(self, offset: int = 0) -> "GeneratorStepOutput": ... @property - def outputs(self) -> List[str]: + def outputs(self) -> "StepColumns": ... ``` @@ -96,15 +100,18 @@ We can define a custom generator step by creating a new subclass of the [`Genera The `@step` decorator will take care of the boilerplate code, and will allow to define the `outputs`, and `process` methods in a more straightforward way. One downside is that it won't let you access the `self` attributes if any, neither set those, so if you need to access or set any attribute, you should go with the first approach of defining the custom [`GeneratorStep`][distilabel.steps.GeneratorStep] subclass. ```python + from typing import TYPE_CHECKING from distilabel.steps import step - from distilabel.steps.typing import GeneratorStepOutput + + if TYPE_CHECKING: + from distilabel.steps.typing import GeneratorStepOutput @step(outputs=[...], step_type="generator") - def CustomGeneratorStep(offset: int = 0) -> GeneratorStepOutput: + def CustomGeneratorStep(offset: int = 0) -> "GeneratorStepOutput": yield ( ..., True if offset == 10 else False, ) step = CustomGeneratorStep(name="my-step") - ``` \ No newline at end of file + ``` diff --git a/docs/sections/how_to_guides/basic/step/global_step.md b/docs/sections/how_to_guides/basic/step/global_step.md index c9044a87d2..814f01a0fb 100644 --- a/docs/sections/how_to_guides/basic/step/global_step.md +++ b/docs/sections/how_to_guides/basic/step/global_step.md @@ -6,9 +6,9 @@ The [`GlobalStep`][distilabel.steps.GlobalStep] is a subclass of [`Step`][distil We can define a custom step by creating a new subclass of the [`GlobalStep`][distilabel.steps.GlobalStep] and defining the following: -- `inputs`: is a property that returns a list of strings with the names of the required input fields. +- `inputs`: is a property that returns a list of strings with the names of the required input fields or a dictionary in which the keys are the names of the columns and the values are boolean indicating whether the column is required or not. -- `outputs`: is a property that returns a list of strings with the names of the output fields. +- `outputs`: is a property that returns a list of strings with the names of the output fields or a dictionary in which the keys are the names of the columns and the values are boolean indicating whether the column is required or not. - `process`: is a method that receives the input data and returns the output data, and it should be a generator, meaning that it should `yield` the output data. @@ -23,20 +23,23 @@ We can define a custom step by creating a new subclass of the [`GlobalStep`][dis We can inherit from the `GlobalStep` class and define the `inputs`, `outputs`, and `process` methods as follows: ```python + from typing import TYPE_CHECKING from distilabel.steps import GlobalStep, StepInput - from distilabel.steps.typing import StepOutput + + if TYPE_CHECKING: + from distilabel.steps.typing import StepColumns, StepOutput class CustomStep(Step): @property - def inputs(self) -> List[str]: + def inputs(self) -> "StepColumns": ... @property - def outputs(self) -> List[str]: + def outputs(self) -> "StepColumns": ... def process(self, *inputs: StepInput) -> StepOutput: - for input in inputs: + for upstream_step_inputs in inputs: for item in input: ... yield item @@ -54,14 +57,17 @@ We can define a custom step by creating a new subclass of the [`GlobalStep`][dis The `@step` decorator will take care of the boilerplate code, and will allow to define the `inputs`, `outputs`, and `process` methods in a more straightforward way. One downside is that it won't let you access the `self` attributes if any, neither set those, so if you need to access or set any attribute, you should go with the first approach of defining the custom [`GlobalStep`][distilabel.steps.GlobalStep] subclass. ```python + from typing import TYPE_CHECKING from distilabel.steps import StepInput, step - from distilabel.steps.typing import StepOutput + + if TYPE_CHECKING: + from distilabel.steps.typing import StepOutput @step(inputs=[...], outputs=[...], step_type="global") - def CustomStep(inputs: StepInput) -> StepOutput: + def CustomStep(inputs: StepInput) -> "StepOutput": for input in inputs: ... yield inputs step = CustomStep(name="my-step") - ``` \ No newline at end of file + ``` diff --git a/docs/sections/how_to_guides/basic/step/index.md b/docs/sections/how_to_guides/basic/step/index.md index e3b19e5334..82040eefdc 100644 --- a/docs/sections/how_to_guides/basic/step/index.md +++ b/docs/sections/how_to_guides/basic/step/index.md @@ -7,13 +7,19 @@ The [`Step`][distilabel.steps.Step] is intended to be used within the scope of a Assuming that we have a [`Step`][distilabel.steps.Step] already defined as it follows: ```python +from typing import TYPE_CHECKING +from distilabel.steps import Step, StepInput + +if TYPE_CHECKING: + from distilabel.steps.typing import StepColumns, StepOutput + class MyStep(Step): @property - def inputs(self) -> List[str]: + def inputs(self) -> "StepColumns": return ["input_field"] @property - def outputs(self) -> List[str]: + def outputs(self) -> "StepColumns": return ["output_field"] def process(self, inputs: StepInput) -> "StepOutput": @@ -71,9 +77,9 @@ There are two special types of [`Step`][distilabel.steps.Step] in `distilabel`: We can define a custom step by creating a new subclass of the [`Step`][distilabel.steps.Step] and defining the following: -- `inputs`: is a property that returns a list of strings with the names of the required input fields. +- `inputs`: is a property that returns a list of strings with the names of the required input fields or a dictionary in which the keys are the names of the columns and the values are boolean indicating whether the column is required or not. -- `outputs`: is a property that returns a list of strings with the names of the output fields. +- `outputs`: is a property that returns a list of strings with the names of the output fields or a dictionary in which the keys are the names of the columns and the values are boolean indicating whether the column is required or not. - `process`: is a method that receives the input data and returns the output data, and it should be a generator, meaning that it should `yield` the output data. @@ -88,20 +94,23 @@ We can define a custom step by creating a new subclass of the [`Step`][distilabe We can inherit from the `Step` class and define the `inputs`, `outputs`, and `process` methods as follows: ```python + from typing import TYPE_CHECKING from distilabel.steps import Step, StepInput - from distilabel.steps.typing import StepOutput + + if TYPE_CHECKING: + from distilabel.steps.typing import StepColumns, StepOutput class CustomStep(Step): @property - def inputs(self) -> List[str]: + def inputs(self) -> "StepColumns": ... @property - def outputs(self) -> List[str]: + def outputs(self) -> "StepColumns": ... - def process(self, *inputs: StepInput) -> StepOutput: - for input in inputs: + def process(self, *inputs: StepInput) -> "StepOutput": + for upstream_step_inputs in inputs: ... yield item @@ -119,14 +128,17 @@ We can define a custom step by creating a new subclass of the [`Step`][distilabe ```python + from typing import TYPE_CHECKING from distilabel.steps import StepInput, step - from distilabel.steps.typing import StepOutput + + if TYPE_CHECKING: + from distilabel.steps.typing import StepOutput @step(inputs=[...], outputs=[...]) - def CustomStep(inputs: StepInput) -> StepOutput: + def CustomStep(inputs: StepInput) -> "StepOutput": for input in inputs: ... yield inputs step = CustomStep(name="my-step") - ``` \ No newline at end of file + ``` diff --git a/docs/sections/how_to_guides/basic/task/index.md b/docs/sections/how_to_guides/basic/task/index.md index c315fa1bb0..0bf90c3aad 100644 --- a/docs/sections/how_to_guides/basic/task/index.md +++ b/docs/sections/how_to_guides/basic/task/index.md @@ -134,27 +134,30 @@ next(task.process([{"instruction": "What's the capital of Spain?"}])) We can define a custom step by creating a new subclass of the [`Task`][distilabel.steps.tasks.Task] and defining the following: -- `inputs`: is a property that returns a list of strings with the names of the required input fields. +- `inputs`: is a property that returns a list of strings with the names of the required input fields or a dictionary in which the keys are the names of the columns and the values are boolean indicating whether the column is required or not. - `format_input`: is a method that receives a dictionary with the input data and returns a [`ChatType`][distilabel.steps.tasks.ChatType] following [the chat-completion OpenAI message formatting](https://platform.openai.com/docs/guides/text-generation). -- `outputs`: is a property that returns a list of strings with the names of the output fields, this property should always include `model_name` as one of the outputs since that's automatically injected from the LLM. +- `outputs`: is a property that returns a list of strings with the names of the output fields or a dictionary in which the keys are the names of the columns and the values are boolean indicating whether the column is required or not. This property should always include `model_name` as one of the outputs since that's automatically injected from the LLM. - `format_output`: is a method that receives the output from the [`LLM`][distilabel.llms.LLM] and optionally also the input data (which may be useful to build the output in some scenarios), and returns a dictionary with the output data formatted as needed i.e. with the values for the columns in `outputs`. Note that there's no need to include the `model_name` in the output. ```python -from typing import Any, Dict, List, Union +from typing import Any, Dict, List, Union, TYPE_CHECKING from distilabel.steps.tasks.base import Task -from distilabel.steps.tasks.typing import ChatType + +if TYPE_CHECKING: + from distilabel.steps.typing import StepColumns + from distilabel.steps.tasks.typing import ChatType class MyCustomTask(Task): @property - def inputs(self) -> List[str]: + def inputs(self) -> "StepColumns": return ["input_field"] - def format_input(self, input: Dict[str, Any]) -> ChatType: + def format_input(self, input: Dict[str, Any]) -> "ChatType": return [ { "role": "user", @@ -163,7 +166,7 @@ class MyCustomTask(Task): ] @property - def outputs(self) -> List[str]: + def outputs(self) -> "StepColumns": return ["output_field", "model_name"] def format_output( diff --git a/src/distilabel/pipeline/_dag.py b/src/distilabel/pipeline/_dag.py index 3253ef864d..c6bf9a73a3 100644 --- a/src/distilabel/pipeline/_dag.py +++ b/src/distilabel/pipeline/_dag.py @@ -374,9 +374,10 @@ def _step_inputs_are_available(self, step: "_Step") -> None: for output in self.get_step(step_name)[STEP_ATTR_NAME].get_outputs() # type: ignore ] step_inputs = step.get_inputs() - if not all(input in inputs_available_for_step for input in step_inputs): + required_inputs = [input for input, required in step_inputs.items() if required] + if not all(input in inputs_available_for_step for input in required_inputs): raise ValueError( - f"Step '{step.name}' requires inputs {step_inputs}, but only the inputs" + f"Step '{step.name}' requires inputs {required_inputs}, but only the inputs" f"={inputs_available_for_step} are available, which means that the inputs" f"={list(set(step_inputs) - set(inputs_available_for_step))} are missing or not" " available when the step gets to be executed in the pipeline." diff --git a/src/distilabel/steps/argilla/base.py b/src/distilabel/steps/argilla/base.py index 1de89d38cf..0d60ec89d0 100644 --- a/src/distilabel/steps/argilla/base.py +++ b/src/distilabel/steps/argilla/base.py @@ -15,7 +15,7 @@ import importlib.util import os from abc import ABC, abstractmethod -from typing import TYPE_CHECKING, Any, List, Optional +from typing import TYPE_CHECKING, Any, Optional from pydantic import Field, PrivateAttr, SecretStr @@ -30,7 +30,7 @@ if TYPE_CHECKING: from argilla import Argilla, Dataset - from distilabel.steps.typing import StepOutput + from distilabel.steps.typing import StepColumns, StepOutput _ARGILLA_API_URL_ENV_VAR_NAME = "ARGILLA_API_URL" @@ -128,7 +128,7 @@ def _dataset_exists_in_workspace(self) -> bool: ) @property - def outputs(self) -> List[str]: + def outputs(self) -> "StepColumns": """The outputs of the step is an empty list, since the steps subclassing from this one, will always be leaf nodes and won't propagate the inputs neither generate any outputs. """ @@ -151,7 +151,7 @@ def load(self) -> None: @property @abstractmethod - def inputs(self) -> List[str]: ... + def inputs(self) -> "StepColumns": ... @abstractmethod def process(self, *inputs: StepInput) -> "StepOutput": ... diff --git a/src/distilabel/steps/base.py b/src/distilabel/steps/base.py index 1aa6e8bb1d..a1f863781a 100644 --- a/src/distilabel/steps/base.py +++ b/src/distilabel/steps/base.py @@ -51,7 +51,7 @@ DownstreamConnectableSteps, UpstreamConnectableSteps, ) - from distilabel.steps.typing import GeneratorStepOutput, StepOutput + from distilabel.steps.typing import GeneratorStepOutput, StepColumns, StepOutput DEFAULT_INPUT_BATCH_SIZE = 50 @@ -381,8 +381,10 @@ def is_normal(self) -> bool: return not self.is_generator and not self.is_global @property - def inputs(self) -> List[str]: - """List of strings with the names of the columns that the step needs as input. + def inputs(self) -> "StepColumns": + """List of strings with the names of the mandatory columns that the step needs as + input or dictionary in which the keys are the input columns of the step and the + values are booleans indicating whether the column is optional or not. Returns: List of strings with the names of the columns that the step needs as input. @@ -390,9 +392,10 @@ def inputs(self) -> List[str]: return [] @property - def outputs(self) -> List[str]: + def outputs(self) -> "StepColumns": """List of strings with the names of the columns that the step will produce as - output. + output or dictionary in which the keys are the output columns of the step and the + values are booleans indicating whether the column is optional or not. Returns: List of strings with the names of the columns that the step will produce as @@ -479,23 +482,42 @@ def verify_outputs_mappings(self) -> None: " Please, review the `outputs_mappings` argument of the step." ) - def get_inputs(self) -> List[str]: + def get_inputs(self) -> Dict[str, bool]: """Gets the inputs of the step after the `input_mappings`. This method is meant to be used to run validations on the inputs of the step. Returns: - The inputs of the step after the `input_mappings`. + The inputs of the step after the `input_mappings` and if they are required or + not. """ - return [self.input_mappings.get(input, input) for input in self.inputs] + if isinstance(self.inputs, list): + return { + self.input_mappings.get(input, input): True for input in self.inputs + } - def get_outputs(self) -> List[str]: + return { + self.input_mappings.get(input, input): required + for input, required in self.inputs.items() + } + + def get_outputs(self) -> Dict[str, bool]: """Gets the outputs of the step after the `outputs_mappings`. This method is meant to be used to run validations on the outputs of the step. Returns: - The outputs of the step after the `outputs_mappings`. + The outputs of the step after the `outputs_mappings` and if they are required + or not. """ - return [self.output_mappings.get(output, output) for output in self.outputs] + if isinstance(self.outputs, list): + return { + self.output_mappings.get(output, output): True + for output in self.outputs + } + + return { + self.output_mappings.get(output, output): required + for output, required in self.outputs.items() + } def set_pipeline_artifacts_path(self, path: Path) -> None: """Sets the `_pipeline_artifacts_path` attribute. This method is meant to be used @@ -729,9 +751,9 @@ class GlobalStep(Step, ABC): """ @property - def inputs(self) -> List[str]: + def inputs(self) -> "StepColumns": return [] @property - def outputs(self) -> List[str]: + def outputs(self) -> "StepColumns": return [] diff --git a/src/distilabel/steps/columns/expand.py b/src/distilabel/steps/columns/expand.py index 7312e1a4fd..84cec77121 100644 --- a/src/distilabel/steps/columns/expand.py +++ b/src/distilabel/steps/columns/expand.py @@ -20,7 +20,7 @@ from distilabel.steps.base import Step, StepInput if TYPE_CHECKING: - from distilabel.steps.typing import StepOutput + from distilabel.steps.typing import StepColumns, StepOutput class ExpandColumns(Step): @@ -87,12 +87,12 @@ def always_dict(cls, value: Union[Dict[str, str], List[str]]) -> Dict[str, str]: return value @property - def inputs(self) -> List[str]: + def inputs(self) -> "StepColumns": """The columns to be expanded.""" return list(self.columns.keys()) @property - def outputs(self) -> List[str]: + def outputs(self) -> "StepColumns": """The expanded columns.""" return [ new_column if new_column else expand_column diff --git a/src/distilabel/steps/columns/group.py b/src/distilabel/steps/columns/group.py index ff761b07a1..87afe174d2 100644 --- a/src/distilabel/steps/columns/group.py +++ b/src/distilabel/steps/columns/group.py @@ -21,7 +21,7 @@ from distilabel.steps.base import Step, StepInput if TYPE_CHECKING: - from distilabel.steps.typing import StepOutput + from distilabel.steps.typing import StepColumns, StepOutput class GroupColumns(Step): @@ -93,12 +93,12 @@ class GroupColumns(Step): output_columns: Optional[List[str]] = None @property - def inputs(self) -> List[str]: + def inputs(self) -> "StepColumns": """The inputs for the task are the column names in `columns`.""" return self.columns @property - def outputs(self) -> List[str]: + def outputs(self) -> "StepColumns": """The outputs for the task are the column names in `output_columns` or `grouped_{column}` for each column in `columns`.""" return ( diff --git a/src/distilabel/steps/columns/keep.py b/src/distilabel/steps/columns/keep.py index 58380660fa..84f889072a 100644 --- a/src/distilabel/steps/columns/keep.py +++ b/src/distilabel/steps/columns/keep.py @@ -19,7 +19,7 @@ from distilabel.steps.base import Step, StepInput if TYPE_CHECKING: - from distilabel.steps.typing import StepOutput + from distilabel.steps.typing import StepColumns, StepOutput class KeepColumns(Step): @@ -69,12 +69,12 @@ class KeepColumns(Step): columns: List[str] @property - def inputs(self) -> List[str]: + def inputs(self) -> "StepColumns": """The inputs for the task are the column names in `columns`.""" return self.columns @property - def outputs(self) -> List[str]: + def outputs(self) -> "StepColumns": """The outputs for the task are the column names in `columns`.""" return self.columns diff --git a/src/distilabel/steps/columns/merge.py b/src/distilabel/steps/columns/merge.py index 390de687d0..3b9f295c93 100644 --- a/src/distilabel/steps/columns/merge.py +++ b/src/distilabel/steps/columns/merge.py @@ -20,7 +20,7 @@ from distilabel.steps.base import Step, StepInput if TYPE_CHECKING: - from distilabel.steps.typing import StepOutput + from distilabel.steps.typing import StepColumns, StepOutput class MergeColumns(Step): @@ -79,11 +79,11 @@ class MergeColumns(Step): output_column: Optional[str] = None @property - def inputs(self) -> List[str]: + def inputs(self) -> "StepColumns": return self.columns @property - def outputs(self) -> List[str]: + def outputs(self) -> "StepColumns": return [self.output_column] if self.output_column else ["merged_column"] @override diff --git a/src/distilabel/steps/decorator.py b/src/distilabel/steps/decorator.py index da2cbb8dcc..0816ca13eb 100644 --- a/src/distilabel/steps/decorator.py +++ b/src/distilabel/steps/decorator.py @@ -37,7 +37,7 @@ if TYPE_CHECKING: from distilabel.steps.base import _Step - from distilabel.steps.typing import GeneratorStepOutput, StepOutput + from distilabel.steps.typing import GeneratorStepOutput, StepColumns, StepOutput _STEP_MAPPING = { "normal": Step, @@ -50,16 +50,16 @@ @overload def step( - inputs: Union[List[str], None] = None, - outputs: Union[List[str], None] = None, + inputs: Union["StepColumns", None] = None, + outputs: Union["StepColumns", None] = None, step_type: Literal["normal"] = "normal", ) -> Callable[..., Type["Step"]]: ... @overload def step( - inputs: Union[List[str], None] = None, - outputs: Union[List[str], None] = None, + inputs: Union["StepColumns", None] = None, + outputs: Union["StepColumns", None] = None, step_type: Literal["global"] = "global", ) -> Callable[..., Type["GlobalStep"]]: ... @@ -67,26 +67,29 @@ def step( @overload def step( inputs: None = None, - outputs: Union[List[str], None] = None, + outputs: Union["StepColumns", None] = None, step_type: Literal["generator"] = "generator", ) -> Callable[..., Type["GeneratorStep"]]: ... def step( - inputs: Union[List[str], None] = None, - outputs: Union[List[str], None] = None, + inputs: Union["StepColumns", None] = None, + outputs: Union["StepColumns", None] = None, step_type: Literal["normal", "global", "generator"] = "normal", ) -> Callable[..., Type["_Step"]]: """Creates an `Step` from a processing function. Args: - inputs: a list containing the name of the inputs columns/keys expected by this step. - If not provided the default will be an empty list `[]` and it will be assumed - that the step doesn't need any specific columns. Defaults to `None`. - outputs: a list containing the name of the outputs columns/keys that the step - will generate. If not provided the default will be an empty list `[]` and it - will be assumed that the step doesn't need any specific columns. Defaults to - `None`. + inputs: a list containing the name of the inputs columns/keys or a dictionary + where the keys are the columns and the values are booleans indicating whether + the column is required or not, that are required by the step. If not provided + the default will be an empty list `[]` and it will be assumed that the step + doesn't need any specific columns. Defaults to `None`. + outputs: a list containing the name of the outputs columns/keys or a dictionary + where the keys are the columns and the values are booleans indicating whether + the column will be generated or not. If not provided the default will be an + empty list `[]` and it will be assumed that the step doesn't need any specific + columns. Defaults to `None`. step_type: the kind of step to create. Valid choices are: "normal" (`Step`), "global" (`GlobalStep`) or "generator" (`GeneratorStep`). Defaults to `"normal"`. diff --git a/src/distilabel/steps/embeddings/embedding_generation.py b/src/distilabel/steps/embeddings/embedding_generation.py index 55e8838274..d97e1da03a 100644 --- a/src/distilabel/steps/embeddings/embedding_generation.py +++ b/src/distilabel/steps/embeddings/embedding_generation.py @@ -12,13 +12,13 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import TYPE_CHECKING, List +from typing import TYPE_CHECKING from distilabel.embeddings.base import Embeddings from distilabel.steps.base import Step, StepInput if TYPE_CHECKING: - from distilabel.steps.typing import StepOutput + from distilabel.steps.typing import StepColumns, StepOutput class EmbeddingGeneration(Step): @@ -61,11 +61,11 @@ class EmbeddingGeneration(Step): embeddings: Embeddings @property - def inputs(self) -> List[str]: + def inputs(self) -> "StepColumns": return ["text"] @property - def outputs(self) -> List[str]: + def outputs(self) -> "StepColumns": return ["embedding", "model_name"] def load(self) -> None: diff --git a/src/distilabel/steps/formatting/conversation.py b/src/distilabel/steps/formatting/conversation.py index 22cc582c54..95c2369ad5 100644 --- a/src/distilabel/steps/formatting/conversation.py +++ b/src/distilabel/steps/formatting/conversation.py @@ -12,12 +12,12 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import TYPE_CHECKING, List +from typing import TYPE_CHECKING from distilabel.steps.base import Step, StepInput if TYPE_CHECKING: - from distilabel.steps.typing import StepOutput + from distilabel.steps.typing import StepColumns, StepOutput class ConversationTemplate(Step): @@ -61,12 +61,12 @@ class ConversationTemplate(Step): """ @property - def inputs(self) -> List[str]: + def inputs(self) -> "StepColumns": """The instruction and response.""" return ["instruction", "response"] @property - def outputs(self) -> List[str]: + def outputs(self) -> "StepColumns": """The conversation template.""" return ["conversation"] diff --git a/src/distilabel/steps/formatting/dpo.py b/src/distilabel/steps/formatting/dpo.py index 9402436ee9..3c0e7355db 100644 --- a/src/distilabel/steps/formatting/dpo.py +++ b/src/distilabel/steps/formatting/dpo.py @@ -18,7 +18,7 @@ from distilabel.steps.base import Step, StepInput if TYPE_CHECKING: - from distilabel.steps.typing import StepOutput + from distilabel.steps.typing import StepColumns, StepOutput class FormatTextGenerationDPO(Step): @@ -103,10 +103,16 @@ class FormatTextGenerationDPO(Step): """ @property - def inputs(self) -> List[str]: + def inputs(self) -> "StepColumns": """List of inputs required by the `Step`, which in this case are: `instruction`, `generations`, and `ratings`.""" - return ["instruction", "generations", "ratings"] + return { + "system_prompt": False, + "instruction": True, + "generations": True, + "generation_models": False, + "ratings": True, + } @property def optional_inputs(self) -> List[str]: @@ -115,7 +121,7 @@ def optional_inputs(self) -> List[str]: return ["system_prompt", "generation_models"] @property - def outputs(self) -> List[str]: + def outputs(self) -> "StepColumns": """List of outputs generated by the `Step`, which are: `prompt`, `prompt_id`, `chosen`, `chosen_model`, `chosen_rating`, `rejected`, `rejected_model`, `rejected_rating`. Both the `chosen_model` and `rejected_model` being optional and only used if `generation_models` @@ -272,7 +278,7 @@ class FormatChatGenerationDPO(Step): """ @property - def inputs(self) -> List[str]: + def inputs(self) -> "StepColumns": """List of inputs required by the `Step`, which in this case are: `messages`, `generations`, and `ratings`.""" return ["messages", "generations", "ratings"] @@ -284,7 +290,7 @@ def optional_inputs(self) -> List[str]: return ["generation_models"] @property - def outputs(self) -> List[str]: + def outputs(self) -> "StepColumns": """List of outputs generated by the `Step`, which are: `prompt`, `prompt_id`, `chosen`, `chosen_model`, `chosen_rating`, `rejected`, `rejected_model`, `rejected_rating`. Both the `chosen_model` and `rejected_model` being optional and only used if `generation_models` diff --git a/src/distilabel/steps/formatting/sft.py b/src/distilabel/steps/formatting/sft.py index ec93aadf79..838512f85f 100644 --- a/src/distilabel/steps/formatting/sft.py +++ b/src/distilabel/steps/formatting/sft.py @@ -18,7 +18,7 @@ from distilabel.steps.base import Step, StepInput if TYPE_CHECKING: - from distilabel.steps.typing import StepOutput + from distilabel.steps.typing import StepColumns, StepOutput class FormatTextGenerationSFT(Step): @@ -84,9 +84,13 @@ class FormatTextGenerationSFT(Step): """ @property - def inputs(self) -> List[str]: + def inputs(self) -> "StepColumns": """List of inputs required by the `Step`, which in this case are: `instruction`, and `generation`.""" - return ["instruction", "generation"] + return { + "system_prompt": False, + "instruction": True, + "generation": True, + } @property def optional_inputs(self) -> List[str]: @@ -95,7 +99,7 @@ def optional_inputs(self) -> List[str]: return ["system_prompt"] @property - def outputs(self) -> List[str]: + def outputs(self) -> "StepColumns": """List of outputs generated by the `Step`, which are: `prompt`, `prompt_id`, `messages`. Reference: @@ -201,12 +205,12 @@ class FormatChatGenerationSFT(Step): """ @property - def inputs(self) -> List[str]: + def inputs(self) -> "StepColumns": """List of inputs required by the `Step`, which in this case are: `instruction`, and `generation`.""" return ["messages", "generation"] @property - def outputs(self) -> List[str]: + def outputs(self) -> "StepColumns": """List of outputs generated by the `Step`, which are: `prompt`, `prompt_id`, `messages`. Reference: diff --git a/src/distilabel/steps/reward_model.py b/src/distilabel/steps/reward_model.py index 1617d86bc3..b6d68d0f47 100644 --- a/src/distilabel/steps/reward_model.py +++ b/src/distilabel/steps/reward_model.py @@ -26,7 +26,7 @@ from transformers import PreTrainedModel, PreTrainedTokenizer from distilabel.steps.tasks.typing import ChatType - from distilabel.steps.typing import StepOutput + from distilabel.steps.typing import StepColumns, StepOutput class RewardModelScore(Step, CudaDevicePlacementMixin): @@ -179,12 +179,12 @@ def load(self) -> None: ) @property - def inputs(self) -> List[str]: + def inputs(self) -> "StepColumns": """Either `response` and `instruction`, or a `conversation` columns.""" return [] @property - def outputs(self) -> List[str]: + def outputs(self) -> "StepColumns": """The `score` given by the reward model.""" return ["score"] diff --git a/src/distilabel/steps/tasks/generate_embeddings.py b/src/distilabel/steps/tasks/generate_embeddings.py index 70edbd564a..e9a4db7756 100644 --- a/src/distilabel/steps/tasks/generate_embeddings.py +++ b/src/distilabel/steps/tasks/generate_embeddings.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import TYPE_CHECKING, Any, Dict, List +from typing import TYPE_CHECKING, Any, Dict from distilabel.llms.base import LLM from distilabel.steps.base import Step, StepInput @@ -20,7 +20,7 @@ if TYPE_CHECKING: from distilabel.steps.tasks.typing import ChatType - from distilabel.steps.typing import StepOutput + from distilabel.steps.typing import StepColumns, StepOutput class GenerateEmbeddings(Step): @@ -99,13 +99,13 @@ def load(self) -> None: self.llm.load() @property - def inputs(self) -> List[str]: + def inputs(self) -> "StepColumns": """The inputs for the task is a `text` column containing either a string or a list of dictionaries in OpenAI chat-like format.""" return ["text"] @property - def outputs(self) -> List[str]: + def outputs(self) -> "StepColumns": """The outputs for the task is an `embedding` column containing the embedding of the `text` input.""" return ["embedding", "model_name"] diff --git a/src/distilabel/steps/tasks/improving_text_embeddings.py b/src/distilabel/steps/tasks/improving_text_embeddings.py index 64465c9d61..77ec51d22f 100644 --- a/src/distilabel/steps/tasks/improving_text_embeddings.py +++ b/src/distilabel/steps/tasks/improving_text_embeddings.py @@ -256,6 +256,11 @@ class EmbeddingTaskGenerator(GeneratorTask): with one row only containing a list with around 20 tasks; otherwise, if set to `True`, it will return a `distilabel.Distiset` with around 20 rows, each containing one task. + Output columns: + - tasks (`List[str]`): the list of tasks generated by the `LLM`. + - task (`str`): the task generated by the `LLM` if `flatten_tasks=True`. + - model_name (`str`): the name of the model used to generate the tasks. + References: - [Improving Text Embeddings with Large Language Models](https://arxiv.org/abs/2401.00368) @@ -427,6 +432,15 @@ class GenerateTextRetrievalData(_EmbeddingDataGeneration): Defaults to `None`, meaning that it will be randomly sampled. seed: The random seed to be set in case there's any sampling within the `format_input` method. + Input columns: + - task (`str`): The task description to be used in the generation. + + Output columns: + - user_query (`str`): the user query generated by the `LLM`. + - positive_document (`str`): the positive document generated by the `LLM`. + - hard_negative_document (`str`): the hard negative document generated by the `LLM`. + - model_name (`str`): the name of the model used to generate the text retrieval data. + References: - [Improving Text Embeddings with Large Language Models](https://arxiv.org/abs/2401.00368) @@ -541,6 +555,15 @@ class GenerateShortTextMatchingData(_EmbeddingDataGeneration): seed: The random seed to be set in case there's any sampling within the `format_input` method. Note that in this task the `seed` has no effect since there are no sampling params. + Input columns: + - task (`str`): The task description to be used in the generation. + + Output columns: + - input (`str`): the input generated by the `LLM`. + - positive_document (`str`): the positive document generated by the `LLM`. + - model_name (`str`): the name of the model used to generate the short text matching + data. + References: - [Improving Text Embeddings with Large Language Models](https://arxiv.org/abs/2401.00368) @@ -622,6 +645,15 @@ class GenerateLongTextMatchingData(_EmbeddingDataGeneration): seed: The random seed to be set in case there's any sampling within the `format_input` method. Note that in this task the `seed` has no effect since there are no sampling params. + Input columns: + - task (`str`): The task description to be used in the generation. + + Output columns: + - input (`str`): the input generated by the `LLM`. + - positive_document (`str`): the positive document generated by the `LLM`. + - model_name (`str`): the name of the model used to generate the long text matching + data. + References: - [Improving Text Embeddings with Large Language Models](https://arxiv.org/abs/2401.00368) @@ -706,6 +738,16 @@ class GenerateTextClassificationData(_EmbeddingDataGeneration): or `ambiguous`. Defaults to `None`, meaning that it will be randomly sampled. seed: The random seed to be set in case there's any sampling within the `format_input` method. + Input columns: + - task (`str`): The task description to be used in the generation. + + Output columns: + - input_text (`str`): the input text generated by the `LLM`. + - label (`str`): the label generated by the `LLM`. + - misleading_label (`str`): the misleading label generated by the `LLM`. + - model_name (`str`): the name of the model used to generate the text classification + data. + References: - [Improving Text Embeddings with Large Language Models](https://arxiv.org/abs/2401.00368) @@ -802,6 +844,12 @@ class MonolingualTripletGenerator(_EmbeddingDataGenerator): Defaults to `None`, meaning that it will be randomly sampled. seed: The random seed to be set in case there's any sampling within the `format_input` method. + Output columns: + - S1 (`str`): the first sentence generated by the `LLM`. + - S2 (`str`): the second sentence generated by the `LLM`. + - S3 (`str`): the third sentence generated by the `LLM`. + - model_name (`str`): the name of the model used to generate the monolingual triplets. + Examples: Generate monolingual triplets for training embedding models: @@ -887,6 +935,13 @@ class BitextRetrievalGenerator(_EmbeddingDataGenerator): Defaults to `None`, meaning that it will be randomly sampled. seed: The random seed to be set in case there's any sampling within the `format_input` method. + Output columns: + - S1 (`str`): the first sentence generated by the `LLM`. + - S2 (`str`): the second sentence generated by the `LLM`. + - S3 (`str`): the third sentence generated by the `LLM`. + - model_name (`str`): the name of the model used to generate the bitext retrieval + data. + Examples: Generate bitext retrieval data for training embedding models: diff --git a/src/distilabel/steps/tasks/magpie/base.py b/src/distilabel/steps/tasks/magpie/base.py index 3a611f73a2..1f702b95a6 100644 --- a/src/distilabel/steps/tasks/magpie/base.py +++ b/src/distilabel/steps/tasks/magpie/base.py @@ -28,7 +28,7 @@ if TYPE_CHECKING: from distilabel.steps.tasks.typing import ChatType - from distilabel.steps.typing import StepOutput + from distilabel.steps.typing import StepColumns, StepOutput MAGPIE_MULTI_TURN_SYSTEM_PROMPT = ( "You are a helpful Al assistant. The user will engage in a multi−round conversation" @@ -448,15 +448,15 @@ def model_post_init(self, __context: Any) -> None: self.llm.use_magpie_template = True @property - def inputs(self) -> List[str]: - return [] + def inputs(self) -> "StepColumns": + return {"system_prompt": False} def format_input(self, input: Dict[str, Any]) -> "ChatType": """Does nothing.""" return [] @property - def outputs(self) -> List[str]: + def outputs(self) -> "StepColumns": """Either a multi-turn conversation or the instruction generated.""" if self.only_instruction: return ["instruction", "model_name"] diff --git a/src/distilabel/steps/tasks/magpie/generator.py b/src/distilabel/steps/tasks/magpie/generator.py index 33a910ef34..e0fb0e6bb3 100644 --- a/src/distilabel/steps/tasks/magpie/generator.py +++ b/src/distilabel/steps/tasks/magpie/generator.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import TYPE_CHECKING, Any, Dict, List, Union +from typing import TYPE_CHECKING, Any, Dict, Union from pydantic import Field @@ -22,7 +22,7 @@ from distilabel.steps.tasks.magpie.base import MagpieBase if TYPE_CHECKING: - from distilabel.steps.typing import GeneratorStepOutput + from distilabel.steps.typing import GeneratorStepOutput, StepColumns class MagpieGenerator(GeneratorTask, MagpieBase): @@ -244,7 +244,7 @@ def format_output( return {} @property - def outputs(self) -> List[str]: + def outputs(self) -> "StepColumns": """Either a multi-turn conversation or the instruction generated.""" if self.only_instruction: return ["instruction", "model_name"] diff --git a/src/distilabel/steps/tasks/pair_rm.py b/src/distilabel/steps/tasks/pair_rm.py index d4e20c22bf..c9ec217fdb 100644 --- a/src/distilabel/steps/tasks/pair_rm.py +++ b/src/distilabel/steps/tasks/pair_rm.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import TYPE_CHECKING, Any, Dict, List, Optional +from typing import TYPE_CHECKING, Any, Dict, Optional import numpy as np @@ -20,7 +20,7 @@ from distilabel.steps.tasks.base import Step if TYPE_CHECKING: - from distilabel.steps.typing import StepOutput + from distilabel.steps.typing import StepColumns, StepOutput class PairRM(Step): @@ -114,13 +114,13 @@ def load(self) -> None: self._blender.loadranker(self.model) @property - def inputs(self) -> List[str]: + def inputs(self) -> "StepColumns": """The input columns correspond to the two required arguments from `Blender.rank`: `inputs` and `candidates`.""" return ["input", "candidates"] @property - def outputs(self) -> List[str]: + def outputs(self) -> "StepColumns": """The outputs will include the `ranks` and the `ranked_candidates`.""" return ["ranks", "ranked_candidates", "model_name"] diff --git a/src/distilabel/steps/typing.py b/src/distilabel/steps/typing.py index 3bdd2e2553..720037a74f 100644 --- a/src/distilabel/steps/typing.py +++ b/src/distilabel/steps/typing.py @@ -12,10 +12,18 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Any, Dict, Iterator, List, Tuple +from typing import Any, Dict, Iterator, List, Tuple, Union StepOutput = Iterator[List[Dict[str, Any]]] -"""StepOutput is an alias of the typing `Iterator[List[Dict[str, Any]]]`""" +"""`StepOutput` is an alias of the typing `Iterator[List[Dict[str, Any]]]`""" GeneratorStepOutput = Iterator[Tuple[List[Dict[str, Any]], bool]] -"""GeneratorStepOutput is an alias of the typing `Iterator[Tuple[List[Dict[str, Any]], bool]]`""" +"""`GeneratorStepOutput` is an alias of the typing `Iterator[Tuple[List[Dict[str, Any]], bool]]`""" + +StepColumns = Union[List[str], Dict[str, bool]] +"""`StepColumns` is an alias of the typing `Union[List[str], Dict[str, bool]]` used by the +`inputs` and `outputs` properties of an `Step`. In the case of a `List[str]`, it is a list +with the required columns. In the case of a `Dict[str, bool]`, it is a dictionary where +the keys are the columns and the values are booleans indicating whether the column is +required or not. +""" diff --git a/tests/unit/steps/test_base.py b/tests/unit/steps/test_base.py index daf95bedb7..73b112891a 100644 --- a/tests/unit/steps/test_base.py +++ b/tests/unit/steps/test_base.py @@ -162,7 +162,18 @@ def test_get_inputs(self) -> None: pipeline=Pipeline(name="unit-test-pipeline"), input_mappings={"instruction": "prompt"}, ) - assert step.get_inputs() == ["prompt"] + assert step.get_inputs() == {"prompt": True} + + def test_get_inputs_with_dict(self) -> None: + @step(inputs={"instruction": False, "completion": True}, outputs=["score"]) + def DummyStepWithDict(input: StepInput): + pass + + dummy_step_with_dict = DummyStepWithDict() + assert dummy_step_with_dict.get_inputs() == { + "instruction": False, + "completion": True, + } def test_get_outputs(self) -> None: step = DummyStep( @@ -170,7 +181,15 @@ def test_get_outputs(self) -> None: pipeline=Pipeline(name="unit-test-pipeline"), output_mappings={"response": "generation"}, ) - assert step.get_outputs() == ["generation"] + assert step.get_outputs() == {"generation": True} + + def test_get_outputs_with_dict(self) -> None: + @step(outputs={"score": False}) + def DummyStepWithDict(input: StepInput): + pass + + dummy_step_with_dict = DummyStepWithDict() + assert dummy_step_with_dict.get_outputs() == {"score": False} def test_apply_input_mappings(self) -> None: step = DummyStep( From ed874ba17acd45b2aaaff9d6a864445fe04a42fe Mon Sep 17 00:00:00 2001 From: Agus Date: Thu, 15 Aug 2024 08:10:53 +0200 Subject: [PATCH 19/82] Update mistrallm (#904) * Update mistralai client to version 1.*.* * Update tests for new mistral client --- pyproject.toml | 2 +- src/distilabel/llms/mistral.py | 15 ++++++++------- tests/unit/llms/test_mistral.py | 4 +++- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index c4617885fd..da50920dd8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -79,7 +79,7 @@ hf-transformers = ["transformers >= 4.34.1", "torch >= 2.0.0"] instructor = ["instructor >= 1.2.3"] litellm = ["litellm >= 1.30.0"] llama-cpp = ["llama-cpp-python >= 0.2.0"] -mistralai = ["mistralai >= 0.1.0"] +mistralai = ["mistralai >= 1.0.0"] ollama = ["ollama >= 0.1.7"] openai = ["openai >= 1.0.0"] outlines = ["outlines >= 0.0.40"] diff --git a/src/distilabel/llms/mistral.py b/src/distilabel/llms/mistral.py index ed1c3af7d5..73b5fc13a9 100644 --- a/src/distilabel/llms/mistral.py +++ b/src/distilabel/llms/mistral.py @@ -26,7 +26,7 @@ ) if TYPE_CHECKING: - from mistralai.async_client import MistralAsyncClient + from mistralai import Mistral _MISTRALAI_API_KEY_ENV_VAR_NAME = "MISTRAL_API_KEY" @@ -50,7 +50,7 @@ class MistralLLM(AsyncLLM): `InstructorStructuredOutputType` from `distilabel.steps.tasks.structured_outputs.instructor`. _api_key_env_var: the name of the environment variable to use for the API key. It is meant to be used internally. - _aclient: the `MistralAsyncClient` to use for the Mistral API. It is meant to be used internally. + _aclient: the `Mistral` to use for the Mistral API. It is meant to be used internally. Set in the `load` method. Runtime parameters: @@ -126,14 +126,14 @@ class User(BaseModel): _num_generations_param_supported = False _api_key_env_var: str = PrivateAttr(_MISTRALAI_API_KEY_ENV_VAR_NAME) - _aclient: Optional["MistralAsyncClient"] = PrivateAttr(...) + _aclient: Optional["Mistral"] = PrivateAttr(...) def load(self) -> None: - """Loads the `MistralAsyncClient` client to benefit from async requests.""" + """Loads the `Mistral` client to benefit from async requests.""" super().load() try: - from mistralai.async_client import MistralAsyncClient + from mistralai import Mistral except ImportError as ie: raise ImportError( "MistralAI Python client is not installed. Please install it using" @@ -146,7 +146,7 @@ def load(self) -> None: f" attribute or runtime parameter, or set the environment variable `{self._api_key_env_var}`." ) - self._aclient = MistralAsyncClient( + self._aclient = Mistral( api_key=self.api_key.get_secret_value(), endpoint=self.endpoint, max_retries=self.max_retries, # type: ignore @@ -218,7 +218,8 @@ async def agenerate( # type: ignore # We need to check instructor and see if we can create a PR. completion = await self._aclient.chat.completions.create(**kwargs) # type: ignore else: - completion = await self._aclient.chat(**kwargs) # type: ignore + # completion = await self._aclient.chat(**kwargs) # type: ignore + completion = await self._aclient.chat.complete_async(**kwargs) # type: ignore if structured_output: generations.append(completion.model_dump_json()) diff --git a/tests/unit/llms/test_mistral.py b/tests/unit/llms/test_mistral.py index 5bb2337481..89f8e9649f 100644 --- a/tests/unit/llms/test_mistral.py +++ b/tests/unit/llms/test_mistral.py @@ -97,7 +97,9 @@ async def test_generate(self, mock_mistral: MagicMock) -> None: mocked_completion = Mock( choices=[Mock(message=Mock(content=" Aenean hendrerit aliquam velit. ..."))] ) - llm._aclient.chat = AsyncMock(return_value=mocked_completion) + llm._aclient.chat = Mock( + complete_async=AsyncMock(return_value=mocked_completion) + ) nest_asyncio.apply() From 10fff29c58b8054de191455bb57b5794484c3741 Mon Sep 17 00:00:00 2001 From: Agus Date: Thu, 15 Aug 2024 17:59:34 +0200 Subject: [PATCH 20/82] Deepseek prover (#907) * Add deepseek prover autoformalization task * Add task for the scorer as a jinja template to make it easy to maintain * Add deepseek prover scorer task * Add tests for the scorer task * Redirect import * Create a folder for the deepseek-prover templates * Make generator task more general including few shot examples * Remove the few shot argument as we can determine by just checking for examples * Remove deepseek-prover from the core as they are not that relevant for general pipelines * Add deepseek prover pipeline * Add entry for the paper implementation * Remove tests * Remove import * Remove redirected import --- .../tutorials-assets/deepseek_prover.png | Bin 0 -> 74899 bytes .../papers/deepseek_prover.md | 304 +++++++++++ examples/deepseek_prover.py | 502 ++++++++++++++++++ mkdocs.yml | 1 + 4 files changed, 807 insertions(+) create mode 100644 docs/assets/tutorials-assets/deepseek_prover.png create mode 100644 docs/sections/pipeline_samples/papers/deepseek_prover.md create mode 100644 examples/deepseek_prover.py diff --git a/docs/assets/tutorials-assets/deepseek_prover.png b/docs/assets/tutorials-assets/deepseek_prover.png new file mode 100644 index 0000000000000000000000000000000000000000..540fa5a624a98c2103d91caec49588d3ca8e9186 GIT binary patch literal 74899 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 +DeepSeekProverAutoFormalization + +```python +_PARSE_DEEPSEEK_PROVER_AUTOFORMAL_REGEX = r"```lean4(.*?)```" + +template_deepseek_prover_auto_formalization = """\ +Mathematical Problem in Natural Language: +{{ informal_statement }} +{%- if few_shot %} + +Please use the following examples to guide you with the answer: +{%- for example in examples %} +- {{ example }} +{%- endfor %} +{% endif -%}""" + + +class DeepSeekProverAutoFormalization(Task): + examples: Optional[List[str]] = None + system_prompt: str = "Translate the problem to Lean 4 (only the core declaration):\n```lean4\nformal statement goes here\n```" + _template: Union[Template, None] = PrivateAttr(...) + _few_shot: bool = PrivateAttr(default=False) + + def load(self) -> None: + super().load() + self._template = Template(template_deepseek_prover_auto_formalization) + + @property + def inputs(self) -> List[str]: + return ["informal_statement"] + + @property + def outputs(self): + return ["formal_statement", "model_name"] + + def format_input(self, input: str) -> ChatType: # type: ignore + return [ + { + "role": "system", + "content": self.system_prompt, + }, + { + "role": "user", + "content": self._template.render( + informal_statement=input[self.inputs[0]], + few_shot=bool(self.examples), + examples=self.examples, + ), + }, + ] + + @override + def format_output( # type: ignore + self, output: Union[str, None], input: Dict[str, Any] = None + ) -> Dict[str, Any]: # type: ignore + match = re.search(_PARSE_DEEPSEEK_PROVER_AUTOFORMAL_REGEX, output, re.DOTALL) + if match: + match = match.group(1).strip() + return {"formal_statement": match} +``` + + + +Following the paper, they found that the model yields better results if it uses examples in a few shot setting, so this class allows to take some examples to help in generating the formulation. Let's see an example of how we can instantiate it: + +```python +from textwrap import dedent + +examples = [ + dedent(""" + ## Statement in natural language: + For real numbers k and x: + If x is equal to (13 - √131) / 4, and + If the equation 2x² - 13x + k = 0 is satisfied, + Then k must be equal to 19/4. + ## Formalized: + theorem mathd_algebra_116 (k x : ℝ) (h₀ : x = (13 - Real.sqrt 131) / 4) + (h₁ : 2 * x ^ 2 - 13 * x + k = 0) : k = 19 / 4 :="""), + dedent(""" + ## Statement in natural language: + The greatest common divisor (GCD) of 20 factorial (20!) and 200,000 is equal to 40,000. + ## Formalized: + theorem mathd_algebra_116 (k x : ℝ) (h₀ : x = (13 - Real.sqrt 131) / 4) + (h₁ : 2 * x ^ 2 - 13 * x + k = 0) : k = 19 / 4 :="""), + dedent(""" + ## Statement in natural language: + Given two integers x and y: + If y is positive (greater than 0), + And y is less than x, + And the equation x + y + xy = 80 is true, + Then x must be equal to 26. + ## Formalized: + theorem mathd_algebra_116 (k x : ℝ) (h₀ : x = (13 - Real.sqrt 131) / 4) + (h₁ : 2 * x ^ 2 - 13 * x + k = 0) : k = 19 / 4 :="""), +] + +auto_formalization = DeepSeekProverAutoFormalization( + name="auto_formalization", + input_batch_size=8, + llm=llm, + examples=examples +) +``` + +### DeepSeekProverScorer + +The next `Task` corresponds to the second step, the model scoring and assessment. It uses an LLM as judge to evaluate the relevance of the theorem, and assigns a score so it can be filtered afterwards. + +
+DeepSeekProverScorer + +```python +template_deepseek_prover_scorer = """\ +To evaluate whether a formal Lean4 statement will be of interest to the community, consider the following criteria: + +1. Relevance to Current Research: Does the statement address a problem or concept that is actively being researched in mathematics or related fields? Higher relevance scores indicate greater potential interest. +2. Complexity and Depth: Is the statement complex enough to challenge existing theories and methodologies, yet deep enough to provide significant insights or advancements? Complexity and depth showcase Lean4's capabilities and attract interest. +3. Interdisciplinary Potential: Does the statement offer opportunities for interdisciplinary research, connecting mathematics with other fields such as computer science, physics, or biology? Interdisciplinary projects often garner wide interest. +4. Community Needs and Gaps: Does the statement fill an identified need or gap within the Lean4 community or the broader mathematical community? Addressing these needs directly correlates with interest. +5. Innovativeness: How innovative is the statement? Does it propose new methods, concepts, or applications? Innovation drives interest and engagement. + +Customize your evaluation for each problem accordingly, assessing it as 'excellent', 'good', 'above average', 'fair' or 'poor'. + +You should respond in the following format for each statement: + +''' +Natural language: (Detailed explanation of the informal statement, including any relevant background information, assumptions, and definitions.) +Analysis: (Provide a brief justification for each score, highlighting why the statement scored as it did across the criteria.) +Assessment: (Based on the criteria, rate the statement as 'excellent', 'good', 'above average', 'fair' or 'poor'. JUST the Assessment.) +'''""" + +class DeepSeekProverScorer(Task): + _template: Union[Template, None] = PrivateAttr(...) + + def load(self) -> None: + super().load() + self._template = Template(template_deepseek_prover_scorer) + + @property + def inputs(self) -> List[str]: + return ["informal_statement", "formal_statement"] + + @property + def outputs(self): + return ["natural_language", "analysis", "assessment", "model_name"] + + def format_input(self, input: str) -> ChatType: + return [ + { + "role": "system", + "content": self._template.render(), + }, + { + "role": "user", + "content": f"## Informal statement:\n{input[self.inputs[0]]}\n\n ## Formal statement:\n{input[self.inputs[1]]}", + }, + ] + + @override + def format_output( + self, output: Union[str, None], input: Dict[str, Any] = None + ) -> Dict[str, Any]: + try: + result = output.split("Natural language:")[1].strip() + natural_language, analysis = result.split("Analysis:") + analysis, assessment = analysis.split("Assessment:") + natural_language = natural_language.strip() + analysis = analysis.strip() + assessment = assessment.strip() + except Exception: + natural_language = analysis = assessment = None + + return { + "natural_language": natural_language, + "analysis": analysis, + "assessment": assessment + } + +``` + +
+ +### DeepSeekProverSolver + +The last task is in charge of generating a proof for the theorems generated in the previous steps. + +
+DeepSeekProverSolver + +```python +class DeepSeekProverSolver(Task): + system_prompt: str = ( + "You are an expert in proving mathematical theorems formalized in lean4 language. " + "Your answers consist just in the proof to the theorem given, and nothing else." + ) + + @property + def inputs(self) -> List[str]: + return ["formal_statement"] + + @property + def outputs(self): + return ["proof"] + + def format_input(self, input: str) -> ChatType: + prompt = dedent(""" + Give me a proof for the following theorem: + ```lean4 + {theorem} + ```""" + ) + return [ + { + "role": "system", + "content": self.system_prompt, + }, + { + "role": "user", + "content": prompt.format(theorem=input["formal_statement"]), + }, + ] + + def format_output( + self, output: Union[str, None], input: Dict[str, Any] = None + ) -> Dict[str, Any]: + import re + match = re.search(_PARSE_DEEPSEEK_PROVER_AUTOFORMAL_REGEX, output, re.DOTALL) + if match: + match = match.group(1).strip() + return {"proof": match} +``` + +
+ +Additionally, the original pipeline defined in the paper includes a step to check the final proofs using the lean 4 language that we have omitted for simplicity. The fine tuning can be done completely offline, and come back to the pipeline after each iteration/training run. + +*All the docstrings have been removed from the code blocks, but can be seen in the full pipeline.* + +## Code + +Lets's put the building blocks together to create the final pipeline with `distilabel`. For this example we have generated a sample dataset [plaguss/informal-mathematical-statements-tiny](https://huggingface.co/datasets/plaguss/informal-mathematical-statements-tiny) of informal mathematical statements starting from [casey-martin/multilingual-mathematical-autoformalization](https://huggingface.co/datasets/casey-martin/multilingual-mathematical-autoformalization), but as the paper mentions, we can create formal statements and it's corresponding proofs starting from informal ones: + +
+Click to see the full pipeline + +```python title="deepseek_prover.py" +--8<-- "examples/deepseek_prover.py" +``` + +
+ +The script can be run run for a dry run or not, depending on the argument (the pipeline will run without dry run by default), and will be pushed to the hub with the name `your_username/test_deepseek_prover`: + +```bash +python deepseek_prover.py [-d | --dry-run | --no-dry-run] +``` + +Final dataset: [plaguss/test_deepseek_prover](https://huggingface.co/datasets/plaguss/test_deepseek_prover). diff --git a/examples/deepseek_prover.py b/examples/deepseek_prover.py new file mode 100644 index 0000000000..b61f1c6836 --- /dev/null +++ b/examples/deepseek_prover.py @@ -0,0 +1,502 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import re +from pathlib import Path +from textwrap import dedent +from typing import Any, Dict, List, Optional, Union + +from distilabel.llms import InferenceEndpointsLLM +from distilabel.pipeline import Pipeline +from distilabel.steps import LoadDataFromHub +from distilabel.steps.tasks.base import Task +from distilabel.steps.tasks.typing import ChatType +from jinja2 import Template +from pydantic import PrivateAttr +from typing_extensions import override + +_PARSE_DEEPSEEK_PROVER_AUTOFORMAL_REGEX = r"```lean4(.*?)```" + + +template_deepseek_prover_auto_formalization = """\ +Mathematical Problem in Natural Language: +{{ informal_statement }} +{%- if few_shot %} + +Please use the following examples to guide you with the answer: +{%- for example in examples %} +- {{ example }} +{%- endfor %} +{% endif -%}""" + + +class DeepSeekProverAutoFormalization(Task): + """Task to translate a mathematical problem from natural language to Lean 4. + + Note: + A related dataset (MMA from the paper) can be found in Hugging Face: + [casey-martin/multilingual-mathematical-autoformalization](https://huggingface.co/datasets/casey-martin/multilingual-mathematical-autoformalization). + + Input columns: + - informal_statement (`str`): The statement to be formalized using Lean 4. + + Output columns: + - formal_statement (`str`): The formalized statement using Lean 4, to be analysed. + + Categories: + - generation + + References: + - [`DeepSeek-Prover: Advancing Theorem Proving in LLMs through Large-Scale Synthetic Data`](https://arxiv.org/abs/2405.14333). + - [`Lean 4`](https://github.com/leanprover/lean4). + + Examples: + + Formalize a mathematical problem from natural language to Lean 4: + + ```python + from distilabel.steps.tasks import DeepSeekProverAutoFormalization + from distilabel.llms.huggingface import InferenceEndpointsLLM + + # Consider this as a placeholder for your actual LLM. + prover_autoformal = DeepSeekProverAutoFormalization( + llm=InferenceEndpointsLLM( + model_id="deepseek-ai/deepseek-math-7b-instruct", + tokenizer_id="deepseek-ai/deepseek-math-7b-instruct", + ), + ) + + prover_autoformal.load() + + result = next( + prover_autoformal.process( + [ + {"informal_statement": "If a polynomial g is monic, then the root of g is integral over the ring R."}, + ] + ) + ) + # result + # [ + # { + # 'informal_statement': 'If a polynomial g is monic, then the root of g is integral over the ring R.', + # 'formal_statement': 'theorem isIntegral_root (hg : g.Monic) : IsIntegral R (root g):=', + # 'distilabel_metadata': { + # 'raw_output_deep_seek_prover_auto_formalization_0': '```lean4\ntheorem isIntegral_root (hg : g.Monic) : IsIntegral R (root g):=\n```' + # }, + # 'model_name': 'deepseek-prover' + # } + # ] + ``` + + Use a few-shot setting to formalize a mathematical problem from natural language to Lean 4: + + ```python + from distilabel.steps.tasks import DeepSeekProverAutoFormalization + from distilabel.llms.huggingface import InferenceEndpointsLLM + + # You can gain inspiration from the following examples to create your own few-shot examples: + # https://github.com/yangky11/miniF2F-lean4/blob/main/MiniF2F/Valid.lean + # Consider this as a placeholder for your actual LLM. + prover_autoformal = DeepSeekProverAutoFormalization( + llm=InferenceEndpointsLLM( + model_id="deepseek-ai/deepseek-math-7b-instruct", + tokenizer_id="deepseek-ai/deepseek-math-7b-instruct", + ), + examples=[ + "theorem amc12a_2019_p21 (z : ℂ) (h₀ : z = (1 + Complex.I) / Real.sqrt 2) :\n\n((∑ k : ℤ in Finset.Icc 1 12, z ^ k ^ 2) * (∑ k : ℤ in Finset.Icc 1 12, 1 / z ^ k ^ 2)) = 36 := by\n\nsorry", + "theorem amc12a_2015_p10 (x y : ℤ) (h₀ : 0 < y) (h₁ : y < x) (h₂ : x + y + x * y = 80) : x = 26 := by\n\nsorry" + ] + ) + + prover_autoformal.load() + + result = next( + prover_autoformal.process( + [ + {"informal_statement": "If a polynomial g is monic, then the root of g is integral over the ring R."}, + ] + ) + ) + # result + # [ + # { + # 'informal_statement': 'If a polynomial g is monic, then the root of g is integral over the ring R.', + # 'formal_statement': 'theorem isIntegral_root (hg : g.Monic) : IsIntegral R (root g):=', + # 'distilabel_metadata': { + # 'raw_output_deep_seek_prover_auto_formalization_0': '```lean4\ntheorem isIntegral_root (hg : g.Monic) : IsIntegral R (root g):=\n```' + # }, + # 'model_name': 'deepseek-prover' + # } + # ] + ``` + """ + + examples: Optional[List[str]] = None + system_prompt: str = "Translate the problem to Lean 4 (only the core declaration):\n```lean4\nformal statement goes here\n```" + _template: Union[Template, None] = PrivateAttr(...) + _few_shot: bool = PrivateAttr(default=False) + + def load(self) -> None: + """Loads the Jinja2 template.""" + super().load() + + self._template = Template(template_deepseek_prover_auto_formalization) + + @property + def inputs(self) -> List[str]: + """The input for the task is the `instruction`.""" + return ["informal_statement"] + + @property + def outputs(self): + """The output for the task is a list of `instructions` containing the generated instructions.""" + return ["formal_statement", "model_name"] + + def format_input(self, input: str) -> ChatType: # type: ignore + """The input is formatted as a `ChatType` assuming that the instruction + is the first interaction from the user within a conversation. And the + `system_prompt` is added as the first message if it exists.""" + return [ + { + "role": "system", + "content": self.system_prompt, + }, + { + "role": "user", + "content": self._template.render( + informal_statement=input[self.inputs[0]], + few_shot=bool(self.examples), + examples=self.examples, + ), + }, + ] + + @override + def format_output( # type: ignore + self, output: Union[str, None], input: Dict[str, Any] = None + ) -> Dict[str, Any]: # type: ignore + """Extracts the formal statement from the Lean 4 output.""" + match = re.search(_PARSE_DEEPSEEK_PROVER_AUTOFORMAL_REGEX, output, re.DOTALL) + if match: + match = match.group(1).strip() + return {"formal_statement": match} + + +template_deepseek_prover_scorer = """\ +To evaluate whether a formal Lean4 statement will be of interest to the community, consider the following criteria: + +1. Relevance to Current Research: Does the statement address a problem or concept that is actively being researched in mathematics or related fields? Higher relevance scores indicate greater potential interest. +2. Complexity and Depth: Is the statement complex enough to challenge existing theories and methodologies, yet deep enough to provide significant insights or advancements? Complexity and depth showcase Lean4's capabilities and attract interest. +3. Interdisciplinary Potential: Does the statement offer opportunities for interdisciplinary research, connecting mathematics with other fields such as computer science, physics, or biology? Interdisciplinary projects often garner wide interest. +4. Community Needs and Gaps: Does the statement fill an identified need or gap within the Lean4 community or the broader mathematical community? Addressing these needs directly correlates with interest. +5. Innovativeness: How innovative is the statement? Does it propose new methods, concepts, or applications? Innovation drives interest and engagement. + +Customize your evaluation for each problem accordingly, assessing it as 'excellent', 'good', 'above average', 'fair' or 'poor'. + +You should respond in the following format for each statement: + +''' +Natural language: (Detailed explanation of the informal statement, including any relevant background information, assumptions, and definitions.) +Analysis: (Provide a brief justification for each score, highlighting why the statement scored as it did across the criteria.) +Assessment: (Based on the criteria, rate the statement as 'excellent', 'good', 'above average', 'fair' or 'poor'. JUST the Assessment.) +'''""" + + +class DeepSeekProverScorer(Task): + """Task to evaluate the quality of a formalized mathematical problem in Lean 4, + inspired by the DeepSeek-Prover task for scoring. + + Note: + A related dataset (MMA from the paper) can be found in Hugging Face: + [casey-martin/multilingual-mathematical-autoformalization](https://huggingface.co/datasets/casey-martin/multilingual-mathematical-autoformalization). + + Input columns: + - informal_statement (`str`): The statement to be formalized using Lean 4. + - formal_statement (`str`): The formalized statement using Lean 4, to be analysed. + + Output columns: + - natural_language (`str`): Explanation for the problem. + - analysis (`str`): Analysis of the different points defined in the prompt. + - assessment (`str`): Result of the assessment. + + Categories: + - scorer + - quality + - response + + References: + - [`DeepSeek-Prover: Advancing Theorem Proving in LLMs through Large-Scale Synthetic Data`](https://arxiv.org/abs/2405.14333). + - [`Lean 4`](https://github.com/leanprover/lean4). + + Examples: + + Analyse a formal statement in Lean 4: + + ```python + from distilabel.steps.tasks import DeepSeekProverScorer + from distilabel.llms.huggingface import InferenceEndpointsLLM + + # Consider this as a placeholder for your actual LLM. + prover_scorer = DeepSeekProverAutoFormalization( + llm=InferenceEndpointsLLM( + model_id="deepseek-ai/deepseek-math-7b-instruct", + tokenizer_id="deepseek-ai/deepseek-math-7b-instruct", + ), + ) + + prover_scorer.load() + + result = next( + prover_scorer.process( + [ + {"formal_statement": "theorem isIntegral_root (hg : g.Monic) : IsIntegral R (root g):="}, + ] + ) + ) + # result + # [ + # { + # 'formal_statement': 'theorem isIntegral_root (hg : g.Monic) : IsIntegral R (root g):=', + # 'informal_statement': 'INFORMAL', + # 'analysis': 'ANALYSIS', + # 'assessment': 'ASSESSMENT', + # 'distilabel_metadata': { + # 'raw_output_deep_seek_prover_scorer_0': 'Natural language:\nINFORMAL\nAnalysis:\nANALYSIS\nAssessment:\nASSESSMENT' + # }, + # 'model_name': 'deepseek-prover-scorer' + # } + # ] + ``` + """ + + _template: Union[Template, None] = PrivateAttr(...) + + def load(self) -> None: + """Loads the Jinja2 template.""" + super().load() + + self._template = Template(template_deepseek_prover_scorer) + + @property + def inputs(self) -> List[str]: + """The input for the task is the `instruction`.""" + return ["informal_statement", "formal_statement"] + + @property + def outputs(self): + """The output for the task is a list of `instructions` containing the generated instructions.""" + return ["natural_language", "analysis", "assessment", "model_name"] + + def format_input(self, input: str) -> ChatType: # type: ignore + """The input is formatted as a `ChatType` assuming that the instruction + is the first interaction from the user within a conversation. And the + `system_prompt` is added as the first message if it exists.""" + return [ + { + "role": "system", + "content": self._template.render(), + }, + { + "role": "user", + "content": f"## Informal statement:\n{input[self.inputs[0]]}\n\n ## Formal statement:\n{input[self.inputs[1]]}", + }, + ] + + @override + def format_output( # type: ignore + self, output: Union[str, None], input: Dict[str, Any] = None + ) -> Dict[str, Any]: # type: ignore + """Analyses the formal statement with Lean 4 output and generates an assessment + and the corresponding informal assessment.""" + + try: + result = output.split("Natural language:")[1].strip() + natural_language, analysis = result.split("Analysis:") + analysis, assessment = analysis.split("Assessment:") + natural_language = natural_language.strip() + analysis = analysis.strip() + assessment = assessment.strip() + except Exception: + natural_language = analysis = assessment = None + + return { + "natural_language": natural_language, + "analysis": analysis, + "assessment": assessment, + } + + +class DeepSeekProverSolver(Task): + """Task to generate a proof for a formal statement (theorem) in lean4. + + Input columns: + - formal_statement (`str`): The formalized statement using Lean 4. + + Output columns: + - proof (`str`): The proof for the formal statement theorem. + + Categories: + - scorer + - quality + - response + + References: + - [`DeepSeek-Prover: Advancing Theorem Proving in LLMs through Large-Scale Synthetic Data`](https://arxiv.org/abs/2405.14333). + """ + + system_prompt: str = ( + "You are an expert in proving mathematical theorems formalized in lean4 language. " + "Your answers consist just in the proof to the theorem given, and nothing else." + ) + + @property + def inputs(self) -> List[str]: + """The input for the task is the `formal_statement`.""" + return ["formal_statement"] + + @property + def outputs(self): + """The output for the task is the proof for the formal statement theorem.""" + return ["proof"] + + def format_input(self, input: str) -> ChatType: # type: ignore + """The input is formatted as a `ChatType`, with a system prompt to guide our model.""" + prompt = dedent(""" + Give me a proof for the following theorem: + ```lean4 + {theorem} + ```""") + return [ + { + "role": "system", + "content": self.system_prompt, + }, + { + "role": "user", + "content": prompt.format(theorem=input["formal_statement"]), + }, + ] + + def format_output( # type: ignore + self, output: Union[str, None], input: Dict[str, Any] = None + ) -> Dict[str, Any]: # type: ignore + import re + + match = re.search(_PARSE_DEEPSEEK_PROVER_AUTOFORMAL_REGEX, output, re.DOTALL) + if match: + match = match.group(1).strip() + return {"proof": match} + + +examples = [ + dedent(""" + ## Statement in natural language: + For real numbers k and x: + If x is equal to (13 - √131) / 4, and + If the equation 2x² - 13x + k = 0 is satisfied, + Then k must be equal to 19/4. + ## Formalized: + theorem mathd_algebra_116 (k x : ℝ) (h₀ : x = (13 - Real.sqrt 131) / 4) + (h₁ : 2 * x ^ 2 - 13 * x + k = 0) : k = 19 / 4 :="""), + dedent(""" + ## Statement in natural language: + The greatest common divisor (GCD) of 20 factorial (20!) and 200,000 is equal to 40,000. + ## Formalized: + theorem mathd_algebra_116 (k x : ℝ) (h₀ : x = (13 - Real.sqrt 131) / 4) + (h₁ : 2 * x ^ 2 - 13 * x + k = 0) : k = 19 / 4 :="""), + dedent(""" + ## Statement in natural language: + Given two integers x and y: + If y is positive (greater than 0), + And y is less than x, + And the equation x + y + xy = 80 is true, + Then x must be equal to 26. + ## Formalized: + theorem mathd_algebra_116 (k x : ℝ) (h₀ : x = (13 - Real.sqrt 131) / 4) + (h₁ : 2 * x ^ 2 - 13 * x + k = 0) : k = 19 / 4 :="""), +] + + +with Pipeline(name="test_deepseek_prover") as pipeline: + data_loader = LoadDataFromHub( + repo_id="plaguss/informal-mathematical-statements-tiny", + split="val", + batch_size=8, + ) + + llm = InferenceEndpointsLLM( + model_id="meta-llama/Meta-Llama-3-70B-Instruct", + ) + auto_formalization = DeepSeekProverAutoFormalization( + name="auto_formalization", input_batch_size=8, llm=llm, examples=examples + ) + prover_scorer = DeepSeekProverScorer( + name="prover_scorer", + input_batch_size=8, + llm=llm, + ) + proof_generator = DeepSeekProverSolver( + name="proof_generator", input_batch_size=8, llm=llm + ) + + (data_loader >> auto_formalization >> prover_scorer >> proof_generator) + + +if __name__ == "__main__": + import argparse + + parser = argparse.ArgumentParser() + parser.add_argument( + "-d", + "--dry-run", + action=argparse.BooleanOptionalAction, + help="Do a dry run for testing purposes.", + ) + args = parser.parse_args() + + pipeline_parameters = { + data_loader.name: {"split": "val"}, + auto_formalization.name: { + "llm": { + "generation_kwargs": { + "temperature": 0.6, + "top_p": 0.9, + "max_new_tokens": 512, + } + } + }, + prover_scorer.name: { + "llm": { + "generation_kwargs": { + "temperature": 0.6, + "top_p": 0.9, + "max_new_tokens": 512, + } + } + }, + } + + ds_name = "test_deepseek_prover" + + if args.dry_run: + distiset = pipeline.dry_run(batch_size=1, parameters=pipeline_parameters) + distiset.save_to_disk(Path.home() / f"Downloads/{ds_name}") + + import pprint + + pprint.pprint(distiset["default"]["train"][0]) + + else: + distiset = pipeline.run(parameters=pipeline_parameters) + distiset.push_to_hub(ds_name, include_script=True) diff --git a/mkdocs.yml b/mkdocs.yml index 082c7ef27a..5846a1020d 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -191,6 +191,7 @@ nav: - Instruction Backtranslation: "sections/pipeline_samples/papers/instruction_backtranslation.md" - Prometheus 2: "sections/pipeline_samples/papers/prometheus.md" - UltraFeedback: "sections/pipeline_samples/papers/ultrafeedback.md" + - DeepSeek Prover: "sections/pipeline_samples/papers/deepseek_prover.md" - API Reference: - Step: - "api/step/index.md" From 974f0db33036bf3be1961f0d7b750d5364be4519 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Mart=C3=ADn=20Bl=C3=A1zquez?= Date: Thu, 15 Aug 2024 19:10:38 +0200 Subject: [PATCH 21/82] Update `RewardModelScore.inputs` to define optional input columns (#908) --- src/distilabel/steps/reward_model.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/distilabel/steps/reward_model.py b/src/distilabel/steps/reward_model.py index b6d68d0f47..9a88d8d659 100644 --- a/src/distilabel/steps/reward_model.py +++ b/src/distilabel/steps/reward_model.py @@ -181,7 +181,11 @@ def load(self) -> None: @property def inputs(self) -> "StepColumns": """Either `response` and `instruction`, or a `conversation` columns.""" - return [] + return { + "response": False, + "instruction": False, + "conversation": False, + } @property def outputs(self) -> "StepColumns": From 3264563e044e36dc0f67e7dd3dd4d264245ef58e Mon Sep 17 00:00:00 2001 From: David Berenstein Date: Mon, 19 Aug 2024 09:40:23 +0200 Subject: [PATCH 22/82] Add tutorial - generate data for training embeddings and reranking models (#893) * Add initial outline tutorial * Add section on data quality evaluation * Add conslusion * Update pipeline_samples structure for adding tutorials in a similar way as Argilla docs * Update new structure tutorials * Update title * Update to use Free serverless Inference API * Process comments from code review * Remove sections from header * Updated formatting examples * Add grid arror on new line * update phrasing * update phrasing --- .../advanced/structured_generation.md | 4 +- .../examples/benchmarking_with_distilabel.md | 20 + .../pipeline_samples/examples/index.md | 108 +-- .../examples/llama_cpp_with_outlines.md | 20 + .../examples/mistralai_with_instructor.md | 38 + .../sections/pipeline_samples/papers/deita.md | 6 +- .../sections/pipeline_samples/papers/index.md | 3 - .../papers/instruction_backtranslation.md | 12 +- .../pipeline_samples/papers/prometheus.md | 6 +- .../pipeline_samples/papers/ultrafeedback.md | 8 +- .../tutorials/GenerateSentencePair.ipynb | 694 ++++++++++++++++++ mkdocs.yml | 10 +- src/distilabel/llms/vllm.py | 2 +- 13 files changed, 862 insertions(+), 69 deletions(-) create mode 100644 docs/sections/pipeline_samples/examples/benchmarking_with_distilabel.md create mode 100644 docs/sections/pipeline_samples/examples/llama_cpp_with_outlines.md create mode 100644 docs/sections/pipeline_samples/examples/mistralai_with_instructor.md delete mode 100644 docs/sections/pipeline_samples/papers/index.md create mode 100644 docs/sections/pipeline_samples/tutorials/GenerateSentencePair.ipynb diff --git a/docs/sections/how_to_guides/advanced/structured_generation.md b/docs/sections/how_to_guides/advanced/structured_generation.md index d3e750aa93..02b8cc8e4a 100644 --- a/docs/sections/how_to_guides/advanced/structured_generation.md +++ b/docs/sections/how_to_guides/advanced/structured_generation.md @@ -111,7 +111,7 @@ These were some simple examples, but one can see the options this opens. !!! Tip A full pipeline example can be seen in the following script: - [`examples/structured_generation_with_outlines.py`](../../pipeline_samples/examples/index.md#llamacpp-with-outlines) + [`examples/structured_generation_with_outlines.py`](../../pipeline_samples/examples/llama_cpp_with_outlines.md) [^1]: You can check the variable type by importing it from: @@ -189,7 +189,7 @@ We get back a Python dictionary (formatted as a string) that we can parse using !!! Tip A full pipeline example can be seen in the following script: - [`examples/structured_generation_with_instructor.py`](../../pipeline_samples/examples/index.md#mistralai-with-instructor) + [`examples/structured_generation_with_instructor.py`](../../pipeline_samples/examples/mistralai_with_instructor.md) ## OpenAI JSON diff --git a/docs/sections/pipeline_samples/examples/benchmarking_with_distilabel.md b/docs/sections/pipeline_samples/examples/benchmarking_with_distilabel.md new file mode 100644 index 0000000000..f1f18b415f --- /dev/null +++ b/docs/sections/pipeline_samples/examples/benchmarking_with_distilabel.md @@ -0,0 +1,20 @@ +--- +hide: toc +--- +# [Benchmarking with `distilabel`: Arena Hard](#benchmarking-with-distilabel-arena-hard) + +Benchmark LLMs with `distilabel`: reproducing the Arena Hard benchmark. + +The script below first defines both the `ArenaHard` and the `ArenaHardResults` tasks, so as to generate responses for a given collection of prompts/questions with up to two LLMs, and then calculate the results as per the original implementation, respectively. Additionally, the second part of the example builds a `Pipeline` to run the generation on top of the prompts with `InferenceEndpointsLLM` while streaming the rest of the generations from a pre-computed set of GPT-4 generations, and then evaluate one against the other with `OpenAILLM` generating an alternate response, a comparison between the responses, and a result as A>>B, A>B, B>A, B>>A, or tie. + +To run this example you will first need to install the Arena Hard optional dependencies, being `pandas`, `scikit-learn`, and `numpy`. + +??? Run + + ```python + python examples/arena_hard.py + ``` + +```python title="arena_hard.py" +--8<-- "examples/arena_hard.py" +``` \ No newline at end of file diff --git a/docs/sections/pipeline_samples/examples/index.md b/docs/sections/pipeline_samples/examples/index.md index 68e25fc888..2638c36716 100644 --- a/docs/sections/pipeline_samples/examples/index.md +++ b/docs/sections/pipeline_samples/examples/index.md @@ -1,78 +1,96 @@ -# Examples +--- +hide: toc +--- +# Pipeline Samples -This section contains different example pipelines that showcase different tasks, maybe you can take inspiration from them. +- **Tutorials** provide detailed step-by-step explanations and the code used for end-to-end workflows. +- **Paper implementations** provide reproductions of fundamental papers in the synthetic data domain. +- **Examples** don't provide explenations but simply show code for different tasks. -### [llama.cpp with `outlines`](#llamacpp-with-outlines) +## Tutorials -Generate RPG characters following a `pydantic.BaseModel` with `outlines` in `distilabel`. +
-??? Example "See example" +- __Retrieval and reranking models__ - This script makes use of [`LlamaCppLLM`][distilabel.llms.llamacpp.LlamaCppLLM] and the structured output capabilities thanks to [`outlines`](https://outlines-dev.github.io/outlines/welcome/) to generate RPG characters that adhere to a JSON schema. + --- - It makes use of a local model which can be downloaded using curl (explained in the script itself), and can be exchanged with other `LLMs` like [`vLLM`][distilabel.llms.vllm.vLLM]. + Learn about synthetic data generation for fine-tuning custom retrieval and reranking models. - ??? Run + [:octicons-arrow-right-24: Tutorial](../tutorials/GenerateSentencePair.ipynb) - ```python - python examples/structured_generation_with_outlines.py - ``` +
- ```python title="structured_generation_with_outlines.py" - --8<-- "examples/structured_generation_with_outlines.py" - ``` +## Paper Implementations +
-### [MistralAI with `instructor`](#mistralai-with-instructor) +- __DEITA__ -Answer instructions with knowledge graphs defined as `pydantic.BaseModel` objects using `instructor` in `distilabel`. + --- -??? Example "See example" + Learn about prompt, response tuning for complexity and quality and LLMs as judges for automatic data selection. - This script makes use of [`MistralLLM`][distilabel.llms.mistral.MistralLLM] and the structured output capabilities thanks to [`instructor`](https://python.useinstructor.com/) to generate knowledge graphs from complex topics. + [:octicons-arrow-right-24: Paper](../papers/deita.md) - This example is translated from this [awesome example](https://python.useinstructor.com/examples/knowledge_graph/) from `instructor` cookbook. +- __Instruction Backtranslation__ - ??? Run + --- - ```python - python examples/structured_generation_with_instructor.py - ``` + Learn about automatically labeling human-written text with corresponding instructions. - ```python title="structured_generation_with_instructor.py" - --8<-- "examples/structured_generation_with_instructor.py" - ``` + [:octicons-arrow-right-24: Paper](../papers/instruction_backtranslation.md) - ??? "Visualizing the graphs" +- __Prometheus 2__ - Want to see how to visualize the graphs? You can test it using the following script. Generate some samples on your own and take a look: + --- - !!! NOTE + Learn about using open-source models as judges for direct assessment and pair-wise ranking. - This example uses graphviz to render the graph, you can install with `pip` in the following way: + [:octicons-arrow-right-24: Paper](../papers/prometheus.md) - ```console - pip install graphviz - ``` +- __UltraFeedback__ - ```python - python examples/draw_kg.py 2 # You can pass 0,1,2 to visualize each of the samples. - ``` + --- - ![Knowledge graph figure](../../../assets/images/sections/examples/knowledge-graph-example.png) + Learn about a large-scale, fine-grained, diverse preference dataset, used for training powerful reward and critic models. + [:octicons-arrow-right-24: Paper](../papers/ultrafeedback.md) -### [Benchmarking with `distilabel`: Arena Hard](#benchmarking-with-distilabel-arena-hard) +
-Benchmark LLMs with `distilabel`: reproducing the Arena Hard benchmark. +## Examples + +
+ +- __Benchmarking with distilabel__ + + --- + + Learn about reproducing the Arena Hard benchmark with disitlabel. + + [:octicons-arrow-right-24: Example](./benchmarking_with_distilabel.md) + +- __llama.cpp with outlines__ + + --- + + Learn about generating RPG characters following a pydantic.BaseModel with outlines in distilabel. + + [:octicons-arrow-right-24: Example](./llama_cpp_with_outlines.md) + +- __MistralAI with instructor__ + + --- + + Learn about answering instructions with knowledge graphs defined as pydantic.BaseModel objects using instructor in distilabel. + + [:octicons-arrow-right-24: Example](../papers/prometheus.md) + + +
-??? Example "See example" - The script below first defines both the `ArenaHard` and the `ArenaHardResults` tasks, so as to generate responses for a given collection of prompts/questions with up to two LLMs, and then calculate the results as per the original implementation, respectively. Additionally, the second part of the example builds a `Pipeline` to run the generation on top of the prompts with `InferenceEndpointsLLM` while streaming the rest of the generations from a pre-computed set of GPT-4 generations, and then evaluate one against the other with `OpenAILLM` generating an alternate response, a comparison between the responses, and a result as A>>B, A>B, B>A, B>>A, or tie. - To run this example you will first need to install the Arena Hard optional dependencies, being `pandas`, `scikit-learn`, and `numpy`. - ```python title="arena_hard.py" - --8<-- "examples/arena_hard.py" - ``` diff --git a/docs/sections/pipeline_samples/examples/llama_cpp_with_outlines.md b/docs/sections/pipeline_samples/examples/llama_cpp_with_outlines.md new file mode 100644 index 0000000000..38ac6bb6fe --- /dev/null +++ b/docs/sections/pipeline_samples/examples/llama_cpp_with_outlines.md @@ -0,0 +1,20 @@ +--- +hide: toc +--- +# [llama.cpp with `outlines`](#llamacpp-with-outlines) + +Generate RPG characters following a `pydantic.BaseModel` with `outlines` in `distilabel`. + +This script makes use of [`LlamaCppLLM`][distilabel.llms.llamacpp.LlamaCppLLM] and the structured output capabilities thanks to [`outlines`](https://outlines-dev.github.io/outlines/welcome/) to generate RPG characters that adhere to a JSON schema. + +It makes use of a local model which can be downloaded using curl (explained in the script itself), and can be exchanged with other `LLMs` like [`vLLM`][distilabel.llms.vllm.vLLM]. + +??? Run + + ```python + python examples/structured_generation_with_outlines.py + ``` + +```python title="structured_generation_with_outlines.py" +--8<-- "examples/structured_generation_with_outlines.py" +``` \ No newline at end of file diff --git a/docs/sections/pipeline_samples/examples/mistralai_with_instructor.md b/docs/sections/pipeline_samples/examples/mistralai_with_instructor.md new file mode 100644 index 0000000000..3b39d51e31 --- /dev/null +++ b/docs/sections/pipeline_samples/examples/mistralai_with_instructor.md @@ -0,0 +1,38 @@ +--- +hide: toc +--- +# [MistralAI with `instructor`](#mistralai-with-instructor) + +Answer instructions with knowledge graphs defined as `pydantic.BaseModel` objects using `instructor` in `distilabel`. + +This script makes use of [`MistralLLM`][distilabel.llms.mistral.MistralLLM] and the structured output capabilities thanks to [`instructor`](https://python.useinstructor.com/) to generate knowledge graphs from complex topics. + +This example is translated from this [awesome example](https://python.useinstructor.com/examples/knowledge_graph/) from `instructor` cookbook. + +??? Run + + ```python + python examples/structured_generation_with_instructor.py + ``` + +```python title="structured_generation_with_instructor.py" +--8<-- "examples/structured_generation_with_instructor.py" +``` + +??? "Visualizing the graphs" + + Want to see how to visualize the graphs? You can test it using the following script. Generate some samples on your own and take a look: + + !!! NOTE + + This example uses graphviz to render the graph, you can install with `pip` in the following way: + + ```console + pip install graphviz + ``` + + ```python + python examples/draw_kg.py 2 # You can pass 0,1,2 to visualize each of the samples. + ``` + + ![Knowledge graph figure](../../../assets/images/sections/examples/knowledge-graph-example.png) \ No newline at end of file diff --git a/docs/sections/pipeline_samples/papers/deita.md b/docs/sections/pipeline_samples/papers/deita.md index 5c9036d756..d3ff1da59b 100644 --- a/docs/sections/pipeline_samples/papers/deita.md +++ b/docs/sections/pipeline_samples/papers/deita.md @@ -1,10 +1,10 @@ # DEITA -DEITA (Data-Efficient Instruction Tuning for Alignment) studies an automatic data selection process by first quantifying the data quality based on complexity, quality and diversity. And second, selecting across the best potential combination from an open-source dataset that would fit into the budget you allocate to tune your own LLM. +[DEITA (Data-Efficient Instruction Tuning for Alignment)](https://arxiv.org/abs/2312.15685) studies an automatic data selection process by first quantifying the data quality based on complexity, quality and diversity. Second, select the best potential combination from an open-source dataset that would fit into the budget you allocate to tune your own LLM. -In most setting we cannot allocate unlimited resources for instruction-tuning LLMs. Therefore, the DEITA authors investigated how to select qualitative data for instruction-tuning based on a principle of fewer high quality samples. Liu et al. tackle the issue of first defining good data and second identifying it to respect an initial budget to instruct-tune your LLM. +In most setting we cannot allocate unlimited resources for instruction-tuning LLMs. Therefore, the DEITA authors investigated how to select qualitative data for instruction tuning based on the principle of fewer high-quality samples. Liu et al. tackle the issue of first defining good data and second identifying it to respect an initial budget to instruct-tune your LLM. -The strategy utilizes **LLMs to replace human effort in time-intensive data quality tasks on instruction tuning datasets**. DEITA introduces a way to measure data quality across three critical dimensions: complexity, quality and diversity. +The strategy utilizes **LLMs to replace human effort in time-intensive data quality **tasks on **instruction-tuning** datasets**. DEITA introduces a way to measure data quality across three critical dimensions: complexity, quality and diversity. ![DEITA pipeline overview](../../../assets/tutorials-assets/deita/overview.png) diff --git a/docs/sections/pipeline_samples/papers/index.md b/docs/sections/pipeline_samples/papers/index.md deleted file mode 100644 index 7fed3da03a..0000000000 --- a/docs/sections/pipeline_samples/papers/index.md +++ /dev/null @@ -1,3 +0,0 @@ -# Paper Implementations - -Contains some implementations for synthetic data generation papers, using `distilabel`, providing reproducible pipelines so that anyone can play around with those approaches and customize that to their needs. We strongly believe that better data leads to better models, and synthetic data has proven to be really effective towards improving LLMs, so we aim to bridge the gap between research and practice by providing these implementations. diff --git a/docs/sections/pipeline_samples/papers/instruction_backtranslation.md b/docs/sections/pipeline_samples/papers/instruction_backtranslation.md index 8434742984..588fc50480 100644 --- a/docs/sections/pipeline_samples/papers/instruction_backtranslation.md +++ b/docs/sections/pipeline_samples/papers/instruction_backtranslation.md @@ -1,18 +1,18 @@ # Instruction Backtranslation -["Self Alignment with Instruction Backtranslation"](https://arxiv.org/abs/2308.06259) presents a scalable method to build a high quality instruction following language model by automatically labelling human-written text with corresponding instructions. Their approach, named instruction backtranslation, starts with a language model finetuned on a small amount of seed data, and a given web corpus. The seed model is used to construct training examples by generating instruction prompts for web documents (self-augmentation), and then selecting high quality examples from among these candidates (self-curation). This data is then used to finetune a stronger model. +["Self Alignment with Instruction Backtranslation"](https://arxiv.org/abs/2308.06259) presents a scalable method to build high-quality instruction following a language model by automatically labeling human-written text with corresponding instructions. Their approach, named instruction backtranslation, starts with a language model finetuned on a small amount of seed data, and a given web corpus. The seed model is used to construct training examples by generating instruction prompts for web documents (self-augmentation), and then selecting high-quality examples from among these candidates (self-curation). This data is then used to finetune a stronger model. -Their self-training approach assumes access to a base language model, a small amount of seed data, and a collection of unlabelled examples, e.g. a web corpus. The unlabelled data is a large, diverse set of human-written documents which includes writing about all manner of topics humans are interested in – but crucially is not paired with instructions. +Their self-training approach assumes access to a base language model, a small amount of seed data, and a collection of unlabelled examples, e.g. a web corpus. The unlabelled data is a large, diverse set of human-written documents that includes writing about all manner of topics humans are interested in – but crucially is not paired with instructions. -A first key assumption is that there exists some subset of this very large human-written text that would be suitable as gold generations for some user instructions. A second key assumption is that they can predict instructions for these candidate gold answers that can be used as high quality example pairs to train an instruction following model. +A first key assumption is that there exists some subset of this very large human-written text that would be suitable as gold generations for some user instructions. A second key assumption is that they can predict instructions for these candidate gold answers that can be used as high-quality example pairs to train an instruction-following model. -Their overall process, called instruction backtranslation performs two core steps: +Their overall process, called instruction back translation performs two core steps: 1. Self-augment: Generate instructions for unlabelled data, i.e. the web corpus, to produce candidate training data of (instruction, output) pairs for instruction tuning. -2. Self-curate: Self-select high quality demonstration examples as training data to finetune the base model to follow instructions. This approach is done iteratively where a better intermediate instruction-following model can improve on selecting data for finetuning in the next iteration. +2. Self-curate: Self-select high-quality demonstration examples as training data to finetune the base model to follow instructions. This approach is done iteratively where a better intermediate instruction-following model can improve on selecting data for finetuning in the next iteration. -This replication covers the self-curation step i.e. the second / latter step as mentioned above, so as to be able to use the proposed prompting approach to rate the quality of the generated text, which can either be synthetically generated or real human-written text. +This replication covers the self-curation step i.e. the second/latter step as mentioned above, so as to be able to use the proposed prompting approach to rate the quality of the generated text, which can either be synthetically generated or real human-written text. ### Replication diff --git a/docs/sections/pipeline_samples/papers/prometheus.md b/docs/sections/pipeline_samples/papers/prometheus.md index 7f7b1d19d5..c86ed39309 100644 --- a/docs/sections/pipeline_samples/papers/prometheus.md +++ b/docs/sections/pipeline_samples/papers/prometheus.md @@ -1,20 +1,20 @@ # Prometheus 2 -["Prometheus 2: An Open Source Language Model Specialized in Evaluating Other Language Models"](https://arxiv.org/pdf/2405.01535) presents Prometheus 2, a new and more powerful evaluator LLM compared to Prometheus (its predecessor) presented in ["Prometheus: Inducing Fine-grained Evaluation Capability in Language Models"](https://arxiv.org/abs/2310.08491); since GPT-4, as well as other proprietary LLMs, are commonly used to asses the quality of the responses for various LLMs, but there are concerns about transparency, controllability, and affordability, that motivate the need of open-source LLMs specialized in evaluations. +["Prometheus 2: An Open Source Language Model Specialized in Evaluating Other Language Models"](https://arxiv.org/pdf/2405.01535) presents Prometheus 2, a new and more powerful evaluator LLM compared to Prometheus (its predecessor) presented in ["Prometheus: Inducing Fine-grained Evaluation Capability in Language Models"](https://arxiv.org/abs/2310.08491); since GPT-4, as well as other proprietary LLMs, are commonly used to assess the quality of the responses for various LLMs, but there are concerns about transparency, controllability, and affordability, that motivate the need of open-source LLMs specialized in evaluations. Existing open evaluator LMs exhibit critical shortcomings: 1. They issue scores that significantly diverge from those assigned by humans. 2. They lack the flexibility to perform both direct assessment and pairwise ranking, the two most prevalent forms of assessment. -Additionally, they do not possess the ability to evaluate based on custom evaluation criteria, focusing instead on general attributes like helpfulness and harmlessness. Prometheus 2 is capable of processing both direct assessment and pair-wise ranking formats grouped with a user-defined evaluation criteria. +Additionally, they do not possess the ability to evaluate based on custom evaluation criteria, focusing instead on general attributes like helpfulness and harmlessness. Prometheus 2 is capable of processing both direct assessment and pair-wise ranking formats grouped with user-defined evaluation criteria. Prometheus 2 released two variants: - [`prometheus-eval/prometheus-7b-v2.0`](https://hf.co/prometheus-eval/prometheus-7b-v2.0): fine-tuned on top of [`mistralai/Mistral-7B-Instruct-v0.2`](https://hf.co/mistralai/Mistral-7B-Instruct-v0.2) - [`prometheus-eval/prometheus-8x7b-v2.0`](https://hf.co/prometheus-eval/prometheus-8x7b-v2.0): fine-tuned on top of [`mistralai/Mixtral-8x7B-Instruct-v0.1`](https://hf.co/mistralai/Mixtral-8x7B-Instruct-v0.1) -Both models have been fine-tuned for both direct assessment and pairwise ranking tasks i.e. assessing the quality of a single isolated response for a given instruction with or without a reference answer, and assessing the quality of one response against another one for a given instruction with or without a reference answer, respectively. +Both models have been fine-tuned for both direct assessment and pairwise ranking tasks i.e. assessing the quality of a single isolated response for a given instruction with or without a reference answer and assessing the quality of one response against another one for a given instruction with or without a reference answer, respectively. On four direct assessment benchmarks and four pairwise ranking benchmarks, Prometheus 2 scores the highest correlation and agreement with humans and proprietary LM judges among all tested open evaluator LMs. Their models, code, and data are all publicly available at [`prometheus-eval/prometheus-eval`](https://github.com/prometheus-eval/prometheus-eval). diff --git a/docs/sections/pipeline_samples/papers/ultrafeedback.md b/docs/sections/pipeline_samples/papers/ultrafeedback.md index 704309e263..afa21a5717 100644 --- a/docs/sections/pipeline_samples/papers/ultrafeedback.md +++ b/docs/sections/pipeline_samples/papers/ultrafeedback.md @@ -6,13 +6,13 @@ UltraFeedback collects about 64k prompts from diverse resources (including Ultra To collect high-quality preference and textual feedback, they design a fine-grained annotation instruction, which contains four different aspects, namely instruction-following, truthfulness, honesty and helpfulness (even though within the paper they also mention a fifth one named verbalized calibration). Finally, GPT-4 is used to generate the ratings for the generated responses to the given prompt using the previously mentioned aspects. -### Replication +## Replication To replicate the paper we will be using `distilabel` and a smaller dataset created by the Hugging Face H4 team named [`HuggingFaceH4/instruction-dataset`](https://huggingface.co/datasets/HuggingFaceH4/instruction-dataset) for testing purposes. Also for testing purposes we will just show how to evaluate the generated responses for a given prompt using a new global aspect named `overall-rating` defined by Argilla, that computes the average of the four aspects, so as to reduce number of requests to be sent to OpenAI, but note that all the aspects are implemented within `distilabel` and can be used instead for a more faithful reproduction. Besides that we will generate three responses for each instruction using three LLMs selected from a pool of six: [`HuggingFaceH4/zephyr-7b-beta`](https://huggingface.co/HuggingFaceH4/zephyr-7b-beta), [`argilla/notus-7b-v1`](https://huggingface.co/argilla/notus-7b-v1), [`google/gemma-1.1-7b-it`](https://huggingface.co/google/gemma-1.1-7b-it), [`meta-llama/Meta-Llama-3-8B-Instruct`](https://huggingface.co/meta-llama/Meta-Llama-3-8B-Instruct), [`HuggingFaceH4/zephyr-7b-gemma-v0.1`](https://huggingface.co/HuggingFaceH4/zephyr-7b-gemma-v0.1) and [`mlabonne/UltraMerge-7B`](https://huggingface.co/mlabonne/UltraMerge-7B). -#### Installation +### Installation To replicate UltraFeedback one will need to install `distilabel` as it follows: @@ -22,7 +22,7 @@ pip install "distilabel[argilla,openai,vllm]>=1.0.0" And since we will be using `vllm` we will need to use a VM with at least 6 NVIDIA GPUs with at least 16GB of memory each to run the text generation, and set the `OPENAI_API_KEY` environment variable value. -#### Building blocks +### Building blocks - [`LoadDataFromHub`][distilabel.steps.LoadDataFromHub]: Generator Step to load a dataset from the Hugging Face Hub. - [`sample_n_steps`][distilabel.pipeline.sample_n_steps]: Function to create a `routing_batch_function` that samples `n` downstream steps for each batch generated by the upstream step. This is the key to replicate the LLM pooling mechanism described in the paper. @@ -34,7 +34,7 @@ And since we will be using `vllm` we will need to use a VM with at least 6 NVIDI - [`KeepColumns`][distilabel.steps.KeepColumns]: Task to keep the desired columns while removing the not needed ones, as well as defining the order for those. - (optional) [`PreferenceToArgilla`][distilabel.steps.PreferenceToArgilla]: Task to optionally push the generated dataset to Argilla to do some further analysis and human annotation. -#### Code +### Code As mentioned before, we will put the previously mentioned building blocks together to replicate UltraFeedback. diff --git a/docs/sections/pipeline_samples/tutorials/GenerateSentencePair.ipynb b/docs/sections/pipeline_samples/tutorials/GenerateSentencePair.ipynb new file mode 100644 index 0000000000..7869cef9e1 --- /dev/null +++ b/docs/sections/pipeline_samples/tutorials/GenerateSentencePair.ipynb @@ -0,0 +1,694 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Synthetic data generation for fine-tuning custom retrieval and reranking models\n", + "\n", + "- **Goal**: Bootstrap, optimize and maintain your embedding models and rerankers through synthetic data generation and human feedback.\n", + "- **Libraries**: [argilla](https://github.com/argilla-io/argilla), [hf-inference-endpoints](https://github.com/huggingface/huggingface_hub), [sentence-transformers](https://github.com/UKPLab/sentence-transformers)\n", + "- **Components**: [LoadDataFromHub](https://distilabel.argilla.io/latest/components-gallery/steps/loaddatafromhub/), [GenerateSentencePair](https://distilabel.argilla.io/latest/components-gallery/tasks/generatesentencepair/), [InferenceEndpointsLLM](https://distilabel.argilla.io/latest/components-gallery/llms/inferenceendpointsllm/)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Getting started\n", + "\n", + "### Install the dependencies\n", + "\n", + "To complete this tutorial, you need to install the distilabel SDK and a few third-party libraries via pip. We will be using **the free but rate-limited Hugging Face serverless Inference API** for this tutorial, so we need to install this as extra distilabel dependency. You can install them by running the following command:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!pip install \"distilabel[hf-inference-endpoints]\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!pip install \"sentence-transformer>=3,<4\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's make the needed imports:\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from distilabel.llms.huggingface import InferenceEndpointsLLM\n", + "from distilabel.pipeline import Pipeline\n", + "from distilabel.steps.tasks import GenerateSentencePair\n", + "from distilabel.steps import LoadDataFromHub\n", + "\n", + "from sentence_transformers import SentenceTransformer, CrossEncoder\n", + "import torch" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "### (optional) Deploy Argilla\n", + "\n", + "You can skip this step or replace it with any other data evaluation tool but the quality of your model will suffer from a lack of data quality so we do recommend to look at your data. If you already have deployed Argilla, you can skip this step. Otherwise, you can quickly deploy Argilla following [this guide](https://docs.argilla.io/latest/getting_started/quickstart/). \n", + "\n", + "Allong with that, you will need to install argilla as distilabel extra." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!pip install \"distilabel[argilla, hf-inference-endpoints]\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's make the extra needed imports:" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [], + "source": [ + "import argilla as rg" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## The dataset\n", + "\n", + "Before starting any project, it is always important to look at your data. Our data is publicly available [on the Hugging Face Hub](https://huggingface.co/datasets/plaguss/argilla_sdk_docs_raw_unstructured?row=0) so we can have a quick look through [their dataset viewer within an embedded iFrame](https://huggingface.co/docs/hub/datasets-viewer-embed). \n", + "\n", + "\n", + "\n", + "As we can see, our dataset contains a column called `chunks`, which was obtained from the Argilla docs. Normally, you would need to download and chunk the data but we will not cover that in this tutorial. To read a full explanation for how this dataset was generated, please refer to [How we leveraged distilabel to create an Argilla 2.0 Chatbot](https://huggingface.co/blog/argilla-chatbot#downloading-and-chunking-data).\n", + "\n", + "Alternatively, we can load the entire dataset to disk with `datasets.load_dataset`." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Synthetic data generation\n", + "\n", + "The [`GenerateSentencePair`](https://distilabel.argilla.io/latest/components-gallery/tasks/generatesentencepair/) component from `distilabel` can be used to generate training datasets for embeddings models. \n", + "\n", + "It is a pre-defined `Task` that given an `anchor` sentence generate data for a specific `action`. Supported actions are: `\"paraphrase\", \"semantically-similar\", \"query\", \"answer\"`. In our case the `chunks` column corresponds to the `anchor`. This means we will use `query` to generate potential queries for a fine-tuning a retrieval model and that we will use `semantically-similar` to generate texts that are similar to the intial anchor for fine-tuning a reranking model.\n", + "\n", + "We will `triplet=True` in order to generate both positive and negative examples, which should help the model generalize better during fine-tuning and we will set `hard_negative=True` to generate more challenging examples that are closer to the anchor and discussed topics.\n", + "\n", + "Lastly, we can seed the LLM with `context` to generate more relevant examples." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "context = context = (\n", + "\"\"\"\n", + "The text is a chunk from technical Python SDK documentation of Argilla.\n", + "Argilla is a collaboration tool for AI engineers and domain experts to build high-quality datasets.\n", + "Along with prose explanations, the text chunk may include code snippets and Python references.\n", + "\"\"\"\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Retrieval\n", + "\n", + "For retrieval, we will thus generate queries that are similar to the `chunks` column. We will use the `query` action to generate potential queries for a fine-tuning a retrieval model.\n", + "\n", + "```python\n", + "generate_sentence_pair = GenerateSentencePair(\n", + " triplet=True, \n", + " hard_negative=True,\n", + " action=\"query\",\n", + " llm=llm,\n", + " input_batch_size=10,\n", + " context=context,\n", + ")\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Reranking\n", + "\n", + "For reranking, we will generate texts that are similar to the intial anchor. We will use the `semantically-similar` action to generate texts that are similar to the intial anchor for fine-tuning a reranking model. In this case, we set `hard_negative=False` to generate more diverse and potentially wrong examples, which can be used as negative examples for similarity fine-tuning because [rerankers cannot be fine-tuned using triplets](https://github.com/UKPLab/sentence-transformers/issues/2366).\n", + "\n", + "```python\n", + "generate_sentence_pair = GenerateSentencePair(\n", + " triplet=True,\n", + " hard_negative=False,\n", + " action=\"semantically-similar\",\n", + " llm=llm,\n", + " input_batch_size=10,\n", + " context=context,\n", + ")\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Combined pipeline\n", + "\n", + "We will now use the `GenerateSentencePair` task to generate synthetic data for both retrieval and reranking models in a single pipeline. Note that, we map the `chunks` column to the `anchor` argument." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [], + "source": [ + "llm = InferenceEndpointsLLM(model_id=\"mistralai/Mistral-7B-Instruct-v0.2\")\n", + "\n", + "with Pipeline(name=\"generate\") as pipeline:\n", + " load_dataset = LoadDataFromHub(\n", + " num_examples=15,\n", + " output_mappings={\"chunks\": \"anchor\"},\n", + " )\n", + " generate_retrieval_pairs = GenerateSentencePair(\n", + " name=\"generate_retrieval_pairs\",\n", + " triplet=True,\n", + " hard_negative=True,\n", + " action=\"query\",\n", + " llm=llm,\n", + " input_batch_size=10,\n", + " context=context,\n", + " )\n", + " generate_reranking_pairs = GenerateSentencePair(\n", + " name=\"generate_reranking_pairs\",\n", + " triplet=True,\n", + " hard_negative=False, # to potentially generate non-relevant pairs\n", + " action=\"semantically-similar\",\n", + " llm=llm,\n", + " input_batch_size=10,\n", + " context=context,\n", + " )\n", + "\n", + " load_dataset >> [generate_retrieval_pairs, generate_reranking_pairs]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Next, we can execute this using `pipeline.run`. We will provide some `parameters` to specific components within our pipeline." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "generation_kwargs = {\n", + " \"llm\": {\n", + " \"generation_kwargs\": {\n", + " \"temperature\": 0.7,\n", + " \"max_new_tokens\": 512,\n", + " }\n", + " }\n", + "}\n", + "\n", + "distiset = pipeline.run( #\n", + " parameters={\n", + " load_dataset.name: {\n", + " \"repo_id\": \"plaguss/argilla_sdk_docs_raw_unstructured\",\n", + " \"split\": \"train\",\n", + " },\n", + " generate_retrieval_pairs.name: generation_kwargs,\n", + " generate_reranking_pairs.name: generation_kwargs,\n", + " },\n", + " use_cache=False, # comment out for demo\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Distiset({\n", + " generate_reranking_pairs: DatasetDict({\n", + " train: Dataset({\n", + " features: ['filename', 'anchor', 'repo_name', 'positive', 'negative', 'distilabel_metadata', 'model_name'],\n", + " num_rows: 15\n", + " })\n", + " })\n", + " generate_retrieval_pairs: DatasetDict({\n", + " train: Dataset({\n", + " features: ['filename', 'anchor', 'repo_name', 'positive', 'negative', 'distilabel_metadata', 'model_name'],\n", + " num_rows: 15\n", + " })\n", + " })\n", + "})" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "distiset" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Data generation can be a expensive, so it is recommended to store the data somewhere. For now, we will store it on the Hugging Face Hub, using our `push_to_hub` method." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "distiset.push_to_hub(\"my-org/my-dataset-name\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We have got 2 different leaf/end nodes, therefore we've got a distil configurations we can access, one for the retrieval data, and one for the reranking data." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'filename': 'argilla-python/docs/index.md',\n", + " 'anchor': 'description: Argilla is a collaboration platform for AI engineers and domain experts that require high-quality outputs, full data ownership, and overall efficiency.\\nhide: navigation\\n\\nWelcome to Argilla\\n\\nArgilla is a collaboration platform for AI engineers and domain experts that require high-quality outputs, full data ownership, and overall efficiency.',\n", + " 'repo_name': 'argilla-io/argilla-python',\n", + " 'positive': 'description: Argilla is a collaboration tool designed for AI engineers and domain experts who need high-quality outputs, full data control, and maximum efficiency.\\nhide: navigation\\n\\nWelcome to Argilla\\n\\nArgilla is a collaboration tool designed for AI engineers and domain experts who need high-quality outputs, full data control, and maximum efficiency.',\n", + " 'negative': 'description: Argilla is a platform for marketing professionals and sales teams that prioritizes customer engagement, brand visibility, and revenue growth.\\nhide: navigation\\n\\nWelcome to Argilla\\n\\nArgilla is a platform for marketing professionals and sales teams that prioritizes customer engagement, brand visibility, and revenue growth.',\n", + " 'distilabel_metadata': {'raw_output_generate_reranking_pairs': '## Positive\\n\\ndescription: Argilla is a collaboration tool designed for AI engineers and domain experts who need high-quality outputs, full data control, and maximum efficiency.\\nhide: navigation\\n\\nWelcome to Argilla\\n\\nArgilla is a collaboration tool designed for AI engineers and domain experts who need high-quality outputs, full data control, and maximum efficiency.\\n\\n## Negative\\n\\ndescription: Argilla is a platform for marketing professionals and sales teams that prioritizes customer engagement, brand visibility, and revenue growth.\\nhide: navigation\\n\\nWelcome to Argilla\\n\\nArgilla is a platform for marketing professionals and sales teams that prioritizes customer engagement, brand visibility, and revenue growth.'},\n", + " 'model_name': 'gpt-4o'}" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "distiset[\"generate_reranking_pairs\"][\"train\"][0]" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'filename': 'argilla-python/docs/index.md',\n", + " 'anchor': 'description: Argilla is a collaboration platform for AI engineers and domain experts that require high-quality outputs, full data ownership, and overall efficiency.\\nhide: navigation\\n\\nWelcome to Argilla\\n\\nArgilla is a collaboration platform for AI engineers and domain experts that require high-quality outputs, full data ownership, and overall efficiency.',\n", + " 'repo_name': 'argilla-io/argilla-python',\n", + " 'positive': 'What is Argilla and how does it benefit AI engineers and domain experts?',\n", + " 'negative': \"How does Argilla's interface compare with other project management tools?\",\n", + " 'distilabel_metadata': {'raw_output_generate_retrieval_pairs': \"## Positive\\n\\nWhat is Argilla and how does it benefit AI engineers and domain experts?\\n\\n## Negative\\n\\nHow does Argilla's interface compare with other project management tools?\"},\n", + " 'model_name': 'gpt-4o'}" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "distiset[\"generate_retrieval_pairs\"][\"train\"][0]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Looking at these initial examples, we can see they nicely capture the essence of the `chunks` column but we will need to evaluate the quality of the data a bit more before we can use it for fine-tuning." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Data quality evaluation \n", + "\n", + "Data is never as clean as it can be and this also holds for synthetically generated data too, therefore, it is always good to spent some time and look at your data.\n", + "\n", + "### Feature engineering\n", + "\n", + "In order to evaluate the quality of our data we will use features of the models that we intent to fine-tune as proxy for data quality. We can then use these features to filter out the best examples.\n", + "\n", + "In order to choose a good default model, we will use the [Massive Text Embedding Benchmark (MTEB) Leaderboard](https://huggingface.co/spaces/mteb/leaderboard). We want to optimize for size and speed, so we will set model size `<100M` and then filter for `Retrieval` and `Reranking` based on the highest average score, resulting in [Snowflake/snowflake-arctic-embed-s](https://huggingface.co/Snowflake/snowflake-arctic-embed-s) and [sentence-transformers/all-MiniLM-L12-v2](https://huggingface.co/sentence-transformers/all-MiniLM-L12-v2) respectively.\n", + "\n", + "\n", + "\n", + "#### Retrieval\n", + "\n", + "For retrieval, we will compute similarities for the current embeddings of `anchor-positive`, `positive-negative` and `anchor-negative` pairs. We assume that an overlap of these similarities will cause the model to have difficulties generalizing and therefore we can use these features to evaluate the quality of our data." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "model_id = \"Snowflake/snowflake-arctic-embed-m\" # Hugging Face model ID\n", + "\n", + "model_retrieval = SentenceTransformer(\n", + " model_id, device=\"cuda\" if torch.cuda.is_available() else \"cpu\"\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Next, we will encode the generated text pairs and compute the similarities. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from sklearn.metrics.pairwise import cosine_similarity\n", + "\n", + "def get_embeddings(texts):\n", + " vectors = model_retrieval.encode(texts)\n", + " return [vector.tolist() for vector in vectors]\n", + "\n", + "\n", + "def get_similarities(vector_batch_a, vector_batch_b):\n", + " similarities = []\n", + " for vector_a, vector_b in zip(vector_batch_a, vector_batch_b):\n", + " similarity = cosine_similarity([vector_a], [vector_b])[0][0]\n", + " similarities.append(similarity)\n", + " return similarities\n", + "\n", + "def format_data_retriever(batch):# -> Any:\n", + " batch[\"anchor-vector\"] = get_embeddings(batch[\"anchor\"])\n", + " batch[\"positive-vector\"] = get_embeddings(batch[\"positive\"])\n", + " batch[\"negative-vector\"] = get_embeddings(batch[\"negative\"]) \n", + " batch[\"similarity-positive-negative\"] = get_similarities(batch[\"positive-vector\"], batch[\"negative-vector\"])\n", + " batch[\"similarity-anchor-positive\"] = get_similarities(batch[\"anchor-vector\"], batch[\"positive-vector\"])\n", + " batch[\"similarity-anchor-negative\"] = get_similarities(batch[\"anchor-vector\"], batch[\"negative-vector\"])\n", + " return batch\n", + "\n", + "dataset_generate_retrieval_pairs = distiset[\"generate_retrieval_pairs\"][\"train\"].map(format_data_retriever, batched=True, batch_size=250)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "#### Reranking\n", + "\n", + "For reranking, we will compute the compute the relevance scores from an existing reranker model for `anchor-positive`, `positive-negative` and `anchor-negative` pais and make a similar assumption as for the retrieval model." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Some weights of BertForSequenceClassification were not initialized from the model checkpoint at sentence-transformers/all-MiniLM-L12-v2 and are newly initialized: ['classifier.bias', 'classifier.weight']\n", + "You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.\n" + ] + } + ], + "source": [ + "model_id = \"sentence-transformers/all-MiniLM-L12-v2\"\n", + "\n", + "model = CrossEncoder(model_id)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Next, we will compute the similarity for the generated text pairs using the reranker. On top of that, we will compute an `anchor-vector` to allow for doing semantic search." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def format_data_retriever(batch):# -> Any:\n", + " batch[\"anchor-vector\"] = get_embeddings(batch[\"anchor\"])\n", + " batch[\"similarity-positive-negative\"] = model.predict(zip(batch[\"positive-vector\"], batch[\"negative-vector\"]))\n", + " batch[\"similarity-anchor-positive\"] = model.predict(zip(batch[\"anchor-vector\"], batch[\"positive-vector\"]))\n", + " batch[\"similarity-anchor-negative\"] = model.predict(zip(batch[\"anchor-vector\"], batch[\"negative-vector\"]))\n", + " return batch\n", + "\n", + "dataset_generate_reranking_pairs = distiset[\"generate_reranking_pairs\"][\"train\"].map(format_data_retriever, batched=True, batch_size=250)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And voila, we have our proxies for quality evaluation which we can use to filter out the best and worst examples." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### (Optional) Argilla\n", + "\n", + "To get the most out of you data and actually look at our data, we will use Argilla. If you are not familiar with Argilla, we recommend taking a look at the [Argilla quickstart docs](https://docs.argilla.io/latest/getting_started/quickstart/). Alternatively, you can use your Hugging Face account to login to the [Argilla demo Space](https://argilla-argilla-template-space.hf.space).\n", + "\n", + "To start exploring data, we first need to define an `argilla.Dataset`. We will create a basic datset with some input `TextFields` for the `anchor` and output `TextQuestions` for the `positive` and `negative` pairs. Additionally, we will use the `file_name` as `MetaDataProperty`. Lastly, we will be re-using the vectors obtained from our previous step to allow for semantic search and we will add te similarity scores for some basic filtering and sorting." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "First, we need to define the setting for our Argilla dataset. We will create two different datasets, one for the retrieval data and one for the reranking data to ensure our annotators can focus on the task at hand." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import argilla as rg\n", + "from argilla._exceptions import ConflictError\n", + "\n", + "api_key = \"ohh so secret\"\n", + "api_url = \"https://davidberenstein1957-my-argilla.hf.space\"\n", + "\n", + "client = rg.Argilla(api_url=api_url, api_key=api_key)\n", + "\n", + "settings = rg.Settings(\n", + " fields=[\n", + " rg.TextField(\"anchor\")\n", + " ],\n", + " questions=[\n", + " rg.TextQuestion(\"positive\"),\n", + " rg.TextQuestion(\"negative\"),\n", + " rg.LabelQuestion(\n", + " name=\"is_positive_relevant\",\n", + " title=\"Is the positive query relevant?\",\n", + " labels=[\"yes\", \"no\"],\n", + " ),\n", + " rg.LabelQuestion(\n", + " name=\"is_negative_irrelevant\",\n", + " title=\"Is the negative query irrelevant?\",\n", + " labels=[\"yes\", \"no\"],\n", + " )\n", + " ],\n", + " metadata=[\n", + " rg.TermsMetadataProperty(\"filename\"),\n", + " rg.FloatMetadataProperty(\"similarity-positive-negative\"),\n", + " rg.FloatMetadataProperty(\"similarity-anchor-positive\"),\n", + " rg.FloatMetadataProperty(\"similarity-anchor-negative\"),\n", + " ],\n", + " vectors=[\n", + " rg.VectorField(\"anchor-vector\", dimensions=model.get_sentence_embedding_dimension())\n", + " ]\n", + ")\n", + "rg_datasets = []\n", + "for dataset_name in [\"generate_retrieval_pairs\", \"generate_reranking_pairs\"]:\n", + " ds = rg.Dataset(\n", + " name=dataset_name,\n", + " settings=settings\n", + " )\n", + " try:\n", + " ds.create()\n", + " except ConflictError:\n", + " ds = client.datasets(dataset_name)\n", + " rg_datasets.append(ds)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now, we've got our dataset definitions setup in Argilla, we can upload our data to Argilla." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "ds_datasets = [dataset_generate_retrieval_pairs, dataset_generate_reranking_pairs]\n", + "\n", + "records = []\n", + "\n", + "for rg_dataset, ds_dataset in zip(rg_datasets, ds_datasets):\n", + " for idx, entry in enumerate(ds_dataset):\n", + " records.append(\n", + " rg.Record(\n", + " id=idx,\n", + " fields={\"anchor\": entry[\"anchor\"]},\n", + " suggestions=[\n", + " rg.Suggestion(\"positive\", value=entry[\"positive\"], agent=\"gpt-4o\", type=\"model\"),\n", + " rg.Suggestion(\"negative\", value=entry[\"negative\"], agent=\"gpt-4o\", type=\"model\"),\n", + " ],\n", + " metadata={\n", + " \"filename\": entry[\"filename\"],\n", + " \"similarity-positive-negative\": entry[\"similarity-positive-negative\"],\n", + " \"similarity-anchor-positive\": entry[\"similarity-anchor-positive\"],\n", + " \"similarity-anchor-negative\": entry[\"similarity-anchor-negative\"]\n", + " },\n", + " vectors={\"anchor-vector\": entry[\"anchor-vector\"]}\n", + " )\n", + " )\n", + " rg_dataset.records.log(records)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we can explore the UI and add a final human touch to get he most out of our dataset. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Fine-tuning\n", + "\n", + "At last, we can fine-tune our models. We will use the `sentence-transformers` library to fine-tune our models.\n", + "\n", + "### Retrieval\n", + "\n", + "For retrieval, we have created a script that fine-tunes a model on our generated data the generated data based [https://github.com/argilla-io/argilla-sdk-chatbot/blob/main/train_embedding.ipynb](https://github.com/argilla-io/argilla-sdk-chatbot/blob/main/train_embedding.ipynb).You can also [open it in Google Colab directly](https://githubtocolab.com/argilla-io/argilla-sdk-chatbot/blob/main/train_embedding.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Retrieval\n", + "\n", + "For reranking, `sentence-transformers` provides a script that shows [how to fine-tune a CrossEncoder models](https://github.com/UKPLab/sentence-transformers/tree/master/examples/training/cross-encoder). Ad of now, there is [some uncertainty over fine-tuning CrossEncoder models with triplets](https://github.com/UKPLab/sentence-transformers/issues/2366) but you can still use the `positive` and `anchor`" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Conclusions\n", + "\n", + "In this tutorial, we present an end-to-end example of fine-tuning retrievers and rerankers for RAG. This serves as a good starting point for optimizing and maintaining your data and model but need to be adapted to your specific use case.\n", + "\n", + "We started with some seed data from the Argilla docs, generated synthetic data for retrieval and reranking models, evaluated the quality of the data, and showed how to fine-tune the models. We also used Argilla to get a human touch on the data." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".env", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.6" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/mkdocs.yml b/mkdocs.yml index 5846a1020d..b98a9edbaf 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -150,6 +150,7 @@ plugins: members_order: source # order methods according to their order of definition in the source code, not alphabetical order heading_level: 4 - social + - mknotebooks - distilabel/components-gallery: add_after_page: How-to guides @@ -184,13 +185,18 @@ nav: - Serving an LLM for sharing it between several tasks: "sections/how_to_guides/advanced/serving_an_llm_for_reuse.md" - Scaling and distributing a pipeline with Ray: "sections/how_to_guides/advanced/scaling_with_ray.md" - Pipeline Samples: - - Examples: "sections/pipeline_samples/examples/index.md" + - "sections/pipeline_samples/examples/index.md" + - Tutorials: + - Synthetic data generation for fine-tuning custom retrieval and reranking models: "sections/pipeline_samples/tutorials/GenerateSentencePair.ipynb" - Papers: - - "sections/pipeline_samples/papers/index.md" - DEITA: "sections/pipeline_samples/papers/deita.md" - Instruction Backtranslation: "sections/pipeline_samples/papers/instruction_backtranslation.md" - Prometheus 2: "sections/pipeline_samples/papers/prometheus.md" - UltraFeedback: "sections/pipeline_samples/papers/ultrafeedback.md" + - Examples: + - Benchmarking with distilabel: "sections/pipeline_samples/examples/benchmarking_with_distilabel.md" + - Llama cpp with outlines: "sections/pipeline_samples/examples/llama_cpp_with_outlines.md" + - MistralAI with instructor: "sections/pipeline_samples/examples/mistralai_with_instructor.md" - DeepSeek Prover: "sections/pipeline_samples/papers/deepseek_prover.md" - API Reference: - Step: diff --git a/src/distilabel/llms/vllm.py b/src/distilabel/llms/vllm.py index 4ff30c07f4..6fcf614f0b 100644 --- a/src/distilabel/llms/vllm.py +++ b/src/distilabel/llms/vllm.py @@ -537,7 +537,7 @@ async def agenerate( # type: ignore """Generates `num_generations` responses for each input. Args: - inputs: a list of inputs in chat format to generate responses for. + input: a single input in chat format to generate responses for. num_generations: the number of generations to create per input. Defaults to `1`. max_new_tokens: the maximum number of new tokens that the model will generate. From ebe7e25255b34ea8de5d7d3b24a84229ede37f94 Mon Sep 17 00:00:00 2001 From: Agus Date: Mon, 19 Aug 2024 15:00:04 +0200 Subject: [PATCH 23/82] Fix load data from disk (#910) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix repo_id in load and make config argument optional if possible * Add tests for LoadFromDisk * Update src/distilabel/steps/generators/huggingface.py Co-authored-by: Gabriel Martín Blázquez * Make error more informative --------- Co-authored-by: Gabriel Martín Blázquez --- .../steps/generators/huggingface.py | 36 +++++++++++-------- .../unit/steps/generators/test_huggingface.py | 26 ++++++++++++++ 2 files changed, 48 insertions(+), 14 deletions(-) diff --git a/src/distilabel/steps/generators/huggingface.py b/src/distilabel/steps/generators/huggingface.py index 243277fd49..4789dfe10a 100644 --- a/src/distilabel/steps/generators/huggingface.py +++ b/src/distilabel/steps/generators/huggingface.py @@ -460,16 +460,16 @@ class LoadDataFromDisk(LoadDataFromHub): Attributes: dataset_path: The path to the dataset or distiset. split: The split of the dataset to load (typically will be `train`, `test` or `validation`). - config: The configuration of the dataset to load. This is optional and only needed - if the dataset has multiple configurations. + config: The configuration of the dataset to load. Defaults to `default`, if there are + multiple configurations in the dataset this must be suplied or an error is raised. Runtime parameters: - `batch_size`: The batch size to use when processing the data. - `dataset_path`: The path to the dataset or distiset. - `is_distiset`: Whether the dataset to load is a `Distiset` or not. Defaults to False. - `split`: The split of the dataset to load. Defaults to 'train'. - - `config`: The configuration of the dataset to load. This is optional and only - needed if the dataset has multiple configurations. + - `config`: The configuration of the dataset to load. Defaults to `default`, if there are + multiple configurations in the dataset this must be suplied or an error is raised. - `num_examples`: The number of examples to load from the dataset. By default will load all examples. - `storage_options`: Key/value pairs to be passed on to the file-system backend, if any. @@ -539,10 +539,12 @@ class LoadDataFromDisk(LoadDataFromHub): default=None, description="Path to the dataset or distiset.", ) - config: RuntimeParameter[str] = Field( - default=None, - description="The configuration of the dataset to load. This is optional and only" - " needed if the dataset has multiple configurations.", + config: Optional[RuntimeParameter[str]] = Field( + default="default", + description=( + "The configuration of the dataset to load. Will default to 'default'", + " which corresponds to a distiset with a single configuration.", + ), ) is_distiset: Optional[RuntimeParameter[bool]] = Field( default=False, @@ -557,6 +559,7 @@ class LoadDataFromDisk(LoadDataFromHub): default=None, description="The split of the dataset to load. By default will load the whole Dataset/Distiset.", ) + repo_id: ExcludedField[Union[str, None]] = None def load(self) -> None: """Load the dataset from the file/s in disk.""" @@ -567,8 +570,15 @@ def load(self) -> None: keep_in_memory=self.keep_in_memory, storage_options=self.storage_options, ) - if self.config: - ds = ds[self.config] + if self.config not in ds.keys(): + # TODO: Add FAQ for this case, pointing to the Distiset documentation on configurations. + raise ValueError( + f"Configuration '{self.config}' not found in the Distiset, available ones" + f" are: {list(ds.keys())}. Please try changing the `config` parameter to one " + "of the available configurations.\n\n" + f"For more information on Distiset configurations, please visit https://distilabel.argilla.io/dev/sections/how_to_guides/advanced/distiset/#using-the-distiset-dataset-object" + ) + ds = ds[self.config] else: ds = load_from_disk( @@ -596,9 +606,7 @@ def outputs(self) -> List[str]: The columns that will be generated by this step. """ # We assume there are Dataset/IterableDataset, not it's ...Dict counterparts - if self._dataset is Ellipsis: - raise ValueError( - "Dataset not loaded yet, you must call `load` method first." - ) + if self._dataset is None: + self.load() return self._dataset.column_names diff --git a/tests/unit/steps/generators/test_huggingface.py b/tests/unit/steps/generators/test_huggingface.py index 281d5187e2..23b3b80079 100644 --- a/tests/unit/steps/generators/test_huggingface.py +++ b/tests/unit/steps/generators/test_huggingface.py @@ -168,6 +168,32 @@ def test_load_dataset_from_disk(self) -> None: assert isinstance(generator_step_output[1], bool) assert len(generator_step_output[0]) == 3 + @pytest.mark.parametrize("config_name", ["default", "missnamed_config"]) + def test_load_distiset_from_disk_default(self, config_name: str) -> None: + distiset = Distiset( + { + "default": Dataset.from_dict({"a": [1, 2, 3]}), + } + ) + with tempfile.TemporaryDirectory() as tmpdir: + dataset_path = str(Path(tmpdir) / "dataset_path") + distiset.save_to_disk(dataset_path) + + loader = LoadDataFromDisk( + dataset_path=dataset_path, + is_distiset=True, + config=config_name, + ) + if config_name != "default": + with pytest.raises(ValueError): + loader.load() + else: + loader.load() + generator_step_output = next(loader.process()) + assert isinstance(generator_step_output, tuple) + assert isinstance(generator_step_output[1], bool) + assert len(generator_step_output[0]) == 3 + def test_load_distiset_from_disk(self) -> None: distiset = Distiset( { From 516909e5c07fa6191b7b21fb82220e239edcc500 Mon Sep 17 00:00:00 2001 From: David Berenstein Date: Mon, 19 Aug 2024 16:30:28 +0200 Subject: [PATCH 24/82] docs: minor fixes (#913) * Fix minor error deepseep prover * Fix minor type generate sentence pairs --- docs/sections/pipeline_samples/examples/index.md | 8 ++++++++ .../pipeline_samples/tutorials/GenerateSentencePair.ipynb | 2 +- mkdocs.yml | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/docs/sections/pipeline_samples/examples/index.md b/docs/sections/pipeline_samples/examples/index.md index 2638c36716..4db66a08f9 100644 --- a/docs/sections/pipeline_samples/examples/index.md +++ b/docs/sections/pipeline_samples/examples/index.md @@ -25,6 +25,14 @@ hide: toc
+- __Deepseek Prover__ + + --- + + Learn about an approach to generate mathematical proofs for theorems generated from informal math problems. + + [:octicons-arrow-right-24: Example](../papers/deepseek_prover.md) + - __DEITA__ --- diff --git a/docs/sections/pipeline_samples/tutorials/GenerateSentencePair.ipynb b/docs/sections/pipeline_samples/tutorials/GenerateSentencePair.ipynb index 7869cef9e1..08406cf82f 100644 --- a/docs/sections/pipeline_samples/tutorials/GenerateSentencePair.ipynb +++ b/docs/sections/pipeline_samples/tutorials/GenerateSentencePair.ipynb @@ -653,7 +653,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Retrieval\n", + "### Reranking\n", "\n", "For reranking, `sentence-transformers` provides a script that shows [how to fine-tune a CrossEncoder models](https://github.com/UKPLab/sentence-transformers/tree/master/examples/training/cross-encoder). Ad of now, there is [some uncertainty over fine-tuning CrossEncoder models with triplets](https://github.com/UKPLab/sentence-transformers/issues/2366) but you can still use the `positive` and `anchor`" ] diff --git a/mkdocs.yml b/mkdocs.yml index b98a9edbaf..f10aeb642f 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -189,6 +189,7 @@ nav: - Tutorials: - Synthetic data generation for fine-tuning custom retrieval and reranking models: "sections/pipeline_samples/tutorials/GenerateSentencePair.ipynb" - Papers: + - DeepSeek Prover: "sections/pipeline_samples/papers/deepseek_prover.md" - DEITA: "sections/pipeline_samples/papers/deita.md" - Instruction Backtranslation: "sections/pipeline_samples/papers/instruction_backtranslation.md" - Prometheus 2: "sections/pipeline_samples/papers/prometheus.md" @@ -197,7 +198,6 @@ nav: - Benchmarking with distilabel: "sections/pipeline_samples/examples/benchmarking_with_distilabel.md" - Llama cpp with outlines: "sections/pipeline_samples/examples/llama_cpp_with_outlines.md" - MistralAI with instructor: "sections/pipeline_samples/examples/mistralai_with_instructor.md" - - DeepSeek Prover: "sections/pipeline_samples/papers/deepseek_prover.md" - API Reference: - Step: - "api/step/index.md" From 2a3906d5a6ae637500bbc0e2259aa8e0089580c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Mart=C3=ADn=20Bl=C3=A1zquez?= Date: Thu, 22 Aug 2024 09:18:50 +0200 Subject: [PATCH 25/82] Add `URIAL` task (#921) * Initial work for `URIAL` * Update template * Fix checking last message * Add `format_output` logic * Refine `format_output` and add docstring * Add `References` * Add `URIAL` unit tests --- src/distilabel/steps/tasks/__init__.py | 2 + .../steps/tasks/templates/urial.jinja2 | 16 +++ src/distilabel/steps/tasks/text_generation.py | 15 ++- src/distilabel/steps/tasks/ultrafeedback.py | 15 +-- src/distilabel/steps/tasks/urial.py | 125 ++++++++++++++++++ .../unit/steps/tasks/test_text_generation.py | 5 +- tests/unit/steps/tasks/test_urial.py | 72 ++++++++++ 7 files changed, 231 insertions(+), 19 deletions(-) create mode 100644 src/distilabel/steps/tasks/templates/urial.jinja2 create mode 100644 src/distilabel/steps/tasks/urial.py create mode 100644 tests/unit/steps/tasks/test_urial.py diff --git a/src/distilabel/steps/tasks/__init__.py b/src/distilabel/steps/tasks/__init__.py index 0b3a69596b..7bd96c3ce0 100644 --- a/src/distilabel/steps/tasks/__init__.py +++ b/src/distilabel/steps/tasks/__init__.py @@ -46,6 +46,7 @@ from distilabel.steps.tasks.text_generation import ChatGeneration, TextGeneration from distilabel.steps.tasks.typing import ChatItem, ChatType from distilabel.steps.tasks.ultrafeedback import UltraFeedback +from distilabel.steps.tasks.urial import URIAL __all__ = [ "GeneratorTask", @@ -79,4 +80,5 @@ "ChatItem", "ChatType", "UltraFeedback", + "URIAL", ] diff --git a/src/distilabel/steps/tasks/templates/urial.jinja2 b/src/distilabel/steps/tasks/templates/urial.jinja2 new file mode 100644 index 0000000000..09a45bcc58 --- /dev/null +++ b/src/distilabel/steps/tasks/templates/urial.jinja2 @@ -0,0 +1,16 @@ +# Instruction + +Below is a list of conversations between a human and an AI assistant (you). +Users place their queries under "# User:", and your responses are under "# Assistant:". +You are a helpful, respectful, and honest assistant. +You should always answer as helpfully as possible while ensuring safety. +Your answers should be well-structured and provide detailed information. They should also have an engaging tone. +Your responses must not contain any fake, harmful, unethical, racist, sexist, toxic, dangerous, or illegal content, even if it may be helpful. +Your response must be socially responsible, and thus you can refuse to answer some controversial topics. + +{% for message in messages %} +# {{ message.role | capitalize }}: + +{{ message.content }} +{% endfor %} +# Assistant: diff --git a/src/distilabel/steps/tasks/text_generation.py b/src/distilabel/steps/tasks/text_generation.py index f5c4659651..aeb74c9ec7 100644 --- a/src/distilabel/steps/tasks/text_generation.py +++ b/src/distilabel/steps/tasks/text_generation.py @@ -13,12 +13,15 @@ # limitations under the License. import warnings -from typing import Any, Dict, List, Union +from typing import TYPE_CHECKING, Any, Dict, List, Union from distilabel.steps.tasks.base import Task -from distilabel.steps.tasks.typing import ChatType from distilabel.utils.chat import is_openai_format +if TYPE_CHECKING: + from distilabel.steps.tasks.typing import ChatType + from distilabel.steps.typing import StepColumns + class TextGeneration(Task): """Simple text generation with an `LLM` given an instruction. @@ -78,11 +81,11 @@ class TextGeneration(Task): use_system_prompt: bool = True @property - def inputs(self) -> List[str]: + def inputs(self) -> "StepColumns": """The input for the task is the `instruction`.""" return ["instruction"] - def format_input(self, input: Dict[str, Any]) -> ChatType: + def format_input(self, input: Dict[str, Any]) -> "ChatType": """The input is formatted as a `ChatType` assuming that the instruction is the first interaction from the user within a conversation.""" @@ -189,7 +192,7 @@ def inputs(self) -> List[str]: """The input for the task are the `messages`.""" return ["messages"] - def format_input(self, input: Dict[str, Any]) -> ChatType: + def format_input(self, input: Dict[str, Any]) -> "ChatType": """The input is formatted as a `ChatType` assuming that the messages provided are already formatted that way i.e. following the OpenAI chat format.""" @@ -213,7 +216,7 @@ def outputs(self) -> List[str]: return ["generation", "model_name"] def format_output( - self, output: Union[str, None], input: Dict[str, Any] + self, output: Union[str, None], input: Union[Dict[str, Any], None] = None ) -> Dict[str, Any]: """The output is formatted as a dictionary with the `generation`. The `model_name` will be automatically included within the `process` method of `Task`.""" diff --git a/src/distilabel/steps/tasks/ultrafeedback.py b/src/distilabel/steps/tasks/ultrafeedback.py index eec232aabd..dae68bb48f 100644 --- a/src/distilabel/steps/tasks/ultrafeedback.py +++ b/src/distilabel/steps/tasks/ultrafeedback.py @@ -12,14 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +import importlib.resources as importlib_resources import re -import sys - -if sys.version_info < (3, 9): - import importlib_resources -else: - import importlib.resources as importlib_resources - from typing import Any, Dict, List, Literal, Optional, Union import orjson @@ -264,7 +258,7 @@ def outputs(self) -> List[str]: return columns + ["model_name"] def format_output( - self, output: Union[str, None], input: Dict[str, Any] + self, output: Union[str, None], input: Union[Dict[str, Any], None] = None ) -> Dict[str, Any]: """The output is formatted as a dictionary with the `ratings` and `rationales` for each of the provided `generations` for the given `instruction`. The `model_name` @@ -281,12 +275,15 @@ def format_output( `ratings`, and `rationales-for-ratings` for each of the provided `generations` for the given `instruction` if the provided aspect is either `helpfulness` or `truthfulness`. """ + assert input is not None, "Input is required to format the output." + if self.aspect in [ "honesty", "instruction-following", "overall-rating", ]: return self._format_ratings_rationales_output(output, input) + return self._format_types_ratings_rationales_output(output, input) def _format_ratings_rationales_output( @@ -450,7 +447,7 @@ class SchemaUltraFeedbackWithType(BaseModel): def _format_structured_output( self, output: str, input: Dict[str, Any] - ) -> Dict[str, str]: + ) -> Dict[str, Any]: """Parses the structured response, which should correspond to a dictionary with either `positive`, or `positive` and `negative` keys. diff --git a/src/distilabel/steps/tasks/urial.py b/src/distilabel/steps/tasks/urial.py new file mode 100644 index 0000000000..ed0e72d969 --- /dev/null +++ b/src/distilabel/steps/tasks/urial.py @@ -0,0 +1,125 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import importlib.resources as importlib_resources +from typing import TYPE_CHECKING, Any, Dict, Union + +from jinja2 import Template + +from distilabel.steps.tasks import Task + +if TYPE_CHECKING: + from distilabel.steps.tasks.typing import ChatType + from distilabel.steps.typing import StepColumns + + +class URIAL(Task): + """Generates a response using a non-instruct fine-tuned model. + + `URIAL` is a pre-defined task that generates a response using a non-instruct fine-tuned + model. This task is used to generate a response based on the conversation provided as + input. + + Input columns: + - instruction (`str`, optional): The instruction to generate a response from. + - conversation (`List[Dict[str, str]]`, optional): The conversation to generate + a response from (the last message must be from the user). + + Output columns: + - generation (`str`): The generated response. + - model_name (`str`): The name of the model used to generate the response. + + Categories: + - text-generation + + References: + - [The Unlocking Spell on Base LLMs: Rethinking Alignment via In-Context Learning](https://arxiv.org/abs/2312.01552) + + Examples: + + Generate text from an instruction: + + ```python + from distilabel.llms import vLLM + from distilabel.steps.tasks import URIAL + + step = URIAL( + llm=vLLM( + model="meta-llama/Meta-Llama-3.1-8B", + generation_kwargs={"temperature": 0.7}, + ), + ) + + step.load() + + results = next( + step.process(inputs=[{"instruction": "What's the most most common type of cloud?"}]) + ) + # [ + # { + # 'instruction': "What's the most most common type of cloud?", + # 'generation': 'Clouds are classified into three main types, high, middle, and low. The most common type of cloud is the middle cloud.', + # 'distilabel_metadata': {...}, + # 'model_name': 'meta-llama/Meta-Llama-3.1-8B' + # } + # ] + ``` + """ + + def load(self) -> None: + """Loads the Jinja2 template for the given `aspect`.""" + super().load() + + _path = str( + importlib_resources.files("distilabel") + / "steps" + / "tasks" + / "templates" + / "urial.jinja2" + ) + + self._template = Template(open(_path).read()) + + @property + def inputs(self) -> "StepColumns": + return {"instruction": False, "conversation": False} + + def format_input(self, input: Dict[str, Any]) -> "ChatType": + messages = ( + [{"role": "user", "content": input["instruction"]}] + if "instruction" in input + else input["conversation"] + ) + + if messages[-1]["role"] != "user": + raise ValueError("The last message must be from the user.") + + return [{"role": "user", "content": self._template.render(messages=messages)}] + + @property + def outputs(self) -> "StepColumns": + return ["generation", "model_name"] + + def format_output( + self, output: Union[str, None], input: Union[Dict[str, Any], None] = None + ) -> Dict[str, Any]: + if output is None: + return {"generation": None} + + response = output.split("\n\n# User")[0] + if response.startswith("\n\n"): + response = response[2:] + response = response.strip() + + return {"generation": response} diff --git a/tests/unit/steps/tasks/test_text_generation.py b/tests/unit/steps/tasks/test_text_generation.py index dd43530cd9..2ed399b237 100644 --- a/tests/unit/steps/tasks/test_text_generation.py +++ b/tests/unit/steps/tasks/test_text_generation.py @@ -21,11 +21,8 @@ class TestTextGeneration: def test_format_input(self) -> None: - pipeline = Pipeline(name="unit-test-pipeline") llm = DummyLLM() - task = TextGeneration( - name="task", llm=llm, pipeline=pipeline, use_system_prompt=False - ) + task = TextGeneration(name="task", llm=llm, use_system_prompt=False) assert task.format_input({"instruction": "test", "system_prompt": "test"}) == [ {"role": "user", "content": "test"} diff --git a/tests/unit/steps/tasks/test_urial.py b/tests/unit/steps/tasks/test_urial.py new file mode 100644 index 0000000000..2075d98e6e --- /dev/null +++ b/tests/unit/steps/tasks/test_urial.py @@ -0,0 +1,72 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import pytest +from distilabel.steps.tasks.urial import URIAL + +from tests.unit.conftest import DummyLLM + + +class TestURIAL: + def test_format_input(self) -> None: + task = URIAL(llm=DummyLLM()) + task.load() + assert task.format_input({"instruction": "test"}) == [ + { + "role": "user", + "content": '# Instruction\n\nBelow is a list of conversations between a human and an AI assistant (you). \nUsers place their queries under "# User:", and your responses are under "# Assistant:".\nYou are a helpful, respectful, and honest assistant.\nYou should always answer as helpfully as possible while ensuring safety.\nYour answers should be well-structured and provide detailed information. They should also have an engaging tone.\nYour responses must not contain any fake, harmful, unethical, racist, sexist, toxic, dangerous, or illegal content, even if it may be helpful.\nYour response must be socially responsible, and thus you can refuse to answer some controversial topics.\n\n\n# User:\n\ntest\n\n# Assistant:', + } + ] + + def test_format_input_with_conversation(self) -> None: + task = URIAL(llm=DummyLLM()) + task.load() + assert task.format_input( + { + "conversation": [ + {"role": "user", "content": "test"}, + {"role": "assistant", "content": "test"}, + {"role": "user", "content": "test"}, + ] + } + ) == [ + { + "role": "user", + "content": '# Instruction\n\nBelow is a list of conversations between a human and an AI assistant (you). \nUsers place their queries under "# User:", and your responses are under "# Assistant:".\nYou are a helpful, respectful, and honest assistant.\nYou should always answer as helpfully as possible while ensuring safety.\nYour answers should be well-structured and provide detailed information. They should also have an engaging tone.\nYour responses must not contain any fake, harmful, unethical, racist, sexist, toxic, dangerous, or illegal content, even if it may be helpful.\nYour response must be socially responsible, and thus you can refuse to answer some controversial topics.\n\n\n# User:\n\ntest\n\n# Assistant:\n\ntest\n\n# User:\n\ntest\n\n# Assistant:', + } + ] + + def test_format_input_raise_valueerror(self) -> None: + task = URIAL(llm=DummyLLM()) + task.load() + + with pytest.raises(ValueError, match="The last message must be from the user."): + assert task.format_input( + { + "conversation": [ + {"role": "user", "content": "test"}, + {"role": "assistant", "content": "test"}, + ] + } + ) + + def test_format_output(self) -> None: + task = URIAL(llm=DummyLLM()) + task.load() + + assert task.format_output( + output=" \n\noutput\n\n# User:", input={"instruction": "test"} + ) == { + "generation": "output", + } From a796a7519ccdcebbd0bbdb23f0c60213de440074 Mon Sep 17 00:00:00 2001 From: Agus Date: Thu, 22 Aug 2024 10:12:43 +0200 Subject: [PATCH 26/82] Add `vLLMEmbeddings` (#920) * Add vLLMEmbeddings to work with multiple GPUs * Add mocked tests --- src/distilabel/embeddings/__init__.py | 2 + src/distilabel/embeddings/vllm.py | 130 ++++++++++++++++++++++++++ tests/unit/embeddings/test_vllm.py | 50 ++++++++++ 3 files changed, 182 insertions(+) create mode 100644 src/distilabel/embeddings/vllm.py create mode 100644 tests/unit/embeddings/test_vllm.py diff --git a/src/distilabel/embeddings/__init__.py b/src/distilabel/embeddings/__init__.py index a7e5e63e2c..190ea70e50 100644 --- a/src/distilabel/embeddings/__init__.py +++ b/src/distilabel/embeddings/__init__.py @@ -14,8 +14,10 @@ from distilabel.embeddings.base import Embeddings from distilabel.embeddings.sentence_transformers import SentenceTransformerEmbeddings +from distilabel.embeddings.vllm import vLLMEmbeddings __all__ = [ "Embeddings", "SentenceTransformerEmbeddings", + "vLLMEmbeddings", ] diff --git a/src/distilabel/embeddings/vllm.py b/src/distilabel/embeddings/vllm.py new file mode 100644 index 0000000000..8e0c7caed2 --- /dev/null +++ b/src/distilabel/embeddings/vllm.py @@ -0,0 +1,130 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union + +from pydantic import Field, PrivateAttr + +from distilabel.embeddings.base import Embeddings +from distilabel.llms.mixins.cuda_device_placement import CudaDevicePlacementMixin +from distilabel.mixins.runtime_parameters import RuntimeParameter + +if TYPE_CHECKING: + from vllm import LLM as _vLLM + + +class vLLMEmbeddings(Embeddings, CudaDevicePlacementMixin): + """`vllm` library implementation for embedding generation. + + Attributes: + model: the model Hugging Face Hub repo id or a path to a directory containing the + model weights and configuration files. + dtype: the data type to use for the model. Defaults to `auto`. + trust_remote_code: whether to trust the remote code when loading the model. Defaults + to `False`. + quantization: the quantization mode to use for the model. Defaults to `None`. + revision: the revision of the model to load. Defaults to `None`. + enforce_eager: whether to enforce eager execution. Defaults to `True`. + seed: the seed to use for the random number generator. Defaults to `0`. + extra_kwargs: additional dictionary of keyword arguments that will be passed to the + `LLM` class of `vllm` library. Defaults to `{}`. + _model: the `vLLM` model instance. This attribute is meant to be used internally + and should not be accessed directly. It will be set in the `load` method. + + References: + - [Offline inference embeddings](https://docs.vllm.ai/en/latest/getting_started/examples/offline_inference_embedding.html) + + Examples: + + Generating sentence embeddings: + + ```python + from distilabel.embeddings import vLLMEmbeddings + + embeddings = vLLMEmbeddings(model="intfloat/e5-mistral-7b-instruct") + + embeddings.load() + + results = embeddings.encode(inputs=["distilabel is awesome!", "and Argilla!"]) + # [ + # [-0.05447685346007347, -0.01623094454407692, ...], + # [4.4889533455716446e-05, 0.044016145169734955, ...], + # ] + ``` + """ + + model: str + dtype: str = "auto" + trust_remote_code: bool = False + quantization: Optional[str] = None + revision: Optional[str] = None + + enforce_eager: bool = True + + seed: int = 0 + + extra_kwargs: Optional[RuntimeParameter[Dict[str, Any]]] = Field( + default_factory=dict, + description="Additional dictionary of keyword arguments that will be passed to the" + " `vLLM` class of `vllm` library. See all the supported arguments at: " + "https://github.com/vllm-project/vllm/blob/main/vllm/entrypoints/llm.py", + ) + + _model: "_vLLM" = PrivateAttr(None) + + def load(self) -> None: + """Loads the `vLLM` model using either the path or the Hugging Face Hub repository id.""" + super().load() + + CudaDevicePlacementMixin.load(self) + + try: + from vllm import LLM as _vLLM + + except ImportError as ie: + raise ImportError( + "vLLM is not installed. Please install it using `pip install vllm`." + ) from ie + + self._model = _vLLM( + self.model, + dtype=self.dtype, + trust_remote_code=self.trust_remote_code, + quantization=self.quantization, + revision=self.revision, + enforce_eager=self.enforce_eager, + seed=self.seed, + **self.extra_kwargs, # type: ignore + ) + + def unload(self) -> None: + """Unloads the `vLLM` model.""" + CudaDevicePlacementMixin.unload(self) + super().unload() + + @property + def model_name(self) -> str: + """Returns the name of the model.""" + return self.model + + def encode(self, inputs: List[str]) -> List[List[Union[int, float]]]: + """Generates embeddings for the provided inputs. + + Args: + inputs: a list of texts for which an embedding has to be generated. + + Returns: + The generated embeddings. + """ + return [output.outputs.embedding for output in self._model.encode(inputs)] diff --git a/tests/unit/embeddings/test_vllm.py b/tests/unit/embeddings/test_vllm.py new file mode 100644 index 0000000000..8291f434e9 --- /dev/null +++ b/tests/unit/embeddings/test_vllm.py @@ -0,0 +1,50 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from unittest.mock import MagicMock, Mock + +from distilabel.embeddings.vllm import vLLMEmbeddings + + +# @patch("vllm.entrypoints.LLM") +class TestSentenceTransformersEmbeddings: + model_name = "group/model-name" + + def test_model_name(self) -> None: + embeddings = vLLMEmbeddings(model=self.model_name) + + assert embeddings.model_name == self.model_name + + def test_encode(self) -> None: + embeddings = vLLMEmbeddings(model=self.model_name) + + # the loading should be done here, it's just mocked + # embeddings.load() + embeddings._model = MagicMock() + + mocked_response = Mock(outputs=Mock(embedding=[0.1] * 10)) + embeddings._model.encode = Mock( + side_effect=lambda x: [mocked_response for _ in range(len(x))] + ) + + results = embeddings.encode( + inputs=[ + "Hello, how are you?", + "What a nice day!", + "I hear that llamas are very popular now.", + ] + ) + + for result in results: + assert len(result) == 10 From 46d55edbad9357558a98356ffb425683402781fe Mon Sep 17 00:00:00 2001 From: Sara Han <127759186+sdiazlor@users.noreply.github.com> Date: Thu, 22 Aug 2024 10:31:01 +0200 Subject: [PATCH 27/82] docs: add tutorials preference and clean (#917) * add tutorials * clean dataset tutorial * generate preference dataset tutorial * modify sentence pairs tutorial * add to index * add missing component * fix: first feedback * fix: add headers * fix: process for steps * fix: typo and note * add torch * fix typo --- .../pipeline_samples/{examples => }/index.md | 36 +- .../tutorials/GenerateSentencePair.ipynb | 35 +- .../tutorials/clean_existing_dataset.ipynb | 589 ++++++++++++++++++ .../generate_preference_dataset.ipynb | 584 +++++++++++++++++ mkdocs.yml | 4 +- 5 files changed, 1231 insertions(+), 17 deletions(-) rename docs/sections/pipeline_samples/{examples => }/index.md (64%) create mode 100644 docs/sections/pipeline_samples/tutorials/clean_existing_dataset.ipynb create mode 100644 docs/sections/pipeline_samples/tutorials/generate_preference_dataset.ipynb diff --git a/docs/sections/pipeline_samples/examples/index.md b/docs/sections/pipeline_samples/index.md similarity index 64% rename from docs/sections/pipeline_samples/examples/index.md rename to docs/sections/pipeline_samples/index.md index 4db66a08f9..800d4342ba 100644 --- a/docs/sections/pipeline_samples/examples/index.md +++ b/docs/sections/pipeline_samples/index.md @@ -11,13 +11,31 @@ hide: toc
+- __Generate a preference dataset__ + + --- + + Learn about synthetic data generation for ORPO and DPO. + + [:octicons-arrow-right-24: Tutorial](tutorials/generate_preference_dataset.ipynb) + + +- __Clean an existing preference dataset__ + + --- + + Learn about how to provide AI feedback to clean an existing dataset. + + [:octicons-arrow-right-24: Tutorial](tutorials/clean_existing_dataset.ipynb) + + - __Retrieval and reranking models__ --- Learn about synthetic data generation for fine-tuning custom retrieval and reranking models. - [:octicons-arrow-right-24: Tutorial](../tutorials/GenerateSentencePair.ipynb) + [:octicons-arrow-right-24: Tutorial](tutorials/GenerateSentencePair.ipynb)
@@ -31,7 +49,7 @@ hide: toc Learn about an approach to generate mathematical proofs for theorems generated from informal math problems. - [:octicons-arrow-right-24: Example](../papers/deepseek_prover.md) + [:octicons-arrow-right-24: Example](papers/deepseek_prover.md) - __DEITA__ @@ -39,7 +57,7 @@ hide: toc Learn about prompt, response tuning for complexity and quality and LLMs as judges for automatic data selection. - [:octicons-arrow-right-24: Paper](../papers/deita.md) + [:octicons-arrow-right-24: Paper](papers/deita.md) - __Instruction Backtranslation__ @@ -47,7 +65,7 @@ hide: toc Learn about automatically labeling human-written text with corresponding instructions. - [:octicons-arrow-right-24: Paper](../papers/instruction_backtranslation.md) + [:octicons-arrow-right-24: Paper](papers/instruction_backtranslation.md) - __Prometheus 2__ @@ -55,7 +73,7 @@ hide: toc Learn about using open-source models as judges for direct assessment and pair-wise ranking. - [:octicons-arrow-right-24: Paper](../papers/prometheus.md) + [:octicons-arrow-right-24: Paper](papers/prometheus.md) - __UltraFeedback__ @@ -63,7 +81,7 @@ hide: toc Learn about a large-scale, fine-grained, diverse preference dataset, used for training powerful reward and critic models. - [:octicons-arrow-right-24: Paper](../papers/ultrafeedback.md) + [:octicons-arrow-right-24: Paper](papers/ultrafeedback.md)
@@ -77,7 +95,7 @@ hide: toc Learn about reproducing the Arena Hard benchmark with disitlabel. - [:octicons-arrow-right-24: Example](./benchmarking_with_distilabel.md) + [:octicons-arrow-right-24: Example](benchmarking_with_distilabel.md) - __llama.cpp with outlines__ @@ -85,7 +103,7 @@ hide: toc Learn about generating RPG characters following a pydantic.BaseModel with outlines in distilabel. - [:octicons-arrow-right-24: Example](./llama_cpp_with_outlines.md) + [:octicons-arrow-right-24: Example](llama_cpp_with_outlines.md) - __MistralAI with instructor__ @@ -93,7 +111,7 @@ hide: toc Learn about answering instructions with knowledge graphs defined as pydantic.BaseModel objects using instructor in distilabel. - [:octicons-arrow-right-24: Example](../papers/prometheus.md) + [:octicons-arrow-right-24: Example](papers/prometheus.md) diff --git a/docs/sections/pipeline_samples/tutorials/GenerateSentencePair.ipynb b/docs/sections/pipeline_samples/tutorials/GenerateSentencePair.ipynb index 08406cf82f..f4450c4a91 100644 --- a/docs/sections/pipeline_samples/tutorials/GenerateSentencePair.ipynb +++ b/docs/sections/pipeline_samples/tutorials/GenerateSentencePair.ipynb @@ -8,7 +8,10 @@ "\n", "- **Goal**: Bootstrap, optimize and maintain your embedding models and rerankers through synthetic data generation and human feedback.\n", "- **Libraries**: [argilla](https://github.com/argilla-io/argilla), [hf-inference-endpoints](https://github.com/huggingface/huggingface_hub), [sentence-transformers](https://github.com/UKPLab/sentence-transformers)\n", - "- **Components**: [LoadDataFromHub](https://distilabel.argilla.io/latest/components-gallery/steps/loaddatafromhub/), [GenerateSentencePair](https://distilabel.argilla.io/latest/components-gallery/tasks/generatesentencepair/), [InferenceEndpointsLLM](https://distilabel.argilla.io/latest/components-gallery/llms/inferenceendpointsllm/)" + "- **Components**: [LoadDataFromHub](https://distilabel.argilla.io/latest/components-gallery/steps/loaddatafromhub/), [GenerateSentencePair](https://distilabel.argilla.io/latest/components-gallery/tasks/generatesentencepair/), [InferenceEndpointsLLM](https://distilabel.argilla.io/latest/components-gallery/llms/inferenceendpointsllm/)\n", + "\n", + "!!! note\n", + " For a comprehensive overview on optimizing the retrieval performance in a RAG pipeline, check this [guide](https://docs.zenml.io/user-guide/llmops-guide/finetuning-embeddings) in collaboration with [ZenML](https://github.com/zenml-io/zenml), an open-source MLOps framework designed for building portable and production-ready machine learning pipelines." ] }, { @@ -19,7 +22,7 @@ "\n", "### Install the dependencies\n", "\n", - "To complete this tutorial, you need to install the distilabel SDK and a few third-party libraries via pip. We will be using **the free but rate-limited Hugging Face serverless Inference API** for this tutorial, so we need to install this as extra distilabel dependency. You can install them by running the following command:" + "To complete this tutorial, you need to install the distilabel SDK and a few third-party libraries via pip. We will be using **the free but rate-limited Hugging Face serverless Inference API** for this tutorial, so we need to install this as an extra distilabel dependency. You can install them by running the following command:" ] }, { @@ -37,7 +40,7 @@ "metadata": {}, "outputs": [], "source": [ - "!pip install \"sentence-transformer>=3,<4\"" + "!pip install \"sentence-transformers>=3.0,<4.0\"" ] }, { @@ -67,12 +70,30 @@ "cell_type": "markdown", "metadata": {}, "source": [ + "You'll need an `HF_TOKEN` to use the HF Inference Endpoints. Login to use it directly within this notebook." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "from huggingface_hub import login\n", "\n", + "login(token=os.getenv(\"HF_TOKEN\"), add_to_git_credential=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ "### (optional) Deploy Argilla\n", "\n", - "You can skip this step or replace it with any other data evaluation tool but the quality of your model will suffer from a lack of data quality so we do recommend to look at your data. If you already have deployed Argilla, you can skip this step. Otherwise, you can quickly deploy Argilla following [this guide](https://docs.argilla.io/latest/getting_started/quickstart/). \n", + "You can skip this step or replace it with any other data evaluation tool, but the quality of your model will suffer from a lack of data quality, so we do recommend looking at your data. If you already have deployed Argilla, you can skip this step. Otherwise, you can quickly deploy Argilla following [this guide](https://docs.argilla.io/latest/getting_started/quickstart/). \n", "\n", - "Allong with that, you will need to install argilla as distilabel extra." + "Along with that, you will need to install argilla as distilabel extra." ] }, { @@ -136,7 +157,7 @@ "metadata": {}, "outputs": [], "source": [ - "context = context = (\n", + "context = (\n", "\"\"\"\n", "The text is a chunk from technical Python SDK documentation of Argilla.\n", "Argilla is a collaboration tool for AI engineers and domain experts to build high-quality datasets.\n", @@ -545,7 +566,7 @@ "from argilla._exceptions import ConflictError\n", "\n", "api_key = \"ohh so secret\"\n", - "api_url = \"https://davidberenstein1957-my-argilla.hf.space\"\n", + "api_url = \"https://[your-owner-name]-[your-space-name].hf.space\"\n", "\n", "client = rg.Argilla(api_url=api_url, api_key=api_key)\n", "\n", diff --git a/docs/sections/pipeline_samples/tutorials/clean_existing_dataset.ipynb b/docs/sections/pipeline_samples/tutorials/clean_existing_dataset.ipynb new file mode 100644 index 0000000000..c4cf4a4c52 --- /dev/null +++ b/docs/sections/pipeline_samples/tutorials/clean_existing_dataset.ipynb @@ -0,0 +1,589 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Clean an existing preference dataset" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- **Goal**: Clean an existing preference dataset by providing AI feedback on the quality of the data.\n", + "- **Libraries**: [argilla](https://github.com/argilla-io/argilla), [hf-inference-endpoints](https://github.com/huggingface/huggingface_hub)\n", + "- **Components**: [LoadDataFromDicts](https://distilabel.argilla.io/dev/components-gallery/steps/loaddatafromdicts/), [UltraFeedback](https://distilabel.argilla.io/latest/components-gallery/tasks/ultrafeedback/), [KeepColumns](https://distilabel.argilla.io/latest/components-gallery/steps/groupcolumns/), [PreferenceToArgilla](https://distilabel.argilla.io/latest/components-gallery/steps/textgenerationtoargilla/), [InferenceEndpointsLLM](https://distilabel.argilla.io/latest/components-gallery/llms/inferenceendpointsllm/), [GlobalStep](../../how_to_guides/basic/step/global_step.md)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Getting Started" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Install the dependencies\n", + "\n", + "To complete this tutorial, you need to install the distilabel SDK and a few third-party libraries via pip. We will be using **the free but rate-limited Hugging Face serverless Inference API** for this tutorial, so we need to install this as an extra distilabel dependency. You can install them by running the following command:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!pip install \"distilabel[hf-inference-endpoints]\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!pip install \"transformers>=4.0,<5.0\" \"torch>=2.0,<3.0\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's make the required imports:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "import random\n", + "\n", + "from datasets import load_dataset\n", + "\n", + "from distilabel.llms import InferenceEndpointsLLM\n", + "from distilabel.pipeline import Pipeline\n", + "from distilabel.steps import (\n", + " KeepColumns,\n", + " LoadDataFromDicts,\n", + " PreferenceToArgilla,\n", + ")\n", + "from distilabel.steps.tasks import UltraFeedback" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You'll need an `HF_TOKEN` to use the HF Inference Endpoints. Login to use it directly within this notebook." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "from huggingface_hub import login\n", + "\n", + "login(token=os.getenv(\"HF_TOKEN\"), add_to_git_credential=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### (optional) Deploy Argilla\n", + "\n", + "You can skip this step or replace it with any other data evaluation tool, but the quality of your model will suffer from a lack of data quality, so we do recommend looking at your data. If you already have deployed Argilla, you can skip this step. Otherwise, you can quickly deploy Argilla following [this guide](https://docs.argilla.io/latest/getting_started/quickstart/). \n", + "\n", + "Along with that, you will need to install argilla as distilabel extra." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!pip install \"distilabel[argilla, hf-inference-endpoints]\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Load the dataset" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In this case, we will clean a preference dataset, so we will use the `Intel/orca_dpo_pairs` dataset from the [Hugging Face Hub](https://huggingface.co/datasets/Intel/orca_dpo_pairs)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "dataset = load_dataset(\"Intel/orca_dpo_pairs\", split=\"train[:20]\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Next, we will shuffle the `chosen` and `rejected` columns to avoid any bias in the dataset." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "def shuffle_and_track(chosen, rejected):\n", + " pair = [chosen, rejected]\n", + " random.shuffle(pair)\n", + " order = [\"chosen\" if x == chosen else \"rejected\" for x in pair]\n", + " return {\"generations\": pair, \"order\": order}\n", + "\n", + "dataset = dataset.map(lambda x: shuffle_and_track(x[\"chosen\"], x[\"rejected\"]))" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "dataset = dataset.to_list()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "??? tip \"As a custom step\"\n", + " You can also [create a custom step](../../how_to_guides/basic/step/global_step.md) in a separate module, import it and add it to the pipeline after loading the `orca_dpo_pairs` dataset using the `LoadDataFromHub` step.\n", + "\n", + " ```python title=\"shuffle_step.py\"\n", + " from typing import TYPE_CHECKING, List\n", + " from distilabel.steps import GlobalStep, StepInput\n", + "\n", + " if TYPE_CHECKING:\n", + " from distilabel.steps.typing import StepOutput\n", + " \n", + " import random\n", + "\n", + " class ShuffleStep(GlobalStep):\n", + " @property\n", + " def inputs(self) -> List[str]:\n", + " return [\"instruction\", \"chosen\", \"rejected\"]\n", + "\n", + " @property\n", + " def outputs(self) -> List[str]:\n", + " return [\"instruction\", \"generations\", \"order\"]\n", + "\n", + " def process(self, inputs: StepInput) -> \"StepOutput\": # type: ignore\n", + " outputs = []\n", + "\n", + " for input in inputs:\n", + " chosen = input[\"chosen\"]\n", + " rejected = input[\"rejected\"]\n", + " pair = [chosen, rejected]\n", + " random.shuffle(pair)\n", + " order = [\"chosen\" if x == chosen else \"rejected\" for x in pair]\n", + " \n", + " outputs.append({\"instruction\": input[\"instruction\"], \"generations\": pair, \"order\": order})\n", + "\n", + " yield outputs\n", + " ```\n", + " \n", + " ```python\n", + " from shuffle_step import ShuffleStep\n", + " ```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Define the pipeline" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To clean an existing preference dataset, we will need to define a `Pipeline` with all the necessary steps. However, a similar workflow can be used to clean a SFT dataset. Below, we will go over each step in detail." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Load the dataset\n", + "We will use the dataset we just shuffled as source data.\n", + "\n", + "- Component: `LoadDataFromDicts`\n", + "- Input columns: `system`, `question`, `chosen`, `rejected`, `generations` and `order`, the same keys as in the loaded list of dictionaries.\n", + "- Output columns: `system`, `instruction`, `chosen`, `rejected`, `generations` and `order`. We will use `output_mappings` to rename the columns." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "([{'system': '',\n", + " 'question': \"You will be given a definition of a task first, then some input of the task.\\nThis task is about using the specified sentence and converting the sentence to Resource Description Framework (RDF) triplets of the form (subject, predicate object). The RDF triplets generated must be such that the triplets accurately capture the structure and semantics of the input sentence. The input is a sentence and the output is a list of triplets of the form [subject, predicate, object] that capture the relationships present in the sentence. When a sentence has more than 1 RDF triplet possible, the output must contain all of them.\\n\\nAFC Ajax (amateurs)'s ground is Sportpark De Toekomst where Ajax Youth Academy also play.\\nOutput:\",\n", + " 'chosen': '[\\n [\"AFC Ajax (amateurs)\", \"has ground\", \"Sportpark De Toekomst\"],\\n [\"Ajax Youth Academy\", \"plays at\", \"Sportpark De Toekomst\"]\\n]',\n", + " 'rejected': \" Sure, I'd be happy to help! Here are the RDF triplets for the input sentence:\\n\\n[AFC Ajax (amateurs), hasGround, Sportpark De Toekomst]\\n[Ajax Youth Academy, playsAt, Sportpark De Toekomst]\\n\\nExplanation:\\n\\n* AFC Ajax (amateurs) is the subject of the first triplet, and hasGround is the predicate that describes the relationship between AFC Ajax (amateurs) and Sportpark De Toekomst.\\n* Ajax Youth Academy is the subject of the second triplet, and playsAt is the predicate that describes the relationship between Ajax Youth Academy and Sportpark De Toekomst.\\n\\nNote that there may be other possible RDF triplets that could be derived from the input sentence, but the above triplets capture the main relationships present in the sentence.\",\n", + " 'generations': [\" Sure, I'd be happy to help! Here are the RDF triplets for the input sentence:\\n\\n[AFC Ajax (amateurs), hasGround, Sportpark De Toekomst]\\n[Ajax Youth Academy, playsAt, Sportpark De Toekomst]\\n\\nExplanation:\\n\\n* AFC Ajax (amateurs) is the subject of the first triplet, and hasGround is the predicate that describes the relationship between AFC Ajax (amateurs) and Sportpark De Toekomst.\\n* Ajax Youth Academy is the subject of the second triplet, and playsAt is the predicate that describes the relationship between Ajax Youth Academy and Sportpark De Toekomst.\\n\\nNote that there may be other possible RDF triplets that could be derived from the input sentence, but the above triplets capture the main relationships present in the sentence.\",\n", + " '[\\n [\"AFC Ajax (amateurs)\", \"has ground\", \"Sportpark De Toekomst\"],\\n [\"Ajax Youth Academy\", \"plays at\", \"Sportpark De Toekomst\"]\\n]'],\n", + " 'order': ['rejected', 'chosen']}],\n", + " True)" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "load_dataset = LoadDataFromDicts(\n", + " data=dataset[:1],\n", + " output_mappings={\"question\": \"instruction\"},\n", + " pipeline=Pipeline(name=\"showcase-pipeline\"),\n", + ")\n", + "load_dataset.load()\n", + "next(load_dataset.process())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Evaluate the responses\n", + "\n", + "To evaluate the quality of the responses, we will use [`meta-llama/Meta-Llama-3.1-70B-Instruct`](https://huggingface.co/meta-llama/Meta-Llama-3.1-70B-Instruct), applying the `UltraFeedback` task that judges the responses according to different dimensions (helpfulness, honesty, instruction-following, truthfulness). For an SFT dataset, you can use [`PrometheusEval`](../papers/prometheus.md) instead.\n", + "\n", + "- Component: `UltraFeedback` task with LLMs using `InferenceEndpointsLLM`\n", + "- Input columns: `instruction`, `generations`\n", + "- Output columns: `ratings`, `rationales`, `distilabel_metadata`, `model_name`\n", + "\n", + "For your use case and to improve the results, you can use any [other LLM of your choice](https://distilabel.argilla.io/latest/components-gallery/llms/)." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[{'instruction': \"What's the capital of Spain?\",\n", + " 'generations': ['Madrid', 'Barcelona'],\n", + " 'ratings': [5, 1],\n", + " 'rationales': [\"The answer is correct, directly addressing the question, and is free of hallucinations or unnecessary details. It confidently provides the accurate information, aligning perfectly with the user's intent.\",\n", + " \"The answer is incorrect as Barcelona is not the capital of Spain. This introduces a significant inaccuracy, failing to provide helpful information and deviating entirely from the user's intent.\"],\n", + " 'distilabel_metadata': {'raw_output_ultra_feedback_0': \"#### Output for Text 1\\nRating: 5 (Excellent)\\nRationale: The answer is correct, directly addressing the question, and is free of hallucinations or unnecessary details. It confidently provides the accurate information, aligning perfectly with the user's intent.\\n\\n#### Output for Text 2\\nRating: 1 (Low Quality)\\nRationale: The answer is incorrect as Barcelona is not the capital of Spain. This introduces a significant inaccuracy, failing to provide helpful information and deviating entirely from the user's intent.\"},\n", + " 'model_name': 'meta-llama/Meta-Llama-3.1-70B-Instruct'}]" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "evaluate_responses = UltraFeedback(\n", + " aspect=\"overall-rating\",\n", + " llm=InferenceEndpointsLLM(\n", + " model_id=\"meta-llama/Meta-Llama-3.1-70B-Instruct\",\n", + " tokenizer_id=\"meta-llama/Meta-Llama-3.1-70B-Instruct\",\n", + " generation_kwargs={\"max_new_tokens\": 512, \"temperature\": 0.7},\n", + " ),\n", + " pipeline=Pipeline(name=\"showcase-pipeline\"),\n", + ")\n", + "evaluate_responses.load()\n", + "next(\n", + " evaluate_responses.process(\n", + " [\n", + " {\n", + " \"instruction\": \"What's the capital of Spain?\",\n", + " \"generations\": [\"Madrid\", \"Barcelona\"],\n", + " }\n", + " ]\n", + " )\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Keep only the required columns\n", + "\n", + "We will get rid of the unneeded columns.\n", + "\n", + "- Component: `KeepColumns`\n", + "- Input columns: `system`, `instruction`, `chosen`, `rejected`, `generations`, `ratings`, `rationales`, `distilabel_metadata` and `model_name`\n", + "- Output columns: `instruction`, `chosen`, `rejected`, `generations` and `order`" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[{'instruction': \"What's the capital of Spain?\",\n", + " 'generations': ['Madrid', 'Barcelona'],\n", + " 'order': ['chosen', 'rejected'],\n", + " 'ratings': [5, 1],\n", + " 'rationales': ['', ''],\n", + " 'model_name': 'meta-llama/Meta-Llama-3.1-70B-Instruct'}]" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "keep_columns = KeepColumns(\n", + " columns=[\n", + " \"instruction\",\n", + " \"generations\",\n", + " \"order\",\n", + " \"ratings\",\n", + " \"rationales\",\n", + " \"model_name\",\n", + " ],\n", + " pipeline=Pipeline(name=\"showcase-pipeline\"),\n", + ")\n", + "keep_columns.load()\n", + "next(\n", + " keep_columns.process(\n", + " [\n", + " {\n", + " \"system\": \"\",\n", + " \"instruction\": \"What's the capital of Spain?\",\n", + " \"chosen\": \"Madrid\",\n", + " \"rejected\": \"Barcelona\",\n", + " \"generations\": [\"Madrid\", \"Barcelona\"],\n", + " \"order\": [\"chosen\", \"rejected\"],\n", + " \"ratings\": [5, 1],\n", + " \"rationales\": [\"\", \"\"],\n", + " \"model_name\": \"meta-llama/Meta-Llama-3.1-70B-Instruct\",\n", + " }\n", + " ]\n", + " )\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### (Optional) Further data curation\n", + "\n", + "You can use Argilla to further curate your data.\n", + "\n", + "- Component: `PreferenceToArgilla` step\n", + "- Input columns: `instruction`, `generations`, `generation_models`, `ratings`\n", + "- Output columns: `instruction`, `generations`, `generation_models`, `ratings`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "to_argilla = PreferenceToArgilla(\n", + " dataset_name=\"cleaned-dataset\",\n", + " dataset_workspace=\"argilla\",\n", + " api_url=\"https://[your-owner-name]-[your-space-name].hf.space\",\n", + " api_key=\"[your-api-key]\",\n", + " num_generations=2\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Run the pipeline" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Below, you can see the full pipeline definition:" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [], + "source": [ + "with Pipeline(name=\"clean-dataset\") as pipeline:\n", + "\n", + " load_dataset = LoadDataFromDicts(\n", + " data=dataset, output_mappings={\"question\": \"instruction\"}\n", + " )\n", + "\n", + " evaluate_responses = UltraFeedback(\n", + " aspect=\"overall-rating\",\n", + " llm=InferenceEndpointsLLM(\n", + " model_id=\"meta-llama/Meta-Llama-3.1-70B-Instruct\",\n", + " tokenizer_id=\"meta-llama/Meta-Llama-3.1-70B-Instruct\",\n", + " generation_kwargs={\"max_new_tokens\": 512, \"temperature\": 0.7},\n", + " ),\n", + " )\n", + "\n", + " keep_columns = KeepColumns(\n", + " columns=[\n", + " \"instruction\",\n", + " \"generations\",\n", + " \"order\",\n", + " \"ratings\",\n", + " \"rationales\",\n", + " \"model_name\",\n", + " ]\n", + " )\n", + "\n", + " to_argilla = PreferenceToArgilla(\n", + " dataset_name=\"cleaned-dataset\",\n", + " dataset_workspace=\"argilla\",\n", + " api_url=\"https://[your-owner-name]-[your-space-name].hf.space\",\n", + " api_key=\"[your-api-key]\",\n", + " num_generations=2,\n", + " )\n", + "\n", + " load_dataset >> evaluate_responses >> keep_columns >> to_argilla" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's now run the pipeline and clean our preference dataset." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "distiset = pipeline.run()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's check it! If you have loaded the data to Argilla, you can [start annotating in the Argilla UI](https://docs.argilla.io/latest/how_to_guides/annotate/)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You can push the dataset to the Hub for sharing with the community and [embed it to explore the data](https://huggingface.co/docs/hub/datasets-viewer-embed)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "distiset.push_to_hub(\"[your-owner-name]/example-cleaned-preference-dataset\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Conclusions" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In this tutorial, we showcased the detailed steps to build a pipeline for cleaning a preference dataset using distilabel. However, you can customize this pipeline for your own use cases, such as cleaning an SFT dataset or adding custom steps.\n", + "\n", + "We used a preference dataset as our starting point and shuffled the data to avoid any bias. Next, we evaluated the responses using a model through the serverless Hugging Face Inference API, following the UltraFeedback standards. Finally, we kept the needed columns and used Argilla for further curation." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "distilabel-tutorials", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.4" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/sections/pipeline_samples/tutorials/generate_preference_dataset.ipynb b/docs/sections/pipeline_samples/tutorials/generate_preference_dataset.ipynb new file mode 100644 index 0000000000..72e4b9fce9 --- /dev/null +++ b/docs/sections/pipeline_samples/tutorials/generate_preference_dataset.ipynb @@ -0,0 +1,584 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Generate a preference dataset" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- **Goal**: Generate a synthetic preference dataset for DPO/ORPO.\n", + "- **Libraries**: [argilla](https://github.com/argilla-io/argilla), [hf-inference-endpoints](https://github.com/huggingface/huggingface_hub)\n", + "- **Components**: [LoadDataFromHub](https://distilabel.argilla.io/latest/components-gallery/steps/loaddatafromhub/), [TextGeneration](https://distilabel.argilla.io/latest/components-gallery/tasks/textgeneration/), [UltraFeedback](https://distilabel.argilla.io/latest/components-gallery/tasks/ultrafeedback/), [GroupColumns](https://distilabel.argilla.io/latest/components-gallery/steps/groupcolumns/), [FormatTextGenerationDPO](https://distilabel.argilla.io/latest/components-gallery/steps/formattextgenerationdpo/), [PreferenceToArgilla](https://distilabel.argilla.io/latest/components-gallery/steps/textgenerationtoargilla/), [InferenceEndpointsLLM](https://distilabel.argilla.io/latest/components-gallery/llms/inferenceendpointsllm/)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Getting started" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Install the dependencies\n", + "\n", + "To complete this tutorial, you need to install the distilabel SDK and a few third-party libraries via pip. We will be using **the free but rate-limited Hugging Face serverless Inference API** for this tutorial, so we need to install this as an extra distilabel dependency. You can install them by running the following command:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!pip install \"distilabel[hf-inference-endpoints]\"" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": {}, + "outputs": [], + "source": [ + "!pip install \"transformers>=4.0,<5.0\" \"torch>=2.0,<3.0\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's make the required imports:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from distilabel.llms import InferenceEndpointsLLM\n", + "from distilabel.pipeline import Pipeline\n", + "from distilabel.steps import (\n", + " LoadDataFromHub,\n", + " GroupColumns,\n", + " FormatTextGenerationDPO,\n", + " PreferenceToArgilla,\n", + ")\n", + "from distilabel.steps.tasks import TextGeneration, UltraFeedback" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You'll need an `HF_TOKEN` to use the HF Inference Endpoints. Log in to use it directly within this notebook." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "from huggingface_hub import login\n", + "\n", + "login(token=os.getenv(\"HF_TOKEN\"), add_to_git_credential=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "### (optional) Deploy Argilla\n", + "\n", + "You can skip this step or replace it with any other data evaluation tool, but the quality of your model will suffer from a lack of data quality, so we do recommend looking at your data. If you already have deployed Argilla, you can skip this step. Otherwise, you can quickly deploy Argilla following [this guide](https://docs.argilla.io/latest/getting_started/quickstart/). \n", + "\n", + "Along with that, you will need to install argilla as distilabel extra." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!pip install \"distilabel[argilla, hf-inference-endpoints]\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Define the pipeline" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To generate our preference dataset, we will need to define a `Pipeline` with all the necessary steps. Below, we will go over each step in detail." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Load the dataset\n", + "\n", + "We will use as source data the `argilla/10Kprompts-mini` dataset from the [Hugging Face Hub](https://huggingface.co/datasets/argilla/10Kprompts-mini).\n", + "\n", + "- Component: `LoadDataFromHub`\n", + "- Input columns: `instruction` and `topic`, the same as in the loaded dataset\n", + "- Output columns: `instruction` and `topic`" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "([{'instruction': 'How can I create an efficient and robust workflow that utilizes advanced automation techniques to extract targeted data, including customer information, from diverse PDF documents and effortlessly integrate it into a designated Google Sheet? Furthermore, I am interested in establishing a comprehensive and seamless system that promptly activates an SMS notification on my mobile device whenever a new PDF document is uploaded to the Google Sheet, ensuring real-time updates and enhanced accessibility.',\n", + " 'topic': 'Software Development'}],\n", + " True)" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "load_dataset = LoadDataFromHub(\n", + " repo_id= \"argilla/10Kprompts-mini\",\n", + " num_examples=1,\n", + " pipeline=Pipeline(name=\"showcase-pipeline\"),\n", + " )\n", + "load_dataset.load()\n", + "next(load_dataset.process())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Generate responses\n", + "\n", + "We need to generate the responses for the given instructions. We will use two different models available in the Hugging Face Hub through the Serverless Inference API: [`meta-llama/Meta-Llama-3-8B-Instruct`](https://huggingface.co/meta-llama/Meta-Llama-3-8B-Instruct) and [`mistralai/Mixtral-8x7B-Instruct-v0.1`](https://huggingface.co/mistralai/Mixtral-8x7B-Instruct-v0.1). We will also indicate the generation parameters for each model.\n", + "\n", + "- Component: `TextGeneration` task with LLMs using `InferenceEndpointsLLM`\n", + "- Input columns: `instruction`\n", + "- Output columns: `generation`, `distilabel_metadata`, `model_name` for each model\n", + "\n", + "For your use case and to improve the results, you can use any [other LLM of your choice](https://distilabel.argilla.io/latest/components-gallery/llms/)." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[{'instruction': 'Which are the top cities in Spain?', 'generation': 'Spain is a country with a rich culture, history, and architecture, and it has many great cities to visit. Here are some of the top cities in Spain:\\n\\n1. **Madrid**: The capital city of Spain, known for its vibrant nightlife, museums, and historic landmarks like the Royal Palace and Prado Museum.\\n2. **Barcelona**: The second-largest city in Spain, famous for its modernist architecture, beaches, and iconic landmarks like La Sagrada Família and Park Güell, designed by Antoni Gaudí.\\n3. **Valencia**: Located on the Mediterranean coast, Valencia is known for its beautiful beaches, City of Arts and Sciences, and delicious local cuisine, such as paella.\\n4. **Seville**: The capital of Andalusia, Seville is famous for its stunning cathedral, Royal Alcázar Palace, and lively flamenco music scene.\\n5. **Málaga**: A coastal city in southern Spain, Málaga is known for its rich history, beautiful beaches, and being the birthplace of Pablo Picasso.\\n6. **Zaragoza**: Located in the northeastern region of Aragon, Zaragoza is a city with a rich history, known for its Roman ruins, Gothic cathedral, and beautiful parks.\\n7. **Granada**: A city in the Andalusian region, Granada is famous for its stunning Alhambra palace and generalife gardens, a UNESCO World Heritage Site.\\n8. **Bilbao**: A city in the Basque Country, Bilbao is known for its modern architecture, including the Guggenheim Museum, and its rich cultural heritage.\\n9. **Alicante**: A coastal city in the Valencia region, Alicante is famous for its beautiful beaches, historic castle, and lively nightlife.\\n10. **San Sebastián**: A city in the Basque Country, San Sebastián is known for its stunning beaches, gastronomic scene, and cultural events like the San Sebastián International Film Festival.\\n\\nThese are just a few of the many great cities in Spain, each with its own unique character and attractions.', 'distilabel_metadata': {'raw_output_text_generation_0': 'Spain is a country with a rich culture, history, and architecture, and it has many great cities to visit. Here are some of the top cities in Spain:\\n\\n1. **Madrid**: The capital city of Spain, known for its vibrant nightlife, museums, and historic landmarks like the Royal Palace and Prado Museum.\\n2. **Barcelona**: The second-largest city in Spain, famous for its modernist architecture, beaches, and iconic landmarks like La Sagrada Família and Park Güell, designed by Antoni Gaudí.\\n3. **Valencia**: Located on the Mediterranean coast, Valencia is known for its beautiful beaches, City of Arts and Sciences, and delicious local cuisine, such as paella.\\n4. **Seville**: The capital of Andalusia, Seville is famous for its stunning cathedral, Royal Alcázar Palace, and lively flamenco music scene.\\n5. **Málaga**: A coastal city in southern Spain, Málaga is known for its rich history, beautiful beaches, and being the birthplace of Pablo Picasso.\\n6. **Zaragoza**: Located in the northeastern region of Aragon, Zaragoza is a city with a rich history, known for its Roman ruins, Gothic cathedral, and beautiful parks.\\n7. **Granada**: A city in the Andalusian region, Granada is famous for its stunning Alhambra palace and generalife gardens, a UNESCO World Heritage Site.\\n8. **Bilbao**: A city in the Basque Country, Bilbao is known for its modern architecture, including the Guggenheim Museum, and its rich cultural heritage.\\n9. **Alicante**: A coastal city in the Valencia region, Alicante is famous for its beautiful beaches, historic castle, and lively nightlife.\\n10. **San Sebastián**: A city in the Basque Country, San Sebastián is known for its stunning beaches, gastronomic scene, and cultural events like the San Sebastián International Film Festival.\\n\\nThese are just a few of the many great cities in Spain, each with its own unique character and attractions.'}, 'model_name': 'meta-llama/Meta-Llama-3-8B-Instruct'}]\n", + "[{'instruction': 'Which are the top cities in Spain?', 'generation': ' Here are some of the top cities in Spain based on various factors such as tourism, culture, history, and quality of life:\\n\\n1. Madrid: The capital and largest city in Spain, Madrid is known for its vibrant nightlife, world-class museums (such as the Prado Museum and Reina Sofia Museum), stunning parks (such as the Retiro Park), and delicious food.\\n\\n2. Barcelona: Famous for its unique architecture, Barcelona is home to several UNESCO World Heritage sites designed by Antoni Gaudí, including the Sagrada Familia and Park Güell. The city also boasts beautiful beaches, a lively arts scene, and delicious Catalan cuisine.\\n\\n3. Valencia: A coastal city located in the east of Spain, Valencia is known for its City of Arts and Sciences, a modern architectural complex that includes a planetarium, opera house, and museum of interactive science. The city is also famous for its paella, a traditional Spanish dish made with rice, vegetables, and seafood.\\n\\n4. Seville: The capital of Andalusia, Seville is famous for its flamenco dancing, stunning cathedral (the largest Gothic cathedral in the world), and the Alcázar, a beautiful palace made up of a series of rooms and courtyards.\\n\\n5. Granada: Located in the foothills of the Sierra Nevada mountains, Granada is known for its stunning Alhambra palace, a Moorish fortress that dates back to the 9th century. The city is also famous for its tapas, a traditional Spanish dish that is often served for free with drinks.\\n\\n6. Bilbao: A city in the Basque Country, Bilbao is famous for its modern architecture, including the Guggenheim Museum, a contemporary art museum designed by Frank Gehry. The city is also known for its pintxos, a type of Basque tapas that are served in bars and restaurants.\\n\\n7. Málaga: A coastal city in Andalusia, Málaga is known for its beautiful beaches, historic sites (including the Alcazaba and Gibralfaro castles), and the Picasso Museum, which is dedicated to the famous Spanish artist who was born in the city.\\n\\nThese are just a few of the many wonderful cities in Spain.', 'distilabel_metadata': {'raw_output_text_generation_0': ' Here are some of the top cities in Spain based on various factors such as tourism, culture, history, and quality of life:\\n\\n1. Madrid: The capital and largest city in Spain, Madrid is known for its vibrant nightlife, world-class museums (such as the Prado Museum and Reina Sofia Museum), stunning parks (such as the Retiro Park), and delicious food.\\n\\n2. Barcelona: Famous for its unique architecture, Barcelona is home to several UNESCO World Heritage sites designed by Antoni Gaudí, including the Sagrada Familia and Park Güell. The city also boasts beautiful beaches, a lively arts scene, and delicious Catalan cuisine.\\n\\n3. Valencia: A coastal city located in the east of Spain, Valencia is known for its City of Arts and Sciences, a modern architectural complex that includes a planetarium, opera house, and museum of interactive science. The city is also famous for its paella, a traditional Spanish dish made with rice, vegetables, and seafood.\\n\\n4. Seville: The capital of Andalusia, Seville is famous for its flamenco dancing, stunning cathedral (the largest Gothic cathedral in the world), and the Alcázar, a beautiful palace made up of a series of rooms and courtyards.\\n\\n5. Granada: Located in the foothills of the Sierra Nevada mountains, Granada is known for its stunning Alhambra palace, a Moorish fortress that dates back to the 9th century. The city is also famous for its tapas, a traditional Spanish dish that is often served for free with drinks.\\n\\n6. Bilbao: A city in the Basque Country, Bilbao is famous for its modern architecture, including the Guggenheim Museum, a contemporary art museum designed by Frank Gehry. The city is also known for its pintxos, a type of Basque tapas that are served in bars and restaurants.\\n\\n7. Málaga: A coastal city in Andalusia, Málaga is known for its beautiful beaches, historic sites (including the Alcazaba and Gibralfaro castles), and the Picasso Museum, which is dedicated to the famous Spanish artist who was born in the city.\\n\\nThese are just a few of the many wonderful cities in Spain.'}, 'model_name': 'mistralai/Mixtral-8x7B-Instruct-v0.1'}]\n" + ] + } + ], + "source": [ + "generate_responses = [\n", + " TextGeneration(\n", + " llm=InferenceEndpointsLLM(\n", + " model_id=\"meta-llama/Meta-Llama-3-8B-Instruct\",\n", + " tokenizer_id=\"meta-llama/Meta-Llama-3-8B-Instruct\",\n", + " generation_kwargs={\"max_new_tokens\": 512, \"temperature\": 0.7},\n", + " ),\n", + " pipeline=Pipeline(name=\"showcase-pipeline\"),\n", + " ),\n", + " TextGeneration(\n", + " llm=InferenceEndpointsLLM(\n", + " model_id=\"mistralai/Mixtral-8x7B-Instruct-v0.1\",\n", + " tokenizer_id=\"mistralai/Mixtral-8x7B-Instruct-v0.1\",\n", + " generation_kwargs={\"max_new_tokens\": 512, \"temperature\": 0.7},\n", + " ),\n", + " pipeline=Pipeline(name=\"showcase-pipeline\"),\n", + " ),\n", + "]\n", + "for task in generate_responses:\n", + " task.load()\n", + " print(next(task.process([{\"instruction\": \"Which are the top cities in Spain?\"}])))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Group the responses\n", + "\n", + "The task to evaluate the responses needs as input a list of generations. However, each model response was saved in the generation column of the subsets `text_generation_0` and `text_generation_1`. We will combine these two columns into a single column and the `default` subset.\n", + "\n", + "- Component: `GroupColumns`\n", + "- Input columns: `generation` and `model_name`from `text_generation_0` and `text_generation_1`\n", + "- Output columns: `generations` and `model_names`" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[{'generations': ['Madrid', 'Barcelona'],\n", + " 'model_names': ['meta-llama/Meta-Llama-3-8B-Instruct',\n", + " 'mistralai/Mixtral-8x7B-Instruct-v0.1']}]" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "group_responses = GroupColumns(\n", + " columns=[\"generation\", \"model_name\"],\n", + " output_columns=[\"generations\", \"model_names\"],\n", + " pipeline=Pipeline(name=\"showcase-pipeline\"),\n", + ")\n", + "next(\n", + " group_responses.process(\n", + " [\n", + " {\n", + " \"generation\": \"Madrid\",\n", + " \"model_name\": \"meta-llama/Meta-Llama-3-8B-Instruct\",\n", + " },\n", + " ],\n", + " [\n", + " {\n", + " \"generation\": \"Barcelona\",\n", + " \"model_name\": \"mistralai/Mixtral-8x7B-Instruct-v0.1\",\n", + " }\n", + " ],\n", + " )\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Evaluate the responses\n", + "\n", + "To build our preference dataset, we need to evaluate the responses generated by the models. We will use [`meta-llama/Meta-Llama-3-70B-Instruct`](https://huggingface.co/meta-llama/Meta-Llama-3-70B-Instruct) for this, applying the `UltraFeedback` task that judges the responses according to different dimensions (helpfulness, honesty, instruction-following, truthfulness).\n", + "\n", + "- Component: `UltraFeedback` task with LLMs using `InferenceEndpointsLLM`\n", + "- Input columns: `instruction`, `generations`\n", + "- Output columns: `ratings`, `rationales`, `distilabel_metadata`, `model_name`\n", + "\n", + "For your use case and to improve the results, you can use any [other LLM of your choice](https://distilabel.argilla.io/latest/components-gallery/llms/)." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[{'instruction': \"What's the capital of Spain?\",\n", + " 'generations': ['Madrid', 'Barcelona'],\n", + " 'ratings': [5, 1],\n", + " 'rationales': [\"The answer is correct, directly addressing the question, and is free of hallucinations or unnecessary details. It confidently provides the accurate information, aligning perfectly with the user's intent.\",\n", + " \"The answer is incorrect as Barcelona is not the capital of Spain. This introduces a significant inaccuracy, failing to provide helpful information and deviating entirely from the user's intent.\"],\n", + " 'distilabel_metadata': {'raw_output_ultra_feedback_0': \"#### Output for Text 1\\nRating: 5 (Excellent)\\nRationale: The answer is correct, directly addressing the question, and is free of hallucinations or unnecessary details. It confidently provides the accurate information, aligning perfectly with the user's intent.\\n\\n#### Output for Text 2\\nRating: 1 (Low Quality)\\nRationale: The answer is incorrect as Barcelona is not the capital of Spain. This introduces a significant inaccuracy, failing to provide helpful information and deviating entirely from the user's intent.\"},\n", + " 'model_name': 'meta-llama/Meta-Llama-3-70B-Instruct'}]" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "evaluate_responses = UltraFeedback(\n", + " aspect=\"overall-rating\",\n", + " llm=InferenceEndpointsLLM(\n", + " model_id=\"meta-llama/Meta-Llama-3-70B-Instruct\",\n", + " tokenizer_id=\"meta-llama/Meta-Llama-3-70B-Instruct\",\n", + " generation_kwargs={\"max_new_tokens\": 512, \"temperature\": 0.7},\n", + " ),\n", + " pipeline=Pipeline(name=\"showcase-pipeline\"),\n", + ")\n", + "evaluate_responses.load()\n", + "next(\n", + " evaluate_responses.process(\n", + " [\n", + " {\n", + " \"instruction\": \"What's the capital of Spain?\",\n", + " \"generations\": [\"Madrid\", \"Barcelona\"],\n", + " }\n", + " ]\n", + " )\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Convert to a preference dataset\n", + "\n", + "- You can automatically convert it to a preference dataset with the `chosen` and `rejected` columns.\n", + " - Component: `FormatTextGenerationDPO` step\n", + " - Input columns: `instruction`, `generations`, `generation_models`, `ratings`\n", + " - Output columns: `prompt`, `prompt_id`, `chosen`, `chosen_model`, `chosen_rating`, `rejected`, `rejected_model`, `rejected_rating`" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[{'instruction': \"What's the capital of Spain?\",\n", + " 'generations': ['Madrid', 'Barcelona'],\n", + " 'generation_models': ['Meta-Llama-3-8B-Instruct',\n", + " 'Mixtral-8x7B-Instruct-v0.1'],\n", + " 'ratings': [5, 1],\n", + " 'prompt': \"What's the capital of Spain?\",\n", + " 'prompt_id': '26174c953df26b3049484e4721102dca6b25d2de9e3aa22aa84f25ed1c798512',\n", + " 'chosen': [{'role': 'user', 'content': \"What's the capital of Spain?\"},\n", + " {'role': 'assistant', 'content': 'Madrid'}],\n", + " 'chosen_model': 'Meta-Llama-3-8B-Instruct',\n", + " 'chosen_rating': 5,\n", + " 'rejected': [{'role': 'user', 'content': \"What's the capital of Spain?\"},\n", + " {'role': 'assistant', 'content': 'Barcelona'}],\n", + " 'rejected_model': 'Mixtral-8x7B-Instruct-v0.1',\n", + " 'rejected_rating': 1}]" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "format_dpo = FormatTextGenerationDPO(pipeline=Pipeline(name=\"showcase-pipeline\"))\n", + "format_dpo.load()\n", + "next(\n", + " format_dpo.process(\n", + " [\n", + " {\n", + " \"instruction\": \"What's the capital of Spain?\",\n", + " \"generations\": [\"Madrid\", \"Barcelona\"],\n", + " \"generation_models\": [\n", + " \"Meta-Llama-3-8B-Instruct\",\n", + " \"Mixtral-8x7B-Instruct-v0.1\",\n", + " ],\n", + " \"ratings\": [5, 1],\n", + " }\n", + " ]\n", + " )\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- Or you can use Argilla to manually label the data and convert it to a preference dataset.\n", + " - Component: `PreferenceToArgilla` step\n", + " - Input columns: `instruction`, `generations`, `generation_models`, `ratings`\n", + " - Output columns: `instruction`, `generations`, `generation_models`, `ratings`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "to_argilla = PreferenceToArgilla(\n", + " dataset_name=\"preference-dataset\",\n", + " dataset_workspace=\"argilla\",\n", + " api_url=\"https://[your-owner-name]-[your-space-name].hf.space\",\n", + " api_key=\"[your-api-key]\",\n", + " num_generations=2\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Run the pipeline" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Below, you can see the full pipeline definition:" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": {}, + "outputs": [], + "source": [ + "with Pipeline(name=\"generate-dataset\") as pipeline:\n", + "\n", + " load_dataset = LoadDataFromHub(repo_id=\"argilla/10Kprompts-mini\")\n", + "\n", + " generate_responses = [\n", + " TextGeneration(\n", + " llm=InferenceEndpointsLLM(\n", + " model_id=\"meta-llama/Meta-Llama-3-8B-Instruct\",\n", + " tokenizer_id=\"meta-llama/Meta-Llama-3-8B-Instruct\",\n", + " generation_kwargs={\"max_new_tokens\": 512, \"temperature\": 0.7},\n", + " )\n", + " ),\n", + " TextGeneration(\n", + " llm=InferenceEndpointsLLM(\n", + " model_id=\"mistralai/Mixtral-8x7B-Instruct-v0.1\",\n", + " tokenizer_id=\"mistralai/Mixtral-8x7B-Instruct-v0.1\",\n", + " generation_kwargs={\"max_new_tokens\": 512, \"temperature\": 0.7},\n", + " )\n", + " ),\n", + " ]\n", + "\n", + " group_responses = GroupColumns(\n", + " columns=[\"generation\", \"model_name\"],\n", + " output_columns=[\"generations\", \"model_names\"],\n", + " )\n", + "\n", + " evaluate_responses = UltraFeedback(\n", + " aspect=\"overall-rating\",\n", + " llm=InferenceEndpointsLLM(\n", + " model_id=\"meta-llama/Meta-Llama-3-70B-Instruct\",\n", + " tokenizer_id=\"meta-llama/Meta-Llama-3-70B-Instruct\",\n", + " generation_kwargs={\"max_new_tokens\": 512, \"temperature\": 0.7},\n", + " )\n", + " )\n", + "\n", + " format_dpo = FormatTextGenerationDPO()\n", + " \n", + " to_argilla = PreferenceToArgilla(\n", + " dataset_name=\"preference-dataset\",\n", + " dataset_workspace=\"argilla\",\n", + " api_url=\"https://[your-owner-name]-[your-space-name].hf.space\",\n", + " api_key=\"[your-api-key]\",\n", + " num_generations=2\n", + " )\n", + "\n", + " load_dataset >> generate_responses >> group_responses >> evaluate_responses >> [format_dpo, to_argilla]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's now run the pipeline and generate the preference dataset." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "distiset = pipeline.run()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's check the preference dataset! If you have loaded the data to Argilla, you can [start annotating in the Argilla UI](https://docs.argilla.io/latest/how_to_guides/annotate/)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You can push the dataset to the Hub for sharing with the community and [embed it to explore the data](https://huggingface.co/docs/hub/datasets-viewer-embed)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "distiset.push_to_hub(\"[your-owner-name]/example-preference-dataset\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Conclusions" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In this tutorial, we showcased the detailed steps to build a pipeline for generating a preference dataset using distilabel. You can customize this pipeline for your own use cases and share your datasets with the community through the Hugging Face Hub, or use them to train a model for DPO or ORPO.\n", + "\n", + "We used a dataset containing prompts to generate responses using two different models through the serverless Hugging Face Inference API. Next, we evaluated the responses using a third model, following the UltraFeedback standards. Finally, we converted the data to a preference dataset and used Argilla for further curation." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "distilabel-tutorials", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.4" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/mkdocs.yml b/mkdocs.yml index f10aeb642f..c703f6f111 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -185,8 +185,10 @@ nav: - Serving an LLM for sharing it between several tasks: "sections/how_to_guides/advanced/serving_an_llm_for_reuse.md" - Scaling and distributing a pipeline with Ray: "sections/how_to_guides/advanced/scaling_with_ray.md" - Pipeline Samples: - - "sections/pipeline_samples/examples/index.md" + - "sections/pipeline_samples/index.md" - Tutorials: + - Generate a preference dataset: "sections/pipeline_samples/tutorials/generate_preference_dataset.ipynb" + - Clean an existing dataset: "sections/pipeline_samples/tutorials/clean_existing_dataset.ipynb" - Synthetic data generation for fine-tuning custom retrieval and reranking models: "sections/pipeline_samples/tutorials/GenerateSentencePair.ipynb" - Papers: - DeepSeek Prover: "sections/pipeline_samples/papers/deepseek_prover.md" From 6576d1ad0cab1298de738ebcacc590070fd10470 Mon Sep 17 00:00:00 2001 From: Agus Date: Thu, 22 Aug 2024 14:28:36 +0200 Subject: [PATCH 28/82] Fix `StructuredGeneration` examples and internal check (#912) * Fix error with instructor schema input * Fix examples of structured generation * Try inferring the type of format in case the user forgets informing about it --- src/distilabel/llms/base.py | 5 ++++- src/distilabel/steps/tasks/structured_generation.py | 10 +++++----- .../steps/tasks/structured_outputs/outlines.py | 8 ++++++++ 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/distilabel/llms/base.py b/src/distilabel/llms/base.py index 68d82001d2..2e1978c840 100644 --- a/src/distilabel/llms/base.py +++ b/src/distilabel/llms/base.py @@ -428,7 +428,10 @@ def _prepare_kwargs( # We can deal with json schema or BaseModel, but we need to convert it to a BaseModel # for the Instructor client. schema = structured_output.get("schema", {}) - if not issubclass(schema, BaseModel): + + # If there's already a pydantic model, we don't need to do anything, + # otherwise, try to obtain one. + if not (inspect.isclass(schema) and issubclass(schema, BaseModel)): from distilabel.steps.tasks.structured_outputs.utils import ( json_schema_to_model, ) diff --git a/src/distilabel/steps/tasks/structured_generation.py b/src/distilabel/steps/tasks/structured_generation.py index 240cd44698..174cf20cb5 100644 --- a/src/distilabel/steps/tasks/structured_generation.py +++ b/src/distilabel/steps/tasks/structured_generation.py @@ -69,8 +69,8 @@ class StructuredGeneration(Task): { "instruction": "Create an RPG character", "structured_output": { - "type": "json", - "value": { + "format": "json", + "schema": { "properties": { "name": { "title": "Name", @@ -105,7 +105,7 @@ class StructuredGeneration(Task): ) ``` - Generate structured output from a regex pattern: + Generate structured output from a regex pattern (only works with LLMs that support regex, the providers using outlines): ```python from distilabel.steps.tasks import StructuredGeneration @@ -126,8 +126,8 @@ class StructuredGeneration(Task): { "instruction": "What's the weather like today in Seattle in Celsius degrees?", "structured_output": { - "type": "regex", - "value": r"(\\d{1,2})°C" + "format": "regex", + "schema": r"(\\d{1,2})°C" }, } diff --git a/src/distilabel/steps/tasks/structured_outputs/outlines.py b/src/distilabel/steps/tasks/structured_outputs/outlines.py index d726b5e4f5..6d34f9a92a 100644 --- a/src/distilabel/steps/tasks/structured_outputs/outlines.py +++ b/src/distilabel/steps/tasks/structured_outputs/outlines.py @@ -14,6 +14,7 @@ import importlib import importlib.util +import inspect import json from typing import ( Any, @@ -102,6 +103,13 @@ def prepare_guided_output( format = structured_output.get("format") schema = structured_output.get("schema") + # If schema not informed (may be forgotten), try infering it + if not format: + if isinstance(schema, dict) or inspect.isclass(schema): + format = "json" + elif isinstance(schema, str): + format = "regex" + if format == "json": return { "processor": json_processor( From fc5d0704f652d2064d61fd25922c2a9933ba9a8d Mon Sep 17 00:00:00 2001 From: Agus Date: Thu, 22 Aug 2024 14:35:02 +0200 Subject: [PATCH 29/82] Generate deterministic pipeline name when it's not given (#878) * Generate deterministic pipeline name when it's not given * Use the names of the steps to generate the default pipeline name * Update test with the steps names * Add suggestion from code review --- src/distilabel/pipeline/base.py | 17 ++++++++-- tests/unit/pipeline/test_base.py | 58 +++++++++++++++++--------------- 2 files changed, 46 insertions(+), 29 deletions(-) diff --git a/src/distilabel/pipeline/base.py b/src/distilabel/pipeline/base.py index 911273c2c9..324b3f23c9 100644 --- a/src/distilabel/pipeline/base.py +++ b/src/distilabel/pipeline/base.py @@ -18,7 +18,6 @@ import signal import threading import time -import uuid from abc import ABC, abstractmethod from pathlib import Path from typing import ( @@ -131,6 +130,7 @@ def get_pipeline(cls) -> Union["BasePipeline", None]: _STEP_NOT_LOADED_CODE = -999 _ATTRIBUTES_IGNORED_CACHE = ("disable_cuda_device_placement",) +_PIPELINE_DEFAULT_NAME = "__default_pipeline_name__" class BasePipeline(ABC, RequirementsMixin, _Serializable): @@ -189,7 +189,7 @@ def __init__( Defaults to `None`, but can be helpful to inform in a pipeline to be shared that this requirements must be installed. """ - self.name = name or f"pipeline_{str(uuid.uuid4())[:8]}" + self.name = name or _PIPELINE_DEFAULT_NAME self.description = description self._enable_metadata = enable_metadata self.dag = DAG() @@ -235,6 +235,12 @@ def __enter__(self) -> Self: def __exit__(self, exc_type, exc_value, traceback) -> None: """Unset the global pipeline instance when exiting a pipeline context.""" _GlobalPipelineManager.set_pipeline(None) + self._set_pipeline_name() + + def _set_pipeline_name(self) -> None: + """Creates a name for the pipeline if it's the default one (if hasn't been set).""" + if self.name == _PIPELINE_DEFAULT_NAME: + self.name = f"pipeline_{'_'.join(self.dag)}" def _create_signature(self) -> str: """Makes a signature (hash) of a pipeline, using the step ids and the adjacency between them. @@ -351,6 +357,13 @@ def run( log_queue=self._log_queue, filename=str(self._cache_location["log_file"]) ) + # Set the name of the pipeline if it's the default one. This should be called + # if the pipeline is defined within the context manager, and the run is called + # outside of it. Is here in the following case: + # with Pipeline() as pipeline: + # pipeline.run() + self._set_pipeline_name() + # Validate the pipeline DAG to check that all the steps are chainable, there are # no missing runtime parameters, batch sizes are correct, etc. self.dag.validate() diff --git a/tests/unit/pipeline/test_base.py b/tests/unit/pipeline/test_base.py index a2a043f737..77bfee600b 100644 --- a/tests/unit/pipeline/test_base.py +++ b/tests/unit/pipeline/test_base.py @@ -105,28 +105,29 @@ def test_context_manager(self) -> None: @pytest.mark.parametrize("use_cache", [False, True]) def test_load_batch_manager(self, use_cache: bool) -> None: - pipeline = DummyPipeline(name="unit-test-pipeline") - pipeline._load_batch_manager(use_cache=True) - pipeline._cache() - - with ( - mock.patch( - "distilabel.pipeline.base._BatchManager.load_from_cache" - ) as mock_load_from_cache, - mock.patch( - "distilabel.pipeline.base._BatchManager.from_dag" - ) as mock_from_dag, - ): - pipeline._load_batch_manager(use_cache=use_cache) - - if use_cache: - mock_load_from_cache.assert_called_once_with( - pipeline._cache_location["batch_manager"] - ) - mock_from_dag.assert_not_called() - else: - mock_load_from_cache.assert_not_called() - mock_from_dag.assert_called_once_with(pipeline.dag) + with tempfile.TemporaryDirectory() as temp_dir: + pipeline = DummyPipeline(name="unit-test-pipeline", cache_dir=temp_dir) + pipeline._load_batch_manager(use_cache=True) + pipeline._cache() + + with ( + mock.patch( + "distilabel.pipeline.base._BatchManager.load_from_cache" + ) as mock_load_from_cache, + mock.patch( + "distilabel.pipeline.base._BatchManager.from_dag" + ) as mock_from_dag, + ): + pipeline._load_batch_manager(use_cache=use_cache) + + if use_cache: + mock_load_from_cache.assert_called_once_with( + pipeline._cache_location["batch_manager"] + ) + mock_from_dag.assert_not_called() + else: + mock_load_from_cache.assert_not_called() + mock_from_dag.assert_called_once_with(pipeline.dag) def test_setup_write_buffer(self) -> None: pipeline = DummyPipeline(name="unit-test-pipeline") @@ -1187,13 +1188,16 @@ def test_pipeline_with_dataset_and_generator_step(self): ) def test_optional_name(self): - import random + from distilabel.pipeline.base import _PIPELINE_DEFAULT_NAME + + assert DummyPipeline().name == _PIPELINE_DEFAULT_NAME - random.seed(42) with DummyPipeline() as pipeline: - name = pipeline.name - assert name.startswith("pipeline") - assert len(name.split("_")[-1]) == 8 + gen_step = DummyGeneratorStep() + step1_0 = DummyStep1() + gen_step >> step1_0 + + assert pipeline.name == "pipeline_dummy_generator_step_0_dummy_step1_0" class TestPipelineSerialization: From 22db32c4a3a8c6069d62fec06fc6c47b0697ab5d Mon Sep 17 00:00:00 2001 From: Agus Date: Thu, 22 Aug 2024 16:38:33 +0200 Subject: [PATCH 30/82] Add custom errors (#911) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Module to store custom errors from distilabel * Add tests for the new error types * Refactor ValueError to DistilabelUserError and provide a page in the docs with further info * Fix typo in docs * Refactor ValueError to DistilabelUserError with reference page * Add new error type for TypeErrors * Add DistilabelTypeError for base _Step * Add documentation section for step wrapper * Update src/distilabel/errors.py Co-authored-by: Gabriel Martín Blázquez * Apply comments from code review --------- Co-authored-by: Gabriel Martín Blázquez --- docs/api/pipeline/step_wrapper.md | 4 ++ .../how_to_guides/basic/step/index.md | 2 +- mkdocs.yml | 1 + src/distilabel/cli/pipeline/utils.py | 16 +++-- src/distilabel/constants.py | 3 + src/distilabel/errors.py | 62 +++++++++++++++++++ src/distilabel/llms/base.py | 6 +- src/distilabel/pipeline/_dag.py | 26 +++++--- src/distilabel/pipeline/base.py | 11 ++-- .../pipeline/routing_batch_function.py | 11 ++-- src/distilabel/pipeline/step_wrapper.py | 3 +- src/distilabel/pipeline/utils.py | 2 +- src/distilabel/steps/argilla/base.py | 11 +++- src/distilabel/steps/argilla/preference.py | 6 +- .../steps/argilla/text_generation.py | 6 +- src/distilabel/steps/base.py | 16 +++-- .../steps/generators/huggingface.py | 8 +-- src/distilabel/steps/generators/utils.py | 6 +- .../steps/tasks/generate_embeddings.py | 6 +- src/distilabel/steps/tasks/magpie/base.py | 6 +- .../steps/tasks/magpie/generator.py | 6 +- src/distilabel/steps/tasks/prometheus_eval.py | 40 ++++++------ .../steps/tasks/structured_generation.py | 6 +- .../tasks/structured_outputs/instructor.py | 7 ++- .../tasks/structured_outputs/outlines.py | 11 +++- src/distilabel/steps/tasks/text_generation.py | 19 +++--- src/distilabel/utils/docstring.py | 4 +- tests/unit/test_errors.py | 27 ++++++++ 28 files changed, 242 insertions(+), 90 deletions(-) create mode 100644 docs/api/pipeline/step_wrapper.md create mode 100644 src/distilabel/errors.py create mode 100644 tests/unit/test_errors.py diff --git a/docs/api/pipeline/step_wrapper.md b/docs/api/pipeline/step_wrapper.md new file mode 100644 index 0000000000..e68b64d1d9 --- /dev/null +++ b/docs/api/pipeline/step_wrapper.md @@ -0,0 +1,4 @@ +# Step Wrapper + +::: distilabel.pipeline.step_wrapper._StepWrapper +::: distilabel.pipeline.step_wrapper._StepWrapperException diff --git a/docs/sections/how_to_guides/basic/step/index.md b/docs/sections/how_to_guides/basic/step/index.md index 82040eefdc..e487cc953b 100644 --- a/docs/sections/how_to_guides/basic/step/index.md +++ b/docs/sections/how_to_guides/basic/step/index.md @@ -50,7 +50,7 @@ next(step.process([{"input_field": "value"}])) ### Runtime parameters -`Step`s can also have `RuntimeParameter`, which are parameters that can only used after the pipeline initialisation when calling the `Pipeline.run`. +`Step`s can also have `RuntimeParameter`, which are parameters that can only be used after the pipeline initialisation when calling the `Pipeline.run`. ```python from distilabel.mixins.runtime_parameters import RuntimeParameter diff --git a/mkdocs.yml b/mkdocs.yml index c703f6f111..41df47a0de 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -238,6 +238,7 @@ nav: - "api/pipeline/index.md" - Routing Batch Function: "api/pipeline/routing_batch_function.md" - Typing: "api/pipeline/typing.md" + - Step Wrapper: "api/pipeline/step_wrapper.md" - Utils: "api/pipeline/utils.md" - Mixins: - RuntimeParametersMixin: "api/mixins/runtime_parameters.md" diff --git a/src/distilabel/cli/pipeline/utils.py b/src/distilabel/cli/pipeline/utils.py index 2f8f9170a4..42204c1605 100644 --- a/src/distilabel/cli/pipeline/utils.py +++ b/src/distilabel/cli/pipeline/utils.py @@ -24,6 +24,7 @@ from pydantic.type_adapter import TypeAdapter from distilabel.constants import ROUTING_BATCH_FUNCTION_ATTR_NAME, STEP_ATTR_NAME +from distilabel.errors import DistilabelUserError from distilabel.pipeline.local import Pipeline if TYPE_CHECKING: @@ -106,8 +107,9 @@ def get_config_from_url(url: str) -> Dict[str, Any]: ValueError: If the file format is not supported. """ if not url.endswith((".json", ".yaml", ".yml")): - raise ValueError( - f"Unsupported file format for '{url}'. Only JSON and YAML are supported" + raise DistilabelUserError( + f"Unsupported file format for '{url}'. Only JSON and YAML are supported", + page="sections/how_to_guides/basic/pipeline/?h=seriali#serializing-the-pipeline", ) response = _download_remote_file(url) @@ -134,8 +136,9 @@ def get_pipeline_from_url(url: str, pipeline_name: str = "pipeline") -> "BasePip ValueError: If the file format is not supported. """ if not url.endswith(".py"): - raise ValueError( - f"Unsupported file format for '{url}'. It must be a python file." + raise DistilabelUserError( + f"Unsupported file format for '{url}'. It must be a python file.", + page="sections/how_to_guides/advanced/cli/#distilabel-pipeline-run", ) response = _download_remote_file(url) @@ -179,8 +182,9 @@ def get_pipeline( elif config_or_script.endswith(".py"): script = config_or_script else: - raise ValueError( - "The file must be a valid config file or python script with a pipeline." + raise DistilabelUserError( + "The file must be a valid config file or python script with a pipeline.", + page="sections/how_to_guides/advanced/cli/#distilabel-pipeline-run", ) if valid_http_url(config_or_script): diff --git a/src/distilabel/constants.py b/src/distilabel/constants.py index bd636f8165..87bf7708a6 100644 --- a/src/distilabel/constants.py +++ b/src/distilabel/constants.py @@ -35,6 +35,9 @@ PIPELINE_CONFIG_FILENAME: Final[str] = "pipeline.yaml" PIPELINE_LOG_FILENAME: Final[str] = "pipeline.log" +# Docs page for the custom errors +DISTILABEL_DOCS_URL: Final[str] = "https://distilabel.argilla.io/latest/" + __all__ = [ "STEP_ATTR_NAME", diff --git a/src/distilabel/errors.py b/src/distilabel/errors.py new file mode 100644 index 0000000000..4a1bad0cf6 --- /dev/null +++ b/src/distilabel/errors.py @@ -0,0 +1,62 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import Optional + +from distilabel.constants import DISTILABEL_DOCS_URL + +# The sitemap can be visited for the full list of pages: +# SITEMAP_URL: Final[str] = "https://distilabel.argilla.io/latest/sitemap.xml" + + +class DistilabelError: + """A mixin class for common functionality shared by all Distilabel-specific errors. + + Attributes: + message: A message describing the error. + page: An optional error code from PydanticErrorCodes enum. + + Examples: + + ```python + raise DistilabelUserError("This is an error message.") + This is an error message. + + raise DistilabelUserError("This is an error message.", page="sections/getting_started/faq/") + This is an error message. + For further information visit 'https://distilabel.argilla.io/latest/sections/getting_started/faq/' + ``` + """ + + def __init__(self, message: str, *, page: Optional[str] = None) -> None: + self.message = message + self.page = page + + def __str__(self) -> str: + if self.page is None: + return self.message + else: + return f"{self.message}\n\nFor further information visit '{DISTILABEL_DOCS_URL}{self.page}'" + + +class DistilabelUserError(DistilabelError, ValueError): + """ValueError that we can redirect to a given page in the documentation.""" + + pass + + +class DistilabelTypeError(DistilabelError, TypeError): + """TypeError that we can redirect to a given page in the documentation.""" + + pass diff --git a/src/distilabel/llms/base.py b/src/distilabel/llms/base.py index 2e1978c840..00538b50b8 100644 --- a/src/distilabel/llms/base.py +++ b/src/distilabel/llms/base.py @@ -23,6 +23,7 @@ from pydantic import BaseModel, ConfigDict, Field, PrivateAttr +from distilabel.errors import DistilabelUserError from distilabel.mixins.runtime_parameters import ( RuntimeParameter, RuntimeParametersMixin, @@ -400,8 +401,9 @@ def _prepare_structured_output( # type: ignore schema = structured_output.get("schema") if not schema: - raise ValueError( - f"The `structured_output` argument must contain a schema: {structured_output}" + raise DistilabelUserError( + f"The `structured_output` argument must contain a schema: {structured_output}", + page="sections/how_to_guides/advanced/structured_generation/#instructor", ) if inspect.isclass(schema) and issubclass(schema, BaseModel): # We want a json schema for the serialization, but instructor wants a pydantic BaseModel. diff --git a/src/distilabel/pipeline/_dag.py b/src/distilabel/pipeline/_dag.py index c6bf9a73a3..437021c68c 100644 --- a/src/distilabel/pipeline/_dag.py +++ b/src/distilabel/pipeline/_dag.py @@ -35,6 +35,7 @@ ROUTING_BATCH_FUNCTION_ATTR_NAME, STEP_ATTR_NAME, ) +from distilabel.errors import DistilabelUserError from distilabel.pipeline.routing_batch_function import RoutingBatchFunction from distilabel.steps.base import GeneratorStep from distilabel.utils.serialization import ( @@ -339,9 +340,10 @@ def validate(self) -> None: # Validate that the steps in the first trophic level are `GeneratorStep`s if trophic_level == 1: if not isinstance(step, GeneratorStep): - raise ValueError( + raise DistilabelUserError( f"Step '{step_name}' cannot be a root step because it is not" - " a `GeneratorStep`. It should have a previous step in the pipeline." + " a `GeneratorStep`. It should have a previous step in the pipeline.", + page="sections/how_to_guides/basic/step/#types-of-steps", ) self._validate_generator_step_process_signature(step) else: @@ -479,9 +481,10 @@ def _validate_routing_batch_function( node = self.get_step(predecessor) routing_batch_function = node.get(ROUTING_BATCH_FUNCTION_ATTR_NAME) if routing_batch_function is not None and len(predecessors) > 1: - raise ValueError( + raise DistilabelUserError( f"Step '{step.name}' cannot have multiple predecessors when the batches" - " of one are being routed with a `routing_batch_function`." + " of one are being routed with a `routing_batch_function`.", + page="sections/how_to_guides/basic/pipeline/?h=routing#routing-batches-to-specific-downstream-steps", ) if routing_batch_function is None: @@ -542,24 +545,27 @@ def _validate_process_step_input_parameter( if step_input_parameter is None: if num_predecessors > 1: prev_steps = ", ".join([f"'{step_name}'" for step_name in predecessors]) - raise ValueError( + raise DistilabelUserError( f"Step '{step_name}' should have a `*args` parameter with type hint" - f" `StepInput` to receive outputs from previous steps: {prev_steps}." + f" `StepInput` to receive outputs from previous steps: {prev_steps}.", + page="sections/how_to_guides/basic/step/#define-steps-for-your-pipeline", ) prev_step_name = next(iter(predecessors)) - raise ValueError( + raise DistilabelUserError( f"Step '{step_name}' should have a parameter with type hint `StepInput`" - f" to receive the output from the previous step: '{prev_step_name}'." + f" to receive the output from the previous step: '{prev_step_name}'.", + page="sections/how_to_guides/basic/step/#define-steps-for-your-pipeline", ) if ( num_predecessors > 1 and step_input_parameter.kind != inspect.Parameter.VAR_POSITIONAL ): - raise ValueError( + raise DistilabelUserError( f"Step '{step_name}' should have a `*args` parameter with type hint `StepInput`" - f" to receive outputs from previous steps." + f" to receive outputs from previous steps.", + page="sections/how_to_guides/basic/step/#define-steps-for-your-pipeline", ) def _validate_step_process_runtime_parameters( # noqa: C901 diff --git a/src/distilabel/pipeline/base.py b/src/distilabel/pipeline/base.py index 324b3f23c9..bc75ed9324 100644 --- a/src/distilabel/pipeline/base.py +++ b/src/distilabel/pipeline/base.py @@ -49,6 +49,7 @@ STEPS_OUTPUTS_PATH, ) from distilabel.distiset import create_distiset +from distilabel.errors import DistilabelUserError from distilabel.mixins.requirements import RequirementsMixin from distilabel.pipeline._dag import DAG from distilabel.pipeline.batch import _Batch @@ -464,10 +465,11 @@ def _add_dataset_generator_step(self, dataset: "InputDataset") -> None: for step_name in self.dag: step = self.dag.get_step(step_name)[STEP_ATTR_NAME] if isinstance(step_name, GeneratorStep): - raise ValueError( + raise DistilabelUserError( "There is already a `GeneratorStep` in the pipeline, you can either" " pass a `dataset` to the run method, or create a `GeneratorStep` explictly." - f" `GeneratorStep`: {step}" + f" `GeneratorStep`: {step}", + page="sections/how_to_guides/basic/step/#types-of-steps", ) loader = make_generator_step(dataset) self.dag.add_root_step(loader) @@ -535,9 +537,10 @@ def _setup_fsspec( return if "path" not in storage_parameters: - raise ValueError( + raise DistilabelUserError( "The 'path' key must be present in the `storage_parameters` dictionary" - " if it's not `None`." + " if it's not `None`.", + page="sections/how_to_guides/advanced/fs_to_pass_data/", ) path = storage_parameters.pop("path") diff --git a/src/distilabel/pipeline/routing_batch_function.py b/src/distilabel/pipeline/routing_batch_function.py index ee2ca0f8c8..e29a520405 100644 --- a/src/distilabel/pipeline/routing_batch_function.py +++ b/src/distilabel/pipeline/routing_batch_function.py @@ -19,6 +19,7 @@ from pydantic import BaseModel, PrivateAttr from typing_extensions import Self +from distilabel.errors import DistilabelUserError from distilabel.utils.serialization import ( TYPE_INFO_KEY, _get_module_attr, @@ -134,19 +135,21 @@ def __rshift__( routing batch function. """ if not isinstance(other, list): - raise ValueError( + raise DistilabelUserError( f"Can only set a `routing_batch_function` for a list of steps. Got: {other}." " Please, review the right-hand side of the `routing_batch_function >> other`" " expression. It should be" - " `upstream_step >> routing_batch_function >> [downstream_step_1, dowstream_step_2, ...]`." + " `upstream_step >> routing_batch_function >> [downstream_step_1, dowstream_step_2, ...]`.", + page="sections/how_to_guides/basic/pipeline/?h=routing#routing-batches-to-specific-downstream-steps", ) if not self._step: - raise ValueError( + raise DistilabelUserError( "Routing batch function doesn't have an upstream step. Cannot connect downstream" " steps before connecting the upstream step. Connect this routing batch" " function to an upstream step using the `>>` operator. For example:" - " `upstream_step >> routing_batch_function >> [downstream_step_1, downstream_step_2, ...]`." + " `upstream_step >> routing_batch_function >> [downstream_step_1, downstream_step_2, ...]`.", + page="sections/how_to_guides/basic/pipeline/?h=routing#routing-batches-to-specific-downstream-steps", ) for step in other: diff --git a/src/distilabel/pipeline/step_wrapper.py b/src/distilabel/pipeline/step_wrapper.py index ad4a668bf2..c153a6008b 100644 --- a/src/distilabel/pipeline/step_wrapper.py +++ b/src/distilabel/pipeline/step_wrapper.py @@ -17,6 +17,7 @@ from typing import Any, Dict, List, Optional, Union, cast from distilabel.constants import LAST_BATCH_SENT_FLAG +from distilabel.errors import DISTILABEL_DOCS_URL from distilabel.llms.mixins.cuda_device_placement import CudaDevicePlacementMixin from distilabel.pipeline.batch import _Batch from distilabel.pipeline.typing import StepLoadStatus @@ -294,7 +295,7 @@ def __init__( code: int, subprocess_exception: Optional[Exception] = None, ) -> None: - self.message = message + self.message = f"{message}\n\nFor further information visit {DISTILABEL_DOCS_URL}api/pipeline/step_wrapper" self.step = step self.code = code self.subprocess_exception = subprocess_exception diff --git a/src/distilabel/pipeline/utils.py b/src/distilabel/pipeline/utils.py index 5758053a51..b5bd9e1b0e 100644 --- a/src/distilabel/pipeline/utils.py +++ b/src/distilabel/pipeline/utils.py @@ -39,7 +39,7 @@ def group_columns( group_columns ): raise ValueError( - "The length of output_group_columns must be the same as the length of group_columns" + "The length of `output_group_columns` must be the same as the length of `group_columns`." ) if output_group_columns is None: output_group_columns = [f"grouped_{key}" for key in group_columns] diff --git a/src/distilabel/steps/argilla/base.py b/src/distilabel/steps/argilla/base.py index 0d60ec89d0..ea491e07a5 100644 --- a/src/distilabel/steps/argilla/base.py +++ b/src/distilabel/steps/argilla/base.py @@ -24,6 +24,7 @@ except ImportError: pass +from distilabel.errors import DistilabelUserError from distilabel.mixins.runtime_parameters import RuntimeParameter from distilabel.steps.base import Step, StepInput @@ -110,7 +111,10 @@ def _client_init(self) -> None: else {}, ) except Exception as e: - raise ValueError(f"Failed to initialize the Argilla API: {e}") from e + raise DistilabelUserError( + f"Failed to initialize the Argilla API: {e}", + page="sections/how_to_guides/advanced/argilla/", + ) from e @property def _dataset_exists_in_workspace(self) -> bool: @@ -141,10 +145,11 @@ def load(self) -> None: super().load() if self.api_url is None or self.api_key is None: - raise ValueError( + raise DistilabelUserError( "`Argilla` step requires the `api_url` and `api_key` to be provided. Please," " provide those at step instantiation, via environment variables `ARGILLA_API_URL`" - " and `ARGILLA_API_KEY`, or as `Step` runtime parameters via `pipeline.run(parameters={...})`." + " and `ARGILLA_API_KEY`, or as `Step` runtime parameters via `pipeline.run(parameters={...})`.", + page="sections/how_to_guides/advanced/argilla/", ) self._client_init() diff --git a/src/distilabel/steps/argilla/preference.py b/src/distilabel/steps/argilla/preference.py index 572c2e6746..0843f2f55a 100644 --- a/src/distilabel/steps/argilla/preference.py +++ b/src/distilabel/steps/argilla/preference.py @@ -23,6 +23,7 @@ except ImportError: pass +from distilabel.errors import DistilabelUserError from distilabel.steps.argilla.base import ArgillaBase from distilabel.steps.base import StepInput @@ -166,11 +167,12 @@ def load(self) -> None: ] and field.required ): - raise ValueError( + raise DistilabelUserError( f"The dataset '{self.dataset_name}' in the workspace '{self.dataset_workspace}'" f" already exists, but contains at least a required field that is" f" neither `{self._id}`, `{self._instruction}`, nor `{self._generations}`" - f" (one per generation starting from 0 up to {self.num_generations - 1})." + f" (one per generation starting from 0 up to {self.num_generations - 1}).", + page="components-gallery/steps/preferencetoargilla/", ) self._dataset = _dataset diff --git a/src/distilabel/steps/argilla/text_generation.py b/src/distilabel/steps/argilla/text_generation.py index d3df0767c8..5298412843 100644 --- a/src/distilabel/steps/argilla/text_generation.py +++ b/src/distilabel/steps/argilla/text_generation.py @@ -23,6 +23,7 @@ except ImportError: pass +from distilabel.errors import DistilabelUserError from distilabel.steps.argilla.base import ArgillaBase from distilabel.steps.base import StepInput @@ -117,11 +118,12 @@ def load(self) -> None: field.name not in [self._id, self._instruction, self._generation] and field.required ): - raise ValueError( + raise DistilabelUserError( f"The dataset '{self.dataset_name}' in the workspace '{self.dataset_workspace}'" f" already exists, but contains at least a required field that is" f" neither `{self._id}`, `{self._instruction}`, nor `{self._generation}`," - " so it cannot be reused for this dataset." + " so it cannot be reused for this dataset.", + page="components-gallery/steps/textgenerationtoargilla/", ) self._dataset = _dataset diff --git a/src/distilabel/steps/base.py b/src/distilabel/steps/base.py index a1f863781a..f36931d7d8 100644 --- a/src/distilabel/steps/base.py +++ b/src/distilabel/steps/base.py @@ -33,6 +33,7 @@ from pydantic import BaseModel, ConfigDict, Field, PositiveInt, PrivateAttr from typing_extensions import Annotated, Self +from distilabel.errors import DistilabelTypeError, DistilabelUserError from distilabel.mixins.requirements import RequirementsMixin from distilabel.mixins.runtime_parameters import ( RuntimeParameter, @@ -439,9 +440,10 @@ def get_process_step_input(self) -> Union[inspect.Parameter, None]: for parameter in self.process_parameters: if is_parameter_annotated_with(parameter, _STEP_INPUT_ANNOTATION): if step_input_parameter is not None: - raise TypeError( + raise DistilabelTypeError( f"Step '{self.name}' should have only one parameter with type" - " hint `StepInput`." + " hint `StepInput`.", + page="sections/how_to_guides/basic/step/#defining-custom-steps", ) step_input_parameter = parameter return step_input_parameter @@ -458,10 +460,11 @@ def verify_inputs_mappings(self) -> None: for input in self.input_mappings: if input not in self.inputs: - raise ValueError( + raise DistilabelUserError( f"The input column '{input}' doesn't exist in the inputs of the" f" step '{self.name}'. Inputs of the step are: {self.inputs}." - " Please, review the `inputs_mappings` argument of the step." + " Please, review the `inputs_mappings` argument of the step.", + page="sections/how_to_guides/basic/step/#arguments", ) def verify_outputs_mappings(self) -> None: @@ -476,10 +479,11 @@ def verify_outputs_mappings(self) -> None: for output in self.output_mappings: if output not in self.outputs: - raise ValueError( + raise DistilabelUserError( f"The output column '{output}' doesn't exist in the outputs of the" f" step '{self.name}'. Outputs of the step are: {self.outputs}." - " Please, review the `outputs_mappings` argument of the step." + " Please, review the `outputs_mappings` argument of the step.", + page="sections/how_to_guides/basic/step/#arguments", ) def get_inputs(self) -> Dict[str, bool]: diff --git a/src/distilabel/steps/generators/huggingface.py b/src/distilabel/steps/generators/huggingface.py index 4789dfe10a..6701eafb52 100644 --- a/src/distilabel/steps/generators/huggingface.py +++ b/src/distilabel/steps/generators/huggingface.py @@ -41,6 +41,7 @@ from upath import UPath from distilabel.distiset import Distiset +from distilabel.errors import DistilabelUserError from distilabel.mixins.runtime_parameters import RuntimeParameter from distilabel.steps.base import GeneratorStep @@ -571,12 +572,11 @@ def load(self) -> None: storage_options=self.storage_options, ) if self.config not in ds.keys(): - # TODO: Add FAQ for this case, pointing to the Distiset documentation on configurations. - raise ValueError( + raise DistilabelUserError( f"Configuration '{self.config}' not found in the Distiset, available ones" f" are: {list(ds.keys())}. Please try changing the `config` parameter to one " - "of the available configurations.\n\n" - f"For more information on Distiset configurations, please visit https://distilabel.argilla.io/dev/sections/how_to_guides/advanced/distiset/#using-the-distiset-dataset-object" + "of the available configurations.\n\n", + page="sections/how_to_guides/advanced/distiset/#using-the-distiset-dataset-object", ) ds = ds[self.config] diff --git a/src/distilabel/steps/generators/utils.py b/src/distilabel/steps/generators/utils.py index b9e111d9b9..f99ae41c6e 100644 --- a/src/distilabel/steps/generators/utils.py +++ b/src/distilabel/steps/generators/utils.py @@ -17,6 +17,7 @@ import pandas as pd from datasets import Dataset +from distilabel.errors import DistilabelUserError from distilabel.steps.base import StepResources if TYPE_CHECKING: @@ -62,9 +63,10 @@ def make_generator_step( dataset = Dataset.from_pandas(dataset, preserve_index=False) if not isinstance(dataset, Dataset): - raise ValueError( + raise DistilabelUserError( f"Dataset type not allowed: {type(dataset)}, must be one of: " - "`datasets.Dataset`, `pd.DataFrame`, `List[Dict[str, str]]`" + "`datasets.Dataset`, `pd.DataFrame`, `List[Dict[str, str]]`", + page="sections/how_to_guides/basic/pipeline/?h=make_#__tabbed_1_2", ) loader = LoadDataFromHub( diff --git a/src/distilabel/steps/tasks/generate_embeddings.py b/src/distilabel/steps/tasks/generate_embeddings.py index e9a4db7756..297f3c3be7 100644 --- a/src/distilabel/steps/tasks/generate_embeddings.py +++ b/src/distilabel/steps/tasks/generate_embeddings.py @@ -14,6 +14,7 @@ from typing import TYPE_CHECKING, Any, Dict +from distilabel.errors import DistilabelUserError from distilabel.llms.base import LLM from distilabel.steps.base import Step, StepInput from distilabel.utils.chat import is_openai_format @@ -130,9 +131,10 @@ def format_input(self, input: Dict[str, Any]) -> "ChatType": if is_openai_format(text): return text - raise ValueError( + raise DistilabelUserError( f"Couldn't format input for step {self.name}. The `text` input column has to" - " be a string or a list of dictionaries in OpenAI chat-like format." + " be a string or a list of dictionaries in OpenAI chat-like format.", + page="components-gallery/tasks/generateembeddings/", ) def process(self, inputs: StepInput) -> "StepOutput": # type: ignore diff --git a/src/distilabel/steps/tasks/magpie/base.py b/src/distilabel/steps/tasks/magpie/base.py index 1f702b95a6..dfd7983772 100644 --- a/src/distilabel/steps/tasks/magpie/base.py +++ b/src/distilabel/steps/tasks/magpie/base.py @@ -17,6 +17,7 @@ from pydantic import Field, PositiveInt +from distilabel.errors import DistilabelUserError from distilabel.llms.base import LLM from distilabel.llms.mixins.magpie import MagpieChatTemplateMixin from distilabel.mixins.runtime_parameters import ( @@ -440,9 +441,10 @@ def model_post_init(self, __context: Any) -> None: super().model_post_init(__context) if not isinstance(self.llm, MagpieChatTemplateMixin): - raise ValueError( + raise DistilabelUserError( f"`Magpie` task can only be used with an `LLM` that uses the `MagpieChatTemplateMixin`." - f"`{self.llm.__class__.__name__}` doesn't use the aforementioned mixin." + f"`{self.llm.__class__.__name__}` doesn't use the aforementioned mixin.", + page="components-gallery/tasks/magpie/", ) self.llm.use_magpie_template = True diff --git a/src/distilabel/steps/tasks/magpie/generator.py b/src/distilabel/steps/tasks/magpie/generator.py index e0fb0e6bb3..e7cf77c605 100644 --- a/src/distilabel/steps/tasks/magpie/generator.py +++ b/src/distilabel/steps/tasks/magpie/generator.py @@ -16,6 +16,7 @@ from pydantic import Field +from distilabel.errors import DistilabelUserError from distilabel.llms.mixins.magpie import MagpieChatTemplateMixin from distilabel.mixins.runtime_parameters import RuntimeParameter from distilabel.steps.tasks.base import GeneratorTask @@ -228,9 +229,10 @@ def model_post_init(self, __context: Any) -> None: super().model_post_init(__context) if not isinstance(self.llm, MagpieChatTemplateMixin): - raise ValueError( + raise DistilabelUserError( f"`Magpie` task can only be used with an `LLM` that uses the `MagpieChatTemplateMixin`." - f"`{self.llm.__class__.__name__}` doesn't use the aforementioned mixin." + f"`{self.llm.__class__.__name__}` doesn't use the aforementioned mixin.", + page="components-gallery/tasks/magpiegenerator/", ) self.llm.use_magpie_template = True diff --git a/src/distilabel/steps/tasks/prometheus_eval.py b/src/distilabel/steps/tasks/prometheus_eval.py index 8e1ecbaca9..e902756e8d 100644 --- a/src/distilabel/steps/tasks/prometheus_eval.py +++ b/src/distilabel/steps/tasks/prometheus_eval.py @@ -26,6 +26,7 @@ from pydantic import Field, PrivateAttr, model_validator from typing_extensions import Self +from distilabel.errors import DistilabelUserError from distilabel.steps.tasks.base import Task if TYPE_CHECKING: @@ -75,15 +76,11 @@ class PrometheusEval(Task): """Critique and rank the quality of generations from an `LLM` using Prometheus 2.0. `PrometheusEval` is a task created for Prometheus 2.0, covering both the absolute and relative - evaluations. - - - The absolute evaluation i.e. `mode="absolute"` is used to evaluate a single generation from - an LLM for a given instruction. - - The relative evaluation i.e. `mode="relative"` is used to evaluate two generations from an LLM - for a given instruction. - - Both evaluations provide the possibility whether to use a reference answer to compare with or not - via the `reference` attribute, and both are based on a score rubric that critiques the generation/s + evaluations. The absolute evaluation i.e. `mode="absolute"` is used to evaluate a single generation from + an LLM for a given instruction. The relative evaluation i.e. `mode="relative"` is used to evaluate two generations from an LLM + for a given instruction. + Both evaluations provide the possibility of using a reference answer to compare with or withoug + the `reference` attribute, and both are based on a score rubric that critiques the generation/s based on the following default aspects: `helpfulness`, `harmlessness`, `honesty`, `factual-validity`, and `reasoning`, that can be overridden via `rubrics`, and the selected rubric is set via the attribute `rubric`. @@ -138,7 +135,7 @@ class PrometheusEval(Task): Examples: - Critique and evaluate LLM generation quality using Prometheus 2.0: + Critique and evaluate LLM generation quality using Prometheus 2_0: ```python from distilabel.steps.tasks import PrometheusEval @@ -320,8 +317,9 @@ class PrometheusEval(Task): @model_validator(mode="after") def validate_rubric_and_rubrics(self) -> Self: if not isinstance(self.rubrics, dict) or len(self.rubrics) < 1: - raise ValueError( - "Provided `rubrics` must be a Python dictionary with string keys and string values." + raise DistilabelUserError( + "Provided `rubrics` must be a Python dictionary with string keys and string values.", + page="components-gallery/tasks/prometheuseval/", ) def rubric_matches_pattern(rubric: str) -> bool: @@ -330,17 +328,19 @@ def rubric_matches_pattern(rubric: str) -> bool: return bool(re.match(pattern, rubric, re.MULTILINE)) if not all(rubric_matches_pattern(value) for value in self.rubrics.values()): - raise ValueError( + raise DistilabelUserError( "Provided rubrics should match the format of the default rubrics, which" " is as follows: `[]\nScore 1: \nScore 2: \n" "Score 3: \nScore 4: \nScore 5: `; replacing" " `` and `` with the actual criteria and description" - " for each or the scores, respectively." + " for each or the scores, respectively.", + page="components-gallery/tasks/prometheuseval/", ) if self.rubric not in self.rubrics: - raise ValueError( - f"Provided rubric '{self.rubric}' is not among the available rubrics: {', '.join(self.rubrics.keys())}." + raise DistilabelUserError( + f"Provided rubric '{self.rubric}' is not among the available rubrics: {', '.join(self.rubrics.keys())}.", + page="components-gallery/tasks/prometheuseval/", ) return self @@ -393,9 +393,10 @@ def format_input(self, input: Dict[str, Any]) -> "ChatType": if self.mode == "absolute": if not isinstance(input["generation"], str): - raise ValueError( + raise DistilabelUserError( f"Provided `generation` is of type {type(input['generation'])} but a string" " should be provided instead.", + page="components-gallery/tasks/prometheuseval/", ) template_kwargs["generation"] = input["generation"] @@ -412,8 +413,9 @@ def format_input(self, input: Dict[str, Any]) -> "ChatType": ) or len(input["generations"]) != 2 ): - raise ValueError( - f"Provided `generations` is of type {type(input['generations'])} but a list of strings with length 2 should be provided instead." + raise DistilabelUserError( + f"Provided `generations` is of type {type(input['generations'])} but a list of strings with length 2 should be provided instead.", + page="components-gallery/tasks/prometheuseval/", ) template_kwargs["generations"] = input["generations"] diff --git a/src/distilabel/steps/tasks/structured_generation.py b/src/distilabel/steps/tasks/structured_generation.py index 174cf20cb5..cbea279ebb 100644 --- a/src/distilabel/steps/tasks/structured_generation.py +++ b/src/distilabel/steps/tasks/structured_generation.py @@ -15,6 +15,7 @@ import warnings from typing import Any, Dict, List, Union +from distilabel.errors import DistilabelUserError from distilabel.steps.tasks.base import Task from distilabel.steps.tasks.typing import StructuredInput @@ -153,8 +154,9 @@ def format_input(self, input: Dict[str, Any]) -> StructuredInput: """The input is formatted as a `ChatType` assuming that the instruction is the first interaction from the user within a conversation.""" if not isinstance(input["instruction"], str): - raise ValueError( - f"Input `instruction` must be a string. Got: {input['instruction']}." + raise DistilabelUserError( + f"Input `instruction` must be a string. Got: {input['instruction']}.", + page="components-gallery/tasks/structuredgeneration/", ) messages = [{"role": "user", "content": input["instruction"]}] diff --git a/src/distilabel/steps/tasks/structured_outputs/instructor.py b/src/distilabel/steps/tasks/structured_outputs/instructor.py index 94ab1097e5..93b90d9916 100644 --- a/src/distilabel/steps/tasks/structured_outputs/instructor.py +++ b/src/distilabel/steps/tasks/structured_outputs/instructor.py @@ -24,6 +24,8 @@ get_args, ) +from distilabel.errors import DistilabelUserError + if TYPE_CHECKING: import instructor from anthropic import AsyncAnthropic @@ -115,8 +117,9 @@ def prepare_instructor( mode = mode or default_mode if mode.value not in [m.value for m in instructor.mode.Mode]: - raise ValueError( - f"Invalid mode '{mode}'. Must be one of {[m.value for m in instructor.mode.Mode]}" + raise DistilabelUserError( + f"Invalid mode '{mode}'. Must be one of {[m.value for m in instructor.mode.Mode]}", + page="sections/how_to_guides/advanced/structured_generation/#instructor", ) patched_client: instructor.AsyncInstructor = builder(client, mode=mode) diff --git a/src/distilabel/steps/tasks/structured_outputs/outlines.py b/src/distilabel/steps/tasks/structured_outputs/outlines.py index 6d34f9a92a..62419d37b0 100644 --- a/src/distilabel/steps/tasks/structured_outputs/outlines.py +++ b/src/distilabel/steps/tasks/structured_outputs/outlines.py @@ -29,6 +29,7 @@ from pydantic import BaseModel +from distilabel.errors import DistilabelUserError from distilabel.steps.tasks.structured_outputs.utils import schema_as_dict from distilabel.steps.tasks.typing import StructuredOutputType @@ -64,8 +65,9 @@ def _get_logits_processor(framework: Frameworks) -> Tuple[Callable, Callable]: return JSONLogitsProcessor, RegexLogitsProcessor - raise ValueError( - f"Invalid framework '{framework}'. Must be one of {get_args(Frameworks)}" + raise DistilabelUserError( + f"Invalid framework '{framework}'. Must be one of {get_args(Frameworks)}", + page="sections/how_to_guides/advanced/structured_generation/", ) @@ -123,4 +125,7 @@ def prepare_guided_output( if format == "regex": return {"processor": regex_processor(schema, llm)} - raise ValueError(f"Invalid format '{format}'. Must be either 'json' or 'regex'.") + raise DistilabelUserError( + f"Invalid format '{format}'. Must be either 'json' or 'regex'.", + page="sections/how_to_guides/advanced/structured_generation/", + ) diff --git a/src/distilabel/steps/tasks/text_generation.py b/src/distilabel/steps/tasks/text_generation.py index aeb74c9ec7..6f4d5d7584 100644 --- a/src/distilabel/steps/tasks/text_generation.py +++ b/src/distilabel/steps/tasks/text_generation.py @@ -15,6 +15,7 @@ import warnings from typing import TYPE_CHECKING, Any, Dict, List, Union +from distilabel.errors import DistilabelUserError from distilabel.steps.tasks.base import Task from distilabel.utils.chat import is_openai_format @@ -90,14 +91,16 @@ def format_input(self, input: Dict[str, Any]) -> "ChatType": is the first interaction from the user within a conversation.""" if is_openai_format(input["instruction"]): - raise ValueError( + raise DistilabelUserError( "Providing `instruction` formatted as an OpenAI chat / conversation is" " deprecated, you should use `ChatGeneration` with `messages` as input instead.", + page="components-gallery/tasks/textgeneration/", ) if not isinstance(input["instruction"], str): - raise ValueError( - f"Input `instruction` must be a string. Got: {input['instruction']}." + raise DistilabelUserError( + f"Input `instruction` must be a string. Got: {input['instruction']}.", + page="components-gallery/tasks/textgeneration/", ) messages = [{"role": "user", "content": input["instruction"]}] @@ -197,15 +200,17 @@ def format_input(self, input: Dict[str, Any]) -> "ChatType": are already formatted that way i.e. following the OpenAI chat format.""" if not is_openai_format(input["messages"]): - raise ValueError( + raise DistilabelUserError( "Input `messages` must be an OpenAI chat-like format conversation. " - f"Got: {input['messages']}. Please check: 'https://cookbook.openai.com/examples/how_to_format_inputs_to_chatgpt_models'." + f"Got: {input['messages']}. Please check: 'https://cookbook.openai.com/examples/how_to_format_inputs_to_chatgpt_models'.", + page="components-gallery/tasks/chatgeneration/", ) if input["messages"][-1]["role"] != "user": - raise ValueError( + raise DistilabelUserError( "The last message must be from the user. Please check: " - "'https://cookbook.openai.com/examples/how_to_format_inputs_to_chatgpt_models'." + "'https://cookbook.openai.com/examples/how_to_format_inputs_to_chatgpt_models'.", + page="components-gallery/tasks/chatgeneration/", ) return input["messages"] diff --git a/src/distilabel/utils/docstring.py b/src/distilabel/utils/docstring.py index d33b503b8a..899425e260 100644 --- a/src/distilabel/utils/docstring.py +++ b/src/distilabel/utils/docstring.py @@ -236,9 +236,7 @@ def get_bibtex(ref: str) -> str: from bs4 import BeautifulSoup if not ref.startswith("https://arxiv.org"): - raise ValueError( - f"The url must start with of `https://arxiv.org`, but got: {ref}" - ) + raise ValueError(f"The url must start with `https://arxiv.org`, but got: {ref}") response: bytes = requests.get( rf"https://arxiv2bibtex.org/?q={quote_plus(ref)}&format=bibtex" ) diff --git a/tests/unit/test_errors.py b/tests/unit/test_errors.py new file mode 100644 index 0000000000..420c6ca0af --- /dev/null +++ b/tests/unit/test_errors.py @@ -0,0 +1,27 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from distilabel.errors import DistilabelUserError + + +def test_distilabel_user_error() -> None: + msg = DistilabelUserError("This is an error message.") + assert str(msg) == "This is an error message." + msg = DistilabelUserError( + "This is an error message.", page="sections/getting_started/faq/" + ) + assert ( + str(msg) + == "This is an error message.\n\nFor further information visit 'https://distilabel.argilla.io/latest/sections/getting_started/faq/'" + ) From af3515a70d55b7d46573b5ac2a26b1946adf9a53 Mon Sep 17 00:00:00 2001 From: Sara Han <127759186+sdiazlor@users.noreply.github.com> Date: Mon, 26 Aug 2024 09:06:27 +0200 Subject: [PATCH 31/82] Docs/tutorials fix (#922) * clean: duplicated header, installation, connect * preference: installation, viewer, connect * reranking: installation, tokenizer, connect * example dataset sentence pairs * workaround typing * typo * typo * cookbook feedback --- .../tutorials/GenerateSentencePair.ipynb | 138 ++++-------------- .../tutorials/clean_existing_dataset.ipynb | 33 +++-- .../generate_preference_dataset.ipynb | 28 ++-- mkdocs.yml | 2 +- 4 files changed, 66 insertions(+), 135 deletions(-) diff --git a/docs/sections/pipeline_samples/tutorials/GenerateSentencePair.ipynb b/docs/sections/pipeline_samples/tutorials/GenerateSentencePair.ipynb index f4450c4a91..0994c4ba4f 100644 --- a/docs/sections/pipeline_samples/tutorials/GenerateSentencePair.ipynb +++ b/docs/sections/pipeline_samples/tutorials/GenerateSentencePair.ipynb @@ -40,7 +40,7 @@ "metadata": {}, "outputs": [], "source": [ - "!pip install \"sentence-transformers>=3.0,<4.0\"" + "!pip install \"sentence-transformers~=3.0\"" ] }, { @@ -53,7 +53,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -91,9 +91,9 @@ "source": [ "### (optional) Deploy Argilla\n", "\n", - "You can skip this step or replace it with any other data evaluation tool, but the quality of your model will suffer from a lack of data quality, so we do recommend looking at your data. If you already have deployed Argilla, you can skip this step. Otherwise, you can quickly deploy Argilla following [this guide](https://docs.argilla.io/latest/getting_started/quickstart/). \n", + "You can skip this step or replace it with any other data evaluation tool, but the quality of your model will suffer from a lack of data quality, so we do recommend looking at your data. If you already deployed Argilla, you can skip this step. Otherwise, you can quickly deploy Argilla following [this guide](https://docs.argilla.io/latest/getting_started/quickstart/). \n", "\n", - "Along with that, you will need to install argilla as distilabel extra." + "Along with that, you will need to install Argilla as a distilabel extra." ] }, { @@ -153,7 +153,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -217,11 +217,14 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 23, "metadata": {}, "outputs": [], "source": [ - "llm = InferenceEndpointsLLM(model_id=\"mistralai/Mistral-7B-Instruct-v0.2\")\n", + "llm = InferenceEndpointsLLM(\n", + " model_id=\"mistralai/Mistral-7B-Instruct-v0.2\",\n", + " tokenizer_id=\"mistralai/Mistral-7B-Instruct-v0.2\",\n", + ")\n", "\n", "with Pipeline(name=\"generate\") as pipeline:\n", " load_dataset = LoadDataFromHub(\n", @@ -240,14 +243,14 @@ " generate_reranking_pairs = GenerateSentencePair(\n", " name=\"generate_reranking_pairs\",\n", " triplet=True,\n", - " hard_negative=False, # to potentially generate non-relevant pairs\n", + " hard_negative=False, # to potentially generate non-relevant pairs\n", " action=\"semantically-similar\",\n", " llm=llm,\n", " input_batch_size=10,\n", " context=context,\n", " )\n", "\n", - " load_dataset >> [generate_retrieval_pairs, generate_reranking_pairs]" + " load_dataset.connect(generate_retrieval_pairs, generate_reranking_pairs)" ] }, { @@ -272,7 +275,7 @@ " }\n", "}\n", "\n", - "distiset = pipeline.run( #\n", + "distiset = pipeline.run( \n", " parameters={\n", " load_dataset.name: {\n", " \"repo_id\": \"plaguss/argilla_sdk_docs_raw_unstructured\",\n", @@ -281,43 +284,10 @@ " generate_retrieval_pairs.name: generation_kwargs,\n", " generate_reranking_pairs.name: generation_kwargs,\n", " },\n", - " use_cache=False, # comment out for demo\n", + " use_cache=False, # False for demo\n", ")" ] }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Distiset({\n", - " generate_reranking_pairs: DatasetDict({\n", - " train: Dataset({\n", - " features: ['filename', 'anchor', 'repo_name', 'positive', 'negative', 'distilabel_metadata', 'model_name'],\n", - " num_rows: 15\n", - " })\n", - " })\n", - " generate_retrieval_pairs: DatasetDict({\n", - " train: Dataset({\n", - " features: ['filename', 'anchor', 'repo_name', 'positive', 'negative', 'distilabel_metadata', 'model_name'],\n", - " num_rows: 15\n", - " })\n", - " })\n", - "})" - ] - }, - "execution_count": 18, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "distiset" - ] - }, { "cell_type": "markdown", "metadata": {}, @@ -331,66 +301,21 @@ "metadata": {}, "outputs": [], "source": [ - "distiset.push_to_hub(\"my-org/my-dataset-name\")" + "distiset.push_to_hub(\"[your-owner-name]/example-retrieval-reranking-dataset\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "We have got 2 different leaf/end nodes, therefore we've got a distil configurations we can access, one for the retrieval data, and one for the reranking data." - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'filename': 'argilla-python/docs/index.md',\n", - " 'anchor': 'description: Argilla is a collaboration platform for AI engineers and domain experts that require high-quality outputs, full data ownership, and overall efficiency.\\nhide: navigation\\n\\nWelcome to Argilla\\n\\nArgilla is a collaboration platform for AI engineers and domain experts that require high-quality outputs, full data ownership, and overall efficiency.',\n", - " 'repo_name': 'argilla-io/argilla-python',\n", - " 'positive': 'description: Argilla is a collaboration tool designed for AI engineers and domain experts who need high-quality outputs, full data control, and maximum efficiency.\\nhide: navigation\\n\\nWelcome to Argilla\\n\\nArgilla is a collaboration tool designed for AI engineers and domain experts who need high-quality outputs, full data control, and maximum efficiency.',\n", - " 'negative': 'description: Argilla is a platform for marketing professionals and sales teams that prioritizes customer engagement, brand visibility, and revenue growth.\\nhide: navigation\\n\\nWelcome to Argilla\\n\\nArgilla is a platform for marketing professionals and sales teams that prioritizes customer engagement, brand visibility, and revenue growth.',\n", - " 'distilabel_metadata': {'raw_output_generate_reranking_pairs': '## Positive\\n\\ndescription: Argilla is a collaboration tool designed for AI engineers and domain experts who need high-quality outputs, full data control, and maximum efficiency.\\nhide: navigation\\n\\nWelcome to Argilla\\n\\nArgilla is a collaboration tool designed for AI engineers and domain experts who need high-quality outputs, full data control, and maximum efficiency.\\n\\n## Negative\\n\\ndescription: Argilla is a platform for marketing professionals and sales teams that prioritizes customer engagement, brand visibility, and revenue growth.\\nhide: navigation\\n\\nWelcome to Argilla\\n\\nArgilla is a platform for marketing professionals and sales teams that prioritizes customer engagement, brand visibility, and revenue growth.'},\n", - " 'model_name': 'gpt-4o'}" - ] - }, - "execution_count": 19, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "distiset[\"generate_reranking_pairs\"][\"train\"][0]" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{'filename': 'argilla-python/docs/index.md',\n", - " 'anchor': 'description: Argilla is a collaboration platform for AI engineers and domain experts that require high-quality outputs, full data ownership, and overall efficiency.\\nhide: navigation\\n\\nWelcome to Argilla\\n\\nArgilla is a collaboration platform for AI engineers and domain experts that require high-quality outputs, full data ownership, and overall efficiency.',\n", - " 'repo_name': 'argilla-io/argilla-python',\n", - " 'positive': 'What is Argilla and how does it benefit AI engineers and domain experts?',\n", - " 'negative': \"How does Argilla's interface compare with other project management tools?\",\n", - " 'distilabel_metadata': {'raw_output_generate_retrieval_pairs': \"## Positive\\n\\nWhat is Argilla and how does it benefit AI engineers and domain experts?\\n\\n## Negative\\n\\nHow does Argilla's interface compare with other project management tools?\"},\n", - " 'model_name': 'gpt-4o'}" - ] - }, - "execution_count": 20, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "distiset[\"generate_retrieval_pairs\"][\"train\"][0]" + "We have got 2 different leaf/end nodes, therefore we've got a distil configurations we can access, one for the retrieval data, and one for the reranking data.\n", + "\n", + "" ] }, { @@ -490,18 +415,9 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Some weights of BertForSequenceClassification were not initialized from the model checkpoint at sentence-transformers/all-MiniLM-L12-v2 and are newly initialized: ['classifier.bias', 'classifier.weight']\n", - "You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.\n" - ] - } - ], + "outputs": [], "source": [ "model_id = \"sentence-transformers/all-MiniLM-L12-v2\"\n", "\n", @@ -654,7 +570,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Now we can explore the UI and add a final human touch to get he most out of our dataset. " + "Now, we can explore the UI and add a final human touch to get he most out of our dataset. " ] }, { @@ -707,7 +623,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.6" + "version": "3.11.4" } }, "nbformat": 4, diff --git a/docs/sections/pipeline_samples/tutorials/clean_existing_dataset.ipynb b/docs/sections/pipeline_samples/tutorials/clean_existing_dataset.ipynb index c4cf4a4c52..55daf6cd99 100644 --- a/docs/sections/pipeline_samples/tutorials/clean_existing_dataset.ipynb +++ b/docs/sections/pipeline_samples/tutorials/clean_existing_dataset.ipynb @@ -47,7 +47,7 @@ "metadata": {}, "outputs": [], "source": [ - "!pip install \"transformers>=4.0,<5.0\" \"torch>=2.0,<3.0\"" + "!pip install \"transformers~=4.0\" \"torch~=2.0\"" ] }, { @@ -59,7 +59,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -102,9 +102,9 @@ "source": [ "### (optional) Deploy Argilla\n", "\n", - "You can skip this step or replace it with any other data evaluation tool, but the quality of your model will suffer from a lack of data quality, so we do recommend looking at your data. If you already have deployed Argilla, you can skip this step. Otherwise, you can quickly deploy Argilla following [this guide](https://docs.argilla.io/latest/getting_started/quickstart/). \n", + "You can skip this step or replace it with any other data evaluation tool, but the quality of your model will suffer from a lack of data quality, so we do recommend looking at your data. If you already deployed Argilla, you can skip this step. Otherwise, you can quickly deploy Argilla following [this guide](https://docs.argilla.io/latest/getting_started/quickstart/). \n", "\n", - "Along with that, you will need to install argilla as distilabel extra." + "Along with that, you will need to install Argilla as a distilabel extra." ] }, { @@ -120,14 +120,14 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Load the dataset" + "## The dataset" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "In this case, we will clean a preference dataset, so we will use the `Intel/orca_dpo_pairs` dataset from the [Hugging Face Hub](https://huggingface.co/datasets/Intel/orca_dpo_pairs)." + "In this case, we will clean a preference dataset, so we will use the [`Intel/orca_dpo_pairs`](https://huggingface.co/datasets/Intel/orca_dpo_pairs) dataset from the Hugging Face Hub." ] }, { @@ -144,7 +144,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ @@ -160,7 +160,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ @@ -175,7 +175,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 7, "metadata": {}, "outputs": [], "source": [ @@ -200,14 +200,17 @@ "\n", " class ShuffleStep(GlobalStep):\n", " @property\n", - " def inputs(self) -> List[str]:\n", + " def inputs(self):\n", + " \"\"\"Returns List[str]: The inputs of the step.\"\"\"\n", " return [\"instruction\", \"chosen\", \"rejected\"]\n", "\n", " @property\n", - " def outputs(self) -> List[str]:\n", + " def outputs(self):\n", + " \"\"\"Returns List[str]: The outputs of the step.\"\"\"\n", " return [\"instruction\", \"generations\", \"order\"]\n", "\n", - " def process(self, inputs: StepInput) -> \"StepOutput\": # type: ignore\n", + " def process(self, inputs: StepInput):\n", + " \"\"\"Returns StepOutput: The outputs of the step.\"\"\"\n", " outputs = []\n", "\n", " for input in inputs:\n", @@ -456,7 +459,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 10, "metadata": {}, "outputs": [], "source": [ @@ -494,7 +497,9 @@ " num_generations=2,\n", " )\n", "\n", - " load_dataset >> evaluate_responses >> keep_columns >> to_argilla" + " load_dataset.connect(evaluate_responses)\n", + " evaluate_responses.connect(keep_columns)\n", + " keep_columns.connect(to_argilla)" ] }, { diff --git a/docs/sections/pipeline_samples/tutorials/generate_preference_dataset.ipynb b/docs/sections/pipeline_samples/tutorials/generate_preference_dataset.ipynb index 72e4b9fce9..187de7420d 100644 --- a/docs/sections/pipeline_samples/tutorials/generate_preference_dataset.ipynb +++ b/docs/sections/pipeline_samples/tutorials/generate_preference_dataset.ipynb @@ -47,7 +47,7 @@ "metadata": {}, "outputs": [], "source": [ - "!pip install \"transformers>=4.0,<5.0\" \"torch>=2.0,<3.0\"" + "!pip install \"transformers~=4.0\" \"torch~=2.0\"" ] }, { @@ -97,12 +97,11 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "\n", "### (optional) Deploy Argilla\n", "\n", - "You can skip this step or replace it with any other data evaluation tool, but the quality of your model will suffer from a lack of data quality, so we do recommend looking at your data. If you already have deployed Argilla, you can skip this step. Otherwise, you can quickly deploy Argilla following [this guide](https://docs.argilla.io/latest/getting_started/quickstart/). \n", + "You can skip this step or replace it with any other data evaluation tool, but the quality of your model will suffer from a lack of data quality, so we do recommend looking at your data. If you already deployed Argilla, you can skip this step. Otherwise, you can quickly deploy Argilla following [this guide](https://docs.argilla.io/latest/getting_started/quickstart/). \n", "\n", - "Along with that, you will need to install argilla as distilabel extra." + "Along with that, you will need to install Argilla as a distilabel extra." ] }, { @@ -134,7 +133,14 @@ "source": [ "### Load the dataset\n", "\n", - "We will use as source data the `argilla/10Kprompts-mini` dataset from the [Hugging Face Hub](https://huggingface.co/datasets/argilla/10Kprompts-mini).\n", + "We will use as source data the [`argilla/10Kprompts-mini`](https://huggingface.co/datasets/argilla/10Kprompts-mini) dataset from the Hugging Face Hub.\n", + "\n", + "\n", "\n", "- Component: `LoadDataFromHub`\n", "- Input columns: `instruction` and `topic`, the same as in the loaded dataset\n", @@ -175,7 +181,7 @@ "source": [ "### Generate responses\n", "\n", - "We need to generate the responses for the given instructions. We will use two different models available in the Hugging Face Hub through the Serverless Inference API: [`meta-llama/Meta-Llama-3-8B-Instruct`](https://huggingface.co/meta-llama/Meta-Llama-3-8B-Instruct) and [`mistralai/Mixtral-8x7B-Instruct-v0.1`](https://huggingface.co/mistralai/Mixtral-8x7B-Instruct-v0.1). We will also indicate the generation parameters for each model.\n", + "We need to generate the responses for the given instructions. We will use two different models available on the Hugging Face Hub through the Serverless Inference API: [`meta-llama/Meta-Llama-3-8B-Instruct`](https://huggingface.co/meta-llama/Meta-Llama-3-8B-Instruct) and [`mistralai/Mixtral-8x7B-Instruct-v0.1`](https://huggingface.co/mistralai/Mixtral-8x7B-Instruct-v0.1). We will also indicate the generation parameters for each model.\n", "\n", "- Component: `TextGeneration` task with LLMs using `InferenceEndpointsLLM`\n", "- Input columns: `instruction`\n", @@ -440,7 +446,7 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ @@ -480,7 +486,7 @@ " )\n", "\n", " format_dpo = FormatTextGenerationDPO()\n", - " \n", + "\n", " to_argilla = PreferenceToArgilla(\n", " dataset_name=\"preference-dataset\",\n", " dataset_workspace=\"argilla\",\n", @@ -489,7 +495,11 @@ " num_generations=2\n", " )\n", "\n", - " load_dataset >> generate_responses >> group_responses >> evaluate_responses >> [format_dpo, to_argilla]" + " for task in generate_responses:\n", + " load_dataset.connect(task)\n", + " task.connect(group_responses)\n", + " group_responses.connect(evaluate_responses)\n", + " evaluate_responses.connect(format_dpo, to_argilla)" ] }, { diff --git a/mkdocs.yml b/mkdocs.yml index 41df47a0de..2a3941d6bf 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -188,7 +188,7 @@ nav: - "sections/pipeline_samples/index.md" - Tutorials: - Generate a preference dataset: "sections/pipeline_samples/tutorials/generate_preference_dataset.ipynb" - - Clean an existing dataset: "sections/pipeline_samples/tutorials/clean_existing_dataset.ipynb" + - Clean an existing preference dataset: "sections/pipeline_samples/tutorials/clean_existing_dataset.ipynb" - Synthetic data generation for fine-tuning custom retrieval and reranking models: "sections/pipeline_samples/tutorials/GenerateSentencePair.ipynb" - Papers: - DeepSeek Prover: "sections/pipeline_samples/papers/deepseek_prover.md" From d010f79638794234b05195ba9a8947a8676d81e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Mart=C3=ADn=20Bl=C3=A1zquez?= Date: Mon, 26 Aug 2024 14:25:25 +0200 Subject: [PATCH 32/82] Add `revision` runtime parameter to `LoadDataFromHub` (#928) * Add `revision` runtime parameter * Lint * Skip `PairRM` tests --- src/distilabel/steps/generators/huggingface.py | 6 ++++++ tests/integration/test_multiple_replicas.py | 1 + tests/unit/steps/tasks/test_pair_rm.py | 2 ++ tests/unit/steps/tasks/test_ultrafeedback.py | 1 + tests/unit/steps/tasks/test_urial.py | 2 +- tests/unit/steps/test_truncate.py | 1 + 6 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/distilabel/steps/generators/huggingface.py b/src/distilabel/steps/generators/huggingface.py index 6701eafb52..c7eeadb29e 100644 --- a/src/distilabel/steps/generators/huggingface.py +++ b/src/distilabel/steps/generators/huggingface.py @@ -74,6 +74,7 @@ class LoadDataFromHub(GeneratorStep): - `split`: The split of the dataset to load. Defaults to 'train'. - `config`: The configuration of the dataset to load. This is optional and only needed if the dataset has multiple configurations. + - `revision`: The revision of the dataset to load. Defaults to the latest revision. - `streaming`: Whether to load the dataset in streaming mode or not. Defaults to `False`. - `num_examples`: The number of examples to load from the dataset. @@ -122,6 +123,10 @@ class LoadDataFromHub(GeneratorStep): description="The configuration of the dataset to load. This is optional and only" " needed if the dataset has multiple configurations.", ) + revision: Optional[RuntimeParameter[str]] = Field( + default=None, + description="The revision of the dataset to load. Defaults to the latest revision.", + ) streaming: RuntimeParameter[bool] = Field( default=False, description="Whether to load the dataset in streaming mode or not. Defaults to False.", @@ -149,6 +154,7 @@ def load(self) -> None: self.repo_id, # type: ignore self.config, split=self.split, + revision=self.revision, streaming=self.streaming, ) num_examples = self._get_dataset_num_examples() diff --git a/tests/integration/test_multiple_replicas.py b/tests/integration/test_multiple_replicas.py index 0f7226d0f3..26d0f19b57 100644 --- a/tests/integration/test_multiple_replicas.py +++ b/tests/integration/test_multiple_replicas.py @@ -17,6 +17,7 @@ from typing import TYPE_CHECKING import pytest + from distilabel.pipeline import Pipeline from distilabel.steps import LoadDataFromDicts, StepInput, StepResources, step diff --git a/tests/unit/steps/tasks/test_pair_rm.py b/tests/unit/steps/tasks/test_pair_rm.py index 22d8d3c454..1903ccfb2f 100644 --- a/tests/unit/steps/tasks/test_pair_rm.py +++ b/tests/unit/steps/tasks/test_pair_rm.py @@ -15,11 +15,13 @@ from unittest.mock import MagicMock, patch import numpy as np +import pytest from distilabel.pipeline.local import Pipeline from distilabel.steps.tasks.pair_rm import PairRM +@pytest.mark.skip(reason="Not maintained and to be deprecated.") @patch("llm_blender.Blender") class TestPairRM: def test_process(self, mocker: MagicMock) -> None: diff --git a/tests/unit/steps/tasks/test_ultrafeedback.py b/tests/unit/steps/tasks/test_ultrafeedback.py index 152509f993..5565065d61 100644 --- a/tests/unit/steps/tasks/test_ultrafeedback.py +++ b/tests/unit/steps/tasks/test_ultrafeedback.py @@ -15,6 +15,7 @@ from typing import Any, Dict, List, Union import pytest + from distilabel.llms.base import LLM from distilabel.llms.typing import GenerateOutput from distilabel.steps.tasks.typing import ChatType diff --git a/tests/unit/steps/tasks/test_urial.py b/tests/unit/steps/tasks/test_urial.py index 2075d98e6e..055e1a669f 100644 --- a/tests/unit/steps/tasks/test_urial.py +++ b/tests/unit/steps/tasks/test_urial.py @@ -13,8 +13,8 @@ # limitations under the License. import pytest -from distilabel.steps.tasks.urial import URIAL +from distilabel.steps.tasks.urial import URIAL from tests.unit.conftest import DummyLLM diff --git a/tests/unit/steps/test_truncate.py b/tests/unit/steps/test_truncate.py index b268080336..52a07d6642 100644 --- a/tests/unit/steps/test_truncate.py +++ b/tests/unit/steps/test_truncate.py @@ -15,6 +15,7 @@ from typing import Optional import pytest + from distilabel.steps.truncate import TruncateTextColumn From 2ce44f0a52593b9c33c7b9f249d67c3915123311 Mon Sep 17 00:00:00 2001 From: David Berenstein Date: Mon, 26 Aug 2024 14:30:32 +0200 Subject: [PATCH 33/82] Add Plausible as replacement for GA (#929) --- mkdocs.yml | 5 +++-- pyproject.toml | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/mkdocs.yml b/mkdocs.yml index 2a3941d6bf..6c1bb97594 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -22,8 +22,8 @@ extra: - icon: fontawesome/brands/discord link: http://hf.co/join/discord analytics: - provider: google - property: G-PPKL7LMWCE + provider: plausible + domain: distilabel.argilla.io feedback: title: Was this page helpful? ratings: @@ -151,6 +151,7 @@ plugins: heading_level: 4 - social - mknotebooks + - material-plausible - distilabel/components-gallery: add_after_page: How-to guides diff --git a/pyproject.toml b/pyproject.toml index 820620375b..06503734c0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -55,6 +55,7 @@ docs = [ "mkdocs-literate-nav >= 0.6.1", "mkdocs-section-index >= 0.3.8", "mkdocs-gen-files >= 0.5.0", + "material-plausible-plugin>=0.2.0", "mike >= 2.0.0", "Pillow >= 9.5.0", "CairoSVG >= 2.7.1", From bb14e8b6271dae49d1a2cfb3e087869224d2d7bb Mon Sep 17 00:00:00 2001 From: Agus Date: Wed, 28 Aug 2024 13:51:37 +0200 Subject: [PATCH 34/82] Add minhash related steps to deduplicate texts (#931) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Initial work for minhash * Add minhash step redirect * Add first version of minhash and minhashlsh * Add unit tests for minhash dedup * Add pipeline testing deduplication * Add tests to run with disk backend * Add tests for the disk and ensure unload * Add private _datasketch module to include a custom storage configuration for the minhash index * Add docstrings to the internal classes/functions * Add docstrings for the user facing classes * Update src/distilabel/steps/filtering/minhash.py Co-authored-by: Gabriel Martín Blázquez * Update src/distilabel/steps/filtering/minhash.py Co-authored-by: Gabriel Martín Blázquez * Update tests/integration/test_deduplication.py Co-authored-by: Gabriel Martín Blázquez * Update src/distilabel/steps/filtering/minhash.py Co-authored-by: Gabriel Martín Blázquez * Update src/distilabel/steps/filtering/minhash.py Co-authored-by: Gabriel Martín Blázquez * Add installation dependencies * Apply comments from code review * Add nltk as a dependency for the tests * Update tests and interpretation of keep rows vs duplicates * Remove disk backend from tests temporarily * Add note in the docs related to minhash storage on disk * Update tests to run on dict instead of disk as it never ends on CI * Fix integration test * Hide import inside of function to avoid installing it on docs building * Update command to download nltk --------- Co-authored-by: Gabriel Martín Blázquez --- pyproject.toml | 3 + scripts/install_dependencies.sh | 5 +- src/distilabel/steps/__init__.py | 3 + src/distilabel/steps/filtering/__init__.py | 14 + src/distilabel/steps/filtering/_datasketch.py | 199 +++++++++++ src/distilabel/steps/filtering/minhash.py | 316 ++++++++++++++++++ tests/integration/test_deduplication.py | 51 +++ tests/unit/steps/test_filtering/__init__.py | 14 + .../unit/steps/test_filtering/test_minhash.py | 80 +++++ 9 files changed, 684 insertions(+), 1 deletion(-) create mode 100644 src/distilabel/steps/filtering/__init__.py create mode 100644 src/distilabel/steps/filtering/_datasketch.py create mode 100644 src/distilabel/steps/filtering/minhash.py create mode 100644 tests/integration/test_deduplication.py create mode 100644 tests/unit/steps/test_filtering/__init__.py create mode 100644 tests/unit/steps/test_filtering/test_minhash.py diff --git a/pyproject.toml b/pyproject.toml index 06503734c0..12eddf591e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -96,6 +96,9 @@ sentence-transformers = ["sentence-transformers >= 3.0.0"] faiss-cpu = ["faiss-cpu >= 1.8.0"] faiss-gpu = ["faiss-cpu >= 1.7.2"] +# minhash +minhash = ["datasketch >= 1.6.5", "nltk>3.8.1"] + [project.urls] Documentation = "https://distilabel.argilla.io/" Issues = "https://github.com/argilla/distilabel/issues" diff --git a/scripts/install_dependencies.sh b/scripts/install_dependencies.sh index 328c5fe029..aa10405a8c 100755 --- a/scripts/install_dependencies.sh +++ b/scripts/install_dependencies.sh @@ -6,7 +6,10 @@ python_version=$(python -c "import sys; print(sys.version_info[:2])") python -m pip install uv -uv pip install --system -e ".[anthropic,argilla,cohere,groq,hf-inference-endpoints,hf-transformers,litellm,llama-cpp,ollama,openai,outlines,vertexai,mistralai,instructor,sentence-transformers,faiss-cpu]" +uv pip install --system -e ".[anthropic,argilla,cohere,groq,hf-inference-endpoints,hf-transformers,litellm,llama-cpp,ollama,openai,outlines,vertexai,mistralai,instructor,sentence-transformers,faiss-cpu,minhash]" + +# For the tests of minhash +python -c "import nltk; nltk.download('punkt_tab')" if [ "${python_version}" != "(3, 12)" ]; then uv pip install --system -e .[ray] diff --git a/src/distilabel/steps/__init__.py b/src/distilabel/steps/__init__.py index 420334bca9..efa9095325 100644 --- a/src/distilabel/steps/__init__.py +++ b/src/distilabel/steps/__init__.py @@ -29,6 +29,7 @@ from distilabel.steps.deita import DeitaFiltering from distilabel.steps.embeddings.embedding_generation import EmbeddingGeneration from distilabel.steps.embeddings.nearest_neighbour import FaissNearestNeighbour +from distilabel.steps.filtering.minhash import MinHash, MinHashLSH from distilabel.steps.formatting.conversation import ConversationTemplate from distilabel.steps.formatting.dpo import ( FormatChatGenerationDPO, @@ -73,6 +74,8 @@ "LoadDataFromDisk", "LoadDataFromFileSystem", "LoadDataFromHub", + "MinHash", + "MinHashLSH", "make_generator_step", "PushToHub", "Step", diff --git a/src/distilabel/steps/filtering/__init__.py b/src/distilabel/steps/filtering/__init__.py new file mode 100644 index 0000000000..20ce00bda7 --- /dev/null +++ b/src/distilabel/steps/filtering/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + diff --git a/src/distilabel/steps/filtering/_datasketch.py b/src/distilabel/steps/filtering/_datasketch.py new file mode 100644 index 0000000000..417575bd4d --- /dev/null +++ b/src/distilabel/steps/filtering/_datasketch.py @@ -0,0 +1,199 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +`dataskech` (https://github.com/ekzhu/datasketch) doesn't offer a way to store the hash tables in disk. This +is a custom implementation that uses `shelve` to store the hash tables in disk. +Note: This implementation is not optimized for performance, but could be worth +creating a PR to `datasketch`. +""" + +import shelve +import struct +from pathlib import Path +from typing import Callable, Dict, Final, Optional, Tuple + +from datasketch import MinHashLSH as _MinHashLSH +from datasketch.lsh import _optimal_param +from datasketch.storage import OrderedStorage, UnorderedStorage, _random_name +from datasketch.storage import ordered_storage as _ordered_storage +from datasketch.storage import unordered_storage as _unordered_storage + +SHELVE_DIR: Path = Path.home() / ".cache" / "distilabel" +SHELVE_LIST_NAME: Final[str] = ".shelve_list_storage" +SHELVE_SET_NAME: Final[str] = ".shelve_set_storage" + + +class ShelveListStorage(OrderedStorage): + """Key/Value storage using shelve to store the hash tables in disk. + It mimics the behaviour of `datasketch.DictListStorage`. + The only difference is the storage in disk. + The functionality is on purpose to avoid unnecessary errors. + """ + + def __init__(self, config) -> None: + path = config.get("path", self._get_db_name()) + # Read about writeback here: https://docs.python.org/3/library/shelve.html#shelve.open + writeback = config.get("writeback", True) + # The flag is set to "n" to recreate the file always, we assume + # every pipeline works on it's own and recomputes it instead of trusting + # the cache. + self._db = shelve.open(path, writeback=writeback, flag="n") + + def _get_db_name(self): + return str(SHELVE_DIR / SHELVE_LIST_NAME) + + def keys(self): + return self._db.keys() + + def get(self, key): + return self._db.get(str(key), []) + + def remove(self, *keys): + for key in keys: + del self._db[str(key)] + + def remove_val(self, key, val): + self._db[str(key)].remove(val) + + def insert(self, key, *vals, **kwargs): + key = str(key) + if not self._db.get(key): + self._db[key] = [] + self._db[key].extend(vals) + + def size(self): + return len(self._db) + + def itemcounts(self, **kwargs): + return {k: len(v) for k, v in self._db.items()} + + def has_key(self, key): + return key in self._db + + def close(self): + self._db.close() + + +class ShelveSetStorage(UnorderedStorage, ShelveListStorage): + """Key/Value storage using shelve to store the hash tables in disk. + It mimics the behaviour of `datasketch.DictSetStorage`. + The only difference is the storage in disk. + The functionality is on purpose to avoid unnecessary errors. + """ + + def _get_db_name(self): + return str(SHELVE_DIR / SHELVE_SET_NAME) + + def get(self, key): + return self._db.get(str(key), set()) + + def insert(self, key, *vals, **kwargs): + key = str(key) + if not self._db.get(key): + self._db[key] = set() + self._db[key].update(vals) + + +def ordered_storage(config, name=None): + """Copy of `datasketch.storage.ordered_storage` with the addition of `ShelveListStorage`.""" + tp = config["type"] + if tp == "disk": + return ShelveListStorage(config) + return _ordered_storage(config, name=name) + + +def unordered_storage(config, name=None): + """Copy of `datasketch.storage.ordered_storage` with the addition of `ShelveSetStorage`.""" + tp = config["type"] + if tp == "disk": + return ShelveSetStorage(config) + return _unordered_storage(config, name=name) + + +class MinHashLSH(_MinHashLSH): + """Custom implementation of `datasketch.MinHashLSH` to allow passing a custom + storage configuration to store the hash tables in disk. + + This could be merged in the original repository, the only changes + to the __init__ are the additional `close` method, and the use + of our custom `ordered_storage` and `unordered_storage` functions. + """ + + def __init__( + self, + threshold: float = 0.9, + num_perm: int = 128, + weights: Tuple[float, float] = (0.5, 0.5), + params: Optional[Tuple[int, int]] = None, + storage_config: Optional[Dict] = None, + prepickle: Optional[bool] = None, + hashfunc: Optional[Callable[[bytes], bytes]] = None, + ) -> None: + storage_config = {"type": "dict"} if not storage_config else storage_config + self._buffer_size = 50000 + if threshold > 1.0 or threshold < 0.0: + raise ValueError("threshold must be in [0.0, 1.0]") + if num_perm < 2: + raise ValueError("Too few permutation functions") + if any(w < 0.0 or w > 1.0 for w in weights): + raise ValueError("Weight must be in [0.0, 1.0]") + if sum(weights) != 1.0: + raise ValueError("Weights must sum to 1.0") + self.h = num_perm + if params is not None: + self.b, self.r = params + if self.b * self.r > num_perm: + raise ValueError( + "The product of b and r in params is " + "{} * {} = {} -- it must be less than num_perm {}. " + "Did you forget to specify num_perm?".format( + self.b, self.r, self.b * self.r, num_perm + ) + ) + else: + false_positive_weight, false_negative_weight = weights + self.b, self.r = _optimal_param( + threshold, num_perm, false_positive_weight, false_negative_weight + ) + if self.b < 2: + raise ValueError("The number of bands are too small (b < 2)") + + self.prepickle = ( + storage_config["type"] == "redis" if not prepickle else prepickle + ) + + self.hashfunc = hashfunc + if hashfunc: + self._H = self._hashed_byteswap + else: + self._H = self._byteswap + + basename = storage_config.get("basename", _random_name(11)) + self.hashtables = [ + unordered_storage( + storage_config, + name=b"".join([basename, b"_bucket_", struct.pack(">H", i)]), + ) + for i in range(self.b) + ] + self.hashranges = [(i * self.r, (i + 1) * self.r) for i in range(self.b)] + self.keys = ordered_storage(storage_config, name=b"".join([basename, b"_keys"])) + + def close(self): + """Closes the shelve objects.""" + if isinstance(self.hashtables[0], ShelveListStorage): + for ht in self.hashtables: + ht.close() + self.keys.close() diff --git a/src/distilabel/steps/filtering/minhash.py b/src/distilabel/steps/filtering/minhash.py new file mode 100644 index 0000000000..e5da5bdd16 --- /dev/null +++ b/src/distilabel/steps/filtering/minhash.py @@ -0,0 +1,316 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import importlib +import uuid +from functools import partial +from itertools import tee +from typing import ( + TYPE_CHECKING, + Callable, + Iterable, + Iterator, + List, + Literal, + Optional, + Set, + Tuple, + Union, +) + +from pydantic import PrivateAttr +from typing_extensions import override + +from distilabel.steps.base import GlobalStep, Step, StepInput + +if TYPE_CHECKING: + from datasketch import LeanMinHash, MinHash, MinHashLSH + + from distilabel.steps.typing import StepOutput + + +# Copied from: https://github.com/huggingface/datatrove/blob/main/src/datatrove/utils/text.py#L89C1-L95C65 +def ngrams(sequence: Iterable[str], n: int) -> Iterator[Tuple[str, ...]]: + iterables = tee(sequence, n) + + for i, sub_iterable in enumerate(iterables): # For each window, + for _ in range(i): # iterate through every order of ngrams + next(sub_iterable, None) # generate the ngrams within the window. + return zip(*iterables) # Unpack and flattens the iterables. + + +def tokenized_on_words(texts: Iterable[str]) -> List[Set[bytes]]: + """Tokenizes a list of texts into words, using `nltk.word_tokenize`. + + Args: + texts: List of documents to be tokenized. + + Returns: + List with the set of tokens for each document. + """ + from nltk.tokenize import word_tokenize + + return [{w.encode("utf-8") for w in word_tokenize(text)} for text in texts] + + +def tokenize_on_ngrams(texts: Iterable[str], n: int = 1) -> List[Set[bytes]]: + """Tokenizes a list of texts into ngrams, and returns the set of them as bytes. + + Args: + texts: List of documents to be tokenized. + n: The size of the ngrams, defaults to 1 (single letters). + + Returns: + List with the set of tokens for each document. + """ + + return [ + {"".join(ngram).encode("utf-8") for ngram in ngrams(text, n=n)} + for text in texts + ] + + +# NOTE: This class must be used together with the `MinHashLSH` class. +# We return the `hashvalues` to reproduce the MinHash objects, but we also need +# the seed, so the seed used for the MinHash objects must be kept to be grabbed +# for the next class. We could also pass it as part of the dict, but there's no point. +# Also, instead of returning the values, we could be saving them as artifacts, +# This still needs to be studied. +class MinHash(Step): + """Creates the components for a `MinHash` object to deduplicate texts. + + From `datasketch` documentation: + Estimates the Jaccard similarity (resemblance) between sets of arbitrary sizes in linear + time using a small and fixed memory space. + + Note: + We only keep the hashvalues, as using those values together with the seed + we can reproduce the `MinHash` objects. The `MinHashLSH` will recreate those internally. + + Attributes: + num_perm: the number of permutations to use. Defaults to `128`. + seed: the seed to use for the MinHash. Defaults to `1`. + tokenizer: the tokenizer to use. Available ones are `words` or `ngrams`. + If `words` is selected, it tokenize the text into words using nltk's + word tokenizer. `ngram` estimates the ngrams (together with the size + `n`) using. Defaults to `words`. + n: the size of the ngrams to use. Only relevant if `tokenizer="ngrams"`. Defaults to `1`. + + Input columns: + - text (`str`): the texts to obtain the hashes for. + + Output columns: + - hashvalues (`List[int]`): hash values obtained for the algorithm. + + Categories: + - filtering + + References: + - [`datasketch documentation`](https://ekzhu.com/datasketch/minhash.html#minhash) + - [Identifying and Filtering Near-Duplicate Documents](https://cs.brown.edu/courses/cs253/papers/nearduplicate.pdf) + + Examples: + + Create MinHash objects for a list of texts to be deduplicated: + + ```python + texts: List[str] = [ + "This is a test document.", + "This document is a test.", + "Test document for duplication.", + "Document for duplication test.", + "This is another unique document." + ] + from distilabel.steps import MinHash + minhasher = MinHash(tokenizer="ngrams", n=3) + minhasher.load() + result = next(hasher.process([{"text": t} for t in texts])) + ``` + """ + + num_perm: int = 128 + seed: int = 1 + tokenizer: Literal["words", "ngrams"] = "words" + n: Optional[int] = 1 + _hasher: Union["MinHash", None] = PrivateAttr(None) + _tokenizer: Union[Callable, None] = PrivateAttr(None) + + def load(self) -> None: + super().load() + if not importlib.import_module("datasketch"): + raise ImportError( + "`datasketch` is needed to deduplicate with MinHash, but is not installed. " + "Please install it using `pip install datasketch`." + ) + from datasketch import MinHash + + self._hasher = MinHash.bulk + + if self.tokenizer == "words": + if not importlib.import_module("nltk"): + raise ImportError( + "`nltk` is needed to tokenize based on words, but is not installed. " + "Please install it using `pip install nltk`. Then run `nltk.download('punkt_tab')`." + ) + self._tokenizer = tokenized_on_words + else: + self._tokenizer = partial(tokenize_on_ngrams, n=self.n) + + @property + def inputs(self) -> List[str]: + return ["text"] + + @property + def outputs(self) -> List[str]: + # Do we need to keep anything, or can it be stored in the cache? + return ["hashvalues"] + + @override + def process(self, inputs: StepInput) -> "StepOutput": # type: ignore + tokenized_texts = [] + for input in inputs: + tokenized_texts.append(self._tokenizer([input[self.inputs[0]]])[0]) + + minhashes = self._hasher( + tokenized_texts, num_perm=self.num_perm, seed=self.seed + ) + for input, mh in zip(inputs, minhashes): + input["hashvalues"] = mh.hashvalues + yield inputs + + +class MinHashLSH(GlobalStep): + """Creates a `MinHashLSH` index to deduplicate texts using MinHash. + + This class must be used together with `MinHash` step. It will work with the previous hashes + to detect duplicate texts, and inform whether a given row can be removed. + + Attributes: + seed: the seed to use for the MinHash. This seed must be the same + used for `MinHash`, keep in mind when both steps are created. Defaults to `1`. + num_perm: the number of permutations to use. Defaults to `128`. + threshold: the threshold to consider two MinHashes as duplicates. + Values closer to 0 detect more duplicates. Defaults to `0.9`. + drop_hashvalues: whether to drop the hashvalues after processing. Defaults to `False`. + storage: the storage to use for the LSH. Can be `dict` to store the index + in memory, or `disk`, which uses a custom `shelve` backend. Note the `disk` + is an experimetal feature that may cause issues. Defaults to `dict`. + + Input columns: + - text (`str`): the texts to be filtered. + - hashvalues (`List[int]`): hash values obtained from `MinHash` step. + + Output columns: + - minhash_duplicate (`bool`): boolean indicating if the piece of text is a + duplicate or not, so the user can decide afterwards whether to remove it + or not. + + Categories: + - filtering + + References: + - [`datasketch documentation`](https://ekzhu.github.io/datasketch/lsh.html) + + Examples: + + Deduplicate a list of texts using MinHash and MinHashLSH: + + ```python + from distilabel.pipeline import Pipeline + from distilabel.steps import MinHash, MinHashLSH + + with Pipeline() as pipeline: + ds_size = 1000 + batch_size = 500 # Bigger batch sizes work better for this step + data = LoadDataFromDicts( + data=[ + {"text": "This is a test document."}, + {"text": "This document is a test."}, + {"text": "Test document for duplication."}, + {"text": "Document for duplication test."}, + {"text": "This is another unique document."}, + ] + * (ds_size // 5), + batch_size=batch_size, + ) + minhash = MinHash(tokenizer="ngrams", n=1, input_batch_size=batch_size) + minhash_lsh = MinHashLSH( + threshold=0.9, # lower values will increase the number of duplicates + seed=minhash.seed, # we need to keep the same seed for the LSH + drop_hashvalues=True, # the hashvalues are not needed anymore + storage="dict", # or "disk" for bigger datasets + ) + data >> minhash >> minhash_lsh + + if __name__ == "__main__": + distiset = pipeline.run(use_cache=False) + ds = distiset["default"]["train"] + # Filter out the duplicates + ds_dedup = ds.filter(lambda x: x["minhash_duplicate"] is False) + ``` + """ + + seed: int = 1 + num_perm: int = 128 + threshold: float = 0.9 + drop_hashvalues: bool = False + storage: Literal["dict", "disk"] = "dict" + _lhs: Union["MinHashLSH", None] = PrivateAttr(None) + _minhasher: Union["LeanMinHash", None] = PrivateAttr(None) + + def load(self) -> None: + super().load() + if not importlib.import_module("datasketch"): + raise ImportError( + "`datasketch` is needed to deduplicate with MinHash, but is not installed. " + "Please install it using `pip install datasketch`." + ) + from datasketch import LeanMinHash + + from distilabel.steps.filtering._datasketch import MinHashLSH + + self._lsh = MinHashLSH( + num_perm=self.num_perm, + threshold=self.threshold, + storage_config={"type": self.storage}, + ) + self._minhasher = partial(LeanMinHash, seed=self.seed) + + def unload(self) -> None: + super().unload() + # In case of LSH being stored in disk, we need to close the file. + if self.storage == "disk": + self._lsh.close() + + @property + def inputs(self) -> List[str]: + return ["text", "hashvalues"] + + @property + def outputs(self) -> List[str]: + return ["keep_row_after_minhash_filtering"] + + def process(self, inputs: StepInput) -> "StepOutput": + for input in inputs: + minhash = self._minhasher(hashvalues=input["hashvalues"]) + # Check if the text is already in the LSH index + if self._lsh.query(minhash): + input["keep_row_after_minhash_filtering"] = False + else: + self._lsh.insert(str(uuid.uuid4()), minhash) + input["keep_row_after_minhash_filtering"] = True + if self.drop_hashvalues: + del input["hashvalues"] + + yield inputs diff --git a/tests/integration/test_deduplication.py b/tests/integration/test_deduplication.py new file mode 100644 index 0000000000..f80ff31b93 --- /dev/null +++ b/tests/integration/test_deduplication.py @@ -0,0 +1,51 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from distilabel.pipeline import Pipeline +from distilabel.steps import LoadDataFromDicts, MinHash, MinHashLSH + + +def test_minhash_deduplication() -> None: + with Pipeline() as pipeline: + ds_size = 1000 + batch_size = 500 + data = LoadDataFromDicts( + data=[ + {"text": "This is a test document."}, + {"text": "This document is a test."}, + {"text": "Test document for duplication."}, + {"text": "Document for duplication test."}, + {"text": "This is another unique document."}, + ] + * (ds_size // 5), + batch_size=batch_size, + ) + minhash = MinHash(tokenizer="ngrams", n=1, input_batch_size=batch_size) + minhash_lsh = MinHashLSH( + threshold=0.9, + seed=minhash.seed, + drop_hashvalues=True, + storage="dict", + # storage="disk", + ) + data >> minhash >> minhash_lsh + + distiset = pipeline.run(use_cache=False) + ds = distiset["default"]["train"] + ds_dedup = ds.filter(lambda x: x["keep_row_after_minhash_filtering"]) + assert len(ds_dedup) == 4 + + +if __name__ == "__main__": + test_minhash_deduplication() diff --git a/tests/unit/steps/test_filtering/__init__.py b/tests/unit/steps/test_filtering/__init__.py new file mode 100644 index 0000000000..20ce00bda7 --- /dev/null +++ b/tests/unit/steps/test_filtering/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + diff --git a/tests/unit/steps/test_filtering/test_minhash.py b/tests/unit/steps/test_filtering/test_minhash.py new file mode 100644 index 0000000000..9be0256cbd --- /dev/null +++ b/tests/unit/steps/test_filtering/test_minhash.py @@ -0,0 +1,80 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import List + +import numpy as np +import pytest + +from distilabel.steps.filtering.minhash import ( + MinHash, + MinHashLSH, + tokenize_on_ngrams, + tokenized_on_words, +) + +texts: List[str] = [ + "This is a test document.", + "This document is a test.", + "Test document for duplication.", + "Document for duplication test.", + "This is another unique document.", +] + + +def test_tokenize_on_words() -> None: + tokenized = tokenized_on_words(texts) + assert len(tokenized) == len(texts) + assert tokenized[0] == {b".", b"This", b"a", b"document", b"is", b"test"} + + +@pytest.mark.parametrize("n", [1, 3]) +def test_tokenize_on_ngrams(n: int) -> None: + tokenized = tokenize_on_ngrams(texts, n=n) + assert len(tokenized) == len(texts) + assert all(len(t) == n for t in tokenized[0]) + + +class TestMinHash: + @pytest.mark.parametrize("tokenizer", ["words", "ngrams"]) + def test_process(self, tokenizer: str) -> None: + hasher = MinHash(tokenizer=tokenizer, n=3) + hasher.load() + result = next(hasher.process([{"text": t} for t in texts])) + hashvalues = result[0]["hashvalues"] + assert isinstance(hashvalues, np.ndarray) + + +class TestMinHashLSH: + @pytest.mark.parametrize( + "threshold, keep_row_after_minhash_filtering, storage", + [ + (0.1, 1, "dict"), + (0.9, 4, "dict"), + # (0.9, 4, "disk") # This test is skipped because it fails while testing on CI + ], + ) + def test_process( + self, threshold: float, keep_row_after_minhash_filtering: int, storage: str + ) -> None: + hasher = MinHash() + hasher.load() + results_with_hashes = next(hasher.process([{"text": t} for t in texts])) + + minhash_lsh = MinHashLSH(threshold=threshold, seed=hasher.seed, storage=storage) + minhash_lsh.load() + result = next(minhash_lsh.process(results_with_hashes)) + duplicated = [r["keep_row_after_minhash_filtering"] for r in result] + assert sum(duplicated) == keep_row_after_minhash_filtering + minhash_lsh.unload() From 88615c72182249203463401ec2f767e58ac84d9c Mon Sep 17 00:00:00 2001 From: Sara Han <127759186+sdiazlor@users.noreply.github.com> Date: Thu, 29 Aug 2024 15:58:11 +0200 Subject: [PATCH 35/82] docs: API reference review (#932) * add headers labels API * inherited members to false and ensure references from source * ensure examples are rendered in components_gallery and API reference * Remove space after Examples: in docstrings * ensure citations are rendered * note in CombineColumns and add missing steps * fix \n usage in docstrings tasks, better \\n * automatic LLM references for better maintenance * fix distiset examples * small fixes in galleries * add embedding section and gallery * add contributor docs * fix typos and links * Use HF Inference API instead of OpenAI in quickstart and README * update extra steps * add available models reference * fix fais-gpu dependency * upadate extras * add colab button and align welcome page as argilla --- README.md | 18 +- docs/api/embedding/embedding_gallery.md | 8 + docs/api/embedding/index.md | 7 + docs/api/llm/anthropic.md | 3 - docs/api/llm/anyscale.md | 3 - docs/api/llm/azure.md | 3 - docs/api/llm/cohere.md | 3 - docs/api/llm/groq.md | 3 - docs/api/llm/huggingface.md | 6 - docs/api/llm/litellm.md | 3 - docs/api/llm/llamacpp.md | 3 - docs/api/llm/llm_gallery.md | 10 ++ docs/api/llm/mistral.md | 3 - docs/api/llm/ollama.md | 3 - docs/api/llm/openai.md | 3 - docs/api/llm/together.md | 3 - docs/api/llm/vertexai.md | 3 - docs/api/llm/vllm.md | 3 - docs/api/step/typing.md | 3 + docs/api/step_gallery/extra.md | 13 +- docs/api/step_gallery/hugging_face.md | 1 + .../index.md => task/task_gallery.md} | 0 .../community/compare-pull-request.PNG | Bin 0 -> 14343 bytes .../sections/community/create-branch.PNG | Bin 0 -> 4224 bytes .../images/sections/community/edit-file.PNG | Bin 0 -> 10365 bytes docs/index.md | 26 ++- docs/sections/community/contributor.md | 159 ++++++++++++++++++ docs/sections/getting_started/faq.md | 14 +- docs/sections/getting_started/installation.md | 15 +- docs/sections/getting_started/quickstart.md | 23 ++- .../examples/benchmarking_with_distilabel.md | 2 +- .../examples/llama_cpp_with_outlines.md | 2 +- .../examples/mistralai_with_instructor.md | 2 +- docs/sections/pipeline_samples/index.md | 6 +- mkdocs.yml | 55 +++--- pyproject.toml | 2 +- src/distilabel/distiset.py | 25 ++- .../embeddings/sentence_transformers.py | 1 - src/distilabel/embeddings/vllm.py | 1 - src/distilabel/errors.py | 1 - src/distilabel/llms/anthropic.py | 1 - src/distilabel/llms/anyscale.py | 1 - src/distilabel/llms/azure.py | 1 - src/distilabel/llms/cohere.py | 1 - src/distilabel/llms/groq.py | 1 - .../llms/huggingface/inference_endpoints.py | 1 - .../llms/huggingface/transformers.py | 1 - src/distilabel/llms/litellm.py | 1 - src/distilabel/llms/llamacpp.py | 1 - src/distilabel/llms/mistral.py | 1 - src/distilabel/llms/moa.py | 1 - src/distilabel/llms/ollama.py | 14 ++ src/distilabel/llms/openai.py | 1 - src/distilabel/llms/together.py | 1 - src/distilabel/llms/vertexai.py | 14 ++ src/distilabel/llms/vllm.py | 2 - src/distilabel/steps/argilla/preference.py | 1 - .../steps/argilla/text_generation.py | 1 - src/distilabel/steps/columns/expand.py | 1 - src/distilabel/steps/columns/group.py | 3 +- src/distilabel/steps/columns/keep.py | 1 - src/distilabel/steps/columns/merge.py | 1 - src/distilabel/steps/deita.py | 2 - .../steps/embeddings/embedding_generation.py | 1 - .../steps/embeddings/nearest_neighbour.py | 2 - .../steps/formatting/conversation.py | 1 - src/distilabel/steps/formatting/dpo.py | 9 +- src/distilabel/steps/formatting/sft.py | 7 +- src/distilabel/steps/generators/data.py | 1 - .../steps/generators/huggingface.py | 3 - src/distilabel/steps/globals/huggingface.py | 1 - src/distilabel/steps/reward_model.py | 1 - .../steps/tasks/complexity_scorer.py | 4 +- .../steps/tasks/evol_instruct/base.py | 2 - .../evol_instruct/evol_complexity/base.py | 2 - .../evol_complexity/generator.py | 2 - .../steps/tasks/evol_instruct/generator.py | 2 - .../steps/tasks/evol_quality/base.py | 2 - .../steps/tasks/generate_embeddings.py | 2 - src/distilabel/steps/tasks/genstruct.py | 2 - .../steps/tasks/improving_text_embeddings.py | 8 - .../tasks/instruction_backtranslation.py | 37 +++- src/distilabel/steps/tasks/magpie/base.py | 2 - .../steps/tasks/magpie/generator.py | 2 - src/distilabel/steps/tasks/pair_rm.py | 2 - src/distilabel/steps/tasks/prometheus_eval.py | 12 +- src/distilabel/steps/tasks/quality_scorer.py | 2 - src/distilabel/steps/tasks/self_instruct.py | 2 - .../steps/tasks/sentence_transformers.py | 9 +- .../steps/tasks/structured_generation.py | 1 - src/distilabel/steps/tasks/text_generation.py | 2 - src/distilabel/steps/tasks/ultrafeedback.py | 30 ++-- src/distilabel/steps/tasks/urial.py | 1 - src/distilabel/steps/truncate.py | 1 - src/distilabel/utils/docstring.py | 3 +- 95 files changed, 411 insertions(+), 239 deletions(-) create mode 100644 docs/api/embedding/embedding_gallery.md create mode 100644 docs/api/embedding/index.md delete mode 100644 docs/api/llm/anthropic.md delete mode 100644 docs/api/llm/anyscale.md delete mode 100644 docs/api/llm/azure.md delete mode 100644 docs/api/llm/cohere.md delete mode 100644 docs/api/llm/groq.md delete mode 100644 docs/api/llm/huggingface.md delete mode 100644 docs/api/llm/litellm.md delete mode 100644 docs/api/llm/llamacpp.md create mode 100644 docs/api/llm/llm_gallery.md delete mode 100644 docs/api/llm/mistral.md delete mode 100644 docs/api/llm/ollama.md delete mode 100644 docs/api/llm/openai.md delete mode 100644 docs/api/llm/together.md delete mode 100644 docs/api/llm/vertexai.md delete mode 100644 docs/api/llm/vllm.md create mode 100644 docs/api/step/typing.md rename docs/api/{task_gallery/index.md => task/task_gallery.md} (100%) create mode 100644 docs/assets/images/sections/community/compare-pull-request.PNG create mode 100644 docs/assets/images/sections/community/create-branch.PNG create mode 100644 docs/assets/images/sections/community/edit-file.PNG create mode 100644 docs/sections/community/contributor.md diff --git a/README.md b/README.md index 7dff701ebf..013203067b 100644 --- a/README.md +++ b/README.md @@ -94,16 +94,16 @@ In addition, the following extras are available: ### Example -To run the following example you must install `distilabel` with both `openai` extra: +To run the following example you must install `distilabel` with the `hf-inference-endpoints` extra: ```sh -pip install "distilabel[openai]" --upgrade +pip install "distilabel[hf-inference-endpoints]" --upgrade ``` Then run: ```python -from distilabel.llms import OpenAILLM +from distilabel.llms import InferenceEndpointsLLM from distilabel.pipeline import Pipeline from distilabel.steps import LoadDataFromHub from distilabel.steps.tasks import TextGeneration @@ -114,9 +114,14 @@ with Pipeline( ) as pipeline: load_dataset = LoadDataFromHub(output_mappings={"prompt": "instruction"}) - generate_with_openai = TextGeneration(llm=OpenAILLM(model="gpt-3.5-turbo")) + text_generation = TextGeneration( + llm=InferenceEndpointsLLM( + model_id="meta-llama/Meta-Llama-3.1-8B-Instruct", + tokenizer_id="meta-llama/Meta-Llama-3.1-8B-Instruct", + ), + ) - load_dataset >> generate_with_openai + load_dataset >> text_generation if __name__ == "__main__": distiset = pipeline.run( @@ -125,7 +130,7 @@ if __name__ == "__main__": "repo_id": "distilabel-internal-testing/instruction-dataset-mini", "split": "test", }, - generate_with_openai.name: { + text_generation.name: { "llm": { "generation_kwargs": { "temperature": 0.7, @@ -135,6 +140,7 @@ if __name__ == "__main__": }, }, ) + distiset.push_to_hub(repo_id="distilabel-example") ``` ## Badges diff --git a/docs/api/embedding/embedding_gallery.md b/docs/api/embedding/embedding_gallery.md new file mode 100644 index 0000000000..3eed3ab50e --- /dev/null +++ b/docs/api/embedding/embedding_gallery.md @@ -0,0 +1,8 @@ +# Embedding Gallery + +This section contains the existing [`Embeddings`][distilabel.embeddings] subclasses implemented in `distilabel`. + +::: distilabel.embeddings + options: + filters: + - "!^Embeddings$" \ No newline at end of file diff --git a/docs/api/embedding/index.md b/docs/api/embedding/index.md new file mode 100644 index 0000000000..675593e183 --- /dev/null +++ b/docs/api/embedding/index.md @@ -0,0 +1,7 @@ +# Embedding + +This section contains the API reference for the `distilabel` embeddings. + +For more information on how the [`Embeddings`][distilabel.steps.tasks.Task] works and see some examples. + +::: distilabel.embeddings.base \ No newline at end of file diff --git a/docs/api/llm/anthropic.md b/docs/api/llm/anthropic.md deleted file mode 100644 index 400571c6ed..0000000000 --- a/docs/api/llm/anthropic.md +++ /dev/null @@ -1,3 +0,0 @@ -# AnthropicLLM - -::: distilabel.llms.anthropic diff --git a/docs/api/llm/anyscale.md b/docs/api/llm/anyscale.md deleted file mode 100644 index 90aa0cd6ea..0000000000 --- a/docs/api/llm/anyscale.md +++ /dev/null @@ -1,3 +0,0 @@ -# AnyscaleLLM - -::: distilabel.llms.anyscale diff --git a/docs/api/llm/azure.md b/docs/api/llm/azure.md deleted file mode 100644 index faa127d5bc..0000000000 --- a/docs/api/llm/azure.md +++ /dev/null @@ -1,3 +0,0 @@ -# AzureOpenAILLM - -::: distilabel.llms.azure diff --git a/docs/api/llm/cohere.md b/docs/api/llm/cohere.md deleted file mode 100644 index c7064b7a75..0000000000 --- a/docs/api/llm/cohere.md +++ /dev/null @@ -1,3 +0,0 @@ -# CohereLLM - -::: distilabel.llms.cohere diff --git a/docs/api/llm/groq.md b/docs/api/llm/groq.md deleted file mode 100644 index 0a5264a772..0000000000 --- a/docs/api/llm/groq.md +++ /dev/null @@ -1,3 +0,0 @@ -# GroqLLM - -::: distilabel.llms.groq diff --git a/docs/api/llm/huggingface.md b/docs/api/llm/huggingface.md deleted file mode 100644 index 30920255fe..0000000000 --- a/docs/api/llm/huggingface.md +++ /dev/null @@ -1,6 +0,0 @@ -# Hugging Face - -This section contains the reference for Hugging Face integrations: - -::: distilabel.llms.huggingface.inference_endpoints -::: distilabel.llms.huggingface.transformers diff --git a/docs/api/llm/litellm.md b/docs/api/llm/litellm.md deleted file mode 100644 index 90a4d2d631..0000000000 --- a/docs/api/llm/litellm.md +++ /dev/null @@ -1,3 +0,0 @@ -# LiteLLM - -::: distilabel.llms.litellm diff --git a/docs/api/llm/llamacpp.md b/docs/api/llm/llamacpp.md deleted file mode 100644 index 02598c1a64..0000000000 --- a/docs/api/llm/llamacpp.md +++ /dev/null @@ -1,3 +0,0 @@ -# LlamaCppLLM - -::: distilabel.llms.llamacpp diff --git a/docs/api/llm/llm_gallery.md b/docs/api/llm/llm_gallery.md new file mode 100644 index 0000000000..ad0b1b75f0 --- /dev/null +++ b/docs/api/llm/llm_gallery.md @@ -0,0 +1,10 @@ +# LLM Gallery + +This section contains the existing [`LLM`][distilabel.llms] subclasses implemented in `distilabel`. + +::: distilabel.llms + options: + filters: + - "!^LLM$" + - "!^AsyncLLM$" + - "!typing" \ No newline at end of file diff --git a/docs/api/llm/mistral.md b/docs/api/llm/mistral.md deleted file mode 100644 index 069488eadd..0000000000 --- a/docs/api/llm/mistral.md +++ /dev/null @@ -1,3 +0,0 @@ -# MistralLLM - -::: distilabel.llms.mistral diff --git a/docs/api/llm/ollama.md b/docs/api/llm/ollama.md deleted file mode 100644 index 25e4b662a1..0000000000 --- a/docs/api/llm/ollama.md +++ /dev/null @@ -1,3 +0,0 @@ -# OllamaLLM - -::: distilabel.llms.ollama diff --git a/docs/api/llm/openai.md b/docs/api/llm/openai.md deleted file mode 100644 index 381306ad59..0000000000 --- a/docs/api/llm/openai.md +++ /dev/null @@ -1,3 +0,0 @@ -# OpenAILLM - -::: distilabel.llms.openai diff --git a/docs/api/llm/together.md b/docs/api/llm/together.md deleted file mode 100644 index 6530165203..0000000000 --- a/docs/api/llm/together.md +++ /dev/null @@ -1,3 +0,0 @@ -# TogetherLLM - -::: distilabel.llms.together diff --git a/docs/api/llm/vertexai.md b/docs/api/llm/vertexai.md deleted file mode 100644 index f8990605d8..0000000000 --- a/docs/api/llm/vertexai.md +++ /dev/null @@ -1,3 +0,0 @@ -# VertexAILLM - -::: distilabel.llms.vertexai diff --git a/docs/api/llm/vllm.md b/docs/api/llm/vllm.md deleted file mode 100644 index 053b8535bb..0000000000 --- a/docs/api/llm/vllm.md +++ /dev/null @@ -1,3 +0,0 @@ -# vLLM - -::: distilabel.llms.vllm diff --git a/docs/api/step/typing.md b/docs/api/step/typing.md new file mode 100644 index 0000000000..1a86e7dac1 --- /dev/null +++ b/docs/api/step/typing.md @@ -0,0 +1,3 @@ +# Step Typing + +::: distilabel.steps.typing \ No newline at end of file diff --git a/docs/api/step_gallery/extra.md b/docs/api/step_gallery/extra.md index e310e45d4b..3d3e6f9c57 100644 --- a/docs/api/step_gallery/extra.md +++ b/docs/api/step_gallery/extra.md @@ -1,6 +1,11 @@ # Extra -::: distilabel.steps.generators.data -::: distilabel.steps.deita -::: distilabel.steps.formatting -::: distilabel.steps.typing +::: distilabel.steps + options: + filters: + - "!Argilla" + - "!Columns" + - "!From(Disk|FileSystem)" + - "!Hub" + - "![Ss]tep" + - "!typing" diff --git a/docs/api/step_gallery/hugging_face.md b/docs/api/step_gallery/hugging_face.md index 42fb85e795..c801aca86b 100644 --- a/docs/api/step_gallery/hugging_face.md +++ b/docs/api/step_gallery/hugging_face.md @@ -5,3 +5,4 @@ This section contains the existing steps integrated with `Hugging Face` so as to ::: distilabel.steps.LoadDataFromDisk ::: distilabel.steps.LoadDataFromFileSystem ::: distilabel.steps.LoadDataFromHub +::: distilabel.steps.PushToHub \ No newline at end of file diff --git a/docs/api/task_gallery/index.md b/docs/api/task/task_gallery.md similarity index 100% rename from docs/api/task_gallery/index.md rename to docs/api/task/task_gallery.md diff --git a/docs/assets/images/sections/community/compare-pull-request.PNG b/docs/assets/images/sections/community/compare-pull-request.PNG new file mode 100644 index 0000000000000000000000000000000000000000..ace5c010b93f39b884829c7e078ae3a0eb882a3d GIT binary patch literal 14343 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 literal 0 HcmV?d00001 diff --git a/docs/assets/images/sections/community/create-branch.PNG b/docs/assets/images/sections/community/create-branch.PNG new file mode 100644 index 0000000000000000000000000000000000000000..24dfc197550488543a45bf27ff9cd75cd43a819d GIT binary patch literal 4224 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 literal 0 HcmV?d00001 diff --git a/docs/index.md b/docs/index.md index 37cf6f9fdf..ce76c96956 100644 --- a/docs/index.md +++ b/docs/index.md @@ -38,21 +38,39 @@ hide: Distilabel is the framework for synthetic data and AI feedback for engineers who need fast, reliable and scalable pipelines based on verified research papers. -If you just want to get started, we recommend you check the [documentation](http://distilabel.argilla.io/). Curious, and want to know more? Keep reading! +

+ +- __Get started in 5 minutes!__ + + --- + + Install distilabel with `pip` and run your first `Pipeline` to generate and evaluate synthetic data. + + [:octicons-arrow-right-24: Quickstart](./sections/getting_started/quickstart.md) + +- __How-to guides__ + + --- + + Get familiar with the basics of distilabel. Learn how to define `steps`, `tasks` and `llms` and run your `Pipeline`. + + [:octicons-arrow-right-24: Learn more](./sections/how_to_guides/index.md) + +
## Why use distilabel? Distilabel can be used for generating synthetic data and AI feedback for a wide variety of projects including traditional predictive NLP (classification, extraction, etc.), or generative and large language model scenarios (instruction following, dialogue generation, judging etc.). Distilabel's programmatic approach allows you to build scalable pipelines for data generation and AI feedback. The goal of distilabel is to accelerate your AI development by quickly generating high-quality, diverse datasets based on verified research methodologies for generating and judging with AI feedback. -### Improve your AI output quality through data quality +

Improve your AI output quality through data quality

Compute is expensive and output quality is important. We help you **focus on data quality**, which tackles the root cause of both of these problems at once. Distilabel helps you to synthesize and judge data to let you spend your valuable time **achieving and keeping high-quality standards for your synthetic data**. -### Take control of your data and models +

Take control of your data and models

**Ownership of data for fine-tuning your own LLMs** is not easy but distilabel can help you to get started. We integrate **AI feedback from any LLM provider out there** using one unified API. -### Improve efficiency by quickly iterating on the right research and LLMs +

Improve efficiency by quickly iterating on the right data and models

Synthesize and judge data with **latest research papers** while ensuring **flexibility, scalability and fault tolerance**. So you can focus on improving your data and training your models. diff --git a/docs/sections/community/contributor.md b/docs/sections/community/contributor.md new file mode 100644 index 0000000000..bfc5f287c4 --- /dev/null +++ b/docs/sections/community/contributor.md @@ -0,0 +1,159 @@ +--- +description: This is a step-by-step guide to help you contribute to the distilabel project. We are excited to have you on board! 🚀 +hide: + - footer +--- + +Thank you for investing your time in contributing to the project! Any contribution you make will be reflected in the most recent version of distilabel 🤩. + +??? Question "New to contributing in general?" + If you're a new contributor, read the [README](https://github.com/argilla-io/distilabel/blob/develop/README.md) to get an overview of the project. In addition, here are some resources to help you get started with open-source contributions: + + * **Discord**: You are welcome to join the [distilabel Discord community](http://hf.co/join/discord), where you can keep in touch with other users, contributors and the distilabel team. In the following [section](#first-contact-in-discord), you can find more information on how to get started in Discord. + * **Git**: This is a very useful tool to keep track of the changes in your files. Using the command-line interface (CLI), you can make your contributions easily. For that, you need to have it [installed and updated](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) on your computer. + * **GitHub**: It is a platform and cloud-based service that uses git and allows developers to collaborate on projects. To contribute to distilabel, you'll need to create an account. Check the [Contributor Workflow with Git and Github](#contributor-workflow-with-git-and-github) for more info. + * **Developer Documentation**: To collaborate, you'll need to set up an efficient environment. Check the [Installation](../getting_started/installation.md) guide to know how to do it. + +## First Contact in Discord + +Discord is a handy tool for more casual conversations and to answer day-to-day questions. As part of Hugging Face, we have set up some distilabel channels on the server. Click [here](http://hf.co/join/discord) to join the Hugging Face Discord community effortlessly. + +When part of the Hugging Face Discord, you can select "Channels & roles" and select "Argilla" along with any of the other groups that are interesting to you. "Argilla" will cover anything about argilla and distilabel. You can join the following channels: + +* **#argilla-distilabel-announcements**: 📣 Stay up-to-date. +* **#argilla-distilabel-general**: 💬 For general discussions. +* **#argilla-distilabel-help**: 🙋‍♀️ Need assistance? We're always here to help. Select the appropriate label (argilla or distilabel) for your issue and post it. + +So now there is only one thing left to do: introduce yourself and talk to the community. You'll always be welcome! 🤗👋 + + +## Contributor Workflow with Git and GitHub + +If you're working with distilabel and suddenly a new idea comes to your mind or you find an issue that can be improved, it's time to actively participate and contribute to the project! + +### Report an issue + +If you spot a problem, [search if an issue already exists](https://github.com/argilla-io/distilabel/issues?q=is%3Aissue), you can use the `Label` filter. If that is the case, participate in the conversation. If it does not exist, create an issue by clicking on `New Issue`. This will show various templates; choose the one that best suits your issue. Once you choose one, you will need to fill it in following the guidelines. Try to be as clear as possible. In addition, you can assign yourself to the issue and add or choose the right labels. Finally, click on `Submit new issue`. + + +### Work with a fork + +#### Fork the distilabel repository + +After having reported the issue, you can start working on it. For that, you will need to create a fork of the project. To do that, click on the `Fork` button. Now, fill in the information. Remember to uncheck the `Copy develop branch only` if you are going to work in or from another branch (for instance, to fix documentation, the `main` branch is used). Then, click on `Create fork`. + +You will be redirected to your fork. You can see that you are in your fork because the name of the repository will be your `username/distilabel`, and it will indicate `forked from argilla-io/distilabel`. + + +#### Clone your forked repository + +In order to make the required adjustments, clone the forked repository to your local machine. Choose the destination folder and run the following command: + +```sh +git clone https://github.com/[your-github-username]/distilabel.git +cd distilabel +``` + +To keep your fork’s main/develop branch up to date with our repo, add it as an upstream remote branch. + +```sh +git remote add upstream https://github.com/argilla-io/distilabel.git +``` + + +### Create a new branch + +For each issue you're addressing, it's advisable to create a new branch. GitHub offers a straightforward method to streamline this process. + +> ⚠️ Never work directly on the `main` or `develop` branch. Always create a new branch for your changes. + +Navigate to your issue, and on the right column, select `Create a branch`. + +![Create a branch](../../assets/images/sections/community/create-branch.PNG) + +After the new window pops up, the branch will be named after the issue and include a prefix such as feature/, bug/, or docs/ to facilitate quick recognition of the issue type. In the `Repository destination`, pick your fork ( [your-github-username]/distilabel), and then select `Change branch source` to specify the source branch for creating the new one. Complete the process by clicking `Create branch`. + +> 🤔 Remember that the `main` branch is only used to work with the documentation. For any other changes, use the `develop` branch. + +Now, locally, change to the new branch you just created. + +```sh +git fetch origin +git checkout [branch-name] +``` + +### Make changes and push them + +Make the changes you want in your local repository, and test that everything works and you are following the guidelines. + +Once you have finished, you can check the status of your repository and synchronize with the upstreaming repo with the following command: + +```sh +# Check the status of your repository +git status + +# Synchronize with the upstreaming repo +git checkout [branch-name] +git rebase [default-branch] +``` + +If everything is right, we need to commit and push the changes to your fork. For that, run the following commands: + +```sh +# Add the changes to the staging area +git add filename + +# Commit the changes by writing a proper message +git commit -m "commit-message" + +# Push the changes to your fork +git push origin [branch-name] +``` + +When pushing, you will be asked to enter your GitHub login credentials. Once the push is complete, all local commits will be on your GitHub repository. + + +### Create a pull request + +Come back to GitHub, navigate to the original repository where you created your fork, and click on `Compare & pull request`. + +![compare-and-pr](../../assets/images/sections/community/compare-pull-request.PNG) + +First, click on `compare across forks` and select the right repositories and branches. + +> In the base repository, keep in mind that you should select either `main` or `develop` based on the modifications made. In the head repository, indicate your forked repository and the branch corresponding to the issue. + +Then, fill in the pull request template. You should add a prefix to the PR name, as we did with the branch above. If you are working on a new feature, you can name your PR as `feat: TITLE`. If your PR consists of a solution for a bug, you can name your PR as `bug: TITLE`. And, if your work is for improving the documentation, you can name your PR as `docs: TITLE`. + +In addition, on the right side, you can select a reviewer (for instance, if you discussed the issue with a member of the team) and assign the pull request to yourself. It is highly advisable to add labels to PR as well. You can do this again by the labels section right on the screen. For instance, if you are addressing a bug, add the `bug` label, or if the PR is related to the documentation, add the `documentation` label. This way, PRs can be easily filtered. + +Finally, fill in the template carefully and follow the guidelines. Remember to link the original issue and enable the checkbox to allow maintainer edits so the branch can be updated for a merge. Then, click on `Create pull request`. + + +### Review your pull request + +Once you submit your PR, a team member will review your proposal. We may ask questions, request additional information, or ask for changes to be made before a PR can be merged, either using [suggested changes](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/incorporating-feedback-in-your-pull-request) or pull request comments. + +You can apply the changes directly through the UI (check the files changed and click on the right-corner three dots; see image below) or from your fork, and then commit them to your branch. The PR will be updated automatically, and the suggestions will appear as `outdated`. + +![edit-file-from-UI](../../assets/images/sections/community/edit-file.PNG) + +> If you run into any merge issues, check out this [git tutorial](https://github.com/skills/resolve-merge-conflicts) to help you resolve merge conflicts and other issues. + + +### Your PR is merged! + +Congratulations 🎉🎊 We thank you 🤩 + +Once your PR is merged, your contributions will be publicly visible on the [distilabel GitHub](https://github.com/argilla-io/distilabel#contributors). + +Additionally, we will include your changes in the next release based on our [development branch](https://github.com/argilla-io/argilla/tree/develop). + +## Additional resources + +Here are some helpful resources for your reference. + +* [Configuring Discord](https://support.discord.com/hc/en-us/categories/115000217151), a guide to learning how to get started with Discord. +* [Pro Git](https://git-scm.com/book/en/v2), a book to learn Git. +* [Git in VSCode](https://code.visualstudio.com/docs/sourcecontrol/overview), a guide to learning how to easily use Git in VSCode. +* [GitHub Skills](https://skills.github.com/), an interactive course for learning GitHub. \ No newline at end of file diff --git a/docs/sections/getting_started/faq.md b/docs/sections/getting_started/faq.md index 27768a3c6f..16ad840757 100644 --- a/docs/sections/getting_started/faq.md +++ b/docs/sections/getting_started/faq.md @@ -7,20 +7,20 @@ hide: # Frequent Asked Questions (FAQ) ??? faq "How can I rename the columns in a batch?" - Every [`Step`][distilabel.steps.base.Step] has both `input_mappings` and `output_mappings` attributes, that can be used to rename the columns in each batch. + Every [`Step`][distilabel.steps.base.Step] has both `input_mappings` and `output_mappings` attributes that can be used to rename the columns in each batch. - But `input_mappings` will only map, meaning that if you have a batch with the column `A` and you want to rename to `B`, you should use `input_mappings={"A": "B"}`, but that will only be applied to that specific [`Step`][distilabel.steps.base.Step] meaning that the next step in the pipeline will still have the column `A` instead of `B`. + But `input_mappings` will only map, meaning that if you have a batch with the column `A` and you want to rename it to `B`, you should use `input_mappings={"A": "B"}`, but that will only be applied to that specific [`Step`][distilabel.steps.base.Step] meaning that the next step in the pipeline will still have the column `A` instead of `B`. While `output_mappings` will indeed apply the rename, meaning that if the [`Step`][distilabel.steps.base.Step] produces the column `A` and you want to rename to `B`, you should use `output_mappings={"A": "B"}`, and that will be applied to the next [`Step`][distilabel.steps.base.Step] in the pipeline. ??? faq "Will the API Keys be exposed when sharing the pipeline?" No, those will be masked out using `pydantic.SecretStr`, meaning that those won't be exposed when sharing the pipeline. - This also means that if you want to re-run your own pipeline and the API keys have not been provided via environment variable but either via attribute or runtime parameter, you will need to provide them again. + This also means that if you want to re-run your own pipeline and the API keys have not been provided via environment variable but either via an attribute or runtime parameter, you will need to provide them again. ??? faq "Does it work for Windows?" - Yes, but you may need to set the `multiprocessing` context in advance, to ensure that the `spawn` method is used, since the default method `fork` is not available on Windows. + Yes, but you may need to set the `multiprocessing` context in advance to ensure that the `spawn` method is used since the default method `fork` is not available on Windows. ```python import multiprocessing as mp @@ -29,16 +29,16 @@ hide: ``` ??? faq "Will the custom Steps / Tasks / LLMs be serialized too?" - No, at the moment only the references to the classes within the `distilabel` library will be serialized, meaning that if you define a custom class used within the pipeline, the serialization won't break, but the deserialize will fail since the class won't be available, unless used from the same file. + No, at the moment, only the references to the classes within the `distilabel` library will be serialized, meaning that if you define a custom class used within the pipeline, the serialization won't break, but the deserialize will fail since the class won't be available unless used from the same file. ??? faq "What happens if `Pipeline.run` fails? Do I lose all the data?" - No, indeed we're using a cache mechanism to store all the intermediate results in disk, so that if a [`Step`][distilabel.steps.base.Step] fails, the pipeline can be re-run from that point without losing the data, only if nothing is changed in the `Pipeline`. + No, indeed, we're using a cache mechanism to store all the intermediate results in the disk so, if a [`Step`][distilabel.steps.base.Step] fails; the pipeline can be re-run from that point without losing the data, only if nothing is changed in the `Pipeline`. All the data will be stored in `.cache/distilabel`, but the only data that will persist at the end of the `Pipeline.run` execution is the one from the leaf step/s, so bear that in mind. For more information on the caching mechanism in `distilabel`, you can check the [Learn - Advanced - Caching](../how_to_guides/advanced/caching.md) section. - Also note that when running a [`Step`][distilabel.steps.base.Step] or a [`Task`][distilabel.steps.tasks.Task] standalone, the cache mechanism won't be used, so if you want to use that, you should use the `Pipeline` context manager. + Also, note that when running a [`Step`][distilabel.steps.base.Step] or a [`Task`][distilabel.steps.tasks.Task] standalone, the cache mechanism won't be used, so if you want to use that, you should use the `Pipeline` context manager. ??? faq "How can I use the same `LLM` across several tasks without having to load it several times?" You can serve the LLM using a solution like TGI or vLLM, and then connect to it using an `AsyncLLM` client like `InferenceEndpointsLLM` or `OpenAILLM`. Please refer to [Serving LLMs guide](../how_to_guides/advanced/serving_an_llm_for_reuse.md) for more information. diff --git a/docs/sections/getting_started/installation.md b/docs/sections/getting_started/installation.md index 804aa8de7e..a169241726 100644 --- a/docs/sections/getting_started/installation.md +++ b/docs/sections/getting_started/installation.md @@ -6,9 +6,6 @@ hide: # Installation -!!! NOTE - Since `distilabel` v1.0.0 was recently released, we refactored most of the stuff, so the installation below only applies to `distilabel` v1.0.0 and above. - You will need to have at least Python 3.9 or higher, up to Python 3.12, since support for the latter is still a work in progress. To install the latest release of the package from PyPI you can use the following command: @@ -42,6 +39,8 @@ Additionally, as part of `distilabel` some extra dependencies are available, mai - `hf-transformers`: for using models available in [transformers](https://github.com/huggingface/transformers) package via the `TransformersLLM` integration. +- `instructor`: for using structured generation of LLMs with [Instructor](https://github.com/jxnl/instructor/). + - `litellm`: for using [`LiteLLM`](https://github.com/BerriAI/litellm) to call any LLM using OpenAI format via the `LiteLLM` integration. - `llama-cpp`: for using [llama-cpp-python](https://github.com/abetlen/llama-cpp-python) Python bindings for `llama.cpp` via the `LlamaCppLLM` integration. @@ -52,15 +51,23 @@ Additionally, as part of `distilabel` some extra dependencies are available, mai - `openai`: for using [OpenAI API](https://openai.com/blog/openai-api) models via the `OpenAILLM` integration, or the rest of the integrations based on OpenAI and relying on its client as `AnyscaleLLM`, `AzureOpenAILLM`, and `TogetherLLM`. +- `outlines`: for using structured generation of LLMs with [outlines](https://github.com/outlines-dev/outlines). + +- `ray`: for scaling and distributing a pipeline with [Ray](https://github.com/ray-project/ray). + - `vertexai`: for using [Google Vertex AI](https://cloud.google.com/vertex-ai) proprietary models via the `VertexAILLM` integration. - `vllm`: for using [vllm](https://github.com/vllm-project/vllm) serving engine via the `vLLM` integration. +- `sentence-transformers`: for generating sentence embeddings using [sentence-transformers](https://github.com/UKPLab/sentence-transformers). + +- `faiss-cpu` and `faiss-gpu`: for generating sentence embeddings using [faiss](https://github.com/facebookresearch/faiss). + ## Recommendations / Notes The [`mistralai`](https://github.com/mistralai/client-python) dependency requires Python 3.9 or higher, so if you're willing to use the `distilabel.llms.MistralLLM` implementation, you will need to have Python 3.9 or higher. -In some cases like [`transformers`](https://github.com/huggingface/transformers) and [`vllm`](https://github.com/vllm-project/vllm) the installation of [`flash-attn`](https://github.com/Dao-AILab/flash-attention) is recommended if you are using a GPU accelerator, since it will speed up the inference process, but the installation needs to be done separately, as it's not included in the `distilabel` dependencies. +In some cases like [`transformers`](https://github.com/huggingface/transformers) and [`vllm`](https://github.com/vllm-project/vllm), the installation of [`flash-attn`](https://github.com/Dao-AILab/flash-attention) is recommended if you are using a GPU accelerator since it will speed up the inference process, but the installation needs to be done separately, as it's not included in the `distilabel` dependencies. ```sh pip install flash-attn --no-build-isolation diff --git a/docs/sections/getting_started/quickstart.md b/docs/sections/getting_started/quickstart.md index 4fd7de607b..bf3780739c 100644 --- a/docs/sections/getting_started/quickstart.md +++ b/docs/sections/getting_started/quickstart.md @@ -4,14 +4,20 @@ hide: - toc --- +
+ Open In Colab + + # Quickstart To start off, `distilabel` is a framework for building pipelines for generating synthetic data using LLMs, that defines a [`Pipeline`][distilabel.pipeline.Pipeline] which orchestrates the execution of the [`Step`][distilabel.steps.base.Step] subclasses, and those will be connected as nodes in a Direct Acyclic Graph (DAG). -That being said, in this guide we will walk you through the process of creating a simple pipeline that uses the [`OpenAILLM`][distilabel.llms.OpenAILLM] class to generate text. The [`Pipeline`][distilabel.pipeline.Pipeline] will load a dataset that contains a column named `prompt` from the Hugging Face Hub via the step [`LoadDataFromHub`][distilabel.steps.LoadDataFromHub] and then use the [`OpenAILLM`][distilabel.llms.OpenAILLM] class to generate text based on the dataset using the [`TextGeneration`][distilabel.steps.tasks.TextGeneration] task. +In this guide we will walk you through the process of creating a simple pipeline that uses the [`InferenceEndpointsLLM`][distilabel.llms.InferenceEndpointsLLM] class to generate text. The [`Pipeline`][distilabel.pipeline.Pipeline] will load a dataset that contains a column named `prompt` from the Hugging Face Hub via the step [`LoadDataFromHub`][distilabel.steps.LoadDataFromHub] and then use the [`InferenceEndpointsLLM`][distilabel.llms.InferenceEndpointsLLM] class to generate text based on the dataset using the [`TextGeneration`][distilabel.steps.tasks.TextGeneration] task. + +> You can check the available models in the [Hugging Face Model Hub](https://huggingface.co/models?pipeline_tag=text-generation&sort=trending) and filter by `Inference status`. ```python -from distilabel.llms import OpenAILLM +from distilabel.llms import InferenceEndpointsLLM from distilabel.pipeline import Pipeline from distilabel.steps import LoadDataFromHub from distilabel.steps.tasks import TextGeneration @@ -21,13 +27,14 @@ with Pipeline( # (1) description="A simple text generation pipeline", ) as pipeline: # (2) load_dataset = LoadDataFromHub( # (3) - name="load_dataset", output_mappings={"prompt": "instruction"}, ) text_generation = TextGeneration( # (4) - name="text_generation", - llm=OpenAILLM(model="gpt-3.5-turbo"), # (5) + llm=InferenceEndpointsLLM( + model_id="meta-llama/Meta-Llama-3.1-8B-Instruct", + tokenizer_id="meta-llama/Meta-Llama-3.1-8B-Instruct", + ), # (5) ) load_dataset >> text_generation # (6) @@ -56,11 +63,11 @@ if __name__ == "__main__": 2. We are using the [`Pipeline`][distilabel.pipeline.Pipeline] context manager, meaning that every [`Step`][distilabel.steps.base.Step] subclass that is defined within the context manager will be added to the pipeline automatically. -3. We define a [`LoadDataFromHub`][distilabel.steps.LoadDataFromHub] step named `load_dataset` that will load a dataset from the Hugging Face Hub, as provided via runtime parameters in the `pipeline.run` method below, but it can also be defined within the class instance via the arg `repo_id=...`. This step will basically produce output batches with the rows from the dataset, and the column `prompt` will be mapped to the `instruction` field. +3. We define a [`LoadDataFromHub`][distilabel.steps.LoadDataFromHub] step named `load_dataset` that will load a dataset from the Hugging Face Hub, as provided via runtime parameters in the `pipeline.run` method below, but it can also be defined within the class instance via the arg `repo_id=...`. This step will produce output batches with the rows from the dataset, and the column `prompt` will be mapped to the `instruction` field. -4. We define a [`TextGeneration`][distilabel.steps.tasks.TextGeneration] task named `text_generation` that will generate text based on the `instruction` field from the dataset. This task will use the [`OpenAILLM`][distilabel.llms.OpenAILLM] class with the model `gpt-3.5-turbo`. +4. We define a [`TextGeneration`][distilabel.steps.tasks.TextGeneration] task named `text_generation` that will generate text based on the `instruction` field from the dataset. This task will use the [`InferenceEndpointsLLM`][distilabel.llms.InferenceEndpointsLLM] class with the model `Meta-Llama-3.1-8B-Instruct`. -5. We define the [`OpenAILLM`][distilabel.llms.OpenAILLM] class with the model `gpt-3.5-turbo` that will be used by the [`TextGeneration`][distilabel.steps.tasks.TextGeneration] task. In this case, since the [`OpenAILLM`][distilabel.llms.OpenAILLM] is used, we assume that the `OPENAI_API_KEY` environment variable is set, and the OpenAI API will be used to generate the text. +5. We define the [`InferenceEndpointsLLM`][distilabel.llms.InferenceEndpointsLLM] class with the model `Meta-Llama-3.1-8B-Instruct` that will be used by the [`TextGeneration`][distilabel.steps.tasks.TextGeneration] task. In this case, since the [`InferenceEndpointsLLM`][distilabel.llms.InferenceEndpointsLLM] is used, we assume that the `HF_TOKEN` environment variable is set. 6. We connect the `load_dataset` step to the `text_generation` task using the `rshift` operator, meaning that the output from the `load_dataset` step will be used as input for the `text_generation` task. diff --git a/docs/sections/pipeline_samples/examples/benchmarking_with_distilabel.md b/docs/sections/pipeline_samples/examples/benchmarking_with_distilabel.md index f1f18b415f..6d5e594aa8 100644 --- a/docs/sections/pipeline_samples/examples/benchmarking_with_distilabel.md +++ b/docs/sections/pipeline_samples/examples/benchmarking_with_distilabel.md @@ -1,7 +1,7 @@ --- hide: toc --- -# [Benchmarking with `distilabel`: Arena Hard](#benchmarking-with-distilabel-arena-hard) +# Benchmarking with `distilabel`: Arena Hard Benchmark LLMs with `distilabel`: reproducing the Arena Hard benchmark. diff --git a/docs/sections/pipeline_samples/examples/llama_cpp_with_outlines.md b/docs/sections/pipeline_samples/examples/llama_cpp_with_outlines.md index 38ac6bb6fe..bbd9c97ef0 100644 --- a/docs/sections/pipeline_samples/examples/llama_cpp_with_outlines.md +++ b/docs/sections/pipeline_samples/examples/llama_cpp_with_outlines.md @@ -1,7 +1,7 @@ --- hide: toc --- -# [llama.cpp with `outlines`](#llamacpp-with-outlines) +# llama.cpp with `outlines` Generate RPG characters following a `pydantic.BaseModel` with `outlines` in `distilabel`. diff --git a/docs/sections/pipeline_samples/examples/mistralai_with_instructor.md b/docs/sections/pipeline_samples/examples/mistralai_with_instructor.md index 3b39d51e31..9d862a0056 100644 --- a/docs/sections/pipeline_samples/examples/mistralai_with_instructor.md +++ b/docs/sections/pipeline_samples/examples/mistralai_with_instructor.md @@ -1,7 +1,7 @@ --- hide: toc --- -# [MistralAI with `instructor`](#mistralai-with-instructor) +# MistralAI with `instructor` Answer instructions with knowledge graphs defined as `pydantic.BaseModel` objects using `instructor` in `distilabel`. diff --git a/docs/sections/pipeline_samples/index.md b/docs/sections/pipeline_samples/index.md index 800d4342ba..d983f16822 100644 --- a/docs/sections/pipeline_samples/index.md +++ b/docs/sections/pipeline_samples/index.md @@ -95,7 +95,7 @@ hide: toc Learn about reproducing the Arena Hard benchmark with disitlabel. - [:octicons-arrow-right-24: Example](benchmarking_with_distilabel.md) + [:octicons-arrow-right-24: Example](examples/benchmarking_with_distilabel.md) - __llama.cpp with outlines__ @@ -103,7 +103,7 @@ hide: toc Learn about generating RPG characters following a pydantic.BaseModel with outlines in distilabel. - [:octicons-arrow-right-24: Example](llama_cpp_with_outlines.md) + [:octicons-arrow-right-24: Example](examples/llama_cpp_with_outlines.md) - __MistralAI with instructor__ @@ -111,7 +111,7 @@ hide: toc Learn about answering instructions with knowledge graphs defined as pydantic.BaseModel objects using instructor in distilabel. - [:octicons-arrow-right-24: Example](papers/prometheus.md) + [:octicons-arrow-right-24: Example](examples/mistralai_with_instructor.md) diff --git a/mkdocs.yml b/mkdocs.yml index 6c1bb97594..b247cfa138 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -136,19 +136,29 @@ plugins: - mkdocstrings: handlers: python: - selection: - inherited_members: true # Allow looking up inherited methods + setup_commands: + - import sys; sys.path.insert(0, 'src') # API references are built from source options: - show_protected_members: true - show_private_members: true - rendering: - show_root_heading: true # actually display anything at all... - # show_root_full_path: true # display "diffrax.asdf" not just "asdf" - show_if_no_docstring: true - show_signature_annotations: true - show_source: false # don't include source code + show_inheritance_diagram: false + show_source: true # include source code + # Headings + heading_level: 3 + show_root_heading: true # show the python path of the class + show_root_toc_entry: true # show the toc entry for the root class + show_root_full_path: false # display "diffrax.asdf" not just "asdf" + show_object_full_path: false # display "diffrax.asdf" not just "asdf" + show_symbol_type_heading: true + show_symbol_type_toc: true + # Members + inherited_members: false # allow looking up inherited methods members_order: source # order methods according to their order of definition in the source code, not alphabetical order - heading_level: 4 + show_labels : true + # Docstring + docstring_style: google # more info: https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html + show_if_no_docstring: false + # Signature + separate_signature: false + show_signature_annotations: false - social - mknotebooks - material-plausible @@ -213,28 +223,18 @@ nav: - Hugging Face: "api/step_gallery/hugging_face.md" - Columns: "api/step_gallery/columns.md" - Extra: "api/step_gallery/extra.md" + - Typing: "api/step/typing.md" - Task: - "api/task/index.md" - GeneratorTask: "api/task/generator_task.md" - - Task Gallery: "api/task_gallery/index.md" + - Task Gallery: "api/task/task_gallery.md" - Typing: "api/task/typing.md" - LLM: - "api/llm/index.md" - - LLM Gallery: - - Anthropic: "api/llm/anthropic.md" - - Anyscale: "api/llm/anyscale.md" - - Azure (via OpenAI): "api/llm/azure.md" - - Cohere: "api/llm/cohere.md" - - Groq: "api/llm/groq.md" - - Hugging Face: "api/llm/huggingface.md" - - LiteLLM: "api/llm/litellm.md" - - llama.cpp: "api/llm/llamacpp.md" - - Mistral: "api/llm/mistral.md" - - Ollama: "api/llm/ollama.md" - - OpenAI: "api/llm/openai.md" - - Together AI: "api/llm/together.md" - - Google Vertex AI: "api/llm/vertexai.md" - - vLLM: "api/llm/vllm.md" + - LLM Gallery: "api/llm/llm_gallery.md" + - Embedding: + - "api/embedding/index.md" + - Embedding Gallery: "api/embedding/embedding_gallery.md" - Pipeline: - "api/pipeline/index.md" - Routing Batch Function: "api/pipeline/routing_batch_function.md" @@ -248,4 +248,5 @@ nav: - CLI: "api/cli.md" - Community: - sections/community/index.md + - How to contribute?: sections/community/contributor.md - Issue dashboard: sections/community/popular_issues.md diff --git a/pyproject.toml b/pyproject.toml index 12eddf591e..d5f4e795ed 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -94,7 +94,7 @@ vllm = [ ] sentence-transformers = ["sentence-transformers >= 3.0.0"] faiss-cpu = ["faiss-cpu >= 1.8.0"] -faiss-gpu = ["faiss-cpu >= 1.7.2"] +faiss-gpu = ["faiss-gpu >= 1.7.2"] # minhash minhash = ["datasketch >= 1.6.5", "nltk>3.8.1"] diff --git a/src/distilabel/distiset.py b/src/distilabel/distiset.py index 2263bc1553..b6b31f7a5a 100644 --- a/src/distilabel/distiset.py +++ b/src/distilabel/distiset.py @@ -379,17 +379,17 @@ def save_to_disk( Examples: ```python # Save your distiset in a local folder: - >>> distiset.save_to_disk(distiset_path="my-distiset") + distiset.save_to_disk(distiset_path="my-distiset") # Save your distiset in a remote storage: - >>> storage_options = { - ... "key": os.environ["S3_ACCESS_KEY"], - ... "secret": os.environ["S3_SECRET_KEY"], - ... "client_kwargs": { - ... "endpoint_url": os.environ["S3_ENDPOINT_URL"], - ... "region_name": os.environ["S3_REGION"], - ... }, - ... } - >>> distiset.save_to_disk(distiset_path="my-distiset", storage_options=storage_options) + storage_options = { + "key": os.environ["S3_ACCESS_KEY"], + "secret": os.environ["S3_SECRET_KEY"], + "client_kwargs": { + "endpoint_url": os.environ["S3_ENDPOINT_URL"], + "region_name": os.environ["S3_REGION"], + }, + } + distiset.save_to_disk(distiset_path="my-distiset", storage_options=storage_options) ``` """ distiset_path = str(distiset_path) @@ -606,10 +606,9 @@ def create_distiset( # noqa: C901 correspond to different configurations of the dataset. Examples: - ```python - >>> from pathlib import Path - >>> distiset = create_distiset(Path.home() / ".cache/distilabel/pipelines/path-to-pipe-hashname") + from pathlib import Path + distiset = create_distiset(Path.home() / ".cache/distilabel/pipelines/path-to-pipe-hashname") ``` """ from distilabel.constants import DISTILABEL_METADATA_KEY diff --git a/src/distilabel/embeddings/sentence_transformers.py b/src/distilabel/embeddings/sentence_transformers.py index 08b3465ad1..85baea3de9 100644 --- a/src/distilabel/embeddings/sentence_transformers.py +++ b/src/distilabel/embeddings/sentence_transformers.py @@ -55,7 +55,6 @@ class SentenceTransformerEmbeddings(Embeddings, CudaDevicePlacementMixin): of 1. Defaults to `None`. Examples: - Generating sentence embeddings: ```python diff --git a/src/distilabel/embeddings/vllm.py b/src/distilabel/embeddings/vllm.py index 8e0c7caed2..cbbadd69af 100644 --- a/src/distilabel/embeddings/vllm.py +++ b/src/distilabel/embeddings/vllm.py @@ -46,7 +46,6 @@ class vLLMEmbeddings(Embeddings, CudaDevicePlacementMixin): - [Offline inference embeddings](https://docs.vllm.ai/en/latest/getting_started/examples/offline_inference_embedding.html) Examples: - Generating sentence embeddings: ```python diff --git a/src/distilabel/errors.py b/src/distilabel/errors.py index 4a1bad0cf6..41660a6373 100644 --- a/src/distilabel/errors.py +++ b/src/distilabel/errors.py @@ -28,7 +28,6 @@ class DistilabelError: page: An optional error code from PydanticErrorCodes enum. Examples: - ```python raise DistilabelUserError("This is an error message.") This is an error message. diff --git a/src/distilabel/llms/anthropic.py b/src/distilabel/llms/anthropic.py index 843b14b21f..05e01c2334 100644 --- a/src/distilabel/llms/anthropic.py +++ b/src/distilabel/llms/anthropic.py @@ -75,7 +75,6 @@ class AnthropicLLM(AsyncLLM): Defaults to `6`. Examples: - Generate text: ```python diff --git a/src/distilabel/llms/anyscale.py b/src/distilabel/llms/anyscale.py index 54b777b8a8..d7db410606 100644 --- a/src/distilabel/llms/anyscale.py +++ b/src/distilabel/llms/anyscale.py @@ -40,7 +40,6 @@ class AnyscaleLLM(OpenAILLM): It is meant to be used internally. Examples: - Generate text: ```python diff --git a/src/distilabel/llms/azure.py b/src/distilabel/llms/azure.py index 80c0807572..8d83a82516 100644 --- a/src/distilabel/llms/azure.py +++ b/src/distilabel/llms/azure.py @@ -48,7 +48,6 @@ class AzureOpenAILLM(OpenAILLM): `:material-microsoft-azure:` Examples: - Generate text: ```python diff --git a/src/distilabel/llms/cohere.py b/src/distilabel/llms/cohere.py index a1295a9ba8..e28f62fed3 100644 --- a/src/distilabel/llms/cohere.py +++ b/src/distilabel/llms/cohere.py @@ -70,7 +70,6 @@ class CohereLLM(AsyncLLM): `"distilabel"`. Examples: - Generate text: ```python diff --git a/src/distilabel/llms/groq.py b/src/distilabel/llms/groq.py index 3a362951ec..009f0a07eb 100644 --- a/src/distilabel/llms/groq.py +++ b/src/distilabel/llms/groq.py @@ -63,7 +63,6 @@ class GroqLLM(AsyncLLM): to `120`. Examples: - Generate text: ```python diff --git a/src/distilabel/llms/huggingface/inference_endpoints.py b/src/distilabel/llms/huggingface/inference_endpoints.py index edfc508247..5e82947844 100644 --- a/src/distilabel/llms/huggingface/inference_endpoints.py +++ b/src/distilabel/llms/huggingface/inference_endpoints.py @@ -74,7 +74,6 @@ class InferenceEndpointsLLM(AsyncLLM, MagpieChatTemplateMixin): `:hugging:` Examples: - Free serverless Inference API: ```python diff --git a/src/distilabel/llms/huggingface/transformers.py b/src/distilabel/llms/huggingface/transformers.py index d3d16f6ac9..e939ddfca4 100644 --- a/src/distilabel/llms/huggingface/transformers.py +++ b/src/distilabel/llms/huggingface/transformers.py @@ -76,7 +76,6 @@ class TransformersLLM(LLM, MagpieChatTemplateMixin, CudaDevicePlacementMixin): `:hugging:` Examples: - Generate text: ```python diff --git a/src/distilabel/llms/litellm.py b/src/distilabel/llms/litellm.py index 71a73365bb..43c5975e9d 100644 --- a/src/distilabel/llms/litellm.py +++ b/src/distilabel/llms/litellm.py @@ -41,7 +41,6 @@ class LiteLLM(AsyncLLM): - `verbose`: whether to log the LiteLLM client's logs. Defaults to `False`. Examples: - Generate text: ```python diff --git a/src/distilabel/llms/llamacpp.py b/src/distilabel/llms/llamacpp.py index f66eb214b0..5b310d2b6a 100644 --- a/src/distilabel/llms/llamacpp.py +++ b/src/distilabel/llms/llamacpp.py @@ -59,7 +59,6 @@ class LlamaCppLLM(LLM): - [`llama-cpp-python`](https://github.com/abetlen/llama-cpp-python) Examples: - Generate text: ```python diff --git a/src/distilabel/llms/mistral.py b/src/distilabel/llms/mistral.py index 73b5fc13a9..d1457209e9 100644 --- a/src/distilabel/llms/mistral.py +++ b/src/distilabel/llms/mistral.py @@ -62,7 +62,6 @@ class MistralLLM(AsyncLLM): Defaults to `64`. Examples: - Generate text: ```python diff --git a/src/distilabel/llms/moa.py b/src/distilabel/llms/moa.py index d139da87e3..be32199adc 100644 --- a/src/distilabel/llms/moa.py +++ b/src/distilabel/llms/moa.py @@ -61,7 +61,6 @@ class MixtureOfAgentsLLM(AsyncLLM): - [Mixture-of-Agents Enhances Large Language Model Capabilities](https://arxiv.org/abs/2406.04692) Examples: - Generate text: ```python diff --git a/src/distilabel/llms/ollama.py b/src/distilabel/llms/ollama.py index bd664b30db..fc3abd605b 100644 --- a/src/distilabel/llms/ollama.py +++ b/src/distilabel/llms/ollama.py @@ -79,6 +79,20 @@ class OllamaLLM(AsyncLLM): Runtime parameters: - `host`: the Ollama server host. - `timeout`: the client timeout for the Ollama API. Defaults to `120`. + + Examples: + Generate text: + + ```python + from distilabel.llms import OllamaLLM + + llm = OllamaLLM(model="llama3") + + llm.load() + + # Call the model + output = llm.generate(inputs=[[{"role": "user", "content": "Hello world!"}]]) + ``` """ model: str diff --git a/src/distilabel/llms/openai.py b/src/distilabel/llms/openai.py index 39644e2812..91a3c165c9 100644 --- a/src/distilabel/llms/openai.py +++ b/src/distilabel/llms/openai.py @@ -62,7 +62,6 @@ class OpenAILLM(AsyncLLM): `:simple-openai:` Examples: - Generate text: ```python diff --git a/src/distilabel/llms/together.py b/src/distilabel/llms/together.py index aa63ae1ad5..d4ba0eb473 100644 --- a/src/distilabel/llms/together.py +++ b/src/distilabel/llms/together.py @@ -39,7 +39,6 @@ class TogetherLLM(OpenAILLM): is meant to be used internally. Examples: - Generate text: ```python diff --git a/src/distilabel/llms/vertexai.py b/src/distilabel/llms/vertexai.py index f89a7b0912..0c49fa3931 100644 --- a/src/distilabel/llms/vertexai.py +++ b/src/distilabel/llms/vertexai.py @@ -43,6 +43,20 @@ class VertexAILLM(AsyncLLM): Icon: `:simple-googlecloud:` + + Examples: + Generate text: + + ```python + from distilabel.llms import VertexAILLM + + llm = VertexAILLM(model="gemini-1.5-pro") + + llm.load() + + # Call the model + output = llm.generate(inputs=[[{"role": "user", "content": "Hello world!"}]]) + ``` """ model: str diff --git a/src/distilabel/llms/vllm.py b/src/distilabel/llms/vllm.py index b45e72b93f..1ba6ab3700 100644 --- a/src/distilabel/llms/vllm.py +++ b/src/distilabel/llms/vllm.py @@ -92,7 +92,6 @@ class vLLM(LLM, MagpieChatTemplateMixin, CudaDevicePlacementMixin): the `LLM` class of `vllm` library. Examples: - Generate text: ```python @@ -415,7 +414,6 @@ class ClientvLLM(OpenAILLM, MagpieChatTemplateMixin): created to comunicate with the `vLLM` server. Defaults to `None`. Examples: - Generate text: ```python diff --git a/src/distilabel/steps/argilla/preference.py b/src/distilabel/steps/argilla/preference.py index 0843f2f55a..210cca208f 100644 --- a/src/distilabel/steps/argilla/preference.py +++ b/src/distilabel/steps/argilla/preference.py @@ -71,7 +71,6 @@ class PreferenceToArgilla(ArgillaBase): generated rationales won't be pushed to Argilla. Examples: - Push a preference dataset to an Argilla instance: ```python diff --git a/src/distilabel/steps/argilla/text_generation.py b/src/distilabel/steps/argilla/text_generation.py index 5298412843..ad5323b0bc 100644 --- a/src/distilabel/steps/argilla/text_generation.py +++ b/src/distilabel/steps/argilla/text_generation.py @@ -61,7 +61,6 @@ class TextGenerationToArgilla(ArgillaBase): - generation (`str` or `List[str]`): The completions that were generated based on the input instruction. Examples: - Push a text generation dataset to an Argilla instance: ```python diff --git a/src/distilabel/steps/columns/expand.py b/src/distilabel/steps/columns/expand.py index 84cec77121..bb7d2fe9f4 100644 --- a/src/distilabel/steps/columns/expand.py +++ b/src/distilabel/steps/columns/expand.py @@ -43,7 +43,6 @@ class ExpandColumns(Step): - dynamic (determined by `columns` attribute): The expanded columns. Examples: - Expand the selected columns into multiple rows: ```python diff --git a/src/distilabel/steps/columns/group.py b/src/distilabel/steps/columns/group.py index 87afe174d2..852b1c5205 100644 --- a/src/distilabel/steps/columns/group.py +++ b/src/distilabel/steps/columns/group.py @@ -44,7 +44,6 @@ class GroupColumns(Step): that were grouped. Examples: - Combine columns of a dataset: ```python @@ -125,6 +124,8 @@ def process(self, *inputs: StepInput) -> "StepOutput": class CombineColumns(GroupColumns): + """`CombineColumns` is deprecated and will be removed in version 1.5.0, use `GroupColumns` instead.""" + def __init__(self, **data: Any) -> None: warnings.warn( "`CombineColumns` is deprecated and will be removed in version 1.5.0, use `GroupColumns` instead.", diff --git a/src/distilabel/steps/columns/keep.py b/src/distilabel/steps/columns/keep.py index 84f889072a..88ae7d5409 100644 --- a/src/distilabel/steps/columns/keep.py +++ b/src/distilabel/steps/columns/keep.py @@ -45,7 +45,6 @@ class KeepColumns(Step): - dynamic (determined by `columns` attribute): The columns that were kept. Examples: - Select the columns to keep: ```python diff --git a/src/distilabel/steps/columns/merge.py b/src/distilabel/steps/columns/merge.py index 3b9f295c93..802b17a7df 100644 --- a/src/distilabel/steps/columns/merge.py +++ b/src/distilabel/steps/columns/merge.py @@ -48,7 +48,6 @@ class MergeColumns(Step): that were merged. Examples: - Combine columns in rows of a dataset: ```python diff --git a/src/distilabel/steps/deita.py b/src/distilabel/steps/deita.py index 5d98355c53..f817a4c263 100644 --- a/src/distilabel/steps/deita.py +++ b/src/distilabel/steps/deita.py @@ -66,7 +66,6 @@ class DeitaFiltering(GlobalStep): - [`What Makes Good Data for Alignment? A Comprehensive Study of Automatic Data Selection in Instruction Tuning`](https://arxiv.org/abs/2312.15685) Examples: - Filter the dataset based on the DEITA score and the cosine distance between the embeddings: ```python @@ -102,7 +101,6 @@ class DeitaFiltering(GlobalStep): ``` Citations: - ``` @misc{liu2024makesgooddataalignment, title={What Makes Good Data for Alignment? A Comprehensive Study of Automatic Data Selection in Instruction Tuning}, diff --git a/src/distilabel/steps/embeddings/embedding_generation.py b/src/distilabel/steps/embeddings/embedding_generation.py index d97e1da03a..30dff63ef7 100644 --- a/src/distilabel/steps/embeddings/embedding_generation.py +++ b/src/distilabel/steps/embeddings/embedding_generation.py @@ -37,7 +37,6 @@ class EmbeddingGeneration(Step): - embedding (`List[Union[float, int]]`): the generated sentence embedding. Examples: - Generate sentence embeddings with Sentence Transformers: ```python diff --git a/src/distilabel/steps/embeddings/nearest_neighbour.py b/src/distilabel/steps/embeddings/nearest_neighbour.py index 6d548bc948..adf3fe6c04 100644 --- a/src/distilabel/steps/embeddings/nearest_neighbour.py +++ b/src/distilabel/steps/embeddings/nearest_neighbour.py @@ -77,7 +77,6 @@ class FaissNearestNeighbour(GlobalStep): - [`The Faiss library`](https://arxiv.org/abs/2401.08281) Examples: - Generating embeddings and getting the nearest neighbours: ```python @@ -111,7 +110,6 @@ class FaissNearestNeighbour(GlobalStep): ``` Citations: - ``` @misc{douze2024faisslibrary, title={The Faiss library}, diff --git a/src/distilabel/steps/formatting/conversation.py b/src/distilabel/steps/formatting/conversation.py index 95c2369ad5..29381521bd 100644 --- a/src/distilabel/steps/formatting/conversation.py +++ b/src/distilabel/steps/formatting/conversation.py @@ -36,7 +36,6 @@ class ConversationTemplate(Step): - template Examples: - Create a conversation from an instruction and a response: ```python diff --git a/src/distilabel/steps/formatting/dpo.py b/src/distilabel/steps/formatting/dpo.py index 3c0e7355db..72253eb194 100644 --- a/src/distilabel/steps/formatting/dpo.py +++ b/src/distilabel/steps/formatting/dpo.py @@ -65,7 +65,6 @@ class FormatTextGenerationDPO(Step): - generations Examples: - Format your dataset for DPO fine tuning: ```python @@ -197,12 +196,11 @@ def process(self, *inputs: StepInput) -> "StepOutput": # type: ignore class FormatChatGenerationDPO(Step): - """Format the output of a combination of a `ChatGeneration` + a preference task such as - `UltraFeedback`, for Direct Preference Optimization (DPO) following the standard formatting - from frameworks such as `axolotl` or `alignment-handbook`. + """Format the output of a combination of a `ChatGeneration` + a preference task for Direct Preference Optimization (DPO). `FormatChatGenerationDPO` is a `Step` that formats the output of the combination of a `ChatGeneration` - task with a preference `Task` i.e. a task generating `ratings`, so that those are used to rank the + task with a preference `Task` i.e. a task generating `ratings` such as `UltraFeedback` following the standard + formatting from frameworks such as `axolotl` or `alignment-handbook`., so that those are used to rank the existing generations and provide the `chosen` and `rejected` generations based on the `ratings`. Note: @@ -239,7 +237,6 @@ class FormatChatGenerationDPO(Step): - generations Examples: - Format your dataset for DPO fine tuning: ```python diff --git a/src/distilabel/steps/formatting/sft.py b/src/distilabel/steps/formatting/sft.py index 838512f85f..2793b212e6 100644 --- a/src/distilabel/steps/formatting/sft.py +++ b/src/distilabel/steps/formatting/sft.py @@ -50,7 +50,6 @@ class FormatTextGenerationSFT(Step): - generation Examples: - Format your dataset for SFT fine tuning: ```python @@ -143,8 +142,7 @@ def process(self, *inputs: StepInput) -> "StepOutput": # type: ignore class FormatChatGenerationSFT(Step): - """Format the output of a `ChatGeneration` task for Supervised Fine-Tuning (SFT) following the - standard formatting from frameworks such as `axolotl` or `alignment-handbook`. + """Format the output of a `ChatGeneration` task for Supervised Fine-Tuning (SFT). `FormatChatGenerationSFT` is a `Step` that formats the output of a `ChatGeneration` task for Supervised Fine-Tuning (SFT) following the standard formatting from frameworks such as `axolotl` @@ -172,8 +170,7 @@ class FormatChatGenerationSFT(Step): - generation Examples: - - Format your dataset for Supervised Fine Tuning (SFT): + Format your dataset for SFT: ```python from distilabel.steps import FormatChatGenerationSFT diff --git a/src/distilabel/steps/generators/data.py b/src/distilabel/steps/generators/data.py index fbf29ec7fe..1d26ed8856 100644 --- a/src/distilabel/steps/generators/data.py +++ b/src/distilabel/steps/generators/data.py @@ -42,7 +42,6 @@ class LoadDataFromDicts(GeneratorStep): - load Examples: - Load data from a list of dictionaries: ```python diff --git a/src/distilabel/steps/generators/huggingface.py b/src/distilabel/steps/generators/huggingface.py index c7eeadb29e..5f82f64731 100644 --- a/src/distilabel/steps/generators/huggingface.py +++ b/src/distilabel/steps/generators/huggingface.py @@ -90,7 +90,6 @@ class LoadDataFromHub(GeneratorStep): - load Examples: - Load data from a dataset in Hugging Face Hub: ```python @@ -295,7 +294,6 @@ class LoadDataFromFileSystem(LoadDataFromHub): - load Examples: - Load data from a Hugging Face dataset in your file system: ```python @@ -490,7 +488,6 @@ class LoadDataFromDisk(LoadDataFromHub): - load Examples: - Load data from a Hugging Face Dataset: ```python diff --git a/src/distilabel/steps/globals/huggingface.py b/src/distilabel/steps/globals/huggingface.py index 28ef3932bd..82e7f35ab6 100644 --- a/src/distilabel/steps/globals/huggingface.py +++ b/src/distilabel/steps/globals/huggingface.py @@ -58,7 +58,6 @@ class PushToHub(GlobalStep): - huggingface Examples: - Push batches of your dataset to the Hugging Face Hub repository: ```python diff --git a/src/distilabel/steps/reward_model.py b/src/distilabel/steps/reward_model.py index 9a88d8d659..49ddc065df 100644 --- a/src/distilabel/steps/reward_model.py +++ b/src/distilabel/steps/reward_model.py @@ -68,7 +68,6 @@ class RewardModelScore(Step, CudaDevicePlacementMixin): - scorer Examples: - Assigning an score for an instruction-response pair: ```python diff --git a/src/distilabel/steps/tasks/complexity_scorer.py b/src/distilabel/steps/tasks/complexity_scorer.py index d7ddc23623..5f972eb67c 100644 --- a/src/distilabel/steps/tasks/complexity_scorer.py +++ b/src/distilabel/steps/tasks/complexity_scorer.py @@ -63,7 +63,6 @@ class ComplexityScorer(Task): - [`What Makes Good Data for Alignment? A Comprehensive Study of Automatic Data Selection in Instruction Tuning`](https://arxiv.org/abs/2312.15685) Examples: - Evaluate the complexity of your instructions: ```python @@ -110,11 +109,10 @@ class ComplexityScorer(Task): ) ) # result - # [{'instructions': ['plain instruction', 'highly complex instruction'], 'model_name': 'test', 'scores': [1, 2], 'distilabel_metadata': {'raw_output_complexity_scorer_0': '{ \n "scores": [\n 1, \n 2\n ]\n}'}}] + # [{'instructions': ['plain instruction', 'highly complex instruction'], 'model_name': 'test', 'scores': [1, 2], 'distilabel_metadata': {'raw_output_complexity_scorer_0': '{ \\n "scores": [\\n 1, \\n 2\\n ]\\n}'}}] ``` Citations: - ``` @misc{liu2024makesgooddataalignment, title={What Makes Good Data for Alignment? A Comprehensive Study of Automatic Data Selection in Instruction Tuning}, diff --git a/src/distilabel/steps/tasks/evol_instruct/base.py b/src/distilabel/steps/tasks/evol_instruct/base.py index 71da271554..ea73f25038 100644 --- a/src/distilabel/steps/tasks/evol_instruct/base.py +++ b/src/distilabel/steps/tasks/evol_instruct/base.py @@ -71,7 +71,6 @@ class EvolInstruct(Task): - [GitHub: h2oai/h2o-wizardlm](https://github.com/h2oai/h2o-wizardlm) Examples: - Evolve an instruction using an LLM: ```python @@ -151,7 +150,6 @@ class EvolInstruct(Task): ``` Citations: - ``` @misc{xu2023wizardlmempoweringlargelanguage, title={WizardLM: Empowering Large Language Models to Follow Complex Instructions}, diff --git a/src/distilabel/steps/tasks/evol_instruct/evol_complexity/base.py b/src/distilabel/steps/tasks/evol_instruct/evol_complexity/base.py index 1619db4229..a7e46b154b 100644 --- a/src/distilabel/steps/tasks/evol_instruct/evol_complexity/base.py +++ b/src/distilabel/steps/tasks/evol_instruct/evol_complexity/base.py @@ -63,7 +63,6 @@ class EvolComplexity(EvolInstruct): - [WizardLM: Empowering Large Language Models to Follow Complex Instructions](https://arxiv.org/abs/2304.12244) Examples: - Evolve an instruction using an LLM: ```python @@ -86,7 +85,6 @@ class EvolComplexity(EvolInstruct): ``` Citations: - ``` @misc{liu2024makesgooddataalignment, title={What Makes Good Data for Alignment? A Comprehensive Study of Automatic Data Selection in Instruction Tuning}, diff --git a/src/distilabel/steps/tasks/evol_instruct/evol_complexity/generator.py b/src/distilabel/steps/tasks/evol_instruct/evol_complexity/generator.py index 8749fcd8c1..f1965d9e83 100644 --- a/src/distilabel/steps/tasks/evol_instruct/evol_complexity/generator.py +++ b/src/distilabel/steps/tasks/evol_instruct/evol_complexity/generator.py @@ -61,7 +61,6 @@ class EvolComplexityGenerator(EvolInstructGenerator): - [WizardLM: Empowering Large Language Models to Follow Complex Instructions](https://arxiv.org/abs/2304.12244) Examples: - Generate evolved instructions without initial instructions: ```python @@ -84,7 +83,6 @@ class EvolComplexityGenerator(EvolInstructGenerator): ``` Citations: - ``` @misc{liu2024makesgooddataalignment, title={What Makes Good Data for Alignment? A Comprehensive Study of Automatic Data Selection in Instruction Tuning}, diff --git a/src/distilabel/steps/tasks/evol_instruct/generator.py b/src/distilabel/steps/tasks/evol_instruct/generator.py index 1eea138a69..bc15655ba2 100644 --- a/src/distilabel/steps/tasks/evol_instruct/generator.py +++ b/src/distilabel/steps/tasks/evol_instruct/generator.py @@ -77,7 +77,6 @@ class EvolInstructGenerator(GeneratorTask): - [GitHub: h2oai/h2o-wizardlm](https://github.com/h2oai/h2o-wizardlm) Examples: - Generate evolved instructions without initial instructions: ```python @@ -100,7 +99,6 @@ class EvolInstructGenerator(GeneratorTask): ``` Citations: - ``` @misc{xu2023wizardlmempoweringlargelanguage, title={WizardLM: Empowering Large Language Models to Follow Complex Instructions}, diff --git a/src/distilabel/steps/tasks/evol_quality/base.py b/src/distilabel/steps/tasks/evol_quality/base.py index 1c0d6c4d52..743deeb4f2 100644 --- a/src/distilabel/steps/tasks/evol_quality/base.py +++ b/src/distilabel/steps/tasks/evol_quality/base.py @@ -67,7 +67,6 @@ class EvolQuality(Task): - [`What Makes Good Data for Alignment? A Comprehensive Study of Automatic Data Selection in Instruction Tuning`](https://arxiv.org/abs/2312.15685) Examples: - Evolve the quality of the responses given a prompt: ```python @@ -103,7 +102,6 @@ class EvolQuality(Task): ``` Citations: - ``` @misc{liu2024makesgooddataalignment, title={What Makes Good Data for Alignment? A Comprehensive Study of Automatic Data Selection in Instruction Tuning}, diff --git a/src/distilabel/steps/tasks/generate_embeddings.py b/src/distilabel/steps/tasks/generate_embeddings.py index 297f3c3be7..85db623d94 100644 --- a/src/distilabel/steps/tasks/generate_embeddings.py +++ b/src/distilabel/steps/tasks/generate_embeddings.py @@ -50,7 +50,6 @@ class GenerateEmbeddings(Step): - [What Makes Good Data for Alignment? A Comprehensive Study of Automatic Data Selection in Instruction Tuning](https://arxiv.org/abs/2312.15685) Examples: - Rank LLM candidates: ```python @@ -77,7 +76,6 @@ class GenerateEmbeddings(Step): ``` Citations: - ``` @misc{liu2024makesgooddataalignment, title={What Makes Good Data for Alignment? A Comprehensive Study of Automatic Data Selection in Instruction Tuning}, diff --git a/src/distilabel/steps/tasks/genstruct.py b/src/distilabel/steps/tasks/genstruct.py index 27fd813cfd..02a0657339 100644 --- a/src/distilabel/steps/tasks/genstruct.py +++ b/src/distilabel/steps/tasks/genstruct.py @@ -69,7 +69,6 @@ class Genstruct(Task): - [Ada-Instruct: Adapting Instruction Generators for Complex Reasoning](https://arxiv.org/abs/2310.04484) Examples: - Generate instructions from raw documents using the title and content: ```python @@ -105,7 +104,6 @@ class Genstruct(Task): ``` Citations: - ``` @misc{cui2023adainstructadaptinginstructiongenerators, title={Ada-Instruct: Adapting Instruction Generators for Complex Reasoning}, diff --git a/src/distilabel/steps/tasks/improving_text_embeddings.py b/src/distilabel/steps/tasks/improving_text_embeddings.py index 77ec51d22f..1908a9685c 100644 --- a/src/distilabel/steps/tasks/improving_text_embeddings.py +++ b/src/distilabel/steps/tasks/improving_text_embeddings.py @@ -265,7 +265,6 @@ class EmbeddingTaskGenerator(GeneratorTask): - [Improving Text Embeddings with Large Language Models](https://arxiv.org/abs/2401.00368) Examples: - Generate embedding tasks for text retrieval: ```python @@ -285,7 +284,6 @@ class EmbeddingTaskGenerator(GeneratorTask): ``` Citations: - ``` @misc{wang2024improvingtextembeddingslarge, title={Improving Text Embeddings with Large Language Models}, @@ -445,7 +443,6 @@ class GenerateTextRetrievalData(_EmbeddingDataGeneration): - [Improving Text Embeddings with Large Language Models](https://arxiv.org/abs/2401.00368) Examples: - Generate synthetic text retrieval data for training embedding models: ```python @@ -568,7 +565,6 @@ class GenerateShortTextMatchingData(_EmbeddingDataGeneration): - [Improving Text Embeddings with Large Language Models](https://arxiv.org/abs/2401.00368) Examples: - Generate synthetic short text matching data for training embedding models: ```python @@ -658,7 +654,6 @@ class GenerateLongTextMatchingData(_EmbeddingDataGeneration): - [Improving Text Embeddings with Large Language Models](https://arxiv.org/abs/2401.00368) Examples: - Generate synthetic long text matching data for training embedding models: ```python @@ -752,7 +747,6 @@ class GenerateTextClassificationData(_EmbeddingDataGeneration): - [Improving Text Embeddings with Large Language Models](https://arxiv.org/abs/2401.00368) Examples: - Generate synthetic text classification data for training embedding models: ```python @@ -851,7 +845,6 @@ class MonolingualTripletGenerator(_EmbeddingDataGenerator): - model_name (`str`): the name of the model used to generate the monolingual triplets. Examples: - Generate monolingual triplets for training embedding models: ```python @@ -943,7 +936,6 @@ class BitextRetrievalGenerator(_EmbeddingDataGenerator): data. Examples: - Generate bitext retrieval data for training embedding models: ```python diff --git a/src/distilabel/steps/tasks/instruction_backtranslation.py b/src/distilabel/steps/tasks/instruction_backtranslation.py index 3833333192..9322aa4f2d 100644 --- a/src/distilabel/steps/tasks/instruction_backtranslation.py +++ b/src/distilabel/steps/tasks/instruction_backtranslation.py @@ -50,8 +50,43 @@ class InstructionBacktranslation(Task): References: - [`Self-Alignment with Instruction Backtranslation`](https://arxiv.org/abs/2308.06259) - Citations: + Examples: + Generate a score and reason for a given instruction and generation: + + ```python + from distilabel.steps.tasks import InstructionBacktranslation + + instruction_backtranslation = InstructionBacktranslation( + name="instruction_backtranslation", + llm=llm, + input_batch_size=10, + output_mappings={"model_name": "scoring_model"}, + ) + instruction_backtranslation.load() + + result = next( + instruction_backtranslation.process( + [ + { + "instruction": "How much is 2+2?", + "generation": "4", + } + ] + ) + ) + # result + # [ + # { + # "instruction": "How much is 2+2?", + # "generation": "4", + # "score": 3, + # "reason": "Reason for the generation.", + # "model_name": "meta-llama/Meta-Llama-3.1-8B-Instruct", + # } + # ] + ``` + Citations: ``` @misc{li2024selfalignmentinstructionbacktranslation, title={Self-Alignment with Instruction Backtranslation}, diff --git a/src/distilabel/steps/tasks/magpie/base.py b/src/distilabel/steps/tasks/magpie/base.py index dfd7983772..29d04c29a0 100644 --- a/src/distilabel/steps/tasks/magpie/base.py +++ b/src/distilabel/steps/tasks/magpie/base.py @@ -46,7 +46,6 @@ class MagpieBase(RuntimeParametersMixin): - [Magpie: Alignment Data Synthesis from Scratch by Prompting Aligned LLMs with Nothing](https://arxiv.org/abs/2406.08464) Citations: - ``` @misc{xu2024magpiealignmentdatasynthesis, title={Magpie: Alignment Data Synthesis from Scratch by Prompting Aligned LLMs with Nothing}, @@ -328,7 +327,6 @@ class Magpie(Task, MagpieBase): - [Magpie: Alignment Data Synthesis from Scratch by Prompting Aligned LLMs with Nothing](https://arxiv.org/abs/2406.08464) Examples: - Generating instructions with Llama 3 8B Instruct and TransformersLLM: ```python diff --git a/src/distilabel/steps/tasks/magpie/generator.py b/src/distilabel/steps/tasks/magpie/generator.py index e7cf77c605..dc4fe99387 100644 --- a/src/distilabel/steps/tasks/magpie/generator.py +++ b/src/distilabel/steps/tasks/magpie/generator.py @@ -91,7 +91,6 @@ class MagpieGenerator(GeneratorTask, MagpieBase): - [Magpie: Alignment Data Synthesis from Scratch by Prompting Aligned LLMs with Nothing](https://arxiv.org/abs/2406.08464) Examples: - Generating instructions with Llama 3 8B Instruct and TransformersLLM: ```python @@ -205,7 +204,6 @@ class MagpieGenerator(GeneratorTask, MagpieBase): ``` Citations: - ``` @misc{xu2024magpiealignmentdatasynthesis, title={Magpie: Alignment Data Synthesis from Scratch by Prompting Aligned LLMs with Nothing}, diff --git a/src/distilabel/steps/tasks/pair_rm.py b/src/distilabel/steps/tasks/pair_rm.py index c9ec217fdb..23262a533f 100644 --- a/src/distilabel/steps/tasks/pair_rm.py +++ b/src/distilabel/steps/tasks/pair_rm.py @@ -51,7 +51,6 @@ class PairRM(Step): currently, and we will use a specific `LLM`. Examples: - Rank LLM candidates: ```python @@ -82,7 +81,6 @@ class PairRM(Step): ``` Citations: - ``` @misc{jiang2023llmblenderensemblinglargelanguage, title={LLM-Blender: Ensembling Large Language Models with Pairwise Ranking and Generative Fusion}, diff --git a/src/distilabel/steps/tasks/prometheus_eval.py b/src/distilabel/steps/tasks/prometheus_eval.py index e902756e8d..27cd9622ea 100644 --- a/src/distilabel/steps/tasks/prometheus_eval.py +++ b/src/distilabel/steps/tasks/prometheus_eval.py @@ -134,7 +134,6 @@ class PrometheusEval(Task): - [prometheus-eval: Evaluate your LLM's response with Prometheus 💯](https://github.com/prometheus-eval/prometheus-eval) Examples: - Critique and evaluate LLM generation quality using Prometheus 2_0: ```python @@ -145,7 +144,7 @@ class PrometheusEval(Task): prometheus = PrometheusEval( llm=vLLM( model="prometheus-eval/prometheus-7b-v2.0", - chat_template="[INST] {{ messages[0]\"content\" }}\n{{ messages[1]\"content\" }}[/INST]", + chat_template="[INST] {{ messages[0]\"content\" }}\\n{{ messages[1]\"content\" }}[/INST]", ), mode="absolute", rubric="factual-validity" @@ -182,7 +181,7 @@ class PrometheusEval(Task): prometheus = PrometheusEval( llm=vLLM( model="prometheus-eval/prometheus-7b-v2.0", - chat_template="[INST] {{ messages[0]\"content\" }}\n{{ messages[1]\"content\" }}[/INST]", + chat_template="[INST] {{ messages[0]\"content\" }}\\n{{ messages[1]\"content\" }}[/INST]", ), mode="relative", rubric="honesty" @@ -219,12 +218,12 @@ class PrometheusEval(Task): prometheus = PrometheusEval( llm=vLLM( model="prometheus-eval/prometheus-7b-v2.0", - chat_template="[INST] {{ messages[0]\"content\" }}\n{{ messages[1]\"content\" }}[/INST]", + chat_template="[INST] {{ messages[0]\"content\" }}\\n{{ messages[1]\"content\" }}[/INST]", ), mode="absolute", rubric="custom", rubrics={ - "custom": "[A]\nScore 1: A\nScore 2: B\nScore 3: C\nScore 4: D\nScore 5: E" + "custom": "[A]\\nScore 1: A\\nScore 2: B\\nScore 3: C\\nScore 4: D\\nScore 5: E" } ) @@ -259,7 +258,7 @@ class PrometheusEval(Task): prometheus = PrometheusEval( llm=vLLM( model="prometheus-eval/prometheus-7b-v2.0", - chat_template="[INST] {{ messages[0]\"content\" }}\n{{ messages[1]\"content\" }}[/INST]", + chat_template="[INST] {{ messages[0]\"content\" }}\\n{{ messages[1]\"content\" }}[/INST]", ), mode="absolute", rubric="helpfulness", @@ -293,7 +292,6 @@ class PrometheusEval(Task): ``` Citations: - ``` @misc{kim2024prometheus2opensource, title={Prometheus 2: An Open Source Language Model Specialized in Evaluating Other Language Models}, diff --git a/src/distilabel/steps/tasks/quality_scorer.py b/src/distilabel/steps/tasks/quality_scorer.py index 3e8857c1b6..ff3f199db8 100644 --- a/src/distilabel/steps/tasks/quality_scorer.py +++ b/src/distilabel/steps/tasks/quality_scorer.py @@ -63,7 +63,6 @@ class QualityScorer(Task): - [`What Makes Good Data for Alignment? A Comprehensive Study of Automatic Data Selection in Instruction Tuning`](https://arxiv.org/abs/2312.15685) Examples: - Evaluate the quality of your instructions: ```python @@ -134,7 +133,6 @@ class QualityScorer(Task): ``` Citations: - ``` @misc{liu2024makesgooddataalignment, title={What Makes Good Data for Alignment? A Comprehensive Study of Automatic Data Selection in Instruction Tuning}, diff --git a/src/distilabel/steps/tasks/self_instruct.py b/src/distilabel/steps/tasks/self_instruct.py index 27bcecc0bf..e59cad4b67 100644 --- a/src/distilabel/steps/tasks/self_instruct.py +++ b/src/distilabel/steps/tasks/self_instruct.py @@ -62,7 +62,6 @@ class SelfInstruct(Task): - [`Self-Instruct: Aligning Language Models with Self-Generated Instructions`](https://arxiv.org/abs/2212.10560) Examples: - Generate instructions based on a given input: ```python @@ -90,7 +89,6 @@ class SelfInstruct(Task): ``` Citations: - ``` @misc{wang2023selfinstructaligninglanguagemodels, title={Self-Instruct: Aligning Language Models with Self-Generated Instructions}, diff --git a/src/distilabel/steps/tasks/sentence_transformers.py b/src/distilabel/steps/tasks/sentence_transformers.py index 59c504200d..82f7a2a948 100644 --- a/src/distilabel/steps/tasks/sentence_transformers.py +++ b/src/distilabel/steps/tasks/sentence_transformers.py @@ -104,7 +104,6 @@ class GenerateSentencePair(Task): - embedding Examples: - Paraphrasing: ```python @@ -358,9 +357,11 @@ def format_output( if self.triplet: return { "positive": groups[0].strip(), - "negative": groups[1].strip() - if len(groups) > 1 and groups[1] is not None - else None, + "negative": ( + groups[1].strip() + if len(groups) > 1 and groups[1] is not None + else None + ), } return {"positive": groups[0].strip()} diff --git a/src/distilabel/steps/tasks/structured_generation.py b/src/distilabel/steps/tasks/structured_generation.py index cbea279ebb..81ee74bd85 100644 --- a/src/distilabel/steps/tasks/structured_generation.py +++ b/src/distilabel/steps/tasks/structured_generation.py @@ -48,7 +48,6 @@ class StructuredGeneration(Task): - structured-generation Examples: - Generate structured output from a JSON schema: ```python diff --git a/src/distilabel/steps/tasks/text_generation.py b/src/distilabel/steps/tasks/text_generation.py index 6f4d5d7584..4f6b681d19 100644 --- a/src/distilabel/steps/tasks/text_generation.py +++ b/src/distilabel/steps/tasks/text_generation.py @@ -47,7 +47,6 @@ class TextGeneration(Task): - text-generation Examples: - Generate text from an instruction: ```python @@ -152,7 +151,6 @@ class ChatGeneration(Task): `:material-chat:` Examples: - Generate text from a conversation in OpenAI chat format: ```python diff --git a/src/distilabel/steps/tasks/ultrafeedback.py b/src/distilabel/steps/tasks/ultrafeedback.py index dae68bb48f..091d99599e 100644 --- a/src/distilabel/steps/tasks/ultrafeedback.py +++ b/src/distilabel/steps/tasks/ultrafeedback.py @@ -59,7 +59,6 @@ class UltraFeedback(Task): - [`UltraFeedback - GitHub Repository`](https://github.com/OpenBMB/UltraFeedback) Examples: - Rate generations from different LLMs based on the selected aspect: ```python @@ -130,7 +129,7 @@ class UltraFeedback(Task): # 'ratings': [5, 1], # 'rationales': ['The response is correct and confident, as it directly answers the question without expressing any uncertainty or doubt.', # "The response is confidently incorrect, as it provides unrelated information ('a car') and does not address the question. The model shows no uncertainty or indication that it does not know the answer."], - # 'distilabel_metadata': {'raw_output_ultra_feedback_0': '{"ratings": [\n 5,\n 1\n] \n\n,"rationales": [\n "The response is correct and confident, as it directly answers the question without expressing any uncertainty or doubt.",\n "The response is confidently incorrect, as it provides unrelated information (\'a car\') and does not address the question. The model shows no uncertainty or indication that it does not know the answer."\n] }'}, + # 'distilabel_metadata': {'raw_output_ultra_feedback_0': '{"ratings": [\\n 5,\\n 1\\n] \\n\\n,"rationales": [\\n "The response is correct and confident, as it directly answers the question without expressing any uncertainty or doubt.",\\n "The response is confidently incorrect, as it provides unrelated information (\'a car\') and does not address the question. The model shows no uncertainty or indication that it does not know the answer."\\n] }'}, # 'model_name': 'meta-llama/Meta-Llama-3.1-70B-Instruct'}] ``` @@ -170,12 +169,11 @@ class UltraFeedback(Task): # 'rationales_for_rating': ['Text 1 is rated as Correct (3) because it provides the accurate answer to the question, but lacks comprehensive information or detailed description.', # 'Text 2 is rated as Severely Incorrect (1) because it does not provide any relevant information and seems unrelated to the question.'], # 'types': [1, 3, 1], - # 'distilabel_metadata': {'raw_output_ultra_feedback_0': '{ \n "ratings": [\n 1,\n 5\n ]\n ,\n "rationales": [\n "Text 1 is clear and relevant, providing the correct answer to the question. It is also not lengthy and does not contain repetition. However, it lacks comprehensive information or detailed description.",\n "Text 2 is neither clear nor relevant to the task. It does not provide any useful information and seems unrelated to the question."\n ]\n ,\n "rationales_for_rating": [\n "Text 1 is rated as Correct (3) because it provides the accurate answer to the question, but lacks comprehensive information or detailed description.",\n "Text 2 is rated as Severely Incorrect (1) because it does not provide any relevant information and seems unrelated to the question."\n ]\n ,\n "types": [\n 1, 3,\n 1\n ]\n }'}, + # 'distilabel_metadata': {'raw_output_ultra_feedback_0': '{ \\n "ratings": [\\n 1,\\n 5\\n ]\\n ,\\n "rationales": [\\n "Text 1 is clear and relevant, providing the correct answer to the question. It is also not lengthy and does not contain repetition. However, it lacks comprehensive information or detailed description.",\\n "Text 2 is neither clear nor relevant to the task. It does not provide any useful information and seems unrelated to the question."\\n ]\\n ,\\n "rationales_for_rating": [\\n "Text 1 is rated as Correct (3) because it provides the accurate answer to the question, but lacks comprehensive information or detailed description.",\\n "Text 2 is rated as Severely Incorrect (1) because it does not provide any relevant information and seems unrelated to the question."\\n ]\\n ,\\n "types": [\\n 1, 3,\\n 1\\n ]\\n }'}, # 'model_name': 'meta-llama/Meta-Llama-3.1-70B-Instruct'}] ``` Citations: - ``` @misc{cui2024ultrafeedbackboostinglanguagemodels, title={UltraFeedback: Boosting Language Models with Scaled AI Feedback}, @@ -313,9 +311,11 @@ def _format_ratings_rationales_output( formatted_outputs.append( { - "ratings": int(re.findall(r"\b\d+\b", matches.group(1))[0]) - if matches.group(1) not in ["None", "N/A"] - else None, + "ratings": ( + int(re.findall(r"\b\d+\b", matches.group(1))[0]) + if matches.group(1) not in ["None", "N/A"] + else None + ), "rationales": matches.group(2), } ) @@ -358,13 +358,17 @@ def _format_types_ratings_rationales_output( formatted_outputs.append( { - "types": int(re.findall(r"\b\d+\b", matches.group(1))[0]) - if matches.group(1) not in ["None", "N/A"] - else None, + "types": ( + int(re.findall(r"\b\d+\b", matches.group(1))[0]) + if matches.group(1) not in ["None", "N/A"] + else None + ), "rationales": matches.group(2), - "ratings": int(re.findall(r"\b\d+\b", matches.group(3))[0]) - if matches.group(3) not in ["None", "N/A"] - else None, + "ratings": ( + int(re.findall(r"\b\d+\b", matches.group(3))[0]) + if matches.group(3) not in ["None", "N/A"] + else None + ), "rationales-for-ratings": matches.group(4), } ) diff --git a/src/distilabel/steps/tasks/urial.py b/src/distilabel/steps/tasks/urial.py index ed0e72d969..705b9c4883 100644 --- a/src/distilabel/steps/tasks/urial.py +++ b/src/distilabel/steps/tasks/urial.py @@ -47,7 +47,6 @@ class URIAL(Task): - [The Unlocking Spell on Base LLMs: Rethinking Alignment via In-Context Learning](https://arxiv.org/abs/2312.01552) Examples: - Generate text from an instruction: ```python diff --git a/src/distilabel/steps/truncate.py b/src/distilabel/steps/truncate.py index bb1785b8a1..6e68af6630 100644 --- a/src/distilabel/steps/truncate.py +++ b/src/distilabel/steps/truncate.py @@ -51,7 +51,6 @@ class TruncateTextColumn(Step): - text-manipulation Examples: - Truncating a row to a given number of tokens: ```python diff --git a/src/distilabel/utils/docstring.py b/src/distilabel/utils/docstring.py index 899425e260..913b94700a 100644 --- a/src/distilabel/utils/docstring.py +++ b/src/distilabel/utils/docstring.py @@ -165,7 +165,7 @@ def parse_google_docstring(func: Callable) -> Docstring: # noqa: C901 elif section_name == "examples": # Parse examples into a dictionary example_items = re.findall( - r"(\w[\w\s]*?):\s*\n\s*```python\n(.*?)\n\s*```", + r"(\w[\w\s]*?):\s*\n?\s*```python\n(.*?)\n\s*```", section_content, re.DOTALL, ) @@ -217,7 +217,6 @@ def get_bibtex(ref: str) -> str: The bibtex style citation. Examples: - ```python cite = get_bibtex(r"https://arxiv.org/abs/2406.18518") @misc{other, From 4b3c9c0412a3c853e4435bbbd2b46708db7aa1a3 Mon Sep 17 00:00:00 2001 From: Agus Date: Mon, 2 Sep 2024 09:12:15 +0200 Subject: [PATCH 36/82] Refactor of MinHash to work with a single class and fix the shelve backend (#937) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Initial work for minhash * Add minhash step redirect * Add first version of minhash and minhashlsh * Add unit tests for minhash dedup * Add pipeline testing deduplication * Add tests to run with disk backend * Add tests for the disk and ensure unload * Add private _datasketch module to include a custom storage configuration for the minhash index * Add docstrings to the internal classes/functions * Add docstrings for the user facing classes * Update src/distilabel/steps/filtering/minhash.py Co-authored-by: Gabriel Martín Blázquez * Update src/distilabel/steps/filtering/minhash.py Co-authored-by: Gabriel Martín Blázquez * Update tests/integration/test_deduplication.py Co-authored-by: Gabriel Martín Blázquez * Update src/distilabel/steps/filtering/minhash.py Co-authored-by: Gabriel Martín Blázquez * Update src/distilabel/steps/filtering/minhash.py Co-authored-by: Gabriel Martín Blázquez * Add installation dependencies * Apply comments from code review * Add nltk as a dependency for the tests * Update tests and interpretation of keep rows vs duplicates * Remove disk backend from tests temporarily * Add note in the docs related to minhash storage on disk * Update tests to run on dict instead of disk as it never ends on CI * Fix integration test * Hide import inside of function to avoid installing it on docs building * Update command to download nltk * Allow for a name in the shelve based backend to avoid overwrites * Refactor MinHash to use a single MinHashDedup class that controls all the process * Refactor tests to use the new class * Redirect import to steps level * Create new disk based storage using diskcache * Add docstrings to clarify the difference between dict/disk * Refactor to use diskcache * Fix docstring example * Update src/distilabel/steps/filtering/minhash.py Co-authored-by: Gabriel Martín Blázquez * Update definition of the step --------- Co-authored-by: Gabriel Martín Blázquez --- src/distilabel/steps/__init__.py | 5 +- src/distilabel/steps/filtering/_datasketch.py | 85 ++++---- src/distilabel/steps/filtering/minhash.py | 196 ++++++------------ tests/integration/test_deduplication.py | 15 +- .../unit/steps/test_filtering/test_minhash.py | 37 +--- 5 files changed, 115 insertions(+), 223 deletions(-) diff --git a/src/distilabel/steps/__init__.py b/src/distilabel/steps/__init__.py index efa9095325..56519ede68 100644 --- a/src/distilabel/steps/__init__.py +++ b/src/distilabel/steps/__init__.py @@ -29,7 +29,7 @@ from distilabel.steps.deita import DeitaFiltering from distilabel.steps.embeddings.embedding_generation import EmbeddingGeneration from distilabel.steps.embeddings.nearest_neighbour import FaissNearestNeighbour -from distilabel.steps.filtering.minhash import MinHash, MinHashLSH +from distilabel.steps.filtering.minhash import MinHashDedup from distilabel.steps.formatting.conversation import ConversationTemplate from distilabel.steps.formatting.dpo import ( FormatChatGenerationDPO, @@ -74,8 +74,7 @@ "LoadDataFromDisk", "LoadDataFromFileSystem", "LoadDataFromHub", - "MinHash", - "MinHashLSH", + "MinHashDedup", "make_generator_step", "PushToHub", "Step", diff --git a/src/distilabel/steps/filtering/_datasketch.py b/src/distilabel/steps/filtering/_datasketch.py index 417575bd4d..623cd45f0d 100644 --- a/src/distilabel/steps/filtering/_datasketch.py +++ b/src/distilabel/steps/filtering/_datasketch.py @@ -19,7 +19,7 @@ creating a PR to `datasketch`. """ -import shelve +import shutil import struct from pathlib import Path from typing import Callable, Dict, Final, Optional, Tuple @@ -30,48 +30,46 @@ from datasketch.storage import ordered_storage as _ordered_storage from datasketch.storage import unordered_storage as _unordered_storage -SHELVE_DIR: Path = Path.home() / ".cache" / "distilabel" -SHELVE_LIST_NAME: Final[str] = ".shelve_list_storage" -SHELVE_SET_NAME: Final[str] = ".shelve_set_storage" +KEY_VALUE_DISK_DIR: Path = Path.home() / ".cache" / "distilabel" / "key_value_store" +KV_DISK_LIST_NAME: Final[str] = "disckache_list_storage" +KV_DISK_SET_NAME: Final[str] = "diskcache_set_storage" -class ShelveListStorage(OrderedStorage): - """Key/Value storage using shelve to store the hash tables in disk. - It mimics the behaviour of `datasketch.DictListStorage`. - The only difference is the storage in disk. - The functionality is on purpose to avoid unnecessary errors. - """ +class DiskCacheListStorage(OrderedStorage): + def __init__(self, config, name) -> None: + path = config.get("path", self._get_db_name(name)) + try: + from diskcache import Index + except ImportError as e: + raise ImportError( + "`diskcache` is required for disk storage using `MinHashDedup`. " + "Please install it using `pip install diskcache`." + ) from e - def __init__(self, config) -> None: - path = config.get("path", self._get_db_name()) - # Read about writeback here: https://docs.python.org/3/library/shelve.html#shelve.open - writeback = config.get("writeback", True) - # The flag is set to "n" to recreate the file always, we assume - # every pipeline works on it's own and recomputes it instead of trusting - # the cache. - self._db = shelve.open(path, writeback=writeback, flag="n") + # Start with a clean file on each pipeline + if Path(path).exists(): + shutil.rmtree(path) + self._db = Index(path) - def _get_db_name(self): - return str(SHELVE_DIR / SHELVE_LIST_NAME) + def _get_db_name(self, name): + return str(KEY_VALUE_DISK_DIR / f"{name}_{KV_DISK_LIST_NAME}") def keys(self): return self._db.keys() def get(self, key): - return self._db.get(str(key), []) + return self._db.get(key, []) def remove(self, *keys): - for key in keys: - del self._db[str(key)] + self._db.clear() def remove_val(self, key, val): - self._db[str(key)].remove(val) + self.get(key).remove(val) def insert(self, key, *vals, **kwargs): - key = str(key) - if not self._db.get(key): - self._db[key] = [] - self._db[key].extend(vals) + res = self.get(key) + res.extend(vals) + self._db[key] = res def size(self): return len(self._db) @@ -83,34 +81,27 @@ def has_key(self, key): return key in self._db def close(self): - self._db.close() - + self._db._cache.close() -class ShelveSetStorage(UnorderedStorage, ShelveListStorage): - """Key/Value storage using shelve to store the hash tables in disk. - It mimics the behaviour of `datasketch.DictSetStorage`. - The only difference is the storage in disk. - The functionality is on purpose to avoid unnecessary errors. - """ - def _get_db_name(self): - return str(SHELVE_DIR / SHELVE_SET_NAME) +class DiskCacheSetStorage(UnorderedStorage, DiskCacheListStorage): + def _get_db_name(self, name): + return str(KEY_VALUE_DISK_DIR / f"{name}_{KV_DISK_SET_NAME}") def get(self, key): - return self._db.get(str(key), set()) + return self._db.get(key, set()) def insert(self, key, *vals, **kwargs): - key = str(key) - if not self._db.get(key): - self._db[key] = set() - self._db[key].update(vals) + res = self.get(key) + res.update(vals) + self._db[key] = res def ordered_storage(config, name=None): """Copy of `datasketch.storage.ordered_storage` with the addition of `ShelveListStorage`.""" tp = config["type"] if tp == "disk": - return ShelveListStorage(config) + return DiskCacheListStorage(config, name=name) return _ordered_storage(config, name=name) @@ -118,7 +109,7 @@ def unordered_storage(config, name=None): """Copy of `datasketch.storage.ordered_storage` with the addition of `ShelveSetStorage`.""" tp = config["type"] if tp == "disk": - return ShelveSetStorage(config) + return DiskCacheSetStorage(config, name=name) return _unordered_storage(config, name=name) @@ -192,8 +183,8 @@ def __init__( self.keys = ordered_storage(storage_config, name=b"".join([basename, b"_keys"])) def close(self): - """Closes the shelve objects.""" - if isinstance(self.hashtables[0], ShelveListStorage): + """Closes the internal connections.""" + if isinstance(self.hashtables[0], DiskCacheListStorage): for ht in self.hashtables: ht.close() self.keys.close() diff --git a/src/distilabel/steps/filtering/minhash.py b/src/distilabel/steps/filtering/minhash.py index e5da5bdd16..5b779168a1 100644 --- a/src/distilabel/steps/filtering/minhash.py +++ b/src/distilabel/steps/filtering/minhash.py @@ -30,12 +30,11 @@ ) from pydantic import PrivateAttr -from typing_extensions import override -from distilabel.steps.base import GlobalStep, Step, StepInput +from distilabel.steps.base import Step, StepInput if TYPE_CHECKING: - from datasketch import LeanMinHash, MinHash, MinHashLSH + from datasketch import MinHash, MinHashLSH from distilabel.steps.typing import StepOutput @@ -81,146 +80,49 @@ def tokenize_on_ngrams(texts: Iterable[str], n: int = 1) -> List[Set[bytes]]: ] -# NOTE: This class must be used together with the `MinHashLSH` class. -# We return the `hashvalues` to reproduce the MinHash objects, but we also need -# the seed, so the seed used for the MinHash objects must be kept to be grabbed -# for the next class. We could also pass it as part of the dict, but there's no point. -# Also, instead of returning the values, we could be saving them as artifacts, -# This still needs to be studied. -class MinHash(Step): - """Creates the components for a `MinHash` object to deduplicate texts. +class MinHashDedup(Step): + """Deduplicates text using `MinHash` and `MinHashLSH`. - From `datasketch` documentation: - Estimates the Jaccard similarity (resemblance) between sets of arbitrary sizes in linear - time using a small and fixed memory space. - - Note: - We only keep the hashvalues, as using those values together with the seed - we can reproduce the `MinHash` objects. The `MinHashLSH` will recreate those internally. + `MinHashDedup` is a Step that detects near-duplicates in datasets. The idea roughly translates + to the following steps: + 1. Tokenize the text into words or ngrams. + 2. Create a `MinHash` for each text. + 3. Store the `MinHashes` in a `MinHashLSH`. + 4. Check if the `MinHash` is already in the `LSH`, if so, it is a duplicate. Attributes: num_perm: the number of permutations to use. Defaults to `128`. - seed: the seed to use for the MinHash. Defaults to `1`. + seed: the seed to use for the MinHash. This seed must be the same + used for `MinHash`, keep in mind when both steps are created. Defaults to `1`. tokenizer: the tokenizer to use. Available ones are `words` or `ngrams`. If `words` is selected, it tokenize the text into words using nltk's word tokenizer. `ngram` estimates the ngrams (together with the size `n`) using. Defaults to `words`. - n: the size of the ngrams to use. Only relevant if `tokenizer="ngrams"`. Defaults to `1`. - - Input columns: - - text (`str`): the texts to obtain the hashes for. - - Output columns: - - hashvalues (`List[int]`): hash values obtained for the algorithm. - - Categories: - - filtering - - References: - - [`datasketch documentation`](https://ekzhu.com/datasketch/minhash.html#minhash) - - [Identifying and Filtering Near-Duplicate Documents](https://cs.brown.edu/courses/cs253/papers/nearduplicate.pdf) - - Examples: - - Create MinHash objects for a list of texts to be deduplicated: - - ```python - texts: List[str] = [ - "This is a test document.", - "This document is a test.", - "Test document for duplication.", - "Document for duplication test.", - "This is another unique document." - ] - from distilabel.steps import MinHash - minhasher = MinHash(tokenizer="ngrams", n=3) - minhasher.load() - result = next(hasher.process([{"text": t} for t in texts])) - ``` - """ - - num_perm: int = 128 - seed: int = 1 - tokenizer: Literal["words", "ngrams"] = "words" - n: Optional[int] = 1 - _hasher: Union["MinHash", None] = PrivateAttr(None) - _tokenizer: Union[Callable, None] = PrivateAttr(None) - - def load(self) -> None: - super().load() - if not importlib.import_module("datasketch"): - raise ImportError( - "`datasketch` is needed to deduplicate with MinHash, but is not installed. " - "Please install it using `pip install datasketch`." - ) - from datasketch import MinHash - - self._hasher = MinHash.bulk - - if self.tokenizer == "words": - if not importlib.import_module("nltk"): - raise ImportError( - "`nltk` is needed to tokenize based on words, but is not installed. " - "Please install it using `pip install nltk`. Then run `nltk.download('punkt_tab')`." - ) - self._tokenizer = tokenized_on_words - else: - self._tokenizer = partial(tokenize_on_ngrams, n=self.n) - - @property - def inputs(self) -> List[str]: - return ["text"] - - @property - def outputs(self) -> List[str]: - # Do we need to keep anything, or can it be stored in the cache? - return ["hashvalues"] - - @override - def process(self, inputs: StepInput) -> "StepOutput": # type: ignore - tokenized_texts = [] - for input in inputs: - tokenized_texts.append(self._tokenizer([input[self.inputs[0]]])[0]) - - minhashes = self._hasher( - tokenized_texts, num_perm=self.num_perm, seed=self.seed - ) - for input, mh in zip(inputs, minhashes): - input["hashvalues"] = mh.hashvalues - yield inputs - - -class MinHashLSH(GlobalStep): - """Creates a `MinHashLSH` index to deduplicate texts using MinHash. - - This class must be used together with `MinHash` step. It will work with the previous hashes - to detect duplicate texts, and inform whether a given row can be removed. - - Attributes: - seed: the seed to use for the MinHash. This seed must be the same - used for `MinHash`, keep in mind when both steps are created. Defaults to `1`. - num_perm: the number of permutations to use. Defaults to `128`. + n: the size of the ngrams to use. Only relevant if `tokenizer="ngrams"`. Defaults to `5`. threshold: the threshold to consider two MinHashes as duplicates. Values closer to 0 detect more duplicates. Defaults to `0.9`. - drop_hashvalues: whether to drop the hashvalues after processing. Defaults to `False`. storage: the storage to use for the LSH. Can be `dict` to store the index - in memory, or `disk`, which uses a custom `shelve` backend. Note the `disk` + in memory, or `disk`. Keep in mind, `disk` is an experimental feature + not defined in `datasketch`, that is based on DiskCache's `Index` class. + It should work as a `dict`, but backed by disk, but depending on the system + it can be slower. Defaults to `dict`. + which uses a custom `shelve` backend. Note the `disk` is an experimetal feature that may cause issues. Defaults to `dict`. Input columns: - text (`str`): the texts to be filtered. - - hashvalues (`List[int]`): hash values obtained from `MinHash` step. Output columns: - - minhash_duplicate (`bool`): boolean indicating if the piece of text is a - duplicate or not, so the user can decide afterwards whether to remove it - or not. + - keep_row_after_minhash_filtering (`bool`): boolean indicating if the piece `text` is + not a duplicate i.e. this text should be kept. Categories: - filtering References: - [`datasketch documentation`](https://ekzhu.github.io/datasketch/lsh.html) + - [Identifying and Filtering Near-Duplicate Documents](https://cs.brown.edu/courses/cs253/papers/nearduplicate.pdf) + - [Diskcache's Index](https://grantjenks.com/docs/diskcache/api.html#diskcache.Index) Examples: @@ -228,7 +130,8 @@ class MinHashLSH(GlobalStep): ```python from distilabel.pipeline import Pipeline - from distilabel.steps import MinHash, MinHashLSH + from distilabel.steps import MinHashDedup + from distilabel.steps import LoadDataFromDicts with Pipeline() as pipeline: ds_size = 1000 @@ -244,30 +147,32 @@ class MinHashLSH(GlobalStep): * (ds_size // 5), batch_size=batch_size, ) - minhash = MinHash(tokenizer="ngrams", n=1, input_batch_size=batch_size) - minhash_lsh = MinHashLSH( - threshold=0.9, # lower values will increase the number of duplicates - seed=minhash.seed, # we need to keep the same seed for the LSH - drop_hashvalues=True, # the hashvalues are not needed anymore - storage="dict", # or "disk" for bigger datasets + minhash_dedup = MinHashDedup( + tokenizer="words", + threshold=0.9, # lower values will increase the number of duplicates + storage="dict", # or "disk" for bigger datasets ) - data >> minhash >> minhash_lsh + + data >> minhash_dedup if __name__ == "__main__": distiset = pipeline.run(use_cache=False) ds = distiset["default"]["train"] # Filter out the duplicates - ds_dedup = ds.filter(lambda x: x["minhash_duplicate"] is False) + ds_dedup = ds.filter(lambda x: x["keep_row_after_minhash_filtering"]) ``` """ - seed: int = 1 num_perm: int = 128 + seed: int = 1 + tokenizer: Literal["words", "ngrams"] = "words" + n: Optional[int] = 5 threshold: float = 0.9 - drop_hashvalues: bool = False storage: Literal["dict", "disk"] = "dict" + + _hasher: Union["MinHash", None] = PrivateAttr(None) + _tokenizer: Union[Callable, None] = PrivateAttr(None) _lhs: Union["MinHashLSH", None] = PrivateAttr(None) - _minhasher: Union["LeanMinHash", None] = PrivateAttr(None) def load(self) -> None: super().load() @@ -276,16 +181,26 @@ def load(self) -> None: "`datasketch` is needed to deduplicate with MinHash, but is not installed. " "Please install it using `pip install datasketch`." ) - from datasketch import LeanMinHash + from datasketch import MinHash from distilabel.steps.filtering._datasketch import MinHashLSH + self._hasher = MinHash.bulk self._lsh = MinHashLSH( num_perm=self.num_perm, threshold=self.threshold, storage_config={"type": self.storage}, ) - self._minhasher = partial(LeanMinHash, seed=self.seed) + + if self.tokenizer == "words": + if not importlib.import_module("nltk"): + raise ImportError( + "`nltk` is needed to tokenize based on words, but is not installed. " + "Please install it using `pip install nltk`. Then run `nltk.download('punkt_tab')`." + ) + self._tokenizer = tokenized_on_words + else: + self._tokenizer = partial(tokenize_on_ngrams, n=self.n) def unload(self) -> None: super().unload() @@ -295,22 +210,27 @@ def unload(self) -> None: @property def inputs(self) -> List[str]: - return ["text", "hashvalues"] + return ["text"] @property def outputs(self) -> List[str]: return ["keep_row_after_minhash_filtering"] def process(self, inputs: StepInput) -> "StepOutput": + tokenized_texts = [] for input in inputs: - minhash = self._minhasher(hashvalues=input["hashvalues"]) + tokenized_texts.append(self._tokenizer([input[self.inputs[0]]])[0]) + + minhashes = self._hasher( + tokenized_texts, num_perm=self.num_perm, seed=self.seed + ) + + for input, minhash in zip(inputs, minhashes): # Check if the text is already in the LSH index if self._lsh.query(minhash): input["keep_row_after_minhash_filtering"] = False else: self._lsh.insert(str(uuid.uuid4()), minhash) input["keep_row_after_minhash_filtering"] = True - if self.drop_hashvalues: - del input["hashvalues"] yield inputs diff --git a/tests/integration/test_deduplication.py b/tests/integration/test_deduplication.py index f80ff31b93..0121550f54 100644 --- a/tests/integration/test_deduplication.py +++ b/tests/integration/test_deduplication.py @@ -13,7 +13,7 @@ # limitations under the License. from distilabel.pipeline import Pipeline -from distilabel.steps import LoadDataFromDicts, MinHash, MinHashLSH +from distilabel.steps import LoadDataFromDicts, MinHashDedup def test_minhash_deduplication() -> None: @@ -31,15 +31,14 @@ def test_minhash_deduplication() -> None: * (ds_size // 5), batch_size=batch_size, ) - minhash = MinHash(tokenizer="ngrams", n=1, input_batch_size=batch_size) - minhash_lsh = MinHashLSH( + minhash = MinHashDedup( + tokenizer="ngrams", + n=2, threshold=0.9, - seed=minhash.seed, - drop_hashvalues=True, - storage="dict", - # storage="disk", + storage="disk", + input_batch_size=batch_size, ) - data >> minhash >> minhash_lsh + data >> minhash distiset = pipeline.run(use_cache=False) ds = distiset["default"]["train"] diff --git a/tests/unit/steps/test_filtering/test_minhash.py b/tests/unit/steps/test_filtering/test_minhash.py index 9be0256cbd..f4a6d6c225 100644 --- a/tests/unit/steps/test_filtering/test_minhash.py +++ b/tests/unit/steps/test_filtering/test_minhash.py @@ -14,12 +14,10 @@ from typing import List -import numpy as np import pytest from distilabel.steps.filtering.minhash import ( - MinHash, - MinHashLSH, + MinHashDedup, tokenize_on_ngrams, tokenized_on_words, ) @@ -46,35 +44,20 @@ def test_tokenize_on_ngrams(n: int) -> None: assert all(len(t) == n for t in tokenized[0]) -class TestMinHash: - @pytest.mark.parametrize("tokenizer", ["words", "ngrams"]) - def test_process(self, tokenizer: str) -> None: - hasher = MinHash(tokenizer=tokenizer, n=3) - hasher.load() - result = next(hasher.process([{"text": t} for t in texts])) - hashvalues = result[0]["hashvalues"] - assert isinstance(hashvalues, np.ndarray) - - -class TestMinHashLSH: +class TestMinHashDedup: @pytest.mark.parametrize( "threshold, keep_row_after_minhash_filtering, storage", - [ - (0.1, 1, "dict"), - (0.9, 4, "dict"), - # (0.9, 4, "disk") # This test is skipped because it fails while testing on CI - ], + [(0.1, 1, "dict"), (0.9, 4, "dict"), (0.9, 4, "disk")], ) def test_process( self, threshold: float, keep_row_after_minhash_filtering: int, storage: str ) -> None: - hasher = MinHash() - hasher.load() - results_with_hashes = next(hasher.process([{"text": t} for t in texts])) - - minhash_lsh = MinHashLSH(threshold=threshold, seed=hasher.seed, storage=storage) - minhash_lsh.load() - result = next(minhash_lsh.process(results_with_hashes)) + msh = MinHashDedup( + threshold=threshold, + storage=storage, + ) + msh.load() + result = next(msh.process([{"text": t} for t in texts])) duplicated = [r["keep_row_after_minhash_filtering"] for r in result] assert sum(duplicated) == keep_row_after_minhash_filtering - minhash_lsh.unload() + msh.unload() From 45561359d628b06fae032eb78a3e2d60af20feb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Mart=C3=ADn=20Bl=C3=A1zquez?= Date: Mon, 2 Sep 2024 12:18:21 +0200 Subject: [PATCH 37/82] Update `make_generator_step` to set pipeline to step and add edge to steps in trophic level 1 (#936) * Update `make_generator_step` * Update unit tests --- src/distilabel/pipeline/_dag.py | 9 +++++---- src/distilabel/pipeline/base.py | 2 +- src/distilabel/steps/generators/utils.py | 4 ++++ tests/unit/steps/generators/test_utils.py | 11 +++++++++-- 4 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/distilabel/pipeline/_dag.py b/src/distilabel/pipeline/_dag.py index 437021c68c..383703ccf4 100644 --- a/src/distilabel/pipeline/_dag.py +++ b/src/distilabel/pipeline/_dag.py @@ -154,8 +154,9 @@ def add_root_step(self, step: "GeneratorStep") -> None: Args: step: The generator step that will be set as the new root. """ - self.add_step(step) - self.add_edge(step.name, next(iter(self))) + for other_step, level in self.trophic_levels.items(): + if level == 1 and other_step != step.name: + self.add_edge(step.name, other_step) # type: ignore @cached_property def root_steps(self) -> Set[str]: @@ -175,14 +176,14 @@ def leaf_steps(self) -> Set[str]: """ return {node for node, degree in self.G.out_degree() if degree == 0} - @cached_property + @property def trophic_levels(self) -> Dict[str, int]: """The trophic level of each step in the DAG. Returns: A dictionary with the trophic level of each step. """ - return {step: int(level) for step, level in nx.trophic_levels(self.G).items()} + return nx.trophic_levels(self.G) def get_step_predecessors(self, step_name: str) -> Iterable[str]: """Gets the predecessors of a step. diff --git a/src/distilabel/pipeline/base.py b/src/distilabel/pipeline/base.py index bc75ed9324..b6e1e52957 100644 --- a/src/distilabel/pipeline/base.py +++ b/src/distilabel/pipeline/base.py @@ -471,7 +471,7 @@ def _add_dataset_generator_step(self, dataset: "InputDataset") -> None: f" `GeneratorStep`: {step}", page="sections/how_to_guides/basic/step/#types-of-steps", ) - loader = make_generator_step(dataset) + loader = make_generator_step(dataset, self) self.dag.add_root_step(loader) def get_runtime_parameters_info(self) -> "PipelineRuntimeParametersInfo": diff --git a/src/distilabel/steps/generators/utils.py b/src/distilabel/steps/generators/utils.py index f99ae41c6e..de119ab2ef 100644 --- a/src/distilabel/steps/generators/utils.py +++ b/src/distilabel/steps/generators/utils.py @@ -21,11 +21,13 @@ from distilabel.steps.base import StepResources if TYPE_CHECKING: + from distilabel.pipeline.base import BasePipeline from distilabel.steps import GeneratorStep def make_generator_step( dataset: Union[Dataset, pd.DataFrame, List[Dict[str, str]]], + pipeline: Union["BasePipeline", None] = None, batch_size: int = 50, input_mappings: Optional[Dict[str, str]] = None, output_mappings: Optional[Dict[str, str]] = None, @@ -52,6 +54,7 @@ def make_generator_step( if isinstance(dataset, list): return LoadDataFromDicts( + pipeline=pipeline, data=dataset, batch_size=batch_size, input_mappings=input_mappings or {}, @@ -70,6 +73,7 @@ def make_generator_step( ) loader = LoadDataFromHub( + pipeline=pipeline, repo_id="placeholder_name", batch_size=batch_size, input_mappings=input_mappings or {}, diff --git a/tests/unit/steps/generators/test_utils.py b/tests/unit/steps/generators/test_utils.py index 67323bb9c4..f25f260f79 100644 --- a/tests/unit/steps/generators/test_utils.py +++ b/tests/unit/steps/generators/test_utils.py @@ -18,7 +18,8 @@ import pytest from datasets import Dataset -from distilabel.steps import make_generator_step +from distilabel.pipeline.local import Pipeline +from distilabel.steps.generators.utils import make_generator_step data = [{"instruction": "Tell me a joke."}] * 10 @@ -26,7 +27,7 @@ @pytest.mark.parametrize("dataset", (data, Dataset.from_list(data), pd.DataFrame(data))) def test_make_generator_step( dataset: Union[Dataset, pd.DataFrame, List[Dict[str, str]]], -): +) -> None: batch_size = 5 load_dataset = make_generator_step( dataset, batch_size=batch_size, output_mappings={"instruction": "other"} @@ -40,3 +41,9 @@ def test_make_generator_step( assert isinstance(load_dataset.data, list) assert load_dataset.output_mappings == {"instruction": "other"} + + +def test_make_generator_step_with_pipeline() -> None: + pipeline = Pipeline() + load_dataset = make_generator_step(data, pipeline=pipeline) + assert load_dataset.pipeline == pipeline From d5f2ae3358fc2d94be41ce82d0c472d6bdad7642 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Mart=C3=ADn=20Bl=C3=A1zquez?= Date: Mon, 2 Sep 2024 12:37:45 +0200 Subject: [PATCH 38/82] Add `CombineOutputs` step (#939) * Add `CombineOutputs` step * Add `merge_distilabel_metadata` function * Add unit tests * Add docstrings * Update docs * Update src/distilabel/steps/columns/combine.py Co-authored-by: Agus * Update docstrings * Update mkdocs --------- Co-authored-by: Agus --- docs/api/pipeline/utils.md | 3 - docs/api/step_gallery/columns.md | 1 + mkdocs.yml | 5 +- src/distilabel/steps/__init__.py | 22 +++-- src/distilabel/steps/columns/combine.py | 99 +++++++++++++++++++ src/distilabel/steps/columns/expand.py | 3 + src/distilabel/steps/columns/group.py | 8 +- src/distilabel/steps/columns/keep.py | 3 + src/distilabel/steps/columns/merge.py | 5 +- .../{pipeline => steps/columns}/utils.py | 53 +++++++++- .../steps/embeddings/embedding_generation.py | 3 + .../utils/mkdocs/components_gallery.py | 1 + tests/unit/steps/columns/__init__.py | 14 +++ tests/unit/steps/columns/test_combine.py | 54 ++++++++++ tests/unit/steps/columns/test_group.py | 16 ++- 15 files changed, 265 insertions(+), 25 deletions(-) delete mode 100644 docs/api/pipeline/utils.md create mode 100644 src/distilabel/steps/columns/combine.py rename src/distilabel/{pipeline => steps/columns}/utils.py (66%) create mode 100644 tests/unit/steps/columns/__init__.py create mode 100644 tests/unit/steps/columns/test_combine.py diff --git a/docs/api/pipeline/utils.md b/docs/api/pipeline/utils.md deleted file mode 100644 index c8ad6f2e54..0000000000 --- a/docs/api/pipeline/utils.md +++ /dev/null @@ -1,3 +0,0 @@ -# Pipeline Utils - -::: distilabel.pipeline.utils diff --git a/docs/api/step_gallery/columns.md b/docs/api/step_gallery/columns.md index 9e5392d850..7b75053e6a 100644 --- a/docs/api/step_gallery/columns.md +++ b/docs/api/step_gallery/columns.md @@ -6,3 +6,4 @@ This section contains the existing steps intended to be used for common column o ::: distilabel.steps.columns.keep ::: distilabel.steps.columns.merge ::: distilabel.steps.columns.group +::: distilabel.steps.columns.utils diff --git a/mkdocs.yml b/mkdocs.yml index b247cfa138..72c81ff8b8 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -152,9 +152,9 @@ plugins: # Members inherited_members: false # allow looking up inherited methods members_order: source # order methods according to their order of definition in the source code, not alphabetical order - show_labels : true + show_labels: true # Docstring - docstring_style: google # more info: https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html + docstring_style: google # more info: https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html show_if_no_docstring: false # Signature separate_signature: false @@ -240,7 +240,6 @@ nav: - Routing Batch Function: "api/pipeline/routing_batch_function.md" - Typing: "api/pipeline/typing.md" - Step Wrapper: "api/pipeline/step_wrapper.md" - - Utils: "api/pipeline/utils.md" - Mixins: - RuntimeParametersMixin: "api/mixins/runtime_parameters.md" - RequirementsMixin: "api/mixins/requirements.md" diff --git a/src/distilabel/steps/__init__.py b/src/distilabel/steps/__init__.py index 56519ede68..835f354bd5 100644 --- a/src/distilabel/steps/__init__.py +++ b/src/distilabel/steps/__init__.py @@ -21,6 +21,7 @@ StepInput, StepResources, ) +from distilabel.steps.columns.combine import CombineOutputs from distilabel.steps.columns.expand import ExpandColumns from distilabel.steps.columns.group import CombineColumns, GroupColumns from distilabel.steps.columns.keep import KeepColumns @@ -54,22 +55,26 @@ __all__ = [ "PreferenceToArgilla", "TextGenerationToArgilla", + "GeneratorStep", + "GlobalStep", + "Step", + "StepInput", "StepResources", + "CombineOutputs", + "ExpandColumns", + "CombineColumns", "GroupColumns", + "KeepColumns", "MergeColumns", - "CombineColumns", - "ConversationTemplate", + "step", "DeitaFiltering", "EmbeddingGeneration", "FaissNearestNeighbour", - "ExpandColumns", + "ConversationTemplate", "FormatChatGenerationDPO", - "FormatChatGenerationSFT", "FormatTextGenerationDPO", + "FormatChatGenerationSFT", "FormatTextGenerationSFT", - "GeneratorStep", - "GlobalStep", - "KeepColumns", "LoadDataFromDicts", "LoadDataFromDisk", "LoadDataFromFileSystem", @@ -77,11 +82,8 @@ "MinHashDedup", "make_generator_step", "PushToHub", - "Step", - "StepInput", "RewardModelScore", "TruncateTextColumn", "GeneratorStepOutput", "StepOutput", - "step", ] diff --git a/src/distilabel/steps/columns/combine.py b/src/distilabel/steps/columns/combine.py new file mode 100644 index 0000000000..784beffe47 --- /dev/null +++ b/src/distilabel/steps/columns/combine.py @@ -0,0 +1,99 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import TYPE_CHECKING + +from distilabel.constants import DISTILABEL_METADATA_KEY +from distilabel.steps.base import Step, StepInput +from distilabel.steps.columns.utils import merge_distilabel_metadata + +if TYPE_CHECKING: + from distilabel.steps.typing import StepOutput + + +class CombineOutputs(Step): + """Combine the outputs of several upstream steps. + + `CombineOutputs` is a `Step` that takes the outputs of several upstream steps and combines + them to generate a new dictionary with all keys/columns of the upstream steps outputs. + + Input columns: + - dynamic (based on the upstream `Step`s): All the columns of the upstream steps outputs. + + Output columns: + - dynamic (based on the upstream `Step`s): All the columns of the upstream steps outputs. + + Categories: + - columns + + Examples: + + Combine dictionaries of a dataset: + + ```python + from distilabel.steps import CombineOutputs + + combine_outputs = CombineOutputs() + combine_outputs.load() + + result = next( + combine_outputs.process( + [{"a": 1, "b": 2}, {"a": 3, "b": 4}], + [{"c": 5, "d": 6}, {"c": 7, "d": 8}], + ) + ) + # [ + # {"a": 1, "b": 2, "c": 5, "d": 6}, + # {"a": 3, "b": 4, "c": 7, "d": 8}, + # ] + ``` + + Combine upstream steps outputs in a pipeline: + + ```python + from distilabel.pipeline import Pipeline + from distilabel.steps import CombineOutputs + + with Pipeline() as pipeline: + step_1 = ... + step_2 = ... + step_3 = ... + combine = CombineOutputs() + + [step_1, step_2, step_3] >> combine + ``` + """ + + def process(self, *inputs: StepInput) -> "StepOutput": + combined_outputs = [] + for output_dicts in zip(*inputs): + combined_dict = {} + for output_dict in output_dicts: + combined_dict.update( + { + k: v + for k, v in output_dict.items() + if k != DISTILABEL_METADATA_KEY + } + ) + + if any( + DISTILABEL_METADATA_KEY in output_dict for output_dict in output_dicts + ): + combined_dict[DISTILABEL_METADATA_KEY] = merge_distilabel_metadata( + *output_dicts + ) + combined_outputs.append(combined_dict) + + yield combined_outputs diff --git a/src/distilabel/steps/columns/expand.py b/src/distilabel/steps/columns/expand.py index bb7d2fe9f4..709ca4bc66 100644 --- a/src/distilabel/steps/columns/expand.py +++ b/src/distilabel/steps/columns/expand.py @@ -42,6 +42,9 @@ class ExpandColumns(Step): Output columns: - dynamic (determined by `columns` attribute): The expanded columns. + Categories: + - columns + Examples: Expand the selected columns into multiple rows: diff --git a/src/distilabel/steps/columns/group.py b/src/distilabel/steps/columns/group.py index 852b1c5205..876af1f0ad 100644 --- a/src/distilabel/steps/columns/group.py +++ b/src/distilabel/steps/columns/group.py @@ -17,8 +17,8 @@ from typing_extensions import override -from distilabel.pipeline.utils import group_columns from distilabel.steps.base import Step, StepInput +from distilabel.steps.columns.utils import group_columns if TYPE_CHECKING: from distilabel.steps.typing import StepColumns, StepOutput @@ -43,8 +43,12 @@ class GroupColumns(Step): - dynamic (determined by `columns` and `output_columns` attributes): The columns that were grouped. + Categories: + - columns + Examples: - Combine columns of a dataset: + + Group columns of a dataset: ```python from distilabel.steps import GroupColumns diff --git a/src/distilabel/steps/columns/keep.py b/src/distilabel/steps/columns/keep.py index 88ae7d5409..c12dfdd61d 100644 --- a/src/distilabel/steps/columns/keep.py +++ b/src/distilabel/steps/columns/keep.py @@ -44,6 +44,9 @@ class KeepColumns(Step): Output columns: - dynamic (determined by `columns` attribute): The columns that were kept. + Categories: + - columns + Examples: Select the columns to keep: diff --git a/src/distilabel/steps/columns/merge.py b/src/distilabel/steps/columns/merge.py index 802b17a7df..54ab3e3c75 100644 --- a/src/distilabel/steps/columns/merge.py +++ b/src/distilabel/steps/columns/merge.py @@ -16,8 +16,8 @@ from typing_extensions import override -from distilabel.pipeline.utils import merge_columns from distilabel.steps.base import Step, StepInput +from distilabel.steps.columns.utils import merge_columns if TYPE_CHECKING: from distilabel.steps.typing import StepColumns, StepOutput @@ -47,6 +47,9 @@ class MergeColumns(Step): - dynamic (determined by `columns` and `output_column` attributes): The columns that were merged. + Categories: + - columns + Examples: Combine columns in rows of a dataset: diff --git a/src/distilabel/pipeline/utils.py b/src/distilabel/steps/columns/utils.py similarity index 66% rename from src/distilabel/pipeline/utils.py rename to src/distilabel/steps/columns/utils.py index b5bd9e1b0e..7b3efe2262 100644 --- a/src/distilabel/pipeline/utils.py +++ b/src/distilabel/steps/columns/utils.py @@ -12,16 +12,47 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Any, Dict, List, Optional +from collections import defaultdict +from typing import TYPE_CHECKING, Any, Dict, List, Optional -from distilabel.steps.base import StepInput +from distilabel.constants import DISTILABEL_METADATA_KEY + +if TYPE_CHECKING: + from distilabel.steps.base import StepInput + + +def merge_distilabel_metadata(*output_dicts: Dict[str, Any]) -> Dict[str, Any]: + """ + Merge the `DISTILABEL_METADATA_KEY` from multiple output dictionaries. + + Args: + *output_dicts: Variable number of dictionaries containing distilabel metadata. + + Returns: + A merged dictionary containing all the distilabel metadata from the input dictionaries. + """ + merged_metadata = defaultdict(list) + + for output_dict in output_dicts: + metadata = output_dict.get(DISTILABEL_METADATA_KEY, {}) + for key, value in metadata.items(): + merged_metadata[key].append(value) + + final_metadata = {} + for key, value_list in merged_metadata.items(): + if len(value_list) == 1: + final_metadata[key] = value_list[0] + else: + final_metadata[key] = value_list + + return final_metadata def group_columns( - *inputs: StepInput, + *inputs: "StepInput", group_columns: List[str], output_group_columns: Optional[List[str]] = None, -) -> StepInput: +) -> "StepInput": """Groups multiple list of dictionaries into a single list of dictionaries on the specified `group_columns`. If `group_columns` are provided, then it will also rename `group_columns`. @@ -49,16 +80,30 @@ def group_columns( # Use zip to iterate over lists based on their index for dicts_at_index in zip(*inputs): combined_dict = {} + metadata_dicts = [] # Iterate over dicts at the same index for d in dicts_at_index: + # Extract metadata for merging + if DISTILABEL_METADATA_KEY in d: + metadata_dicts.append( + {DISTILABEL_METADATA_KEY: d[DISTILABEL_METADATA_KEY]} + ) # Iterate over key-value pairs in each dict for key, value in d.items(): + if key == DISTILABEL_METADATA_KEY: + continue # If the key is in the merge_keys, append the value to the existing list if key in group_columns_dict.keys(): combined_dict.setdefault(group_columns_dict[key], []).append(value) # If the key is not in the merge_keys, create a new key-value pair else: combined_dict[key] = value + + if metadata_dicts: + combined_dict[DISTILABEL_METADATA_KEY] = merge_distilabel_metadata( + *metadata_dicts + ) + result.append(combined_dict) return result diff --git a/src/distilabel/steps/embeddings/embedding_generation.py b/src/distilabel/steps/embeddings/embedding_generation.py index 30dff63ef7..8db3bee2ee 100644 --- a/src/distilabel/steps/embeddings/embedding_generation.py +++ b/src/distilabel/steps/embeddings/embedding_generation.py @@ -36,6 +36,9 @@ class EmbeddingGeneration(Step): Output columns: - embedding (`List[Union[float, int]]`): the generated sentence embedding. + Categories: + - embedding + Examples: Generate sentence embeddings with Sentence Transformers: diff --git a/src/distilabel/utils/mkdocs/components_gallery.py b/src/distilabel/utils/mkdocs/components_gallery.py index d1637d87cf..a7dba7e7da 100644 --- a/src/distilabel/utils/mkdocs/components_gallery.py +++ b/src/distilabel/utils/mkdocs/components_gallery.py @@ -86,6 +86,7 @@ "scorer": ":octicons-number-16:", "text-generation": ":material-text-box-edit:", "text-manipulation": ":material-receipt-text-edit:", + "columns": ":material-table-column:", } diff --git a/tests/unit/steps/columns/__init__.py b/tests/unit/steps/columns/__init__.py new file mode 100644 index 0000000000..20ce00bda7 --- /dev/null +++ b/tests/unit/steps/columns/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + diff --git a/tests/unit/steps/columns/test_combine.py b/tests/unit/steps/columns/test_combine.py new file mode 100644 index 0000000000..817d89e90b --- /dev/null +++ b/tests/unit/steps/columns/test_combine.py @@ -0,0 +1,54 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from distilabel.constants import DISTILABEL_METADATA_KEY +from distilabel.steps.columns.combine import CombineOutputs + + +class TestCombineOutputs: + def test_process(self) -> None: + combine = CombineOutputs() + + output = next( + combine.process( + [ + { + "a": 1, + "b": 2, + DISTILABEL_METADATA_KEY: {"model": "model-1", "a": 1}, + } + ], + [ + { + "c": 3, + "d": 4, + DISTILABEL_METADATA_KEY: {"model": "model-2", "b": 1}, + } + ], + ) + ) + + assert output == [ + { + "a": 1, + "b": 2, + "c": 3, + "d": 4, + DISTILABEL_METADATA_KEY: { + "model": ["model-1", "model-2"], + "a": 1, + "b": 1, + }, + } + ] diff --git a/tests/unit/steps/columns/test_group.py b/tests/unit/steps/columns/test_group.py index 258029d7b9..57f9f114de 100644 --- a/tests/unit/steps/columns/test_group.py +++ b/tests/unit/steps/columns/test_group.py @@ -15,6 +15,7 @@ import pytest +from distilabel.constants import DISTILABEL_METADATA_KEY from distilabel.pipeline.local import Pipeline from distilabel.steps.columns.group import CombineColumns, GroupColumns @@ -44,8 +45,19 @@ def test_process(self) -> None: columns=["a", "b"], pipeline=Pipeline(name="unit-test-pipeline"), ) - output = next(group.process([{"a": 1, "b": 2}], [{"a": 3, "b": 4}])) - assert output == [{"grouped_a": [1, 3], "grouped_b": [2, 4]}] + output = next( + group.process( + [{"a": 1, "b": 2, DISTILABEL_METADATA_KEY: {"model": "model-1"}}], + [{"a": 3, "b": 4, DISTILABEL_METADATA_KEY: {"model": "model-2"}}], + ) + ) + assert output == [ + { + "grouped_a": [1, 3], + "grouped_b": [2, 4], + DISTILABEL_METADATA_KEY: {"model": ["model-1", "model-2"]}, + } + ] def test_CombineColumns_deprecation_warning(): From a2a8e86794cbd0235a8fa0749959f70cdedd143b Mon Sep 17 00:00:00 2001 From: Sara Han <127759186+sdiazlor@users.noreply.github.com> Date: Mon, 2 Sep 2024 12:40:19 +0200 Subject: [PATCH 39/82] update regex (#940) --- src/distilabel/steps/tasks/sentence_transformers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/distilabel/steps/tasks/sentence_transformers.py b/src/distilabel/steps/tasks/sentence_transformers.py index 82f7a2a948..f33a223c63 100644 --- a/src/distilabel/steps/tasks/sentence_transformers.py +++ b/src/distilabel/steps/tasks/sentence_transformers.py @@ -33,7 +33,7 @@ GenerationAction = Literal["paraphrase", "semantically-similar", "query", "answer"] POSITIVE_NEGATIVE_PAIR_REGEX = re.compile( - r"## Positive\s+(.*?)(?:\s+## Negative\s+(.*?))?\s*$", + r"\s*## Positive\s+(.*?)(?:\s+## Negative\s+(.*?))?\s*$", re.DOTALL, ) From 28485d08354b3aa44df9d65e857632c2e673e25e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Mart=C3=ADn=20Bl=C3=A1zquez?= Date: Mon, 2 Sep 2024 12:55:06 +0200 Subject: [PATCH 40/82] Offline batch generation (#923) * Initial work for `offline_batch_generate` * Add code for uploading Batch API files to OpenAI * Add `offline_batch_inference` attribute * `offline_batch_generate` finished for `OpenAILLM` * Add attributes for checking task compatibility with `offline_batch_generation` * Extend `is_global` property for `offline_batch_generation` * Move `job_ids` responsability to `LLM` * And remember... `unload` everything before pickling * Move `BASE_CACHE_DIR` to constants * Recover for offline batch generation * Polling sleep * Store input data for recovering offline batch generation * Lint * Add checking no offline batch generation with `RayPipeline` * Update `LLM`s unit tests * Update `Task`s unit tests * Add `OpenAILLM.offline_batch_generate` unit tests * Fix unit test * Add unit tests for adding recovery batch for offline generation * Update tasks that can be used with offline batch generation * Move aux functions to utils * Handle `_SecretField` and excluded attributes when refreshing pipeline from cache * Fix checking inner type * Add simple integration test * Remove unit test * Fix formatting exception * Update type hint * Handle stopping offline batch generation polling * Use `_stop_called_lock` everywhere * Fix deadlock * Fix load * Add Batch API example * Update examples * How to offline batch generation * Add FAQ about OpenAI Batch API * Update links * Add `envs` module * Add setting pipeline running env variables in child process * Update OpenAI file upload to assign custom name * Download nltk everytime * Add missing arguments * Update logging message * Add section about offline batch generation * Add errors and exceptions API docs * Fix unit test * Update mkdocs.yaml --- docs/api/errors.md | 8 + docs/api/exceptions.md | 7 + docs/sections/getting_started/faq.md | 3 + .../advanced/offline_batch_generation.md | 47 ++ .../saving_step_generated_artifacts.md | 2 +- .../advanced/scaling_with_ray.md | 2 +- .../sections/how_to_guides/basic/llm/index.md | 73 ++- mkdocs.yml | 3 + scripts/install_dependencies.sh | 3 - src/distilabel/constants.py | 24 +- src/distilabel/envs.py | 52 +++ src/distilabel/errors.py | 6 + src/distilabel/exceptions.py | 40 ++ src/distilabel/llms/anthropic.py | 8 +- src/distilabel/llms/anyscale.py | 2 +- src/distilabel/llms/azure.py | 14 +- src/distilabel/llms/base.py | 138 +++++- src/distilabel/llms/cohere.py | 4 +- src/distilabel/llms/groq.py | 4 +- .../llms/huggingface/inference_endpoints.py | 8 +- .../llms/huggingface/transformers.py | 2 +- src/distilabel/llms/litellm.py | 2 +- src/distilabel/llms/llamacpp.py | 4 +- src/distilabel/llms/mistral.py | 2 +- src/distilabel/llms/moa.py | 2 +- src/distilabel/llms/openai.py | 439 +++++++++++++++++- src/distilabel/llms/together.py | 2 +- src/distilabel/llms/vllm.py | 6 +- src/distilabel/mixins/runtime_parameters.py | 38 +- src/distilabel/pipeline/base.py | 289 ++++++++---- src/distilabel/pipeline/batch_manager.py | 18 + src/distilabel/pipeline/local.py | 101 ++-- src/distilabel/pipeline/ray.py | 36 +- src/distilabel/pipeline/step_wrapper.py | 29 +- src/distilabel/steps/generators/data.py | 3 +- src/distilabel/steps/tasks/base.py | 47 +- .../steps/tasks/complexity_scorer.py | 1 + .../steps/tasks/improving_text_embeddings.py | 7 + .../tasks/instruction_backtranslation.py | 1 + src/distilabel/steps/tasks/quality_scorer.py | 1 + src/distilabel/steps/tasks/self_instruct.py | 1 + src/distilabel/steps/tasks/text_generation.py | 9 +- src/distilabel/steps/tasks/ultrafeedback.py | 1 + src/distilabel/utils/logging.py | 5 +- src/distilabel/utils/serialization.py | 8 +- src/distilabel/utils/typing_.py | 31 +- .../test_offline_batch_generation.py | 77 +++ tests/unit/conftest.py | 42 +- .../huggingface/test_inference_endpoints.py | 3 + tests/unit/llms/test_anthropic.py | 3 + tests/unit/llms/test_anyscale.py | 3 + tests/unit/llms/test_azure.py | 6 + tests/unit/llms/test_base.py | 28 ++ tests/unit/llms/test_cohere.py | 6 + tests/unit/llms/test_groq.py | 6 + tests/unit/llms/test_litellm.py | 3 + tests/unit/llms/test_llamacpp.py | 6 + tests/unit/llms/test_mistral.py | 9 + tests/unit/llms/test_moa.py | 14 +- tests/unit/llms/test_ollama.py | 3 + tests/unit/llms/test_openai.py | 338 +++++++++++++- tests/unit/llms/test_together.py | 3 + tests/unit/llms/test_vertexai.py | 3 + tests/unit/pipeline/test_base.py | 9 + tests/unit/pipeline/test_batch_manager.py | 32 ++ tests/unit/pipeline/test_ray.py | 14 + .../{test_filtering => filtering}/__init__.py | 0 .../test_minhash.py | 3 + tests/unit/steps/generators/test_data.py | 6 - .../steps/tasks/evol_instruct/test_base.py | 19 +- .../tasks/evol_instruct/test_generator.py | 17 + .../steps/tasks/evol_quality/test_base.py | 17 + tests/unit/steps/tasks/magpie/test_base.py | 19 +- .../unit/steps/tasks/magpie/test_generator.py | 19 +- .../tasks/structured_outputs/test_outlines.py | 6 + tests/unit/steps/tasks/test_base.py | 87 ++-- .../steps/tasks/test_complexity_scorer.py | 7 +- tests/unit/steps/tasks/test_genstruct.py | 6 +- .../unit/steps/tasks/test_prometheus_eval.py | 22 +- tests/unit/steps/tasks/test_quality_scorer.py | 7 +- tests/unit/steps/tasks/test_self_instruct.py | 6 +- .../steps/tasks/test_sentence_transformers.py | 11 +- .../unit/steps/tasks/test_text_generation.py | 24 +- tests/unit/steps/tasks/test_urial.py | 10 +- 84 files changed, 2077 insertions(+), 350 deletions(-) create mode 100644 docs/api/errors.md create mode 100644 docs/api/exceptions.md create mode 100644 docs/sections/how_to_guides/advanced/offline_batch_generation.md create mode 100644 src/distilabel/envs.py create mode 100644 src/distilabel/exceptions.py create mode 100644 tests/integration/test_offline_batch_generation.py create mode 100644 tests/unit/llms/test_base.py rename tests/unit/steps/{test_filtering => filtering}/__init__.py (100%) rename tests/unit/steps/{test_filtering => filtering}/test_minhash.py (98%) diff --git a/docs/api/errors.md b/docs/api/errors.md new file mode 100644 index 0000000000..9ba2166302 --- /dev/null +++ b/docs/api/errors.md @@ -0,0 +1,8 @@ +# Errors + +This section contains the `distilabel` custom errors. Unlike [exceptions](exceptions.md), errors in `distilabel` are used to handle unexpected situations that can't be anticipated and that can't be handled in a controlled way. + +:::distilabel.errors.DistilabelError +:::distilabel.errors.DistilabelUserError +:::distilabel.errors.DistilabelTypeError +:::distilabel.errors.DistilabelNotImplementedError diff --git a/docs/api/exceptions.md b/docs/api/exceptions.md new file mode 100644 index 0000000000..7f8cb05799 --- /dev/null +++ b/docs/api/exceptions.md @@ -0,0 +1,7 @@ +# Exceptions + +This section contains the `distilabel` custom exceptions. Unlike [errors][../errors.md], exceptions in `distilabel` are used to handle specific situations that can be anticipated and that can be handled in a controlled way internally by the library. + +:::distilabel.exceptions.DistilabelException +:::distilabel.exceptions.DistilabelGenerationException +:::distilabel.exceptions.DistilabelOfflineBatchGenerationNotFinishedException diff --git a/docs/sections/getting_started/faq.md b/docs/sections/getting_started/faq.md index 16ad840757..88c6cd7526 100644 --- a/docs/sections/getting_started/faq.md +++ b/docs/sections/getting_started/faq.md @@ -42,3 +42,6 @@ hide: ??? faq "How can I use the same `LLM` across several tasks without having to load it several times?" You can serve the LLM using a solution like TGI or vLLM, and then connect to it using an `AsyncLLM` client like `InferenceEndpointsLLM` or `OpenAILLM`. Please refer to [Serving LLMs guide](../how_to_guides/advanced/serving_an_llm_for_reuse.md) for more information. + +??? faq "Can `distilabel` be used with [OpenAI Batch API](https://platform.openai.com/docs/guides/batch)?" + Yes, `distilabel` is integrated with OpenAI Batch API via [OpenAILLM][distilabel.llms.openai.OpenAILLM]. Check [LLMs - Offline Batch Generation](../how_to_guides/basic/llm/index.md#offline-batch-generation) for a small example on how to use it and [Advanced - Offline Batch Generation](../how_to_guides/advanced/offline_batch_generation.md) for a more detailed guide. diff --git a/docs/sections/how_to_guides/advanced/offline_batch_generation.md b/docs/sections/how_to_guides/advanced/offline_batch_generation.md new file mode 100644 index 0000000000..b45ad1d716 --- /dev/null +++ b/docs/sections/how_to_guides/advanced/offline_batch_generation.md @@ -0,0 +1,47 @@ +The [offline batch generation](../basic/llm/index.md#offline-batch-generation) is a feature that some `LLM`s implemented in `distilabel` offers, allowing to send the inputs to a LLM-as-a-service platform and waiting for the outputs in a asynchronous manner. LLM-as-a-service platforms offer this feature as it allows them to gather many inputs and creating batches as big as the hardware allows, maximizing the hardware utilization and reducing the cost of the service. In exchange, the user has to wait certain time for the outputs to be ready but the cost per token is usually much lower. + +`distilabel` pipelines are able to handle `LLM`s that offer this feature in the following way: + +* The first time the pipeline gets executed, the `LLM` will send the inputs to the platform. The platform will return jobs ids that can be used later to check the status of the jobs and retrieve the results. The `LLM` will save these jobs ids in its `jobs_ids` attribute and raise an special exception [DistilabelOfflineBatchGenerationNotFinishedException][distilabel.exceptions.DistilabelOfflineBatchGenerationNotFinishedException] that will be handled by the `Pipeline`. The jobs ids will be saved in the pipeline cache, so they can be used in subsequent calls. +* The second time and subsequent calls will recover the pipeline execution and the `LLM` won't send the inputs again to the platform. This time as it has the `jobs_ids` it will check if the jobs have finished, and if they have then it will retrieve the results and return the outputs. If they haven't finished, then it will raise again `DistilabelOfflineBatchGenerationNotFinishedException` again. +* In addition, LLMs with offline batch generation can be specified to do polling until the jobs have finished, blocking the pipeline until they are done. If for some reason the polling needs to be stopped, one can press ++ctrl+c++ or ++cmd+c++ depending on your OS (or send a `SIGINT` to the main process) which will stop the polling and raise `DistilabelOfflineBatchGenerationNotFinishedException` that will be handled by the pipeline as described above. + +!!! WARNING + + In order to recover the pipeline execution and retrieve the results, the pipeline cache must be enabled. If the pipeline cache is disabled, then it will send the inputs again and create different jobs incurring in extra costs. + + +## Example pipeline using `OpenAILLM` with offline batch generation + +```python +from distilabel.llms import OpenAILLM +from distilabel.pipeline import Pipeline +from distilabel.steps import LoadDataFromHub +from distilabel.steps.tasks import TextGeneration + +with Pipeline() as pipeline: + load_data = LoadDataFromHub(output_mappings={"prompt": "instruction"}) + + text_generation = TextGeneration( + llm=OpenAILLM( + model="gpt-3.5-turbo", + use_offline_batch_generation=True, # (1) + ) + ) + + load_data >> text_generation + + +if __name__ == "__main__": + distiset = pipeline.run( + parameters={ + load_data.name: { + "repo_id": "distilabel-internal-testing/instruction-dataset", + "split": "test", + "batch_size": 500, + }, + } + ) +``` + +1. Indicate that the `OpenAILLM` should use offline batch generation. diff --git a/docs/sections/how_to_guides/advanced/saving_step_generated_artifacts.md b/docs/sections/how_to_guides/advanced/saving_step_generated_artifacts.md index 9e89f07491..3d2e566047 100644 --- a/docs/sections/how_to_guides/advanced/saving_step_generated_artifacts.md +++ b/docs/sections/how_to_guides/advanced/saving_step_generated_artifacts.md @@ -1,6 +1,6 @@ # Saving step generated artifacts -Some `Step`s might need to produce an auxiliary artifact that is not a result of the computation, but is needed for the computation. For example, the [`FaissNearestNeighbour`](/distilabel/components-gallery/steps/faissnearestneighbour/) needs to create a Faiss index to compute the output of the step which are the top `k` nearest neighbours for each input. Generating the Faiss index takes time and it could potentially be reused outside of the `distilabel` pipeline, so it would be a shame not saving it. +Some `Step`s might need to produce an auxiliary artifact that is not a result of the computation, but is needed for the computation. For example, the [`FaissNearestNeighbour`](../../../components-gallery/steps/faissnearestneighbour.md) needs to create a Faiss index to compute the output of the step which are the top `k` nearest neighbours for each input. Generating the Faiss index takes time and it could potentially be reused outside of the `distilabel` pipeline, so it would be a shame not saving it. For this reason, `Step`s have a method called `save_artifact` that allows saving artifacts that will be included along the outputs of the pipeline in the generated [`Distiset`][distilabel.distiset.Distiset]. The generated artifacts will be uploaded and saved when using `Distiset.push_to_hub` or `Distiset.save_to_disk` respectively. Let's see how to use it with a simple example. diff --git a/docs/sections/how_to_guides/advanced/scaling_with_ray.md b/docs/sections/how_to_guides/advanced/scaling_with_ray.md index 4a8b480126..be959c8b72 100644 --- a/docs/sections/how_to_guides/advanced/scaling_with_ray.md +++ b/docs/sections/how_to_guides/advanced/scaling_with_ray.md @@ -85,7 +85,7 @@ if __name__ == "__main__": 1. We're setting [resources](assigning_resources_to_step.md) for the `text_generation` step and defining that we want two replicas and one GPU per replica. `distilabel` will create two replicas of the step i.e. two actors in the Ray cluster, and each actor will request to be allocated in a node of the cluster that have at least one GPU. You can read more about how Ray manages the resources [here](https://docs.ray.io/en/latest/ray-core/scheduling/resources.html#resources). 2. You should modify this and add your user or organization on the Hugging Face Hub. -It's a basic pipeline with just two steps: one to load a dataset from the Hub with an `instruction` column and one to generate a `response` for that instruction using Llama 3 8B Instruct with [vLLM](/distilabel/components-gallery/llms/vllm/). Simple but enough to demonstrate how to distribute and scale the workload using a Ray cluster! +It's a basic pipeline with just two steps: one to load a dataset from the Hub with an `instruction` column and one to generate a `response` for that instruction using Llama 3 8B Instruct with [vLLM](../../../components-gallery/llms/vllm.md). Simple but enough to demonstrate how to distribute and scale the workload using a Ray cluster! ### Using Ray Jobs API diff --git a/docs/sections/how_to_guides/basic/llm/index.md b/docs/sections/how_to_guides/basic/llm/index.md index 4bd5f9de2b..46716e4352 100644 --- a/docs/sections/how_to_guides/basic/llm/index.md +++ b/docs/sections/how_to_guides/basic/llm/index.md @@ -5,12 +5,12 @@ LLM subclasses are designed to be used within a [Task][distilabel.steps.tasks.Task], but they can also be used standalone. ```python -from distilabel.llms import OpenAILLM +from distilabel.llms import InferenceEndpointsLLM -llm = OpenAILLM(model="gpt-4") +llm = InferenceEndpointsLLM(model="meta-llama/Meta-Llama-3.1-70B-Instruct") llm.load() -llm.generate( +llm.generate_outputs( inputs=[ [{"role": "user", "content": "What's the capital of Spain?"}], ], @@ -21,6 +21,69 @@ llm.generate( !!! NOTE Always call the `LLM.load` or `Task.load` method when using LLMs standalone or as part of a `Task`. If using a `Pipeline`, this is done automatically in `Pipeline.run()`. +### Offline Batch Generation + +By default, all `LLM`s will generate text in a synchronous manner i.e. send inputs using `generate_outputs` method that will get blocked until outputs are generated. There are some `LLM`s (such as [OpenAILLM][distilabel.llms.openai.OpenAILLM]) that implements what we denote as _offline batch generation_, which allows to send the inputs to the LLM-as-a-service which will generate the outputs asynchronously and give us a job id that we can use later to check the status and retrieve the generated outputs when they are ready. LLM-as-a-service platforms offers this feature as a way to save costs in exchange of waiting for the outputs to be generated. + +To use this feature in `distilabel` the only thing we need to do is to set the `use_offline_batch_generation` attribute to `True` when creating the `LLM` instance: + +```python +from distilabel.llms import OpenAILLM + +llm = OpenAILLM( + model="gpt-4o", + use_offline_batch_generation=True, +) + +llm.load() + +llm.jobs_ids # (1) +# None + +llm.generate_outputs( # (2) + inputs=[ + [{"role": "user", "content": "What's the capital of Spain?"}], + ], +) +# DistilabelOfflineBatchGenerationNotFinishedException: Batch generation with jobs_ids=('batch_OGB4VjKpu2ay9nz3iiFJxt5H',) is not finished + +llm.jobs_ids # (3) +# ('batch_OGB4VjKpu2ay9nz3iiFJxt5H',) + + +llm.generate_outputs( # (4) + inputs=[ + [{"role": "user", "content": "What's the capital of Spain?"}], + ], +) +# "The capital of Spain is Madrid." +``` + +1. At first the `jobs_ids` attribute is `None`. +2. The first call to `generate_outputs` will send the inputs to the LLM-as-a-service and return a `DistilabelOfflineBatchGenerationNotFinishedException` since the outputs are not ready yet. +3. After the first call to `generate_outputs` the `jobs_ids` attribute will contain the job ids created for generating the outputs. +4. The second call or subsequent calls to `generate_outputs` will return the outputs if they are ready or raise a `DistilabelOfflineBatchGenerationNotFinishedException` if they are not ready yet. + +The `offline_batch_generation_block_until_done` attribute can be used to block the `generate_outputs` method until the outputs are ready polling the platform the specified amount of seconds. + +```python +from distilabel.llms import OpenAILLM + +llm = OpenAILLM( + model="gpt-4o", + use_offline_batch_generation=True, + offline_batch_generation_block_until_done=5, # poll for results every 5 seconds +) +llm.load() + +llm.generate_outputs( + inputs=[ + [{"role": "user", "content": "What's the capital of Spain?"}], + ], +) +# "The capital of Spain is Madrid." +``` + ### Within a Task Pass the LLM as an argument to the [`Task`][distilabel.steps.tasks.Task], and the task will handle the rest. @@ -81,7 +144,7 @@ To create custom LLMs, subclass either [`LLM`][distilabel.llms.LLM] for synchron * `generate`: A method that takes a list of prompts and returns generated texts. * `agenerate`: A method that takes a single prompt and returns generated texts. This method is used within the `generate` method of the `AsyncLLM` class. -* + * (optional) `get_last_hidden_state`: is a method that will take a list of prompts and return a list of hidden states. This method is optional and will be used by some tasks such as the [`GenerateEmbeddings`][distilabel.steps.tasks.GenerateEmbeddings] task. @@ -142,4 +205,4 @@ To create custom LLMs, subclass either [`LLM`][distilabel.llms.LLM] for synchron ## Available LLMs -[Our LLM gallery](/distilabel/components-gallery/llms/) shows a list of the available LLMs that can be used within the `distilabel` library. \ No newline at end of file +[Our LLM gallery](../../../../components-gallery/llms/index.md) shows a list of the available LLMs that can be used within the `distilabel` library. diff --git a/mkdocs.yml b/mkdocs.yml index 72c81ff8b8..54fd76d244 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -188,6 +188,7 @@ nav: - Cache and recover pipeline executions: "sections/how_to_guides/advanced/caching.md" - Export data to Argilla: "sections/how_to_guides/advanced/argilla.md" - Structured data generation: "sections/how_to_guides/advanced/structured_generation.md" + - Offline Batch Generation: "sections/how_to_guides/advanced/offline_batch_generation.md" - Specify requirements for pipelines and steps: "sections/how_to_guides/advanced/pipeline_requirements.md" - Using CLI to explore and re-run existing Pipelines: "sections/how_to_guides/advanced/cli/index.md" - Using a file system to pass data of batches between steps: "sections/how_to_guides/advanced/fs_to_pass_data.md" @@ -243,6 +244,8 @@ nav: - Mixins: - RuntimeParametersMixin: "api/mixins/runtime_parameters.md" - RequirementsMixin: "api/mixins/requirements.md" + - Exceptions: "api/exceptions.md" + - Errors: "api/errors.md" - Distiset: "api/distiset.md" - CLI: "api/cli.md" - Community: diff --git a/scripts/install_dependencies.sh b/scripts/install_dependencies.sh index aa10405a8c..3f7669deec 100755 --- a/scripts/install_dependencies.sh +++ b/scripts/install_dependencies.sh @@ -8,9 +8,6 @@ python -m pip install uv uv pip install --system -e ".[anthropic,argilla,cohere,groq,hf-inference-endpoints,hf-transformers,litellm,llama-cpp,ollama,openai,outlines,vertexai,mistralai,instructor,sentence-transformers,faiss-cpu,minhash]" -# For the tests of minhash -python -c "import nltk; nltk.download('punkt_tab')" - if [ "${python_version}" != "(3, 12)" ]; then uv pip install --system -e .[ray] fi diff --git a/src/distilabel/constants.py b/src/distilabel/constants.py index 87bf7708a6..44554f8423 100644 --- a/src/distilabel/constants.py +++ b/src/distilabel/constants.py @@ -12,12 +12,17 @@ # See the License for the specific language governing permissions and # limitations under the License. +from pathlib import Path from typing import Final # Steps related constants DISTILABEL_METADATA_KEY: Final[str] = "distilabel_metadata" -# Pipeline related constants +# Cache +BASE_CACHE_DIR = Path.home() / ".cache" / "distilabel" +PIPELINES_CACHE_DIR = BASE_CACHE_DIR / "pipelines" + +# Pipeline dag related constants STEP_ATTR_NAME: Final[str] = "step" INPUT_QUEUE_ATTR_NAME: Final[str] = "input_queue" RECEIVES_ROUTED_BATCHES_ATTR_NAME: Final[str] = "receives_routed_batches" @@ -25,6 +30,11 @@ CONVERGENCE_STEP_ATTR_NAME: Final[str] = "convergence_step" LAST_BATCH_SENT_FLAG: Final[str] = "last_batch_sent" +# Pipeline execution related constants +PIPELINE_NAME_ENV_NAME = "DISTILABEL_PIPELINE_NAME" +PIPELINE_CACHE_ID_ENV_NAME = "DISTILABEL_PIPELINE_CACHE_ID" +SIGINT_HANDLER_CALLED_ENV_NAME = "sigint_handler_called" + # Data paths constants STEPS_OUTPUTS_PATH = "steps_outputs" STEPS_ARTIFACTS_PATH = "steps_artifacts" @@ -40,11 +50,21 @@ __all__ = [ + "DISTILABEL_METADATA_KEY", + "BASE_CACHE_DIR", + "PIPELINES_CACHE_DIR", "STEP_ATTR_NAME", "INPUT_QUEUE_ATTR_NAME", "RECEIVES_ROUTED_BATCHES_ATTR_NAME", "ROUTING_BATCH_FUNCTION_ATTR_NAME", "CONVERGENCE_STEP_ATTR_NAME", "LAST_BATCH_SENT_FLAG", - "DISTILABEL_METADATA_KEY", + "SIGINT_HANDLER_CALLED_ENV_NAME", + "STEPS_OUTPUTS_PATH", + "STEPS_ARTIFACTS_PATH", + "DISTISET_CONFIG_FOLDER", + "DISTISET_ARTIFACTS_FOLDER", + "PIPELINE_CONFIG_FILENAME", + "PIPELINE_LOG_FILENAME", + "DISTILABEL_DOCS_URL", ] diff --git a/src/distilabel/envs.py b/src/distilabel/envs.py new file mode 100644 index 0000000000..500c736e52 --- /dev/null +++ b/src/distilabel/envs.py @@ -0,0 +1,52 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Idea from: https://github.com/vllm-project/vllm/blob/main/vllm/envs.py + +import os +from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional + +from distilabel import constants + +if TYPE_CHECKING: + DISTILABEL_LOG_LEVEL: str = "INFO" + DISTILABEL_PIPELINE_NAME: Optional[str] = None + DISTILABEL_PIPELINE_CACHE_ID: Optional[str] = None + DISTILABEL_CACHE_DIR: Optional[str] = None + +ENVIRONMENT_VARIABLES: Dict[str, Callable[[], Any]] = { + # `distilabel` logging level. + "DISTILABEL_LOG_LEVEL": lambda: os.getenv("DISTILABEL_LOG_LEVEL", "INFO").upper(), + # The name of the `distilabel` pipeline currently running. + constants.PIPELINE_NAME_ENV_NAME: lambda: os.getenv( + constants.PIPELINE_NAME_ENV_NAME, None + ), + # The cache ID of the `distilabel` pipeline currently running. + constants.PIPELINE_CACHE_ID_ENV_NAME: lambda: os.getenv( + constants.PIPELINE_CACHE_ID_ENV_NAME, None + ), + # The cache ID of the `distilabel` pipeline currently running. + "DISTILABEL_CACHE_DIR": lambda: os.getenv("DISTILABEL_CACHE_DIR", None), +} + + +def __getattr__(name: str) -> Any: + # lazy evaluation of environment variables + if name in ENVIRONMENT_VARIABLES: + return ENVIRONMENT_VARIABLES[name]() + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + + +def __dir__() -> List[str]: + return list(ENVIRONMENT_VARIABLES.keys()) diff --git a/src/distilabel/errors.py b/src/distilabel/errors.py index 41660a6373..71603aed7f 100644 --- a/src/distilabel/errors.py +++ b/src/distilabel/errors.py @@ -59,3 +59,9 @@ class DistilabelTypeError(DistilabelError, TypeError): """TypeError that we can redirect to a given page in the documentation.""" pass + + +class DistilabelNotImplementedError(DistilabelError, NotImplementedError): + """NotImplementedError that we can redirect to a given page in the documentation.""" + + pass diff --git a/src/distilabel/exceptions.py b/src/distilabel/exceptions.py new file mode 100644 index 0000000000..79b4f3cfb5 --- /dev/null +++ b/src/distilabel/exceptions.py @@ -0,0 +1,40 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +from typing import Tuple + + +class DistilabelException(Exception): + """Base exception (can be gracefully handled) for `distilabel` framework.""" + + pass + + +class DistilabelGenerationException(DistilabelException): + """Base exception for `LLM` generation errors.""" + + pass + + +class DistilabelOfflineBatchGenerationNotFinishedException( + DistilabelGenerationException +): + """Exception raised when a batch generation is not finished.""" + + jobs_ids: Tuple[str, ...] + + def __init__(self, jobs_ids: Tuple[str, ...]) -> None: + self.jobs_ids = jobs_ids + super().__init__(f"Batch generation with jobs_ids={jobs_ids} is not finished") diff --git a/src/distilabel/llms/anthropic.py b/src/distilabel/llms/anthropic.py index 05e01c2334..f938da58d2 100644 --- a/src/distilabel/llms/anthropic.py +++ b/src/distilabel/llms/anthropic.py @@ -84,11 +84,7 @@ class AnthropicLLM(AsyncLLM): llm.load() - # Synchronous request - output = llm.generate(inputs=[[{"role": "user", "content": "Hello world!"}]]) - - # Asynchronous request - output = await llm.agenerate(input=[{"role": "user", "content": "Hello world!"}]) + output = llm.generate_outputs(inputs=[[{"role": "user", "content": "Hello world!"}]]) ``` Generate structured data: @@ -110,7 +106,7 @@ class User(BaseModel): llm.load() - output = llm.generate(inputs=[[{"role": "user", "content": "Create a user profile for the following marathon"}]]) + output = llm.generate_outputs(inputs=[[{"role": "user", "content": "Create a user profile for the following marathon"}]]) ``` """ diff --git a/src/distilabel/llms/anyscale.py b/src/distilabel/llms/anyscale.py index d7db410606..1d4114d383 100644 --- a/src/distilabel/llms/anyscale.py +++ b/src/distilabel/llms/anyscale.py @@ -49,7 +49,7 @@ class AnyscaleLLM(OpenAILLM): llm.load() - output = llm.generate(inputs=[[{"role": "user", "content": "Hello world!"}]]) + output = llm.generate_outputs(inputs=[[{"role": "user", "content": "Hello world!"}]]) ``` """ diff --git a/src/distilabel/llms/azure.py b/src/distilabel/llms/azure.py index 8d83a82516..58ed15010f 100644 --- a/src/distilabel/llms/azure.py +++ b/src/distilabel/llms/azure.py @@ -57,11 +57,7 @@ class AzureOpenAILLM(OpenAILLM): llm.load() - # Synchrounous request - output = llm.generate(inputs=[[{"role": "user", "content": "Hello world!"}]]) - - # Asynchronous request - output = await llm.agenerate(input=[{"role": "user", "content": "Hello world!"}]) + output = llm.generate_outputs(inputs=[[{"role": "user", "content": "Hello world!"}]]) ``` Generate text from a custom endpoint following the OpenAI API: @@ -76,11 +72,7 @@ class AzureOpenAILLM(OpenAILLM): llm.load() - # Synchronous request - output = llm.generate(inputs=[[{"role": "user", "content": "Hello world!"}]]) - - # Asynchronous request - output = await llm.agenerate(input=[{"role": "user", "content": "Hello world!"}]) + output = llm.generate_outputs(inputs=[[{"role": "user", "content": "Hello world!"}]]) ``` Generate structured data: @@ -102,7 +94,7 @@ class User(BaseModel): llm.load() - output = llm.generate(inputs=[[{"role": "user", "content": "Create a user profile for the following marathon"}]]) + output = llm.generate_outputs(inputs=[[{"role": "user", "content": "Create a user profile for the following marathon"}]]) ``` """ diff --git a/src/distilabel/llms/base.py b/src/distilabel/llms/base.py index 00538b50b8..ced6a8e041 100644 --- a/src/distilabel/llms/base.py +++ b/src/distilabel/llms/base.py @@ -16,14 +16,18 @@ import inspect import json import logging +import os import sys +import time from abc import ABC, abstractmethod from functools import cached_property -from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union +from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union from pydantic import BaseModel, ConfigDict, Field, PrivateAttr -from distilabel.errors import DistilabelUserError +from distilabel.constants import SIGINT_HANDLER_CALLED_ENV_NAME +from distilabel.errors import DistilabelNotImplementedError, DistilabelUserError +from distilabel.exceptions import DistilabelOfflineBatchGenerationNotFinishedException from distilabel.mixins.runtime_parameters import ( RuntimeParameter, RuntimeParametersMixin, @@ -67,6 +71,15 @@ class LLM(RuntimeParametersMixin, BaseModel, _Serializable, ABC): Attributes: generation_kwargs: the kwargs to be propagated to either `generate` or `agenerate` methods within each `LLM`. + use_offline_batch_generation: whether to use the `offline_batch_generate` method to + generate the responses. + offline_batch_generation_block_until_done: if provided, then polling will be done until + the `ofline_batch_generate` method is able to retrieve the results. The value indicate + the time to wait between each polling. + jobs_ids: the job ids generated by the `offline_batch_generate` method. This attribute + is used to store the job ids generated by the `offline_batch_generate` method + so later they can be used to retrieve the results. It is not meant to be set by + the user. _logger: the logger to be used for the `LLM`. It will be initialized when the `load` method is called. """ @@ -84,7 +97,19 @@ class LLM(RuntimeParametersMixin, BaseModel, _Serializable, ABC): description="The kwargs to be propagated to either `generate` or `agenerate`" " methods within each `LLM`.", ) + use_offline_batch_generation: Optional[RuntimeParameter[bool]] = Field( + default=False, + description="Whether to use the `offline_batch_generate` method to generate" + " the responses.", + ) + offline_batch_generation_block_until_done: Optional[RuntimeParameter[int]] = Field( + default=None, + description="If provided, then polling will be done until the `ofline_batch_generate`" + " method is able to retrieve the results. The value indicate the time to wait between" + " each polling.", + ) + jobs_ids: Union[Tuple[str, ...], None] = Field(default=None) _logger: "Logger" = PrivateAttr(None) def load(self) -> None: @@ -138,6 +163,84 @@ def generate( """ pass + def generate_outputs( + self, + inputs: List["FormattedInput"], + num_generations: int = 1, + **kwargs: Any, + ) -> List["GenerateOutput"]: + """Generates outputs for the given inputs using either `generate` method or the + `offine_batch_generate` method if `use_offline_ + """ + if self.use_offline_batch_generation: + if self.offline_batch_generation_block_until_done is not None: + return self._offline_batch_generate_polling( + inputs=inputs, + num_generations=num_generations, + **kwargs, + ) + + # This will raise `DistilabelOfflineBatchGenerationNotFinishedException` right away + # if the batch generation is not finished. + return self.offline_batch_generate( + inputs=inputs, + num_generations=num_generations, + **kwargs, + ) + + return self.generate(inputs=inputs, num_generations=num_generations, **kwargs) + + def _offline_batch_generate_polling( + self, + inputs: List["FormattedInput"], + num_generations: int = 1, + **kwargs: Any, + ) -> List["GenerateOutput"]: + """Method to poll the `offline_batch_generate` method until the batch generation + is finished. + + Args: + inputs: the list of inputs to generate responses for. + num_generations: the number of generations to generate per input. + **kwargs: the additional kwargs to be used for the generation. + + Returns: + A list containing the generations for each input. + """ + while True: + try: + return self.offline_batch_generate( + inputs=inputs, + num_generations=num_generations, + **kwargs, + ) + except DistilabelOfflineBatchGenerationNotFinishedException as e: + self._logger.info( + f"Waiting for the offline batch generation to finish: {e}. Sleeping" + f" for {self.offline_batch_generation_block_until_done} seconds before" + " trying to get the results again." + ) + # When running a `Step` in a child process, SIGINT is overriden so the child + # process doesn't stop when the parent process receives a SIGINT signal. + # The new handler sets an environment variable that is checked here to stop + # the polling. + if os.getenv(SIGINT_HANDLER_CALLED_ENV_NAME) is not None: + self._logger.info( + "Received a KeyboardInterrupt. Stopping polling for checking if the" + " offline batch generation is finished..." + ) + raise e + time.sleep(self.offline_batch_generation_block_until_done) # type: ignore + except KeyboardInterrupt as e: + # This is for the case the `LLM` is being executed outside a pipeline + self._logger.info( + "Received a KeyboardInterrupt. Stopping polling for checking if the" + " offline batch generation is finished..." + ) + raise DistilabelOfflineBatchGenerationNotFinishedException( + jobs_ids=self.jobs_ids # type: ignore + ) from e + @property def generate_parameters(self) -> List["inspect.Parameter"]: """Returns the parameters of the `generate` method. @@ -225,6 +328,7 @@ def get_last_hidden_states( A list containing the last hidden state for each sequence using a NumPy array with shape [num_tokens, hidden_size]. """ + # TODO: update to use `DistilabelNotImplementedError` raise NotImplementedError( f"Method `get_last_hidden_states` is not implemented for `{self.__class__.__name__}`" ) @@ -243,10 +347,40 @@ def _prepare_structured_output( Returns: The structure to be used for the guided generation. """ + # TODO: update to use `DistilabelNotImplementedError` raise NotImplementedError( f"Guided generation is not implemented for `{type(self).__name__}`" ) + def offline_batch_generate( + self, + inputs: Union[List["FormattedInput"], None] = None, + num_generations: int = 1, + **kwargs: Any, + ) -> List["GenerateOutput"]: + """Method to generate a list of outputs for the given inputs using an offline batch + generation method to be implemented by each `LLM`. + + This method should create jobs the first time is called and store the job ids, so + the second and subsequent calls can retrieve the results of the batch generation. + If subsequent calls are made before the batch generation is finished, then the method + should raise a `DistilabelOfflineBatchGenerationNotFinishedException`. This exception + will be handled automatically by the `Pipeline` which will store all the required + information for recovering the pipeline execution when the batch generation is finished. + + Args: + inputs: the list of inputs to generate responses for. + num_generations: the number of generations to generate per input. + **kwargs: the additional kwargs to be used for the generation. + + Returns: + A list containing the generations for each input. + """ + raise DistilabelNotImplementedError( + f"`offline_batch_generate` is not implemented for `{self.__class__.__name__}`", + page="sections/how_to_guides/advanced/offline-batch-generation/", + ) + class AsyncLLM(LLM): """Abstract class for asynchronous LLMs, so as to benefit from the async capabilities diff --git a/src/distilabel/llms/cohere.py b/src/distilabel/llms/cohere.py index e28f62fed3..e9d0d0c0f2 100644 --- a/src/distilabel/llms/cohere.py +++ b/src/distilabel/llms/cohere.py @@ -80,7 +80,7 @@ class CohereLLM(AsyncLLM): llm.load() # Call the model - output = llm.generate(inputs=[[{"role": "user", "content": "Hello world!"}]]) + output = llm.generate_outputs(inputs=[[{"role": "user", "content": "Hello world!"}]]) Generate structured data: @@ -101,7 +101,7 @@ class User(BaseModel): llm.load() - output = llm.generate(inputs=[[{"role": "user", "content": "Create a user profile for the following marathon"}]]) + output = llm.generate_outputs(inputs=[[{"role": "user", "content": "Create a user profile for the following marathon"}]]) ``` """ diff --git a/src/distilabel/llms/groq.py b/src/distilabel/llms/groq.py index 009f0a07eb..c4c2554329 100644 --- a/src/distilabel/llms/groq.py +++ b/src/distilabel/llms/groq.py @@ -73,7 +73,7 @@ class GroqLLM(AsyncLLM): llm.load() # Call the model - output = llm.generate(inputs=[[{"role": "user", "content": "Hello world!"}]]) + output = llm.generate_outputs(inputs=[[{"role": "user", "content": "Hello world!"}]]) Generate structured data: @@ -94,7 +94,7 @@ class User(BaseModel): llm.load() - output = llm.generate(inputs=[[{"role": "user", "content": "Create a user profile for the following marathon"}]]) + output = llm.generate_outputs(inputs=[[{"role": "user", "content": "Create a user profile for the following marathon"}]]) ``` """ diff --git a/src/distilabel/llms/huggingface/inference_endpoints.py b/src/distilabel/llms/huggingface/inference_endpoints.py index 5e82947844..f99593b8e8 100644 --- a/src/distilabel/llms/huggingface/inference_endpoints.py +++ b/src/distilabel/llms/huggingface/inference_endpoints.py @@ -85,7 +85,7 @@ class InferenceEndpointsLLM(AsyncLLM, MagpieChatTemplateMixin): llm.load() - output = llm.generate(inputs=[[{"role": "user", "content": "Hello world!"}]]) + output = llm.generate_outputs(inputs=[[{"role": "user", "content": "Hello world!"}]]) ``` Dedicated Inference Endpoints: @@ -101,7 +101,7 @@ class InferenceEndpointsLLM(AsyncLLM, MagpieChatTemplateMixin): llm.load() - output = llm.generate(inputs=[[{"role": "user", "content": "Hello world!"}]]) + output = llm.generate_outputs(inputs=[[{"role": "user", "content": "Hello world!"}]]) ``` Dedicated Inference Endpoints or TGI: @@ -116,7 +116,7 @@ class InferenceEndpointsLLM(AsyncLLM, MagpieChatTemplateMixin): llm.load() - output = llm.generate(inputs=[[{"role": "user", "content": "Hello world!"}]]) + output = llm.generate_outputs(inputs=[[{"role": "user", "content": "Hello world!"}]]) ``` Generate structured data: @@ -139,7 +139,7 @@ class User(BaseModel): llm.load() - output = llm.generate(inputs=[[{"role": "user", "content": "Create a user profile for the Tour De France"}]]) + output = llm.generate_outputs(inputs=[[{"role": "user", "content": "Create a user profile for the Tour De France"}]]) ``` """ diff --git a/src/distilabel/llms/huggingface/transformers.py b/src/distilabel/llms/huggingface/transformers.py index e939ddfca4..27ab00e5b9 100644 --- a/src/distilabel/llms/huggingface/transformers.py +++ b/src/distilabel/llms/huggingface/transformers.py @@ -86,7 +86,7 @@ class TransformersLLM(LLM, MagpieChatTemplateMixin, CudaDevicePlacementMixin): llm.load() # Call the model - output = llm.generate(inputs=[[{"role": "user", "content": "Hello world!"}]]) + output = llm.generate_outputs(inputs=[[{"role": "user", "content": "Hello world!"}]]) ``` """ diff --git a/src/distilabel/llms/litellm.py b/src/distilabel/llms/litellm.py index 43c5975e9d..48361ef706 100644 --- a/src/distilabel/llms/litellm.py +++ b/src/distilabel/llms/litellm.py @@ -72,7 +72,7 @@ class User(BaseModel): llm.load() - output = llm.generate(inputs=[[{"role": "user", "content": "Create a user profile for the following marathon"}]]) + output = llm.generate_outputs(inputs=[[{"role": "user", "content": "Create a user profile for the following marathon"}]]) ``` """ diff --git a/src/distilabel/llms/llamacpp.py b/src/distilabel/llms/llamacpp.py index 5b310d2b6a..9d158ea525 100644 --- a/src/distilabel/llms/llamacpp.py +++ b/src/distilabel/llms/llamacpp.py @@ -80,7 +80,7 @@ class LlamaCppLLM(LLM): llm.load() # Call the model - output = llm.generate(inputs=[[{"role": "user", "content": "Hello world!"}]]) + output = llm.generate_outputs(inputs=[[{"role": "user", "content": "Hello world!"}]]) ``` Generate structured data: @@ -106,7 +106,7 @@ class User(BaseModel): llm.load() # Call the model - output = llm.generate(inputs=[[{"role": "user", "content": "Create a user profile for the following marathon"}]]) + output = llm.generate_outputs(inputs=[[{"role": "user", "content": "Create a user profile for the following marathon"}]]) ``` """ diff --git a/src/distilabel/llms/mistral.py b/src/distilabel/llms/mistral.py index d1457209e9..a913d6ad0a 100644 --- a/src/distilabel/llms/mistral.py +++ b/src/distilabel/llms/mistral.py @@ -93,7 +93,7 @@ class User(BaseModel): llm.load() - output = llm.generate(inputs=[[{"role": "user", "content": "Create a user profile for the following marathon"}]]) + output = llm.generate_outputs(inputs=[[{"role": "user", "content": "Create a user profile for the following marathon"}]]) ``` """ diff --git a/src/distilabel/llms/moa.py b/src/distilabel/llms/moa.py index be32199adc..a7dd5db19e 100644 --- a/src/distilabel/llms/moa.py +++ b/src/distilabel/llms/moa.py @@ -90,7 +90,7 @@ class MixtureOfAgentsLLM(AsyncLLM): llm.load() - output = llm.generate( + output = llm.generate_outputs( inputs=[ [ { diff --git a/src/distilabel/llms/openai.py b/src/distilabel/llms/openai.py index 91a3c165c9..b73efc223b 100644 --- a/src/distilabel/llms/openai.py +++ b/src/distilabel/llms/openai.py @@ -12,21 +12,30 @@ # See the License for the specific language governing permissions and # limitations under the License. +import io import os -from typing import TYPE_CHECKING, List, Optional, Union +from typing import TYPE_CHECKING, Any, Dict, Generator, List, Optional, Tuple, Union +import orjson from pydantic import Field, PrivateAttr, SecretStr, validate_call +from distilabel import envs +from distilabel.exceptions import DistilabelOfflineBatchGenerationNotFinishedException from distilabel.llms.base import AsyncLLM from distilabel.llms.typing import GenerateOutput from distilabel.mixins.runtime_parameters import RuntimeParameter from distilabel.steps.tasks.typing import FormattedInput, InstructorStructuredOutputType if TYPE_CHECKING: - from openai import AsyncOpenAI + from openai import AsyncOpenAI, OpenAI + from openai.types import Batch as OpenAIBatch + from openai.types import FileObject as OpenAIFileObject + from openai.types.chat import ChatCompletion as OpenAIChatCompletion + from pydantic import BaseModel _OPENAI_API_KEY_ENV_VAR_NAME = "OPENAI_API_KEY" +_OPENAI_BATCH_API_MAX_FILE_SIZE = 100 * 1024 * 1024 # 100MB class OpenAILLM(AsyncLLM): @@ -71,7 +80,7 @@ class OpenAILLM(AsyncLLM): llm.load() - output = llm.generate(inputs=[[{"role": "user", "content": "Hello world!"}]]) + output = llm.generate_outputs(inputs=[[{"role": "user", "content": "Hello world!"}]]) ``` Generate text from a custom endpoint following the OpenAI API: @@ -86,7 +95,7 @@ class OpenAILLM(AsyncLLM): llm.load() - output = llm.generate(inputs=[[{"role": "user", "content": "Hello world!"}]]) + output = llm.generate_outputs(inputs=[[{"role": "user", "content": "Hello world!"}]]) ``` Generate structured data: @@ -108,7 +117,24 @@ class User(BaseModel): llm.load() - output = llm.generate(inputs=[[{"role": "user", "content": "Create a user profile for the following marathon"}]]) + output = llm.generate_outputs(inputs=[[{"role": "user", "content": "Create a user profile for the following marathon"}]]) + ``` + + Generate with Batch API (offline batch generation): + + ```python + from distilabel.llms import OpenAILLM + + load = llm = OpenAILLM( + model="gpt-3.5-turbo", + use_offline_batch_generation=True, + offline_batch_generation_block_until_done=5, # poll for results every 5 seconds + ) + + llm.load() + + output = llm.generate_outputs(inputs=[[{"role": "user", "content": "Hello world!"}]]) + # [['Hello! How can I assist you today?']] ``` """ @@ -140,6 +166,7 @@ class User(BaseModel): ) _api_key_env_var: str = PrivateAttr(_OPENAI_API_KEY_ENV_VAR_NAME) + _client: "OpenAI" = PrivateAttr(None) _aclient: "AsyncOpenAI" = PrivateAttr(None) def load(self) -> None: @@ -147,7 +174,7 @@ def load(self) -> None: super().load() try: - from openai import AsyncOpenAI + from openai import AsyncOpenAI, OpenAI except ImportError as ie: raise ImportError( "OpenAI Python client is not installed. Please install it using" @@ -160,6 +187,13 @@ def load(self) -> None: f" attribute or runtime parameter, or set the environment variable `{self._api_key_env_var}`." ) + self._client = OpenAI( + base_url=self.base_url, + api_key=self.api_key.get_secret_value(), + max_retries=self.max_retries, # type: ignore + timeout=self.timeout, + ) + self._aclient = AsyncOpenAI( base_url=self.base_url, api_key=self.api_key.get_secret_value(), @@ -177,6 +211,15 @@ def load(self) -> None: if structured_output := result.get("structured_output"): self.structured_output = structured_output + def unload(self) -> None: + """Set clients to `None` as they both contain `thread._RLock` which cannot be pickled + in case an exception is raised and has to be handled in the main process""" + + self._client = None # type: ignore + self._aclient = None # type: ignore + self.structured_output = None + super().unload() + @property def model_name(self) -> str: """Returns the model name used for the LLM.""" @@ -227,11 +270,11 @@ async def agenerate( # type: ignore if isinstance(input, tuple): input, structured_output = input result = self._prepare_structured_output( - structured_output=structured_output, + structured_output=structured_output, # type: ignore client=self._aclient, framework="openai", ) - self._aclient = result.get("client") + self._aclient = result.get("client") # type: ignore if structured_output is None and self.structured_output is not None: structured_output = self.structured_output @@ -261,15 +304,41 @@ async def agenerate( # type: ignore kwargs["response_format"] = response_format if structured_output: - kwargs = self._prepare_kwargs(kwargs, structured_output) + kwargs = self._prepare_kwargs(kwargs, structured_output) # type: ignore - generations = [] completion = await self._aclient.chat.completions.create(**kwargs) # type: ignore if structured_output: - generations.append(completion.model_dump_json()) - return generations + return self._generations_from_structured_output(completion) + return self._generations_from_openai_completion(completion) + + def _generations_from_structured_output( + self, completion: "BaseModel" + ) -> "GenerateOutput": + """Get the generations from the structured output object. + + Args: + completion: an instance of `pydantic.BaseModel` with the content of the structuted + output. + + Returns: + A list with the content of the structured output. + """ + return [completion.model_dump_json()] + + def _generations_from_openai_completion( + self, completion: "OpenAIChatCompletion" + ) -> "GenerateOutput": + """Get the generations from the OpenAI Chat Completion object. + + Args: + completion: the completion object to get the generations from. + + Returns: + A list of strings containing the generated responses for the input. + """ + generations = [] for choice in completion.choices: if (content := choice.message.content) is None: self._logger.warning( # type: ignore @@ -278,3 +347,349 @@ async def agenerate( # type: ignore ) generations.append(content) return generations + + def offline_batch_generate( + self, + inputs: Union[List["FormattedInput"], None] = None, + num_generations: int = 1, + max_new_tokens: int = 128, + frequency_penalty: float = 0.0, + presence_penalty: float = 0.0, + temperature: float = 1.0, + top_p: float = 1.0, + stop: Optional[Union[str, List[str]]] = None, + response_format: Optional[str] = None, + **kwargs: Any, + ) -> List["GenerateOutput"]: + """Uses the OpenAI batch API to generate `num_generations` responses for the given + inputs. + + Args: + inputs: a list of inputs in chat format to generate responses for. + num_generations: the number of generations to create per input. Defaults to + `1`. + max_new_tokens: the maximum number of new tokens that the model will generate. + Defaults to `128`. + frequency_penalty: the repetition penalty to use for the generation. Defaults + to `0.0`. + presence_penalty: the presence penalty to use for the generation. Defaults to + `0.0`. + temperature: the temperature to use for the generation. Defaults to `0.1`. + top_p: the top-p value to use for the generation. Defaults to `1.0`. + stop: a string or a list of strings to use as a stop sequence for the generation. + Defaults to `None`. + response_format: the format of the response to return. Must be one of + "text" or "json". Read the documentation [here](https://platform.openai.com/docs/guides/text-generation/json-mode) + for more information on how to use the JSON model from OpenAI. Defaults to `text`. + + Returns: + A list of lists of strings containing the generated responses for each input + in `inputs`. + + Raises: + DistilabelOfflineBatchGenerationNotFinishedException: if the batch generation + is not finished yet. + ValueError: if no job IDs were found to retrieve the results from. + """ + if self.jobs_ids: + return self._check_and_get_batch_results() + + if inputs: + self.jobs_ids = self._create_jobs( + inputs=inputs, + **{ + "model": self.model, + "max_tokens": max_new_tokens, + "n": num_generations, + "frequency_penalty": frequency_penalty, + "presence_penalty": presence_penalty, + "temperature": temperature, + "top_p": top_p, + "stop": stop, + "response_format": response_format, + }, + ) + raise DistilabelOfflineBatchGenerationNotFinishedException( + jobs_ids=self.jobs_ids + ) + + raise ValueError("No `inputs` were provided and no `jobs_ids` were found.") + + def _check_and_get_batch_results(self) -> List["GenerateOutput"]: + """Checks the status of the batch jobs and retrieves the results from the OpenAI + Batch API. + + Returns: + A list of lists of strings containing the generated responses for each input. + + Raises: + ValueError: if no job IDs were found to retrieve the results from. + DistilabelOfflineBatchGenerationNotFinishedException: if the batch generation + is not finished yet. + RuntimeError: if the only batch job found failed. + """ + if not self.jobs_ids: + raise ValueError("No job IDs were found to retrieve the results from.") + + outputs = [] + for batch_id in self.jobs_ids: + batch = self._get_openai_batch(batch_id) + + if batch.status in ("validating", "in_progress", "finalizing"): + raise DistilabelOfflineBatchGenerationNotFinishedException( + jobs_ids=self.jobs_ids + ) + + if batch.status in ("failed", "expired", "cancelled", "cancelling"): + self._logger.error( # type: ignore + f"OpenAI API batch with ID '{batch_id}' failed with status '{batch.status}'." + ) + if len(self.jobs_ids) == 1: + self.jobs_ids = None + raise RuntimeError( + f"The only OpenAI API Batch that was created with ID '{batch_id}'" + f" failed with status '{batch.status}'." + ) + + continue + + outputs.extend(self._retrieve_batch_results(batch)) + + # sort by `custom_id` to return the results in the same order as the inputs + outputs = sorted(outputs, key=lambda x: int(x["custom_id"])) + return [self._parse_output(output) for output in outputs] + + def _parse_output(self, output: Dict[str, Any]) -> "GenerateOutput": + """Parses the output from the OpenAI Batch API into a list of strings. + + Args: + output: the output to parse. + + Returns: + A list of strings containing the generated responses for the input. + """ + from openai.types.chat import ChatCompletion as OpenAIChatCompletion + + if "response" not in output: + return [] + + if output["response"]["status_code"] != 200: + return [] + + return self._generations_from_openai_completion( + OpenAIChatCompletion(**output["response"]["body"]) + ) + + def _get_openai_batch(self, batch_id: str) -> "OpenAIBatch": + """Gets a batch from the OpenAI Batch API. + + Args: + batch_id: the ID of the batch to retrieve. + + Returns: + The batch retrieved from the OpenAI Batch API. + + Raises: + openai.OpenAIError: if there was an error while retrieving the batch from the + OpenAI Batch API. + """ + import openai + + try: + return self._client.batches.retrieve(batch_id) + except openai.OpenAIError as e: + self._logger.error( # type: ignore + f"Error while retrieving batch '{batch_id}' from OpenAI: {e}" + ) + raise e + + def _retrieve_batch_results(self, batch: "OpenAIBatch") -> List[Dict[str, Any]]: + """Retrieves the results of a batch from its output file, parsing the JSONL content + into a list of dictionaries. + + Args: + batch: the batch to retrieve the results from. + + Returns: + A list of dictionaries containing the results of the batch. + + Raises: + AssertionError: if no output file ID was found in the batch. + """ + import openai + + assert batch.output_file_id, "No output file ID was found in the batch." + + try: + file_response = self._client.files.content(batch.output_file_id) + return [orjson.loads(line) for line in file_response.text.splitlines()] + except openai.OpenAIError as e: + self._logger.error( # type: ignore + f"Error while retrieving batch results from file '{batch.output_file_id}': {e}" + ) + return [] + + def _create_jobs( + self, inputs: List["FormattedInput"], **kwargs: Any + ) -> Tuple[str, ...]: + """Creates jobs in the OpenAI Batch API to generate responses for the given inputs. + + Args: + inputs: a list of inputs in chat format to generate responses for. + kwargs: the keyword arguments to use for the generation. + + Returns: + A list of job IDs created in the OpenAI Batch API. + """ + batch_input_files = self._create_batch_files(inputs=inputs, **kwargs) + jobs = [] + for batch_input_file in batch_input_files: + if batch := self._create_batch_api_job(batch_input_file): + jobs.append(batch.id) + return tuple(jobs) + + def _create_batch_api_job( + self, batch_input_file: "OpenAIFileObject" + ) -> Union["OpenAIBatch", None]: + """Creates a job in the OpenAI Batch API to generate responses for the given input + file. + + Args: + batch_input_file: the input file to generate responses for. + + Returns: + The batch job created in the OpenAI Batch API. + """ + import openai + + metadata = {"description": "distilabel"} + + if distilabel_pipeline_name := envs.DISTILABEL_PIPELINE_NAME: + metadata["distilabel_pipeline_name"] = distilabel_pipeline_name + + if distilabel_pipeline_cache_id := envs.DISTILABEL_PIPELINE_CACHE_ID: + metadata["distilabel_pipeline_cache_id"] = distilabel_pipeline_cache_id + + batch = None + try: + batch = self._client.batches.create( + completion_window="24h", + endpoint="/v1/chat/completions", + input_file_id=batch_input_file.id, + metadata=metadata, + ) + except openai.OpenAIError as e: + self._logger.error( # type: ignore + f"Error while creating OpenAI Batch API job for file with ID" + f" '{batch_input_file.id}': {e}." + ) + raise e + return batch + + def _create_batch_files( + self, inputs: List["FormattedInput"], **kwargs: Any + ) -> List["OpenAIFileObject"]: + """Creates the necessary input files for the batch API to generate responses. The + maximum size of each file so the OpenAI Batch API can process it is 100MB, so we + need to split the inputs into multiple files if necessary. + + More information: https://platform.openai.com/docs/api-reference/files/create + + Args: + inputs: a list of inputs in chat format to generate responses for, optionally + including structured output. + kwargs: the keyword arguments to use for the generation. + + Returns: + The list of file objects created for the OpenAI Batch API. + + Raises: + openai.OpenAIError: if there was an error while creating the batch input file + in the OpenAI Batch API. + """ + import openai + + files = [] + for file_no, buffer in enumerate( + self._create_jsonl_buffers(inputs=inputs, **kwargs) + ): + try: + # TODO: add distilabel pipeline name and id + batch_input_file = self._client.files.create( + file=(self._name_for_openai_files(file_no), buffer), + purpose="batch", + ) + files.append(batch_input_file) + except openai.OpenAIError as e: + self._logger.error( # type: ignore + f"Error while creating OpenAI batch input file: {e}" + ) + raise e + return files + + def _create_jsonl_buffers( + self, inputs: List["FormattedInput"], **kwargs: Any + ) -> Generator[io.BytesIO, None, None]: + """Creates a generator of buffers containing the JSONL formatted inputs to be + used by the OpenAI Batch API. The buffers created are of size 100MB or less. + + Args: + inputs: a list of inputs in chat format to generate responses for, optionally + including structured output. + kwargs: the keyword arguments to use for the generation. + + Yields: + A buffer containing the JSONL formatted inputs to be used by the OpenAI Batch + API. + """ + buffer = io.BytesIO() + buffer_current_size = 0 + for i, input in enumerate(inputs): + # We create the smallest `custom_id` so we don't increase the size of the file + # to much, but we can still sort the results with the order of the inputs. + row = self._create_jsonl_row(input=input, custom_id=str(i), **kwargs) + row_size = len(row) + if row_size + buffer_current_size > _OPENAI_BATCH_API_MAX_FILE_SIZE: + buffer.seek(0) + yield buffer + buffer = io.BytesIO() + buffer_current_size = 0 + buffer.write(row) + buffer_current_size += row_size + + if buffer_current_size > 0: + buffer.seek(0) + yield buffer + + def _create_jsonl_row( + self, input: "FormattedInput", custom_id: str, **kwargs: Any + ) -> bytes: + """Creates a JSONL formatted row to be used by the OpenAI Batch API. + + Args: + inputs: a list of inputs in chat format to generate responses for, optionally + including structured output. + custom_id: a custom ID to use for the row. + kwargs: the keyword arguments to use for the generation. + + Returns: + A JSONL formatted row to be used by the OpenAI Batch API. + """ + # TODO: depending on the format of the input, add `response_format` to the kwargs + row = { + "custom_id": custom_id, + "method": "POST", + "url": "/v1/chat/completions", + "body": {"messages": input, **kwargs}, + } + json_row = orjson.dumps(row) + return json_row + b"\n" + + def _name_for_openai_files(self, file_no: int) -> str: + if ( + envs.DISTILABEL_PIPELINE_NAME is None + or envs.DISTILABEL_PIPELINE_CACHE_ID is None + ): + return f"distilabel-pipeline-fileno-{file_no}.jsonl" + + return f"distilabel-pipeline-{envs.DISTILABEL_PIPELINE_NAME}-{envs.DISTILABEL_PIPELINE_CACHE_ID}-fileno-{file_no}.jsonl" diff --git a/src/distilabel/llms/together.py b/src/distilabel/llms/together.py index d4ba0eb473..88e7fd7647 100644 --- a/src/distilabel/llms/together.py +++ b/src/distilabel/llms/together.py @@ -48,7 +48,7 @@ class TogetherLLM(OpenAILLM): llm.load() - output = llm.generate(inputs=[[{"role": "user", "content": "Hello world!"}]]) + output = llm.generate_outputs(inputs=[[{"role": "user", "content": "Hello world!"}]]) ``` """ diff --git a/src/distilabel/llms/vllm.py b/src/distilabel/llms/vllm.py index 1ba6ab3700..be08debf2c 100644 --- a/src/distilabel/llms/vllm.py +++ b/src/distilabel/llms/vllm.py @@ -106,7 +106,7 @@ class vLLM(LLM, MagpieChatTemplateMixin, CudaDevicePlacementMixin): llm.load() # Call the model - output = llm.generate(inputs=[[{"role": "user", "content": "Hello world!"}]]) + output = llm.generate_outputs(inputs=[[{"role": "user", "content": "Hello world!"}]]) ``` Generate structured data: @@ -128,7 +128,7 @@ class User(BaseModel): llm.load() # Call the model - output = llm.generate(inputs=[[{"role": "user", "content": "Create a user profile for the following marathon"}]]) + output = llm.generate_outputs(inputs=[[{"role": "user", "content": "Create a user profile for the following marathon"}]]) ``` """ @@ -426,7 +426,7 @@ class ClientvLLM(OpenAILLM, MagpieChatTemplateMixin): llm.load() - results = llm.generate( + results = llm.generate_outputs( inputs=[[{"role": "user", "content": "Hello, how are you?"}]], temperature=0.7, top_p=1.0, diff --git a/src/distilabel/mixins/runtime_parameters.py b/src/distilabel/mixins/runtime_parameters.py index a7dd848f17..f8371e30ab 100644 --- a/src/distilabel/mixins/runtime_parameters.py +++ b/src/distilabel/mixins/runtime_parameters.py @@ -13,13 +13,16 @@ # limitations under the License. import difflib -import inspect from typing import TYPE_CHECKING, Any, Dict, List, Tuple, TypeVar, Union from pydantic import BaseModel, Field, PrivateAttr -from pydantic.types import _SecretField from typing_extensions import Annotated, get_args, get_origin +from distilabel.utils.typing_ import ( + extract_annotation_inner_type, + is_type_pydantic_secret_field, +) + if TYPE_CHECKING: from pydantic.fields import FieldInfo @@ -73,8 +76,12 @@ def runtime_parameters_names(self) -> "RuntimeParametersNames": if isinstance(attr, RuntimeParametersMixin): runtime_parameters[name] = attr.runtime_parameters_names - # `field: List[RuntiemParametersMixin]` - if isinstance(attr, list) and isinstance(attr[0], RuntimeParametersMixin): + # `field: List[RuntimeParametersMixin]` + if ( + isinstance(attr, list) + and attr + and isinstance(attr[0], RuntimeParametersMixin) + ): runtime_parameters[name] = { str(i): item.runtime_parameters_names for i, item in enumerate(attr) } @@ -170,8 +177,8 @@ def set_runtime_parameters(self, runtime_parameters: Dict[str, Any]) -> None: # Handle settings values for `_SecretField` field_info = self.model_fields[name] - inner_type = _extract_runtime_parameter_inner_type(field_info.annotation) - if inspect.isclass(inner_type) and issubclass(inner_type, _SecretField): + inner_type = extract_annotation_inner_type(field_info.annotation) + if is_type_pydantic_secret_field(inner_type): value = inner_type(value) # Set the value of the runtime parameter @@ -211,22 +218,3 @@ def _is_runtime_parameter(field: "FieldInfo") -> Tuple[bool, bool]: return True, is_optional return False, False - - -def _extract_runtime_parameter_inner_type(type_hint: Any) -> Any: - """Extracts the inner type of a `RuntimeParameter` type hint. - - Args: - type_hint: The type hint to extract the inner type from. - - Returns: - The inner type of the `RuntimeParameter` type hint. - """ - type_hint_args = get_args(type_hint) - if get_origin(type_hint) is Annotated: - return _extract_runtime_parameter_inner_type(type_hint_args[0]) - - if get_origin(type_hint) is Union and type(None) in type_hint_args: - return _extract_runtime_parameter_inner_type(type_hint_args[0]) - - return type_hint diff --git a/src/distilabel/pipeline/base.py b/src/distilabel/pipeline/base.py index b6e1e52957..97ad4f00a8 100644 --- a/src/distilabel/pipeline/base.py +++ b/src/distilabel/pipeline/base.py @@ -19,6 +19,7 @@ import threading import time from abc import ABC, abstractmethod +from inspect import isclass from pathlib import Path from typing import ( TYPE_CHECKING, @@ -34,20 +35,11 @@ ) import fsspec +from pydantic import BaseModel from typing_extensions import Self from upath import UPath -from distilabel import __version__ -from distilabel.constants import ( - CONVERGENCE_STEP_ATTR_NAME, - INPUT_QUEUE_ATTR_NAME, - LAST_BATCH_SENT_FLAG, - RECEIVES_ROUTED_BATCHES_ATTR_NAME, - ROUTING_BATCH_FUNCTION_ATTR_NAME, - STEP_ATTR_NAME, - STEPS_ARTIFACTS_PATH, - STEPS_OUTPUTS_PATH, -) +from distilabel import __version__, constants, envs from distilabel.distiset import create_distiset from distilabel.errors import DistilabelUserError from distilabel.mixins.requirements import RequirementsMixin @@ -63,11 +55,17 @@ _Serializable, read_json, ) +from distilabel.utils.typing_ import ( + extract_annotation_inner_type, + is_type_pydantic_secret_field, +) if TYPE_CHECKING: from os import PathLike from queue import Queue + from pydantic import BaseModel + from distilabel.distiset import Distiset from distilabel.pipeline.routing_batch_function import RoutingBatchFunction from distilabel.pipeline.typing import ( @@ -95,9 +93,6 @@ class _CacheLocation(TypedDict): stages_file: Path -BASE_CACHE_DIR = Path.home() / ".cache" / "distilabel" / "pipelines" - - class _GlobalPipelineManager: """Class to manage the global pipeline instance that will be used by the steps when created within a pipeline context. @@ -130,7 +125,7 @@ def get_pipeline(cls) -> Union["BasePipeline", None]: _STEP_LOAD_FAILED_CODE = -666 _STEP_NOT_LOADED_CODE = -999 -_ATTRIBUTES_IGNORED_CACHE = ("disable_cuda_device_placement",) +_ATTRIBUTES_IGNORED_CACHE = ("disable_cuda_device_placement", "jobs_ids") _PIPELINE_DEFAULT_NAME = "__default_pipeline_name__" @@ -197,10 +192,10 @@ def __init__( if cache_dir: self._cache_dir = Path(cache_dir) - elif env_cache_dir := os.getenv("DISTILABEL_CACHE_DIR"): + elif env_cache_dir := envs.DISTILABEL_CACHE_DIR: self._cache_dir = Path(env_cache_dir) else: - self._cache_dir = BASE_CACHE_DIR + self._cache_dir = constants.PIPELINES_CACHE_DIR self._logger = logging.getLogger("distilabel.pipeline") @@ -214,6 +209,10 @@ def __init__( self._stop_called_lock = threading.Lock() self._stop_calls = 0 + self._recover_offline_batch_generate_for_step: Union[ + Tuple[str, List[List[Dict[str, Any]]]], None + ] = None + self._fs: Optional[fsspec.AbstractFileSystem] = None self._storage_base_path: Optional[str] = None self._use_fs_to_pass_data: bool = False @@ -258,7 +257,7 @@ def _create_signature(self) -> str: for step in pipeline_dump["steps"]: step_info = step["name"] - for argument, value in sorted(step[STEP_ATTR_NAME].items()): + for argument, value in sorted(step[constants.STEP_ATTR_NAME].items()): if (argument == TYPE_INFO_KEY) or (value is None): continue @@ -293,7 +292,7 @@ def _create_signature(self) -> str: for function in pipeline_dump["routing_batch_functions"]: step = function["step"] routing_batch_function: "RoutingBatchFunction" = self.dag.get_step(step)[ - ROUTING_BATCH_FUNCTION_ATTR_NAME + constants.ROUTING_BATCH_FUNCTION_ATTR_NAME ] if type_info := routing_batch_function._get_type_info(): step += f"-{type_info}" @@ -351,6 +350,8 @@ def run( # cache when the pipeline is run, so it's important to do it first. self._set_runtime_parameters(parameters or {}) + self._refresh_pipeline_from_cache() + if dataset is not None: self._add_dataset_generator_step(dataset) @@ -439,7 +440,7 @@ def dry_run( self._dry_run = True for step_name in self.dag: - step = self.dag.get_step(step_name)[STEP_ATTR_NAME] + step = self.dag.get_step(step_name)[constants.STEP_ATTR_NAME] if step.is_generator: if not parameters: @@ -463,7 +464,7 @@ def _add_dataset_generator_step(self, dataset: "InputDataset") -> None: ValueError: If there's already a `GeneratorStep` in the pipeline. """ for step_name in self.dag: - step = self.dag.get_step(step_name)[STEP_ATTR_NAME] + step = self.dag.get_step(step_name)[constants.STEP_ATTR_NAME] if isinstance(step_name, GeneratorStep): raise DistilabelUserError( "There is already a `GeneratorStep` in the pipeline, you can either" @@ -483,7 +484,7 @@ def get_runtime_parameters_info(self) -> "PipelineRuntimeParametersInfo": """ runtime_parameters = {} for step_name in self.dag: - step: "_Step" = self.dag.get_step(step_name)[STEP_ATTR_NAME] + step: "_Step" = self.dag.get_step(step_name)[constants.STEP_ATTR_NAME] runtime_parameters[step_name] = step.get_runtime_parameters_info() return runtime_parameters @@ -496,9 +497,9 @@ def _init_steps_load_status(self) -> None: def _set_pipeline_artifacts_path_in_steps(self) -> None: """Sets the attribute `_pipeline_artifacts_path` in all the `Step`s of the pipeline, so steps can use it to get the path to save the generated artifacts.""" - artifacts_path = self._cache_location["data"] / STEPS_ARTIFACTS_PATH + artifacts_path = self._cache_location["data"] / constants.STEPS_ARTIFACTS_PATH for name in self.dag: - step: "_Step" = self.dag.get_step(name)[STEP_ATTR_NAME] + step: "_Step" = self.dag.get_step(name)[constants.STEP_ATTR_NAME] step.set_pipeline_artifacts_path(path=artifacts_path) def _check_requirements(self) -> None: @@ -569,10 +570,12 @@ def _add_edge(self, from_step: str, to_step: str) -> None: # Check if `from_step` has a `routing_batch_function`. If it does, then mark # `to_step` as a step that will receive a routed batch. node = self.dag.get_step(from_step) # type: ignore - routing_batch_function = node.get(ROUTING_BATCH_FUNCTION_ATTR_NAME, None) + routing_batch_function = node.get( + constants.ROUTING_BATCH_FUNCTION_ATTR_NAME, None + ) self.dag.set_step_attr( name=to_step, - attr=RECEIVES_ROUTED_BATCHES_ATTR_NAME, + attr=constants.RECEIVES_ROUTED_BATCHES_ATTR_NAME, value=routing_batch_function is not None, ) @@ -582,7 +585,7 @@ def _is_convergence_step(self, step_name: str) -> None: Args: step_name: The name of the step. """ - return self.dag.get_step(step_name).get(CONVERGENCE_STEP_ATTR_NAME) + return self.dag.get_step(step_name).get(constants.CONVERGENCE_STEP_ATTR_NAME) def _add_routing_batch_function( self, step_name: str, routing_batch_function: "RoutingBatchFunction" @@ -595,7 +598,7 @@ def _add_routing_batch_function( """ self.dag.set_step_attr( name=step_name, - attr=ROUTING_BATCH_FUNCTION_ATTR_NAME, + attr=constants.ROUTING_BATCH_FUNCTION_ATTR_NAME, value=routing_batch_function, ) @@ -614,7 +617,7 @@ def _set_runtime_parameters(self, parameters: Dict[str, Dict[str, Any]]) -> None f" Available steps are: {step_names}." ) else: - step: "_Step" = self.dag.get_step(step_name)[STEP_ATTR_NAME] + step: "_Step" = self.dag.get_step(step_name)[constants.STEP_ATTR_NAME] step.set_runtime_parameters(step_parameters) def _model_dump(self, obj: Any, **kwargs: Any) -> Dict[str, Any]: @@ -719,6 +722,50 @@ def _load_stages_status(self, use_cache: bool = True) -> None: [] for _ in range(len(self.dag.get_steps_load_stages()[0])) ] + def _refresh_pipeline_from_cache(self) -> None: + """Refresh the DAG (and its steps) from the cache file. This is useful as some + `Step`s can update and change their state during the pipeline execution, and this + method will make sure the pipeline is up-to-date with the latest changes when + the pipeline is reloaded from cache. + """ + + def recursively_handle_secrets_and_excluded_attributes( + cached_model: "BaseModel", model: "BaseModel" + ) -> None: + """Recursively handle the secrets and excluded attributes of a `BaseModel`, + setting the values of the cached model to the values of the model. + + Args: + cached_model: The cached model that will be updated as it doesn't contain + the secrets and excluded attributes (not serialized). + model: The model that contains the secrets and excluded attributes because + it comes from pipeline instantiation. + """ + for field_name, field_info in cached_model.model_fields.items(): + if field_name in ("pipeline"): + continue + + inner_type = extract_annotation_inner_type(field_info.annotation) + if is_type_pydantic_secret_field(inner_type) or field_info.exclude: + setattr(cached_model, field_name, getattr(model, field_name)) + elif isclass(inner_type) and issubclass(inner_type, BaseModel): + recursively_handle_secrets_and_excluded_attributes( + getattr(cached_model, field_name), + getattr(model, field_name), + ) + + if self._cache_location["pipeline"].exists(): + cached_dag = self.from_yaml(self._cache_location["pipeline"]).dag + + for step_name in cached_dag: + step_cached: "_Step" = cached_dag.get_step(step_name)[ + constants.STEP_ATTR_NAME + ] + step: "_Step" = self.dag.get_step(step_name)[constants.STEP_ATTR_NAME] + recursively_handle_secrets_and_excluded_attributes(step_cached, step) + + self.dag = cached_dag + def _load_batch_manager(self, use_cache: bool = True) -> None: """Will try to load the `_BatchManager` from the cache dir if found. Otherwise, it will create one from scratch. @@ -736,7 +783,7 @@ def _setup_write_buffer(self) -> None: """Setups the `_WriteBuffer` that will store the data of the leaf steps of the pipeline while running, so the `Distiset` can be created at the end. """ - buffer_data_path = self._cache_location["data"] / STEPS_OUTPUTS_PATH + buffer_data_path = self._cache_location["data"] / constants.STEPS_OUTPUTS_PATH self._logger.info(f"📝 Pipeline data will be written to '{buffer_data_path}'") self._write_buffer = _WriteBuffer(buffer_data_path, self.dag.leaf_steps) @@ -781,9 +828,10 @@ def _output_queue_loop(self) -> None: # we need to handle the stop of the pipeline and break the loop to avoid # propagating the batches through the pipeline and making the stop process # slower. - if self._stop_called: - self._handle_batch_on_stop(batch) - break + with self._stop_called_lock: + if self._stop_called: + self._handle_batch_on_stop(batch) + break # If there is another load stage and all the `last_batch`es from the stage # have been received, then load the next stage. @@ -820,7 +868,8 @@ def _should_continue_processing(self) -> bool: `True` if should continue consuming batches, `False` otherwise and the pipeline should stop. """ - return self._batch_manager.can_generate() and not self._stop_called # type: ignore + with self._stop_called_lock: + return self._batch_manager.can_generate() and not self._stop_called # type: ignore def _process_batch( self, batch: "_Batch", send_last_batch_flag: bool = True @@ -850,6 +899,43 @@ def _process_batch( if self._is_step_running(step_name): self._send_last_batch_flag_to_step(step_name) + def _set_step_for_recovering_offline_batch_generation( + self, step: "_Step", data: List[List[Dict[str, Any]]] + ) -> None: + """Sets the required information to recover a pipeline execution from a `_Step` + that used an `LLM` with offline batch generation. + + Args: + step: The `_Step` that used an `LLM` with offline batch generation. + data: The data that was used to generate the batches for the step. + """ + # Replace step so the attribute `jobs_ids` of the `LLM` is not lost, as it was + # updated in the child process but not in the main process. + step_name: str = step.name # type: ignore + self.dag.set_step_attr( + name=step_name, attr=constants.STEP_ATTR_NAME, value=step + ) + self._recover_offline_batch_generate_for_step = (step_name, data) + + def _add_batch_for_recovering_offline_batch_generation(self) -> None: + """Adds a dummy `_Batch` to the specified step name (it's a `Task` that used an + `LLM` with offline batch generation) to recover the pipeline state for offline + batch generation in next pipeline executions.""" + assert self._batch_manager, "Batch manager is not set" + + if self._recover_offline_batch_generate_for_step is None: + return + + step_name, data = self._recover_offline_batch_generate_for_step + self._logger.debug( + f"Adding batch to '{step_name}' step to recover pipeline execution for offline" + " batch generation..." + ) + self._batch_manager.add_batch_to_recover_offline_batch_generation( + to_step=step_name, + data=data, + ) + def _register_stages_last_batch(self, batch: "_Batch") -> None: """Registers the last batch received from a step in the `_stages_last_batch` dictionary. @@ -894,16 +980,25 @@ def _should_load_next_stage(self) -> bool: def _finalize_pipeline_execution(self) -> None: """Finalizes the pipeline execution handling the prematurely stop of the pipeline if required, caching the data and ensuring that all the steps finish its execution.""" - if self._stop_called: - self._handle_stop() - - self._cache() # Send `None` to steps `input_queue`s just in case some step is still waiting self._notify_steps_to_stop() - # Reset flag state - self._stop_called = False + for step_name in self.dag: + while self._is_step_running(step_name): + self._logger.debug(f"Waiting for step '{step_name}' to finish...") + time.sleep(0.5) + + with self._stop_called_lock: + if self._stop_called: + self._handle_stop() + + # Reset flag state + self._stop_called = False + + self._add_batch_for_recovering_offline_batch_generation() + + self._cache() def _run_load_queue_loop_in_thread(self) -> threading.Thread: """Runs a background thread that reads from the `load_queue` to update the status @@ -973,46 +1068,47 @@ def _run_stage_steps_and_wait(self, stage: int) -> bool: # Wait for them to be ready self._logger.info(f"⏳ Waiting for all the steps of stage {stage} to load...") previous_message = None - while not self._stop_called: - with self._steps_load_status_lock: - filtered_steps_load_status = { - step_name: replicas - for step_name, replicas in self._steps_load_status.items() - if step_name in steps - } - self._logger.debug( - f"Steps from stage {stage} loaded: {filtered_steps_load_status}" - ) - - if any( - replicas_loaded == _STEP_LOAD_FAILED_CODE - for replicas_loaded in filtered_steps_load_status.values() - ): - self._logger.error( - f"❌ Failed to load all the steps of stage {stage}" - ) - return False - - num_steps_loaded = 0 - replicas_message = "" - for step_name, replicas in filtered_steps_load_status.items(): - step_replica_count = self.dag.get_step_replica_count(step_name) - if replicas == step_replica_count: - num_steps_loaded += 1 - replicas_message += f"\n * '{step_name}' replicas: {max(0, replicas)}/{step_replica_count}" - - message = f"⏳ Steps from stage {stage} loaded: {num_steps_loaded}/{len(filtered_steps_load_status)}{replicas_message}" - if num_steps_loaded > 0 and message != previous_message: - self._logger.info(message) - previous_message = message - - if num_steps_loaded == len(filtered_steps_load_status): - self._logger.info( - f"✅ All the steps from stage {stage} have been loaded!" + with self._stop_called_lock: + while not self._stop_called: + with self._steps_load_status_lock: + filtered_steps_load_status = { + step_name: replicas + for step_name, replicas in self._steps_load_status.items() + if step_name in steps + } + self._logger.debug( + f"Steps from stage {stage} loaded: {filtered_steps_load_status}" ) - return True - time.sleep(2.5) + if any( + replicas_loaded == _STEP_LOAD_FAILED_CODE + for replicas_loaded in filtered_steps_load_status.values() + ): + self._logger.error( + f"❌ Failed to load all the steps of stage {stage}" + ) + return False + + num_steps_loaded = 0 + replicas_message = "" + for step_name, replicas in filtered_steps_load_status.items(): + step_replica_count = self.dag.get_step_replica_count(step_name) + if replicas == step_replica_count: + num_steps_loaded += 1 + replicas_message += f"\n * '{step_name}' replicas: {max(0, replicas)}/{step_replica_count}" + + message = f"⏳ Steps from stage {stage} loaded: {num_steps_loaded}/{len(filtered_steps_load_status)}{replicas_message}" + if num_steps_loaded > 0 and message != previous_message: + self._logger.info(message) + previous_message = message + + if num_steps_loaded == len(filtered_steps_load_status): + self._logger.info( + f"✅ All the steps from stage {stage} have been loaded!" + ) + return True + + time.sleep(2.5) return not self._stop_called @@ -1046,7 +1142,9 @@ def _wait_step_input_queue_empty(self, step_name: str) -> Union["Queue[Any]", No if self._check_step_not_loaded_or_finished(step_name): return None - if input_queue := self.dag.get_step(step_name).get(INPUT_QUEUE_ATTR_NAME): + if input_queue := self.dag.get_step(step_name).get( + constants.INPUT_QUEUE_ATTR_NAME + ): while input_queue.qsize() != 0: pass return input_queue @@ -1085,7 +1183,7 @@ def _create_step_input_queue(self, step_name: str) -> "Queue[Any]": The input queue created. """ input_queue = self.QueueClass() - self.dag.set_step_attr(step_name, INPUT_QUEUE_ATTR_NAME, input_queue) + self.dag.set_step_attr(step_name, constants.INPUT_QUEUE_ATTR_NAME, input_queue) return input_queue @abstractmethod @@ -1107,7 +1205,7 @@ def _run_steps(self, steps: Iterable[str]) -> None: steps: """ for step_name in steps: - step: "Step" = self.dag.get_step(step_name)[STEP_ATTR_NAME] + step: "Step" = self.dag.get_step(step_name)[constants.STEP_ATTR_NAME] input_queue = self._create_step_input_queue(step_name=step_name) # Set `pipeline` to `None` as in some Python environments the pipeline is not @@ -1132,10 +1230,10 @@ def _add_batches_back_to_batch_manager(self) -> None: method should be used when the pipeline has been stopped prematurely.""" for step_name in self.dag: node = self.dag.get_step(step_name) - step: "_Step" = node[STEP_ATTR_NAME] + step: "_Step" = node[constants.STEP_ATTR_NAME] if step.is_generator: continue - if input_queue := node.get(INPUT_QUEUE_ATTR_NAME): + if input_queue := node.get(constants.INPUT_QUEUE_ATTR_NAME): while not input_queue.empty(): batch = input_queue.get() if not isinstance(batch, _Batch): @@ -1229,7 +1327,7 @@ def _send_to_step(self, step_name: str, to_send: Any) -> None: step_name: The name of the step. to_send: The object to send. """ - input_queue = self.dag.get_step(step_name)[INPUT_QUEUE_ATTR_NAME] + input_queue = self.dag.get_step(step_name)[constants.INPUT_QUEUE_ATTR_NAME] input_queue.put(to_send) def _send_batch_to_step(self, batch: "_Batch") -> None: @@ -1248,7 +1346,7 @@ def _send_batch_to_step(self, batch: "_Batch") -> None: ) self._batch_manager.set_last_batch_sent(batch) # type: ignore - step: "_Step" = self.dag.get_step(batch.step_name)[STEP_ATTR_NAME] + step: "_Step" = self.dag.get_step(batch.step_name)[constants.STEP_ATTR_NAME] if not step.is_generator and (step.is_global or self._use_fs_to_pass_data): base_path = UPath(self._storage_base_path) / step.name # type: ignore self._logger.debug( @@ -1269,7 +1367,7 @@ def _gather_requirements(self) -> List[str]: """ steps_requirements = [] for step in self.dag: - step_req = self.dag.get_step(step)[STEP_ATTR_NAME].requirements + step_req = self.dag.get_step(step)[constants.STEP_ATTR_NAME].requirements steps_requirements.extend(step_req) return steps_requirements @@ -1298,7 +1396,7 @@ def _send_last_batch_flag_to_step(self, step_name: str) -> None: ) for _ in range(self.dag.get_step_replica_count(step_name)): - self._send_to_step(step_name, LAST_BATCH_SENT_FLAG) + self._send_to_step(step_name, constants.LAST_BATCH_SENT_FLAG) self._batch_manager.set_last_batch_flag_sent_to(step_name) # type: ignore def _request_initial_batches(self) -> None: @@ -1365,7 +1463,7 @@ def _handle_batch_on_stop(self, batch: "_Batch") -> None: assert self._batch_manager, "Batch manager is not set" self._batch_manager.register_batch(batch) - step: "Step" = self.dag.get_step(batch.step_name)[STEP_ATTR_NAME] + step: "Step" = self.dag.get_step(batch.step_name)[constants.STEP_ATTR_NAME] for successor in self.dag.get_step_successors(step.name): # type: ignore self._batch_manager.add_batch(successor, batch) @@ -1378,7 +1476,7 @@ def _get_step_from_batch(self, batch: "_Batch") -> "Step": Returns: The `Step` instance. """ - return self.dag.get_step(batch.step_name)[STEP_ATTR_NAME] + return self.dag.get_step(batch.step_name)[constants.STEP_ATTR_NAME] def _notify_steps_to_stop(self) -> None: """Notifies the steps to stop their infinite running loop by sending `None` to @@ -1399,12 +1497,14 @@ def _get_successors(self, batch: "_Batch") -> Tuple[List[str], List[str], bool]: a routing function. """ node = self.dag.get_step(batch.step_name) - step: "Step" = node[STEP_ATTR_NAME] + step: "Step" = node[constants.STEP_ATTR_NAME] successors = list(self.dag.get_step_successors(step.name)) # type: ignore route_to = successors # Check if the step has a routing function to send the batch to specific steps - if routing_batch_function := node.get(ROUTING_BATCH_FUNCTION_ATTR_NAME): + if routing_batch_function := node.get( + constants.ROUTING_BATCH_FUNCTION_ATTR_NAME + ): route_to = routing_batch_function(batch, successors) successors_str = ", ".join(f"'{successor}'" for successor in route_to) self._logger.info( @@ -1481,3 +1581,10 @@ def signal_handler(signumber: int, frame: Any) -> None: self._stop() return signal.signal(signal.SIGINT, signal_handler) + + +def set_pipeline_running_env_variables( + pipeline_name: str, pipeline_cache_id: str +) -> None: + os.environ[constants.PIPELINE_NAME_ENV_NAME] = pipeline_name + os.environ[constants.PIPELINE_CACHE_ID_ENV_NAME] = pipeline_cache_id diff --git a/src/distilabel/pipeline/batch_manager.py b/src/distilabel/pipeline/batch_manager.py index 42b736f7c8..286d5ef9da 100644 --- a/src/distilabel/pipeline/batch_manager.py +++ b/src/distilabel/pipeline/batch_manager.py @@ -746,6 +746,24 @@ def add_batch(self, to_step: str, batch: _Batch, prepend: bool = False) -> None: step = self._steps[to_step] step.add_batch(batch, prepend) + def add_batch_to_recover_offline_batch_generation( + self, to_step: str, data: List[List[Dict[str, Any]]] + ) -> None: + """Add a batch to recover pipeline execution from an `_Step` that used an `LLM` + with offline batch generation. It will add the batch to the start of the buffer + of the step and set the last batch received of the step to `None`. + + Args: + to_step: The name of the step that will process the batch. + data: The data that was used with the offline batch generation. + """ + self.add_batch( + to_step=to_step, + batch=_Batch(seq_no=0, step_name=to_step, last_batch=True, data=data), + prepend=True, + ) + self._last_batch_received[to_step] = None + def get_batch(self, step_name: str) -> Union[_Batch, None]: """Get the next batch to be processed by the step. diff --git a/src/distilabel/pipeline/local.py b/src/distilabel/pipeline/local.py index 74bfd0492b..1daa63944d 100644 --- a/src/distilabel/pipeline/local.py +++ b/src/distilabel/pipeline/local.py @@ -20,10 +20,10 @@ import tblib +from distilabel.constants import SIGINT_HANDLER_CALLED_ENV_NAME from distilabel.distiset import create_distiset -from distilabel.pipeline.base import ( - BasePipeline, -) +from distilabel.exceptions import DistilabelOfflineBatchGenerationNotFinishedException +from distilabel.pipeline.base import BasePipeline, set_pipeline_running_env_variables from distilabel.pipeline.ray import RayPipeline from distilabel.pipeline.step_wrapper import _StepWrapper, _StepWrapperException from distilabel.utils.logging import setup_logging, stop_logging @@ -40,13 +40,27 @@ _SUBPROCESS_EXCEPTION: Union[Exception, None] = None -def _init_worker(log_queue: "Queue[Any]") -> None: +def _init_worker( + log_queue: "Queue[Any]", pipeline_name: str, pipeline_cache_id: str +) -> None: """Init function for the child processes that will execute the `Step`s of the `Pipeline`. Args: log_queue: The queue to send the logs to the main process. """ - signal.signal(signal.SIGINT, signal.SIG_IGN) + + # Register a signal handler for SIGINT to avoid the default behavior of the process + # to terminate when the parent process receives a SIGINT signal. Instead, set an env + # variable when SIGINT is received. Child process can check the value of this env + # variable in sections of the code where they need to stop the execution if SIGINT + # was received (such as offline batch generation polling). + def signal_handler(sig: int, frame: Any) -> None: + import os + + os.environ[SIGINT_HANDLER_CALLED_ENV_NAME] = "1" + + signal.signal(signal.SIGINT, signal_handler) + set_pipeline_running_env_variables(pipeline_name, pipeline_cache_id) setup_logging(log_queue) @@ -122,7 +136,7 @@ def ray( def run( self, - parameters: Optional[Dict[str, Dict[str, Any]]] = None, + parameters: Optional[Dict[Any, Dict[str, Any]]] = None, use_cache: bool = True, storage_parameters: Optional[Dict[str, Any]] = None, use_fs_to_pass_data: bool = False, @@ -183,7 +197,11 @@ def run( _NoDaemonPool( num_processes, initializer=_init_worker, - initargs=(self._log_queue,), + initargs=( + self._log_queue, + self.name, + self._create_signature(), + ), ) as pool, ): self._manager = manager @@ -288,6 +306,21 @@ def _error_callback(self, e: BaseException) -> None: self._logger.error(f"Subprocess traceback:\n\n{e.formatted_traceback}") return + # Handle tasks using an `LLM` using offline batch generation + if isinstance( + e.subprocess_exception, DistilabelOfflineBatchGenerationNotFinishedException + ): + self._logger.info( + f"⏹️ '{e.step.name}' task stopped pipeline execution: LLM offline batch" + " generation in progress. Rerun pipeline with cache to check results and" + " continue execution." + ) + self._set_step_for_recovering_offline_batch_generation(e.step, e.data) # type: ignore + with self._stop_called_lock: + if not self._stop_called: + self._stop(acquire_lock=False) + return + # Global step with successors failed self._logger.error(f"An error occurred in global step '{step_name}'") self._logger.error(f"Subprocess traceback:\n\n{e.formatted_traceback}") @@ -324,38 +357,45 @@ def _set_steps_not_loaded_exception(self) -> None: ) self._exception.__cause__ = _SUBPROCESS_EXCEPTION - def _stop(self) -> None: + def _stop(self, acquire_lock: bool = True) -> None: """Stops the pipeline execution. It will first send `None` to the input queues of all the steps and then wait until the output queue is empty i.e. all the steps finished processing the batches that were sent before the stop flag. Then it will - send `None` to the output queue to notify the pipeline to stop.""" + send `None` to the output queue to notify the pipeline to stop. - with self._stop_called_lock: - if self._stop_called: - self._stop_calls += 1 - if self._stop_calls == 1: - self._logger.warning( - "🛑 Press again to force the pipeline to stop." - ) - elif self._stop_calls > 1: - self._logger.warning("🛑 Forcing pipeline interruption.") + Args: + acquire_lock: Whether to acquire the lock to access the `_stop_called` attribute. + """ - if self._pool: - self._pool.terminate() - self._pool.join() - self._pool = None + if acquire_lock: + self._stop_called_lock.acquire() - if self._manager: - self._manager.shutdown() - self._manager.join() - self._manager = None + if self._stop_called: + self._stop_calls += 1 + if self._stop_calls == 1: + self._logger.warning("🛑 Press again to force the pipeline to stop.") + elif self._stop_calls > 1: + self._logger.warning("🛑 Forcing pipeline interruption.") - stop_logging() + if self._pool: + self._pool.terminate() + self._pool.join() + self._pool = None - sys.exit(1) + if self._manager: + self._manager.shutdown() + self._manager.join() + self._manager = None - return - self._stop_called = True + stop_logging() + + sys.exit(1) + + return + self._stop_called = True + + if acquire_lock: + self._stop_called_lock.release() self._logger.debug( f"Steps loaded before calling `stop`: {self._steps_load_status}" @@ -364,5 +404,4 @@ def _stop(self) -> None: "🛑 Stopping pipeline. Waiting for steps to finish processing batches..." ) - self._stop_load_queue_loop() self._stop_output_queue_loop() diff --git a/src/distilabel/pipeline/ray.py b/src/distilabel/pipeline/ray.py index aad72a61cb..bfdff96c64 100644 --- a/src/distilabel/pipeline/ray.py +++ b/src/distilabel/pipeline/ray.py @@ -15,10 +15,11 @@ import sys from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Union -from distilabel.constants import INPUT_QUEUE_ATTR_NAME +from distilabel.constants import INPUT_QUEUE_ATTR_NAME, STEP_ATTR_NAME from distilabel.distiset import create_distiset +from distilabel.errors import DistilabelUserError from distilabel.llms.vllm import vLLM -from distilabel.pipeline.base import BasePipeline +from distilabel.pipeline.base import BasePipeline, set_pipeline_running_env_variables from distilabel.pipeline.step_wrapper import _StepWrapper from distilabel.utils.logging import setup_logging, stop_logging from distilabel.utils.serialization import TYPE_INFO_KEY @@ -110,6 +111,8 @@ def run( Raises: RuntimeError: If the pipeline fails to load all the steps. """ + self._check_no_llms_using_offline_batch_generation() + self._init_ray() self._log_queue = self.QueueClass( @@ -160,6 +163,21 @@ def run( return distiset + def _check_no_llms_using_offline_batch_generation(self) -> None: + """Checks if there are any `LLM` steps using the `offline_batch_generate` method + and raises an exception if so. This method is not supported in the Ray pipeline.""" + for step_name in self.dag: + step: "_Step" = self.dag.get_step(step_name)[STEP_ATTR_NAME] + if not hasattr(step, "llm"): + continue + if step.llm.use_offline_batch_generation: # type: ignore + raise DistilabelUserError( + f"Step '{step_name}' uses an `LLM` with offline batch generation because" + "`use_offline_batch_generation=True`. `LLM`s using this method are not" + " supported in the Ray pipeline.", + page="sections/how_to_guides/advanced/offline-batch-generation", + ) + def _init_ray(self) -> None: """Inits or connects to a Ray cluster.""" try: @@ -231,13 +249,22 @@ def _run_step(self, step: "_Step", input_queue: "Queue[Any]", replica: int) -> N @ray.remote class _StepWrapperRay: def __init__( - self, step_wrapper: _StepWrapper, log_queue: "Queue[Any]" + self, + step_wrapper: _StepWrapper, + log_queue: "Queue[Any]", + pipeline_name: str, + pipeline_cache_id: str, ) -> None: self._step_wrapper = step_wrapper self._log_queue = log_queue + self._pipeline_name = pipeline_name + self._pipeline_cache_id = pipeline_cache_id def run(self) -> str: setup_logging(log_queue=self._log_queue) + set_pipeline_running_env_variables( + self._pipeline_name, self._pipeline_cache_id + ) return self._step_wrapper.run() resources: Dict[str, Any] = { @@ -276,6 +303,8 @@ def run(self) -> str: ray_pipeline=True, ), log_queue=self._log_queue, + pipeline_name=self.name, + pipeline_cache_id=self._create_signature(), ) self._logger.debug( @@ -399,7 +428,6 @@ def _stop(self) -> None: "🛑 Stopping pipeline. Waiting for steps to finish processing batches..." ) - self._stop_load_queue_loop() self._stop_output_queue_loop() def dump(self, **kwargs: Any) -> Dict[str, Any]: diff --git a/src/distilabel/pipeline/step_wrapper.py b/src/distilabel/pipeline/step_wrapper.py index c153a6008b..5fe5572009 100644 --- a/src/distilabel/pipeline/step_wrapper.py +++ b/src/distilabel/pipeline/step_wrapper.py @@ -18,6 +18,7 @@ from distilabel.constants import LAST_BATCH_SENT_FLAG from distilabel.errors import DISTILABEL_DOCS_URL +from distilabel.exceptions import DistilabelOfflineBatchGenerationNotFinishedException from distilabel.llms.mixins.cuda_device_placement import CudaDevicePlacementMixin from distilabel.pipeline.batch import _Batch from distilabel.pipeline.typing import StepLoadStatus @@ -231,7 +232,16 @@ def _non_generator_process_loop(self) -> None: result = next(step.process_applying_mappings(batch.data[0])) except Exception as e: if self.step.is_global: - raise _StepWrapperException(str(e), self.step, 2, e) from e + self.step.unload() + self._notify_unload() + data = ( + batch.data + if isinstance( + e, DistilabelOfflineBatchGenerationNotFinishedException + ) + else None + ) + raise _StepWrapperException(str(e), self.step, 2, e, data) from e # Impute step outputs columns with `None` result = self._impute_step_outputs(batch) @@ -285,7 +295,8 @@ class _StepWrapperException(Exception): message: The error message. step: The `Step` that raised the error. code: The error code. - subprocess_exception: The exception raised by the subprocess. Defaults to `None`. + subprocess_exception: The exception raised by the subprocess. + data: The data that caused the error. Defaults to `None`. """ def __init__( @@ -293,15 +304,21 @@ def __init__( message: str, step: "_Step", code: int, - subprocess_exception: Optional[Exception] = None, + subprocess_exception: Exception, + data: Optional[List[List[Dict[str, Any]]]] = None, ) -> None: - self.message = f"{message}\n\nFor further information visit {DISTILABEL_DOCS_URL}api/pipeline/step_wrapper" + self.message = f"{message}\n\nFor further information visit '{DISTILABEL_DOCS_URL}api/pipeline/step_wrapper'" self.step = step self.code = code self.subprocess_exception = subprocess_exception self.formatted_traceback = "".join( - traceback.format_exception(subprocess_exception) + traceback.format_exception( + type(subprocess_exception), + subprocess_exception, + subprocess_exception.__traceback__, + ) ) + self.data = data @classmethod def create_load_error( @@ -320,7 +337,7 @@ def create_load_error( Returns: The `_StepWrapperException` instance. """ - return cls(message, step, 1, subprocess_exception) + return cls(message, step, 1, subprocess_exception, None) @property def is_load_error(self) -> bool: diff --git a/src/distilabel/steps/generators/data.py b/src/distilabel/steps/generators/data.py index 1d26ed8856..803ee35eac 100644 --- a/src/distilabel/steps/generators/data.py +++ b/src/distilabel/steps/generators/data.py @@ -14,6 +14,7 @@ from typing import TYPE_CHECKING, Any, Dict, List +from pydantic import Field from typing_extensions import override from distilabel.steps.base import GeneratorStep @@ -59,7 +60,7 @@ class LoadDataFromDicts(GeneratorStep): ``` """ - data: List[Dict[str, Any]] + data: List[Dict[str, Any]] = Field(default_factory=list, exclude=True) @override def process(self, offset: int = 0) -> "GeneratorStepOutput": # type: ignore diff --git a/src/distilabel/steps/tasks/base.py b/src/distilabel/steps/tasks/base.py index fd515a4c28..dcf704a807 100644 --- a/src/distilabel/steps/tasks/base.py +++ b/src/distilabel/steps/tasks/base.py @@ -16,14 +16,16 @@ from abc import ABC, abstractmethod from typing import TYPE_CHECKING, Any, Dict, List, Union -from pydantic import Field +from pydantic import Field, PrivateAttr from typing_extensions import override from distilabel.constants import DISTILABEL_METADATA_KEY +from distilabel.errors import DistilabelUserError from distilabel.llms.base import LLM from distilabel.mixins.runtime_parameters import RuntimeParameter from distilabel.steps.base import ( GeneratorStep, + GlobalStep, Step, StepInput, _Step, @@ -73,6 +75,36 @@ class _Task(_Step, ABC): ) use_default_structured_output: bool = False + _can_be_used_with_offline_batch_generation: bool = PrivateAttr(False) + + def model_post_init(self, __context: Any) -> None: + if ( + self.llm.use_offline_batch_generation + and not self._can_be_used_with_offline_batch_generation + ): + raise DistilabelUserError( + f"`{self.__class__.__name__}` task cannot be used with offline batch generation" + " feature.", + page="sections/how_to_guides/advanced/offline-batch-generation", + ) + + super().model_post_init(__context) + + @property + def is_global(self) -> bool: + """Extends the `is_global` property to return `True` if the task is using the + offline batch generation feature, otherwise it returns the value of the parent + class property. `offline_batch_generation` requires to receive all the inputs + at once, so for the `_BatchManager` this is a global step. + + Returns: + Whether the task is a global step or not. + """ + if self.llm.use_offline_batch_generation: + return True + + return super().is_global + def load(self) -> None: """Loads the LLM via the `LLM.load()` method.""" super().load() @@ -264,7 +296,7 @@ def process(self, inputs: StepInput) -> "StepOutput": # type: ignore formatted_inputs = self._format_inputs(inputs) # `outputs` is a list containing a list of generations per input - outputs = self.llm.generate( + outputs = self.llm.generate_outputs( inputs=formatted_inputs, num_generations=self.num_generations, # type: ignore **self.llm.get_generation_kwargs(), # type: ignore @@ -291,7 +323,7 @@ def process(self, inputs: StepInput) -> "StepOutput": # type: ignore class GeneratorTask(_Task, GeneratorStep): - """GeneratorTask is a class that implements the `_Task` abstract class and adds the + """`GeneratorTask` is a class that implements the `_Task` abstract class and adds the `GeneratorStep` interface to be used as a step in the pipeline. Attributes: @@ -302,3 +334,12 @@ class GeneratorTask(_Task, GeneratorStep): """ pass + + +class GlobalTask(_Task, GlobalStep): + """`GlobalTask` is a class that implements the `_Task` abstract class and adds the + `GlobalStep` interface to be used as a step in the pipeline. It's generally used in + combination with `LLM`s that can be used for offline batched inference. + """ + + pass diff --git a/src/distilabel/steps/tasks/complexity_scorer.py b/src/distilabel/steps/tasks/complexity_scorer.py index 5f972eb67c..9ee8befa5a 100644 --- a/src/distilabel/steps/tasks/complexity_scorer.py +++ b/src/distilabel/steps/tasks/complexity_scorer.py @@ -127,6 +127,7 @@ class ComplexityScorer(Task): """ _template: Union[Template, None] = PrivateAttr(...) + _can_be_used_with_offline_batch_generation = True def load(self) -> None: """Loads the Jinja2 template.""" diff --git a/src/distilabel/steps/tasks/improving_text_embeddings.py b/src/distilabel/steps/tasks/improving_text_embeddings.py index 1908a9685c..a23b9dbbac 100644 --- a/src/distilabel/steps/tasks/improving_text_embeddings.py +++ b/src/distilabel/steps/tasks/improving_text_embeddings.py @@ -306,6 +306,7 @@ class EmbeddingTaskGenerator(GeneratorTask): flatten_tasks: bool = False _template: Union[Template, None] = PrivateAttr(...) + _can_be_used_with_offline_batch_generation = True def load(self) -> None: """Loads the Jinja2 template.""" @@ -486,6 +487,7 @@ class GenerateTextRetrievalData(_EmbeddingDataGeneration): num_words: Optional[Literal[50, 100, 200, 300, 400, 500]] = None _template_name: str = PrivateAttr(default="text-retrieval") + _can_be_used_with_offline_batch_generation = True def format_input(self, input: Dict[str, Any]) -> ChatType: """Method to format the input based on the `task` and the provided attributes, or just @@ -593,6 +595,7 @@ class GenerateShortTextMatchingData(_EmbeddingDataGeneration): ) _template_name: str = PrivateAttr(default="short-text-matching") + _can_be_used_with_offline_batch_generation = True def format_input(self, input: Dict[str, Any]) -> ChatType: """Method to format the input based on the `task` and the provided attributes, or just @@ -682,6 +685,7 @@ class GenerateLongTextMatchingData(_EmbeddingDataGeneration): ) _template_name: str = PrivateAttr(default="long-text-matching") + _can_be_used_with_offline_batch_generation = True def format_input(self, input: Dict[str, Any]) -> ChatType: """Method to format the input based on the `task` and the provided attributes, or just @@ -782,6 +786,7 @@ class GenerateTextClassificationData(_EmbeddingDataGeneration): ] = None _template_name: str = PrivateAttr(default="text-classification") + _can_be_used_with_offline_batch_generation = True def format_input(self, input: Dict[str, Any]) -> ChatType: """Method to format the input based on the `task` and the provided attributes, or just @@ -878,6 +883,7 @@ class MonolingualTripletGenerator(_EmbeddingDataGenerator): low_score: Optional[Literal["2.5", "3", "3.5"]] = None _template_name: str = PrivateAttr(default="monolingual-triplet") + _can_be_used_with_offline_batch_generation = True @property def prompt(self) -> ChatType: @@ -974,6 +980,7 @@ class BitextRetrievalGenerator(_EmbeddingDataGenerator): low_score: Optional[Literal["2.5", "3", "3.5"]] = None _template_name: str = PrivateAttr(default="bitext-retrieval") + _can_be_used_with_offline_batch_generation = True @property def prompt(self) -> ChatType: diff --git a/src/distilabel/steps/tasks/instruction_backtranslation.py b/src/distilabel/steps/tasks/instruction_backtranslation.py index 9322aa4f2d..a0420ef8f3 100644 --- a/src/distilabel/steps/tasks/instruction_backtranslation.py +++ b/src/distilabel/steps/tasks/instruction_backtranslation.py @@ -101,6 +101,7 @@ class InstructionBacktranslation(Task): """ _template: Optional["Template"] = PrivateAttr(default=...) + _can_be_used_with_offline_batch_generation = True def load(self) -> None: """Loads the Jinja2 template.""" diff --git a/src/distilabel/steps/tasks/quality_scorer.py b/src/distilabel/steps/tasks/quality_scorer.py index ff3f199db8..57b7e38e2d 100644 --- a/src/distilabel/steps/tasks/quality_scorer.py +++ b/src/distilabel/steps/tasks/quality_scorer.py @@ -147,6 +147,7 @@ class QualityScorer(Task): """ _template: Union[Template, None] = PrivateAttr(...) + _can_be_used_with_offline_batch_generation = True def load(self) -> None: """Loads the Jinja2 template.""" diff --git a/src/distilabel/steps/tasks/self_instruct.py b/src/distilabel/steps/tasks/self_instruct.py index e59cad4b67..28ac346c39 100644 --- a/src/distilabel/steps/tasks/self_instruct.py +++ b/src/distilabel/steps/tasks/self_instruct.py @@ -112,6 +112,7 @@ class SelfInstruct(Task): application_description: str = "AI assistant" _template: Union[Template, None] = PrivateAttr(...) + _can_be_used_with_offline_batch_generation = True def load(self) -> None: """Loads the Jinja2 template.""" diff --git a/src/distilabel/steps/tasks/text_generation.py b/src/distilabel/steps/tasks/text_generation.py index 4f6b681d19..33da5d980e 100644 --- a/src/distilabel/steps/tasks/text_generation.py +++ b/src/distilabel/steps/tasks/text_generation.py @@ -12,7 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -import warnings from typing import TYPE_CHECKING, Any, Dict, List, Union from distilabel.errors import DistilabelUserError @@ -80,6 +79,8 @@ class TextGeneration(Task): use_system_prompt: bool = True + _can_be_used_with_offline_batch_generation = True + @property def inputs(self) -> "StepColumns": """The input for the task is the `instruction`.""" @@ -108,12 +109,6 @@ def format_input(self, input: Dict[str, Any]) -> "ChatType": messages.insert( 0, {"role": "system", "content": input["system_prompt"]} ) - else: - warnings.warn( - "`use_system_prompt` is set to `True`, but no `system_prompt` in input batch, so it will be ignored.", - UserWarning, - stacklevel=2, - ) return messages # type: ignore @property diff --git a/src/distilabel/steps/tasks/ultrafeedback.py b/src/distilabel/steps/tasks/ultrafeedback.py index 091d99599e..04af9c1775 100644 --- a/src/distilabel/steps/tasks/ultrafeedback.py +++ b/src/distilabel/steps/tasks/ultrafeedback.py @@ -206,6 +206,7 @@ class UltraFeedback(Task): ) ) _template: Optional["Template"] = PrivateAttr(default=...) + _can_be_used_with_offline_batch_generation = True def load(self) -> None: """Loads the Jinja2 template for the given `aspect`.""" diff --git a/src/distilabel/utils/logging.py b/src/distilabel/utils/logging.py index 0e409863ff..1fdaf2f3e7 100644 --- a/src/distilabel/utils/logging.py +++ b/src/distilabel/utils/logging.py @@ -14,7 +14,6 @@ import logging import multiprocessing as mp -import os import warnings from logging import FileHandler from logging.handlers import QueueHandler, QueueListener @@ -23,6 +22,8 @@ from rich.logging import RichHandler +from distilabel import envs + if TYPE_CHECKING: from queue import Queue @@ -77,7 +78,7 @@ def setup_logging( ) queue_listener.start() - log_level = os.environ.get("DISTILABEL_LOG_LEVEL", "INFO").upper() + log_level = envs.DISTILABEL_LOG_LEVEL if log_level not in ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]: warnings.warn( f"Invalid log level '{log_level}', using default 'INFO' instead.", diff --git a/src/distilabel/utils/serialization.py b/src/distilabel/utils/serialization.py index 8f32afc2eb..873ff20721 100644 --- a/src/distilabel/utils/serialization.py +++ b/src/distilabel/utils/serialization.py @@ -19,6 +19,8 @@ import orjson +from distilabel.mixins.runtime_parameters import RuntimeParametersMixin + if sys.version_info < (3, 11): from enum import EnumMeta as EnumType else: @@ -205,7 +207,11 @@ def _model_dump(self, obj: Any, **kwargs: Any) -> Dict[str, Any]: "_values": {x.name: x.value for x in v}, # type: ignore } elif isinstance(v, list): - dump[k] = {str(i): list_v for i, list_v in enumerate(v)} + obj_list = getattr(obj, k) + if isinstance(obj_list, list) and isinstance( + obj_list[0], RuntimeParametersMixin + ): + dump[k] = {str(i): list_v for i, list_v in enumerate(v)} # Grab the fields that need extra care (LLMs from inside tasks) to_update = _extra_serializable_fields(obj) diff --git a/src/distilabel/utils/typing_.py b/src/distilabel/utils/typing_.py index ac75aba63c..26dfa1b203 100644 --- a/src/distilabel/utils/typing_.py +++ b/src/distilabel/utils/typing_.py @@ -13,8 +13,9 @@ # limitations under the License. import inspect -from typing import Any +from typing import Any, Union +from pydantic.types import _SecretField from typing_extensions import Annotated, get_args, get_origin @@ -38,3 +39,31 @@ def is_parameter_annotated_with(parameter: inspect.Parameter, annotation: Any) - return True return False + + +def extract_annotation_inner_type(type_hint: Any) -> Any: + """Extracts the inner type of an annotation. + + Args: + type_hint: The type hint to extract the inner type from. + + Returns: + The inner type of the `RuntimeParameter` type hint. + """ + type_hint_args = get_args(type_hint) + if get_origin(type_hint) is Annotated: + return extract_annotation_inner_type(type_hint_args[0]) + + if get_origin(type_hint) is Union and type(None) in type_hint_args: + return extract_annotation_inner_type(type_hint_args[0]) + + return type_hint + + +def is_type_pydantic_secret_field(type_: type) -> bool: + """Checks if a type is a Pydantic `_SecretField`. + + Returns: + `True` if the type is a Pydantic `_SecretField`, `False` otherwise. + """ + return inspect.isclass(type_) and issubclass(type_, _SecretField) diff --git a/tests/integration/test_offline_batch_generation.py b/tests/integration/test_offline_batch_generation.py new file mode 100644 index 0000000000..d7afb0a47f --- /dev/null +++ b/tests/integration/test_offline_batch_generation.py @@ -0,0 +1,77 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from tempfile import TemporaryDirectory +from typing import TYPE_CHECKING, Any, List, Union + +from distilabel.exceptions import DistilabelOfflineBatchGenerationNotFinishedException +from distilabel.llms import LLM +from distilabel.pipeline import Pipeline +from distilabel.steps import LoadDataFromDicts +from distilabel.steps.tasks import TextGeneration + +if TYPE_CHECKING: + from distilabel.llms.typing import GenerateOutput + from distilabel.steps.tasks.typing import FormattedInput + + +class DummyOfflineBatchGenerateLLM(LLM): + def load(self) -> None: + super().load() + + @property + def model_name(self) -> str: + return "test" + + def generate( # type: ignore + self, input: "FormattedInput", num_generations: int = 1 + ) -> "GenerateOutput": + return ["output" for _ in range(num_generations)] + + def offline_batch_generate( + self, + inputs: Union[List["FormattedInput"], None] = None, + num_generations: int = 1, + **kwargs: Any, + ) -> List["GenerateOutput"]: + # Simulate that the first time we create the jobs + if not self.jobs_ids: + self.jobs_ids = ("1234", "5678") + raise DistilabelOfflineBatchGenerationNotFinishedException( + jobs_ids=self.jobs_ids # type: ignore + ) + + return [["output" for _ in range(num_generations)]] + + +def test_offline_batch_generation() -> None: + with TemporaryDirectory() as tmp_dir: + with Pipeline(cache_dir=tmp_dir) as pipeline: + load_data = LoadDataFromDicts( + data=[{"instruction": f"{i} instruction"} for i in range(100)] + ) + + text_generation = TextGeneration( + llm=DummyOfflineBatchGenerateLLM(use_offline_batch_generation=True) + ) + + load_data >> text_generation + + distiset = pipeline.run() + + # First call no results + assert len(distiset) == 0 + + distiset = pipeline.run(use_cache=True) + assert len(distiset) == 1 diff --git a/tests/unit/conftest.py b/tests/unit/conftest.py index a7a4bdcc1d..1903d10e3c 100644 --- a/tests/unit/conftest.py +++ b/tests/unit/conftest.py @@ -12,20 +12,21 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import TYPE_CHECKING, Any, List +from typing import TYPE_CHECKING, Any, Dict, List, Union import pytest from distilabel.llms.base import LLM, AsyncLLM from distilabel.llms.mixins.magpie import MagpieChatTemplateMixin +from distilabel.steps.tasks.base import Task if TYPE_CHECKING: from distilabel.llms.typing import GenerateOutput - from distilabel.steps.tasks.typing import FormattedInput + from distilabel.steps.tasks.typing import ChatType, FormattedInput # Defined here too, so that the serde still works -class DummyLLM(AsyncLLM): +class DummyAsyncLLM(AsyncLLM): structured_output: Any = None def load(self) -> None: @@ -35,23 +36,23 @@ def load(self) -> None: def model_name(self) -> str: return "test" - async def agenerate( + async def agenerate( # type: ignore self, input: "FormattedInput", num_generations: int = 1 ) -> "GenerateOutput": return ["output" for _ in range(num_generations)] -class DummySyncLLM(AsyncLLM): +class DummyLLM(LLM): structured_output: Any = None def load(self) -> None: - pass + super().load() @property def model_name(self) -> str: return "test" - def generate( + def generate( # type: ignore self, input: "FormattedInput", num_generations: int = 1 ) -> "GenerateOutput": return ["output" for _ in range(num_generations)] @@ -73,6 +74,31 @@ def generate( ] +class DummyTask(Task): + @property + def inputs(self) -> List[str]: + return ["instruction", "additional_info"] + + def format_input(self, input: Dict[str, Any]) -> "ChatType": + return [ + {"role": "system", "content": ""}, + {"role": "user", "content": input["instruction"]}, + ] + + @property + def outputs(self) -> List[str]: + return ["output", "info_from_input"] + + def format_output( + self, output: Union[str, None], input: Union[Dict[str, Any], None] = None + ) -> Dict[str, Any]: + return {"output": output, "info_from_input": input["additional_info"]} # type: ignore + + +class DummyTaskOfflineBatchGeneration(DummyTask): + _can_be_used_with_offline_batch_generation = True + + @pytest.fixture def dummy_llm() -> AsyncLLM: - return DummyLLM() + return DummyAsyncLLM() diff --git a/tests/unit/llms/huggingface/test_inference_endpoints.py b/tests/unit/llms/huggingface/test_inference_endpoints.py index 0419cc36ce..d820122a4d 100644 --- a/tests/unit/llms/huggingface/test_inference_endpoints.py +++ b/tests/unit/llms/huggingface/test_inference_endpoints.py @@ -311,6 +311,9 @@ def test_serialization(self, mock_inference_client: MagicMock) -> None: "structured_output": None, "model_display_name": None, "use_magpie_template": False, + "jobs_ids": None, + "offline_batch_generation_block_until_done": None, + "use_offline_batch_generation": False, "type_info": { "module": "distilabel.llms.huggingface.inference_endpoints", "name": "InferenceEndpointsLLM", diff --git a/tests/unit/llms/test_anthropic.py b/tests/unit/llms/test_anthropic.py index 1d7fe44599..11fee764c3 100644 --- a/tests/unit/llms/test_anthropic.py +++ b/tests/unit/llms/test_anthropic.py @@ -163,6 +163,9 @@ def test_serialization( "model": "claude-3-opus-20240229", "timeout": 600.0, "structured_output": None, + "jobs_ids": None, + "offline_batch_generation_block_until_done": None, + "use_offline_batch_generation": False, "type_info": { "module": "distilabel.llms.anthropic", "name": "AnthropicLLM", diff --git a/tests/unit/llms/test_anyscale.py b/tests/unit/llms/test_anyscale.py index 73dd3cb6f7..178419c1b7 100644 --- a/tests/unit/llms/test_anyscale.py +++ b/tests/unit/llms/test_anyscale.py @@ -49,6 +49,9 @@ def test_serialization(self) -> None: "base_url": "https://api.endpoints.anyscale.com/v1", "timeout": 120, "structured_output": None, + "jobs_ids": None, + "offline_batch_generation_block_until_done": None, + "use_offline_batch_generation": False, "type_info": { "module": "distilabel.llms.anyscale", "name": "AnyscaleLLM", diff --git a/tests/unit/llms/test_azure.py b/tests/unit/llms/test_azure.py index 04b76d5545..eee3ed85fb 100644 --- a/tests/unit/llms/test_azure.py +++ b/tests/unit/llms/test_azure.py @@ -74,6 +74,9 @@ def test_azure_openai_llm_env_vars(self) -> None: "base_url": "https://example-resource.azure.openai.com/", "timeout": 120, "structured_output": None, + "jobs_ids": None, + "offline_batch_generation_block_until_done": None, + "use_offline_batch_generation": False, "type_info": { "module": "distilabel.llms.azure", "name": "AzureOpenAILLM", @@ -98,6 +101,9 @@ def test_azure_openai_llm_env_vars(self) -> None: "mode": "tool_call", "max_retries": 1, }, + "jobs_ids": None, + "offline_batch_generation_block_until_done": None, + "use_offline_batch_generation": False, "type_info": { "module": "distilabel.llms.azure", "name": "AzureOpenAILLM", diff --git a/tests/unit/llms/test_base.py b/tests/unit/llms/test_base.py new file mode 100644 index 0000000000..7c94227753 --- /dev/null +++ b/tests/unit/llms/test_base.py @@ -0,0 +1,28 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import pytest + +from distilabel.errors import DistilabelNotImplementedError +from tests.unit.conftest import DummyLLM + + +class TestLLM: + def test_offline_batch_generate_raise_distilabel_not_implemented_error( + self, + ) -> None: + llm = DummyLLM() + + with pytest.raises(DistilabelNotImplementedError): + llm.offline_batch_generate() diff --git a/tests/unit/llms/test_cohere.py b/tests/unit/llms/test_cohere.py index 371816edf6..2e398e01cf 100644 --- a/tests/unit/llms/test_cohere.py +++ b/tests/unit/llms/test_cohere.py @@ -141,6 +141,9 @@ async def test_generate(self, mock_async_client: mock.MagicMock) -> None: "timeout": 120, "client_name": "distilabel", "structured_output": None, + "jobs_ids": None, + "offline_batch_generation_block_until_done": None, + "use_offline_batch_generation": False, "type_info": { "module": "distilabel.llms.cohere", "name": "CohereLLM", @@ -164,6 +167,9 @@ async def test_generate(self, mock_async_client: mock.MagicMock) -> None: "mode": "tool_call", "max_retries": 1, }, + "jobs_ids": None, + "offline_batch_generation_block_until_done": None, + "use_offline_batch_generation": False, "type_info": { "module": "distilabel.llms.cohere", "name": "CohereLLM", diff --git a/tests/unit/llms/test_groq.py b/tests/unit/llms/test_groq.py index c8a782b9a8..f137750292 100644 --- a/tests/unit/llms/test_groq.py +++ b/tests/unit/llms/test_groq.py @@ -119,6 +119,9 @@ async def test_generate(self, mock_groq: MagicMock) -> None: "max_retries": 2, "timeout": 120, "structured_output": None, + "jobs_ids": None, + "offline_batch_generation_block_until_done": None, + "use_offline_batch_generation": False, "type_info": { "module": "distilabel.llms.groq", "name": "GroqLLM", @@ -142,6 +145,9 @@ async def test_generate(self, mock_groq: MagicMock) -> None: "mode": "tool_call", "max_retries": 1, }, + "jobs_ids": None, + "offline_batch_generation_block_until_done": None, + "use_offline_batch_generation": False, "type_info": { "module": "distilabel.llms.groq", "name": "GroqLLM", diff --git a/tests/unit/llms/test_litellm.py b/tests/unit/llms/test_litellm.py index f23722f2fa..56be99e028 100644 --- a/tests/unit/llms/test_litellm.py +++ b/tests/unit/llms/test_litellm.py @@ -83,6 +83,9 @@ def test_serialization(self, _: MagicMock, model: str) -> None: "model": model, "verbose": False, "structured_output": None, + "jobs_ids": None, + "offline_batch_generation_block_until_done": None, + "use_offline_batch_generation": False, "type_info": { "module": "distilabel.llms.litellm", "name": "LiteLLM", diff --git a/tests/unit/llms/test_llamacpp.py b/tests/unit/llms/test_llamacpp.py index 280244964a..35c611722d 100644 --- a/tests/unit/llms/test_llamacpp.py +++ b/tests/unit/llms/test_llamacpp.py @@ -72,6 +72,9 @@ def test_generate(self, llm: LlamaCppLLM) -> None: "seed": 4294967295, "generation_kwargs": {}, "structured_output": None, + "jobs_ids": None, + "offline_batch_generation_block_until_done": None, + "use_offline_batch_generation": False, "type_info": { "module": "distilabel.llms.llamacpp", "name": "LlamaCppLLM", @@ -96,6 +99,9 @@ def test_generate(self, llm: LlamaCppLLM) -> None: "schema": DummyUserDetail.model_json_schema(), "format": "json", }, + "jobs_ids": None, + "offline_batch_generation_block_until_done": None, + "use_offline_batch_generation": False, "type_info": { "module": "distilabel.llms.llamacpp", "name": "LlamaCppLLM", diff --git a/tests/unit/llms/test_mistral.py b/tests/unit/llms/test_mistral.py index 89f8e9649f..f1b7b4b28f 100644 --- a/tests/unit/llms/test_mistral.py +++ b/tests/unit/llms/test_mistral.py @@ -128,6 +128,9 @@ async def test_generate(self, mock_mistral: MagicMock) -> None: "timeout": 120, "max_concurrent_requests": 64, "structured_output": None, + "jobs_ids": None, + "offline_batch_generation_block_until_done": None, + "use_offline_batch_generation": False, "type_info": { "module": "distilabel.llms.mistral", "name": "MistralLLM", @@ -152,6 +155,9 @@ async def test_generate(self, mock_mistral: MagicMock) -> None: "mode": "tool_call", "max_retries": 1, }, + "jobs_ids": None, + "offline_batch_generation_block_until_done": None, + "use_offline_batch_generation": False, "type_info": { "module": "distilabel.llms.mistral", "name": "MistralLLM", @@ -174,6 +180,9 @@ def test_serialization( "timeout": 120, "max_concurrent_requests": 64, "structured_output": None, + "jobs_ids": None, + "offline_batch_generation_block_until_done": None, + "use_offline_batch_generation": False, "type_info": { "module": "distilabel.llms.mistral", "name": "MistralLLM", diff --git a/tests/unit/llms/test_moa.py b/tests/unit/llms/test_moa.py index 5863012d0e..7efd039b7a 100644 --- a/tests/unit/llms/test_moa.py +++ b/tests/unit/llms/test_moa.py @@ -13,22 +13,22 @@ # limitations under the License. from distilabel.llms.moa import MOA_SYSTEM_PROMPT, MixtureOfAgentsLLM -from tests.unit.conftest import DummyLLM +from tests.unit.conftest import DummyAsyncLLM class TestMixtureOfAgents: def test_model_name(self) -> None: llm = MixtureOfAgentsLLM( - aggregator_llm=DummyLLM(), - proposers_llms=[DummyLLM(), DummyLLM(), DummyLLM()], + aggregator_llm=DummyAsyncLLM(), + proposers_llms=[DummyAsyncLLM(), DummyAsyncLLM(), DummyAsyncLLM()], ) assert llm.model_name == "moa-test-test-test-test" def test_build_moa_system_prompt(self) -> None: llm = MixtureOfAgentsLLM( - aggregator_llm=DummyLLM(), - proposers_llms=[DummyLLM(), DummyLLM(), DummyLLM()], + aggregator_llm=DummyAsyncLLM(), + proposers_llms=[DummyAsyncLLM(), DummyAsyncLLM(), DummyAsyncLLM()], ) system_prompt = llm._build_moa_system_prompt( @@ -41,8 +41,8 @@ def test_build_moa_system_prompt(self) -> None: def test_inject_moa_system_prompt(self) -> None: llm = MixtureOfAgentsLLM( - aggregator_llm=DummyLLM(), - proposers_llms=[DummyLLM(), DummyLLM(), DummyLLM()], + aggregator_llm=DummyAsyncLLM(), + proposers_llms=[DummyAsyncLLM(), DummyAsyncLLM(), DummyAsyncLLM()], ) results = llm._inject_moa_system_prompt( diff --git a/tests/unit/llms/test_ollama.py b/tests/unit/llms/test_ollama.py index f21006fa7b..db31d9cb07 100644 --- a/tests/unit/llms/test_ollama.py +++ b/tests/unit/llms/test_ollama.py @@ -82,6 +82,9 @@ def test_serialization(self, _: MagicMock) -> None: "follow_redirects": True, "generation_kwargs": {}, "structured_output": None, + "jobs_ids": None, + "offline_batch_generation_block_until_done": None, + "use_offline_batch_generation": False, "type_info": { "module": "distilabel.llms.ollama", "name": "OllamaLLM", diff --git a/tests/unit/llms/test_openai.py b/tests/unit/llms/test_openai.py index a1b09b0883..03fb94c1d3 100644 --- a/tests/unit/llms/test_openai.py +++ b/tests/unit/llms/test_openai.py @@ -14,29 +14,38 @@ import os import sys +from textwrap import dedent from typing import Any, Dict from unittest import mock from unittest.mock import AsyncMock, MagicMock, Mock, patch import nest_asyncio +import orjson import pytest +from openai.types import Batch -from distilabel.llms.openai import OpenAILLM +from distilabel.exceptions import DistilabelOfflineBatchGenerationNotFinishedException +from distilabel.llms.openai import _OPENAI_BATCH_API_MAX_FILE_SIZE, OpenAILLM from .utils import DummyUserDetail +@patch("openai.OpenAI") @patch("openai.AsyncOpenAI") class TestOpenAILLM: model_id: str = "gpt-4" - def test_openai_llm(self, _: MagicMock) -> None: + def test_openai_llm( + self, _async_openai_mock: MagicMock, _openai_mock: MagicMock + ) -> None: llm = OpenAILLM(model=self.model_id, api_key="api.key") # type: ignore assert isinstance(llm, OpenAILLM) assert llm.model_name == self.model_id - def test_openai_llm_env_vars(self, _: MagicMock) -> None: + def test_openai_llm_env_vars( + self, _async_openai_mock: MagicMock, _openai_mock: MagicMock + ) -> None: with mock.patch.dict(os.environ, clear=True): os.environ["OPENAI_API_KEY"] = "another.api.key" os.environ["OPENAI_BASE_URL"] = "https://example.com" @@ -49,9 +58,11 @@ def test_openai_llm_env_vars(self, _: MagicMock) -> None: assert llm.api_key.get_secret_value() == "another.api.key" # type: ignore @pytest.mark.asyncio - async def test_agenerate(self, mock_openai: MagicMock) -> None: + async def test_agenerate( + self, async_openai_mock: MagicMock, _openai_mock: MagicMock + ) -> None: llm = OpenAILLM(model=self.model_id, api_key="api.key") # type: ignore - llm._aclient = mock_openai + llm._aclient = async_openai_mock mocked_completion = Mock( choices=[Mock(message=Mock(content=" Aenean hendrerit aliquam velit. ..."))] @@ -69,7 +80,9 @@ async def test_agenerate(self, mock_openai: MagicMock) -> None: ) @pytest.mark.asyncio - async def test_agenerate_structured(self, mock_openai: MagicMock) -> None: + async def test_agenerate_structured( + self, async_openai_mock: MagicMock, _openai_mock: MagicMock + ) -> None: llm = OpenAILLM( model=self.model_id, api_key="api.key", @@ -79,7 +92,7 @@ async def test_agenerate_structured(self, mock_openai: MagicMock) -> None: "max_retries": 1, }, ) # type: ignore - llm._aclient = mock_openai + llm._aclient = async_openai_mock sample_user = DummyUserDetail(name="John Doe", age=30) @@ -100,9 +113,11 @@ async def test_agenerate_structured(self, mock_openai: MagicMock) -> None: sys.version_info < (3, 9), reason="`mistralai` requires Python 3.9 or higher" ) @pytest.mark.asyncio - async def test_generate(self, mock_openai: MagicMock) -> None: + async def test_generate( + self, async_openai_mock: MagicMock, _openai_mock: MagicMock + ) -> None: llm = OpenAILLM(model=self.model_id, api_key="api.key") # type: ignore - llm._aclient = mock_openai + llm._aclient = async_openai_mock mocked_completion = Mock( choices=[Mock(message=Mock(content=" Aenean hendrerit aliquam velit. ..."))] @@ -137,6 +152,299 @@ async def test_generate(self, mock_openai: MagicMock) -> None: response_format="unkown_format", ) + def test_offline_batch_generate( + self, _async_openai_mock: MagicMock, _openai_mock: MagicMock + ) -> None: + llm = OpenAILLM(model=self.model_id, api_key="api.key") # type: ignore + llm._create_jobs = mock.MagicMock(return_value=("1234", "5678")) + + with pytest.raises( + DistilabelOfflineBatchGenerationNotFinishedException + ) as exception_info: + llm.offline_batch_generate( + inputs=[{"role": "user", "content": "How much is 2+2?"}] # type: ignore + ) + + assert exception_info.value.jobs_ids == ("1234", "5678") + + def test_offline_batch_generate_with_job_ids( + self, _async_openai_mock: MagicMock, _openai_mock: MagicMock + ) -> None: + llm = OpenAILLM(model=self.model_id, api_key="api.key", jobs_ids=("1234",)) # type: ignore + llm._check_and_get_batch_results = mock.MagicMock( + return_value=[ + ["output 1"], + ["output 2"], + ] + ) + assert llm.offline_batch_generate() == [["output 1"], ["output 2"]] + + def test_check_and_get_batch_results( + self, async_openai_mock: MagicMock, openai_mock: MagicMock + ) -> None: + llm = OpenAILLM(model=self.model_id, api_key="api.key", jobs_ids=("1234",)) # type: ignore + llm._aclient = async_openai_mock + llm._client = openai_mock + llm._retrieve_batch_results = mock.MagicMock( + return_value=[ + { + "custom_id": 2, + "response": { + "status_code": 200, + "body": { + "id": "1234", + "created": 13, + "model": "gpt-4", + "object": "chat.completion", + "choices": [ + { + "finish_reason": "stop", + "index": 0, + "message": { + "role": "assistant", + "content": "output 2", + }, + } + ], + }, + }, + }, + { + "custom_id": 1, + "response": { + "status_code": 200, + "body": { + "id": "1234", + "created": 13, + "model": "gpt-4", + "object": "chat.completion", + "choices": [ + { + "finish_reason": "stop", + "index": 0, + "message": { + "role": "assistant", + "content": "output 1", + }, + } + ], + }, + }, + }, + ] + ) + llm.load() + + outputs = llm._check_and_get_batch_results() + assert outputs == [["output 1"], ["output 2"]] + + def test_check_and_get_batch_results_raises_valueerror( + self, _async_openai_mock: MagicMock, _openai_mock: MagicMock + ) -> None: + llm = OpenAILLM(model=self.model_id, api_key="api.key") # type: ignore + + with pytest.raises(ValueError, match="No job IDs were found"): + llm._check_and_get_batch_results() + + @pytest.mark.parametrize("status", ("validating", "in_progress", "finalizing")) + def test_check_and_get_batch_results_raises_distilabel_exception( + self, async_openai_mock: MagicMock, openai_mock: MagicMock, status: str + ) -> None: + llm = OpenAILLM(model=self.model_id, api_key="api.key", jobs_ids=("1234",)) # type: ignore + llm._aclient = async_openai_mock + llm._client = openai_mock + llm._get_openai_batch = mock.MagicMock( + return_value=Batch( + id="1234", + completion_window="24h", + created_at=13, + endpoint="/v1/chat/completions", + input_file_id="1234", + object="batch", + status=status, # type: ignore + output_file_id="1234", + ) + ) + llm.load() + + with pytest.raises(DistilabelOfflineBatchGenerationNotFinishedException): + llm._check_and_get_batch_results() + + @pytest.mark.parametrize("status", ("failed", "expired", "cancelled", "cancelling")) + def test_check_and_get_batch_results_raises_runtimeerror( + self, async_openai_mock: MagicMock, openai_mock: MagicMock, status: str + ) -> None: + llm = OpenAILLM(model=self.model_id, api_key="api.key", jobs_ids=("1234",)) # type: ignore + llm._aclient = async_openai_mock + llm._client = openai_mock + llm._get_openai_batch = mock.MagicMock( + return_value=Batch( + id="1234", + completion_window="24h", + created_at=13, + endpoint="/v1/chat/completions", + input_file_id="1234", + object="batch", + status=status, # type: ignore + output_file_id="1234", + ) + ) + llm.load() + + with pytest.raises( + RuntimeError, + match=f"The only OpenAI API Batch that was created with ID '1234' failed with status '{status}", + ): + llm._check_and_get_batch_results() + + def test_parse_output( + self, _async_openai_mock: MagicMock, openai_mock: MagicMock + ) -> None: + pass + llm = OpenAILLM(model=self.model_id, api_key="api.key") # type: ignore + + result = llm._parse_output( + { + "response": { + "status_code": 200, + "body": { + "id": "1234", + "created": 13, + "model": "gpt-4", + "object": "chat.completion", + "choices": [ + { + "finish_reason": "stop", + "index": 0, + "message": { + "role": "assistant", + "content": " Aenean hendrerit aliquam velit. ...", + }, + } + ], + }, + } + } + ) + + assert result == [" Aenean hendrerit aliquam velit. ..."] + + def test_retrieve_batch_results( + self, _async_openai_mock: MagicMock, openai_mock: MagicMock + ) -> None: + llm = OpenAILLM(model=self.model_id, api_key="api.key") # type: ignore + llm._client = openai_mock + + class Response: + text: str = dedent( + """ + {"response": {"status_code": 200, "body": {}}} + {"response": {"status_code": 200, "body": {}}} + {"response": {"status_code": 200, "body": {}}} + """.lstrip() + ) + + llm._client.files.content.return_value = Response() + + results = llm._retrieve_batch_results( + batch=Batch( + id="1234", + completion_window="24h", + created_at=13, + endpoint="/v1/chat/completions", + input_file_id="1234", + object="batch", + status="completed", + output_file_id="1234", + ) + ) # type: ignore + assert len(results) == 3 + + def test_create_jobs( + self, _async_openai_mock: MagicMock, openai_mock: MagicMock + ) -> None: + llm = OpenAILLM(model=self.model_id, api_key="api.key") # type: ignore + llm._client = openai_mock + + messages = [ + { + "role": "user", + "content": "x" * ((_OPENAI_BATCH_API_MAX_FILE_SIZE // 100) - 50), + } + ] + inputs = [messages] * 150 + + jobs = llm._create_jobs(inputs=inputs) # type: ignore + assert isinstance(jobs, tuple) + assert len(jobs) == 2 + + def test_create_batch_files( + self, _async_openai_mock: MagicMock, openai_mock: MagicMock + ) -> None: + llm = OpenAILLM(model=self.model_id, api_key="api.key") # type: ignore + llm._client = openai_mock + + messages = [ + { + "role": "user", + "content": "x" * ((_OPENAI_BATCH_API_MAX_FILE_SIZE // 100) - 50), + } + ] + inputs = [messages] * 150 + + files = llm._create_batch_files(inputs=inputs) # type: ignore + assert len(files) == 2 + + def test_create_jsonl_buffers( + self, _async_openai_mock: MagicMock, _openai_mock: MagicMock + ) -> None: + llm = OpenAILLM(model=self.model_id, api_key="api.key") # type: ignore + + # This should be around 1MB + messages = [ + { + "role": "user", + "content": "x" * ((_OPENAI_BATCH_API_MAX_FILE_SIZE // 100) - 50), + } + ] + + # Create an input that is larger than the max file size (150MB) + inputs = [messages] * 150 + output = list(llm._create_jsonl_buffers(inputs=inputs)) # type: ignore + assert len(output) == 2 + + def test_create_jsonl_row( + self, _async_openai_mock: MagicMock, _openai_mock: MagicMock + ) -> None: + llm = OpenAILLM(model=self.model_id, api_key="api.key") # type: ignore + output = llm._create_jsonl_row( + input=[{"role": "user", "content": "How much is 2+2?"}], + custom_id="unit-test", + **{ + "model": "gpt-4", + "temperature": 0.8, + "max_new_tokens": 512, + }, + ) + + assert isinstance(output, bytes) + assert orjson.loads(output.decode("utf-8")) == { + "custom_id": "unit-test", + "method": "POST", + "url": "/v1/chat/completions", + "body": { + "messages": [ + { + "role": "user", + "content": "How much is 2+2?", + } + ], + "model": "gpt-4", + "temperature": 0.8, + "max_new_tokens": 512, + }, + } + @pytest.mark.parametrize( "structured_output, dump", [ @@ -149,6 +457,9 @@ async def test_generate(self, mock_openai: MagicMock) -> None: "base_url": "https://api.openai.com/v1", "timeout": 120, "structured_output": None, + "jobs_ids": None, + "offline_batch_generation_block_until_done": None, + "use_offline_batch_generation": False, "type_info": { "module": "distilabel.llms.openai", "name": "OpenAILLM", @@ -172,6 +483,9 @@ async def test_generate(self, mock_openai: MagicMock) -> None: "mode": "tool_call", "max_retries": 1, }, + "jobs_ids": None, + "offline_batch_generation_block_until_done": None, + "use_offline_batch_generation": False, "type_info": { "module": "distilabel.llms.openai", "name": "OpenAILLM", @@ -181,7 +495,11 @@ async def test_generate(self, mock_openai: MagicMock) -> None: ], ) def test_serialization( - self, _: MagicMock, structured_output: Dict[str, Any], dump: Dict[str, Any] + self, + _async_openai_mock: MagicMock, + _openai_mock: MagicMock, + structured_output: Dict[str, Any], + dump: Dict[str, Any], ) -> None: llm = OpenAILLM(model=self.model_id, structured_output=structured_output) diff --git a/tests/unit/llms/test_together.py b/tests/unit/llms/test_together.py index d9b50b02d0..409f34866f 100644 --- a/tests/unit/llms/test_together.py +++ b/tests/unit/llms/test_together.py @@ -49,6 +49,9 @@ def test_serialization(self) -> None: "base_url": "https://api.together.xyz/v1", "timeout": 120, "structured_output": None, + "jobs_ids": None, + "offline_batch_generation_block_until_done": None, + "use_offline_batch_generation": False, "type_info": { "module": "distilabel.llms.together", "name": "TogetherLLM", diff --git a/tests/unit/llms/test_vertexai.py b/tests/unit/llms/test_vertexai.py index 9ad575fb0a..38f5933849 100644 --- a/tests/unit/llms/test_vertexai.py +++ b/tests/unit/llms/test_vertexai.py @@ -116,6 +116,9 @@ def test_serialization(self, _: MagicMock) -> None: _dump = { "model": "gemini-1.0-pro", "generation_kwargs": {}, + "jobs_ids": None, + "offline_batch_generation_block_until_done": None, + "use_offline_batch_generation": False, "type_info": { "module": "distilabel.llms.vertexai", "name": "VertexAILLM", diff --git a/tests/unit/pipeline/test_base.py b/tests/unit/pipeline/test_base.py index fcbdddcea9..5f38781a04 100644 --- a/tests/unit/pipeline/test_base.py +++ b/tests/unit/pipeline/test_base.py @@ -230,6 +230,15 @@ def test_should_continue_processing(self) -> None: assert not pipeline._should_continue_processing() + def test_set_step_for_recovering_offline_batch_generation(self) -> None: + with DummyPipeline() as pipeline: + step = DummyStep1() + + data = [[{"a": 0}, {"a": 1}, {"a": 2}, {"a": 3}, {"a": 4}]] + pipeline._set_step_for_recovering_offline_batch_generation(step=step, data=data) + + assert pipeline._recover_offline_batch_generate_for_step == (step.name, data) + def test_should_load_next_stage(self) -> None: with DummyPipeline(name="dummy") as pipeline: generator = DummyGeneratorStep() diff --git a/tests/unit/pipeline/test_batch_manager.py b/tests/unit/pipeline/test_batch_manager.py index e0e7547305..c5023b2616 100644 --- a/tests/unit/pipeline/test_batch_manager.py +++ b/tests/unit/pipeline/test_batch_manager.py @@ -1503,6 +1503,38 @@ def test_add_batch_with_prepend(self) -> None: "step2": [], } + def test_add_batch_to_recover_offline_batch_generation(self) -> None: + batch_manager = _BatchManager( + steps={ + "step1": _BatchManagerStep( + step_name="step0", + accumulate=True, + input_batch_size=5, + data={}, + ) + }, + last_batch_received={ + "step1": _Batch(seq_no=0, step_name="step1", last_batch=True) + }, + last_batch_sent={"step1": None}, + last_batch_flag_sent_to=[], + ) + + batch_manager.add_batch_to_recover_offline_batch_generation( + to_step="step1", + data=[[{"a": 1}, {"a": 2}, {"a": 3}, {"a": 4}, {"a": 5}]], + ) + + assert batch_manager._steps["step1"].built_batches == [ + _Batch( + seq_no=0, + step_name="step1", + last_batch=True, + data=[[{"a": 1}, {"a": 2}, {"a": 3}, {"a": 4}, {"a": 5}]], + ) + ] + assert batch_manager._last_batch_received["step1"] is None + def test_from_dag( self, dummy_generator_step: "GeneratorStep", diff --git a/tests/unit/pipeline/test_ray.py b/tests/unit/pipeline/test_ray.py index 127f9eceb4..610f272196 100644 --- a/tests/unit/pipeline/test_ray.py +++ b/tests/unit/pipeline/test_ray.py @@ -16,11 +16,13 @@ import pytest +from distilabel.errors import DistilabelUserError from distilabel.llms.vllm import vLLM from distilabel.pipeline.ray import RayPipeline from distilabel.steps.base import StepResources from distilabel.steps.tasks.text_generation import TextGeneration from distilabel.utils.serialization import TYPE_INFO_KEY +from tests.unit.conftest import DummyAsyncLLM, DummyTaskOfflineBatchGeneration @pytest.fixture @@ -56,6 +58,18 @@ def test_dump(self) -> None: "name": "Pipeline", } + def test_check_no_llms_using_offline_batch_generation(self) -> None: + with RayPipeline(name="unit-test") as pipeline: + DummyTaskOfflineBatchGeneration( + name="unit-test", llm=DummyAsyncLLM(use_offline_batch_generation=True) + ) + + with pytest.raises( + DistilabelUserError, + match="Step 'unit-test' uses an `LLM` with offline batch generation", + ): + pipeline._check_no_llms_using_offline_batch_generation() + def test_get_ray_gpus_per_node(self) -> None: pipeline = RayPipeline(name="unit-test") pipeline._init_ray() diff --git a/tests/unit/steps/test_filtering/__init__.py b/tests/unit/steps/filtering/__init__.py similarity index 100% rename from tests/unit/steps/test_filtering/__init__.py rename to tests/unit/steps/filtering/__init__.py diff --git a/tests/unit/steps/test_filtering/test_minhash.py b/tests/unit/steps/filtering/test_minhash.py similarity index 98% rename from tests/unit/steps/test_filtering/test_minhash.py rename to tests/unit/steps/filtering/test_minhash.py index f4a6d6c225..48b765c1a9 100644 --- a/tests/unit/steps/test_filtering/test_minhash.py +++ b/tests/unit/steps/filtering/test_minhash.py @@ -14,6 +14,7 @@ from typing import List +import nltk import pytest from distilabel.steps.filtering.minhash import ( @@ -22,6 +23,8 @@ tokenized_on_words, ) +nltk.download("punkt_tab") + texts: List[str] = [ "This is a test document.", "This document is a test.", diff --git a/tests/unit/steps/generators/test_data.py b/tests/unit/steps/generators/test_data.py index 9684d5abd0..3767451991 100644 --- a/tests/unit/steps/generators/test_data.py +++ b/tests/unit/steps/generators/test_data.py @@ -12,7 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. import pytest -from pydantic import ValidationError from distilabel.pipeline.local import Pipeline from distilabel.steps.generators.data import LoadDataFromDicts @@ -30,11 +29,6 @@ def test_init(self) -> None: assert task.data == data assert task.batch_size == 10 - def test_with_errors(self) -> None: - pipeline = Pipeline(name="unit-test-pipeline") - with pytest.raises(ValidationError): - LoadDataFromDicts(name="task", pipeline=pipeline) - def test_process(self) -> None: pipeline = Pipeline(name="unit-test-pipeline") data: list[dict[str, str]] = self.data diff --git a/tests/unit/steps/tasks/evol_instruct/test_base.py b/tests/unit/steps/tasks/evol_instruct/test_base.py index 54975b3873..4f6e12c6f1 100644 --- a/tests/unit/steps/tasks/evol_instruct/test_base.py +++ b/tests/unit/steps/tasks/evol_instruct/test_base.py @@ -137,6 +137,9 @@ def test_serialization(self, dummy_llm: LLM) -> None: "llm": { "generation_kwargs": {}, "structured_output": None, + "jobs_ids": None, + "offline_batch_generation_block_until_done": None, + "use_offline_batch_generation": False, "type_info": { "module": task.llm.__module__, "name": task.llm.__class__.__name__, @@ -200,7 +203,21 @@ def test_serialization(self, dummy_llm: LLM) -> None: "name": "generation_kwargs", "description": "The kwargs to be propagated to either `generate` or `agenerate` methods within each `LLM`.", "keys": [], - } + }, + { + "description": "Whether to use the `offline_batch_generate` method to " + "generate the responses.", + "name": "use_offline_batch_generation", + "optional": True, + }, + { + "description": "If provided, then polling will be done until the " + "`ofline_batch_generate` method is able to retrieve the " + "results. The value indicate the time to wait between each " + "polling.", + "name": "offline_batch_generation_block_until_done", + "optional": True, + }, ], }, { diff --git a/tests/unit/steps/tasks/evol_instruct/test_generator.py b/tests/unit/steps/tasks/evol_instruct/test_generator.py index 05ddb43eb7..77b4a8ea05 100644 --- a/tests/unit/steps/tasks/evol_instruct/test_generator.py +++ b/tests/unit/steps/tasks/evol_instruct/test_generator.py @@ -119,6 +119,9 @@ def test_serialization(self, dummy_llm: LLM) -> None: "llm": { "generation_kwargs": {}, "structured_output": None, + "jobs_ids": None, + "offline_batch_generation_block_until_done": None, + "use_offline_batch_generation": False, "type_info": { "module": task.llm.__class__.__module__, "name": task.llm.__class__.__name__, @@ -196,6 +199,20 @@ def test_serialization(self, dummy_llm: LLM) -> None: "keys": [], "name": "generation_kwargs", }, + { + "description": "Whether to use the `offline_batch_generate` method to " + "generate the responses.", + "name": "use_offline_batch_generation", + "optional": True, + }, + { + "description": "If provided, then polling will be done until the " + "`ofline_batch_generate` method is able to retrieve the " + "results. The value indicate the time to wait between each " + "polling.", + "name": "offline_batch_generation_block_until_done", + "optional": True, + }, ], }, { diff --git a/tests/unit/steps/tasks/evol_quality/test_base.py b/tests/unit/steps/tasks/evol_quality/test_base.py index 75eb583194..a7346b1069 100644 --- a/tests/unit/steps/tasks/evol_quality/test_base.py +++ b/tests/unit/steps/tasks/evol_quality/test_base.py @@ -108,6 +108,9 @@ def test_serialization(self, dummy_llm: LLM) -> None: "llm": { "generation_kwargs": {}, "structured_output": None, + "jobs_ids": None, + "offline_batch_generation_block_until_done": None, + "use_offline_batch_generation": False, "type_info": { "module": task.llm.__module__, "name": task.llm.__class__.__name__, @@ -165,6 +168,20 @@ def test_serialization(self, dummy_llm: LLM) -> None: "description": "The kwargs to be propagated to either `generate` or `agenerate` methods within each `LLM`.", "keys": [], }, + { + "description": "Whether to use the `offline_batch_generate` method to " + "generate the responses.", + "name": "use_offline_batch_generation", + "optional": True, + }, + { + "description": "If provided, then polling will be done until the " + "`ofline_batch_generate` method is able to retrieve the " + "results. The value indicate the time to wait between each " + "polling.", + "name": "offline_batch_generation_block_until_done", + "optional": True, + }, ], }, { diff --git a/tests/unit/steps/tasks/magpie/test_base.py b/tests/unit/steps/tasks/magpie/test_base.py index f326c4f87b..dcdc6d9165 100644 --- a/tests/unit/steps/tasks/magpie/test_base.py +++ b/tests/unit/steps/tasks/magpie/test_base.py @@ -399,6 +399,9 @@ def test_serialization(self) -> None: "use_magpie_template": True, "magpie_pre_query_template": "<|begin_of_text|><|start_header_id|>user<|end_header_id|>\n\n", "generation_kwargs": {}, + "jobs_ids": None, + "offline_batch_generation_block_until_done": None, + "use_offline_batch_generation": False, "type_info": { "module": "tests.unit.conftest", "name": "DummyMagpieLLM", @@ -433,7 +436,21 @@ def test_serialization(self) -> None: "name": "generation_kwargs", "description": "The kwargs to be propagated to either `generate` or `agenerate` methods within each `LLM`.", "keys": [{"name": "kwargs", "optional": False}], - } + }, + { + "description": "Whether to use the `offline_batch_generate` method to " + "generate the responses.", + "name": "use_offline_batch_generation", + "optional": True, + }, + { + "description": "If provided, then polling will be done until the " + "`ofline_batch_generate` method is able to retrieve the " + "results. The value indicate the time to wait between each " + "polling.", + "name": "offline_batch_generation_block_until_done", + "optional": True, + }, ], }, { diff --git a/tests/unit/steps/tasks/magpie/test_generator.py b/tests/unit/steps/tasks/magpie/test_generator.py index 144e55532b..3edd91f9b2 100644 --- a/tests/unit/steps/tasks/magpie/test_generator.py +++ b/tests/unit/steps/tasks/magpie/test_generator.py @@ -55,6 +55,9 @@ def test_serialization(self) -> None: "use_magpie_template": True, "magpie_pre_query_template": "<|begin_of_text|><|start_header_id|>user<|end_header_id|>\n\n", "generation_kwargs": {}, + "jobs_ids": None, + "offline_batch_generation_block_until_done": None, + "use_offline_batch_generation": False, "type_info": { "module": "tests.unit.conftest", "name": "DummyMagpieLLM", @@ -90,7 +93,21 @@ def test_serialization(self) -> None: "name": "generation_kwargs", "description": "The kwargs to be propagated to either `generate` or `agenerate` methods within each `LLM`.", "keys": [{"name": "kwargs", "optional": False}], - } + }, + { + "description": "Whether to use the `offline_batch_generate` method to " + "generate the responses.", + "name": "use_offline_batch_generation", + "optional": True, + }, + { + "description": "If provided, then polling will be done until the " + "`ofline_batch_generate` method is able to retrieve the " + "results. The value indicate the time to wait between each " + "polling.", + "name": "offline_batch_generation_block_until_done", + "optional": True, + }, ], }, { diff --git a/tests/unit/steps/tasks/structured_outputs/test_outlines.py b/tests/unit/steps/tasks/structured_outputs/test_outlines.py index f996b23b48..d2be053aa5 100644 --- a/tests/unit/steps/tasks/structured_outputs/test_outlines.py +++ b/tests/unit/steps/tasks/structured_outputs/test_outlines.py @@ -35,6 +35,9 @@ class DummyUserTest(BaseModel): "cuda_devices": "auto", "generation_kwargs": {}, "magpie_pre_query_template": None, + "jobs_ids": None, + "offline_batch_generation_block_until_done": None, + "use_offline_batch_generation": False, "structured_output": { "format": "json", "schema": { @@ -71,6 +74,9 @@ class DummyUserTest(BaseModel): "cuda_devices": "auto", "generation_kwargs": {}, "magpie_pre_query_template": None, + "jobs_ids": None, + "offline_batch_generation_block_until_done": None, + "use_offline_batch_generation": False, "structured_output": { "format": "regex", "schema": "((25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)", diff --git a/tests/unit/steps/tasks/test_base.py b/tests/unit/steps/tasks/test_base.py index b872ee54bf..d00b8edac8 100644 --- a/tests/unit/steps/tasks/test_base.py +++ b/tests/unit/steps/tasks/test_base.py @@ -14,7 +14,7 @@ import sys from dataclasses import field -from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union +from typing import TYPE_CHECKING, Any, Dict, List, Optional import pytest from pydantic import ValidationError @@ -22,42 +22,38 @@ from distilabel.mixins.runtime_parameters import RuntimeParameter from distilabel.pipeline.local import Pipeline from distilabel.steps.tasks.base import Task -from tests.unit.conftest import DummyLLM +from tests.unit.conftest import ( + DummyAsyncLLM, + DummyTask, + DummyTaskOfflineBatchGeneration, +) if TYPE_CHECKING: - from distilabel.steps.tasks.typing import ChatType + pass -class DummyTask(Task): - @property - def inputs(self) -> List[str]: - return ["instruction", "additional_info"] - - def format_input(self, input: Dict[str, Any]) -> "ChatType": - return [ - {"role": "system", "content": ""}, - {"role": "user", "content": input["instruction"]}, - ] - - @property - def outputs(self) -> List[str]: - return ["output", "info_from_input"] - - def format_output( - self, output: Union[str, None], input: Union[Dict[str, Any], None] = None - ) -> Dict[str, Any]: - return {"output": output, "info_from_input": input["additional_info"]} # type: ignore - - -class DummyRuntimeLLM(DummyLLM): +class DummyRuntimeLLM(DummyAsyncLLM): runtime_parameter: RuntimeParameter[int] runtime_parameter_optional: Optional[RuntimeParameter[int]] = field(default=None) class TestTask: + def test_model_post_init_raise_valuerror_use_offline_batch_generation(self) -> None: + with pytest.raises( + ValidationError, + match="`DummyTask` task cannot be used with offline batch generation", + ): + DummyTask(llm=DummyAsyncLLM(use_offline_batch_generation=True)) + + def test_is_global_with_offline_batch_generation(self) -> None: + task = DummyTaskOfflineBatchGeneration( + llm=DummyAsyncLLM(use_offline_batch_generation=True) + ) + assert task.is_global is True + def test_passing_pipeline(self) -> None: pipeline = Pipeline(name="unit-test-pipeline") - llm = DummyLLM() + llm = DummyAsyncLLM() task = DummyTask(name="task", llm=llm, pipeline=pipeline) assert task.name == "task" assert task.llm is llm @@ -67,14 +63,14 @@ def test_passing_pipeline(self) -> None: def test_within_pipeline_context(self) -> None: with Pipeline(name="unit-test-pipeline") as pipeline: - llm = DummyLLM() + llm = DummyAsyncLLM() task = DummyTask(name="task", llm=llm, pipeline=pipeline) assert task.name == "task" assert task.llm is llm assert task.pipeline == pipeline def test_with_errors(self, caplog: pytest.LogCaptureFixture) -> None: - DummyTask(name="task", llm=DummyLLM()) + DummyTask(name="task", llm=DummyAsyncLLM()) assert "Step 'task' hasn't received a pipeline" in caplog.text with pytest.raises( @@ -88,7 +84,7 @@ def test_with_errors(self, caplog: pytest.LogCaptureFixture) -> None: if sys.version_info < (3, 12) else "Can't instantiate abstract class Task without an implementation for abstract methods 'format_input', 'format_output'", ): - Task(name="task", llm=DummyLLM()) # type: ignore + Task(name="task", llm=DummyAsyncLLM()) # type: ignore @pytest.mark.parametrize( "input, group_generations, expected", @@ -407,7 +403,7 @@ def test_process( expected: List[Dict[str, Any]], ) -> None: pipeline = Pipeline(name="unit-test-pipeline") - llm = DummyLLM() + llm = DummyAsyncLLM() task = DummyTask( name="task", llm=llm, @@ -433,6 +429,8 @@ def test_process_with_runtime_parameters(self) -> None: "runtime_parameter": False, "runtime_parameter_optional": True, "generation_kwargs": {}, + "offline_batch_generation_block_until_done": True, + "use_offline_batch_generation": True, } # 2. Runtime parameters in init @@ -448,6 +446,8 @@ def test_process_with_runtime_parameters(self) -> None: "runtime_parameter": False, "runtime_parameter_optional": True, "generation_kwargs": {}, + "offline_batch_generation_block_until_done": True, + "use_offline_batch_generation": True, } # 3. Runtime parameters in init superseded by runtime parameters @@ -464,11 +464,13 @@ def test_process_with_runtime_parameters(self) -> None: "runtime_parameter": False, "runtime_parameter_optional": True, "generation_kwargs": {}, + "offline_batch_generation_block_until_done": True, + "use_offline_batch_generation": True, } def test_serialization(self) -> None: pipeline = Pipeline(name="unit-test-pipeline") - llm = DummyLLM() + llm = DummyAsyncLLM() task = DummyTask(name="task", llm=llm, pipeline=pipeline) assert task.dump() == { "name": "task", @@ -487,9 +489,12 @@ def test_serialization(self) -> None: "llm": { "generation_kwargs": {}, "structured_output": None, + "jobs_ids": None, + "offline_batch_generation_block_until_done": None, + "use_offline_batch_generation": False, "type_info": { "module": "tests.unit.conftest", - "name": "DummyLLM", + "name": "DummyAsyncLLM", }, }, "group_generations": False, @@ -539,6 +544,20 @@ def test_serialization(self) -> None: "keys": [], "name": "generation_kwargs", }, + { + "description": "Whether to use the `offline_batch_generate` method to " + "generate the responses.", + "name": "use_offline_batch_generation", + "optional": True, + }, + { + "description": "If provided, then polling will be done until the " + "`ofline_batch_generate` method is able to retrieve the " + "results. The value indicate the time to wait between each " + "polling.", + "name": "offline_batch_generation_block_until_done", + "optional": True, + }, ], }, { @@ -558,7 +577,7 @@ def test_serialization(self) -> None: }, ], "type_info": { - "module": "tests.unit.steps.tasks.test_base", + "module": "tests.unit.conftest", "name": "DummyTask", }, "use_default_structured_output": False, @@ -581,7 +600,7 @@ def test_add_raw_input_and_or_output( self, add_raw_output: bool, add_raw_input: bool ) -> None: task = DummyTask( - llm=DummyLLM(), + llm=DummyAsyncLLM(), add_raw_output=add_raw_output, add_raw_input=add_raw_input, ) diff --git a/tests/unit/steps/tasks/test_complexity_scorer.py b/tests/unit/steps/tasks/test_complexity_scorer.py index dc7208d5b3..308fe989be 100644 --- a/tests/unit/steps/tasks/test_complexity_scorer.py +++ b/tests/unit/steps/tasks/test_complexity_scorer.py @@ -18,14 +18,14 @@ from distilabel.pipeline.local import Pipeline from distilabel.steps.tasks.complexity_scorer import ComplexityScorer -from tests.unit.conftest import DummyLLM +from tests.unit.conftest import DummyAsyncLLM class TestComplexityScorer: def test_format_input(self) -> None: task = ComplexityScorer( name="complexity_scorer", - llm=DummyLLM(), + llm=DummyAsyncLLM(), pipeline=Pipeline(name="unit-test-pipeline"), ) task.load() @@ -78,7 +78,8 @@ def test_format_output( expected: Dict[str, Any], ) -> None: task = ComplexityScorer( - llm=DummyLLM(), use_default_structured_output=use_default_structured_output + llm=DummyAsyncLLM(), + use_default_structured_output=use_default_structured_output, ) task.load() diff --git a/tests/unit/steps/tasks/test_genstruct.py b/tests/unit/steps/tasks/test_genstruct.py index 88ed5f30ec..300237c843 100644 --- a/tests/unit/steps/tasks/test_genstruct.py +++ b/tests/unit/steps/tasks/test_genstruct.py @@ -18,14 +18,14 @@ from distilabel.pipeline.local import Pipeline from distilabel.steps.tasks.genstruct import Genstruct -from tests.unit.conftest import DummyLLM +from tests.unit.conftest import DummyAsyncLLM class TestGenstruct: def test_format_input(self) -> None: task = Genstruct( name="genstruct", - llm=DummyLLM(), + llm=DummyAsyncLLM(), pipeline=Pipeline(name="unit-test-pipeline"), ) task.load() @@ -62,7 +62,7 @@ def test_format_output( ) -> None: task = Genstruct( name="genstruct", - llm=DummyLLM(), + llm=DummyAsyncLLM(), pipeline=Pipeline(name="unit-test-pipeline"), ) task.load() diff --git a/tests/unit/steps/tasks/test_prometheus_eval.py b/tests/unit/steps/tasks/test_prometheus_eval.py index b7cf1cd55e..1781ac9e6a 100644 --- a/tests/unit/steps/tasks/test_prometheus_eval.py +++ b/tests/unit/steps/tasks/test_prometheus_eval.py @@ -27,7 +27,7 @@ from distilabel.pipeline.local import Pipeline from distilabel.steps.tasks.prometheus_eval import _DEFAULT_RUBRICS, PrometheusEval -from tests.unit.conftest import DummyLLM +from tests.unit.conftest import DummyAsyncLLM def load_template(template: str) -> Template: @@ -131,7 +131,7 @@ def test_format_input( mode=mode, # type: ignore rubric=rubric, # type: ignore reference=reference, - llm=DummyLLM(), + llm=DummyAsyncLLM(), pipeline=Pipeline(name="unit-test-pipeline"), ) task.load() @@ -150,7 +150,7 @@ def test_format_input_errors(self) -> None: mode="absolute", rubric="helpfulness", reference=True, - llm=DummyLLM(), + llm=DummyAsyncLLM(), pipeline=Pipeline(name="unit-test-pipeline"), ) task.load() @@ -164,7 +164,7 @@ def test_format_input_errors(self) -> None: mode="absolute", rubric="helpfulness", reference=False, - llm=DummyLLM(), + llm=DummyAsyncLLM(), pipeline=Pipeline(name="unit-test-pipeline"), ) task.load() @@ -181,7 +181,7 @@ def test_format_input_errors(self) -> None: mode="relative", rubric="helpfulness", reference=False, - llm=DummyLLM(), + llm=DummyAsyncLLM(), pipeline=Pipeline(name="unit-test-pipeline"), ) task.load() @@ -258,7 +258,7 @@ def test_format_output( mode=mode, # type: ignore rubric="factual-validity", reference=False, - llm=DummyLLM(), + llm=DummyAsyncLLM(), pipeline=Pipeline(name="unit-test-pipeline"), ) task.load() @@ -278,7 +278,7 @@ def test_custom_rubrics(self) -> None: "custom": "[A]\nScore 1: A\nScore 2: B\nScore 3: C\nScore 4: D\nScore 5: E" }, reference=False, - llm=DummyLLM(), + llm=DummyAsyncLLM(), pipeline=Pipeline(name="unit-test-pipeline"), ) @@ -294,7 +294,7 @@ def test_custom_rubrics_errors(self) -> None: rubric="custom", rubrics={}, reference=False, - llm=DummyLLM(), + llm=DummyAsyncLLM(), pipeline=Pipeline(name="unit-test-pipeline"), ) with pytest.raises( @@ -307,7 +307,7 @@ def test_custom_rubrics_errors(self) -> None: rubric="custom", rubrics={"custom": 1}, reference=False, - llm=DummyLLM(), + llm=DummyAsyncLLM(), pipeline=Pipeline(name="unit-test-pipeline"), ) # 2. `rubrics` is not compliant with the pre-defined schema @@ -321,7 +321,7 @@ def test_custom_rubrics_errors(self) -> None: rubric="custom", rubrics={"custom": "wrong schema"}, reference=False, - llm=DummyLLM(), + llm=DummyAsyncLLM(), pipeline=Pipeline(name="unit-test-pipeline"), ) # 3. `rubric` is not available in `rubrics` @@ -337,6 +337,6 @@ def test_custom_rubrics_errors(self) -> None: "custom": "[A]\nScore 1: A\nScore 2: B\nScore 3: C\nScore 4: D\nScore 5: E" }, reference=False, - llm=DummyLLM(), + llm=DummyAsyncLLM(), pipeline=Pipeline(name="unit-test-pipeline"), ) diff --git a/tests/unit/steps/tasks/test_quality_scorer.py b/tests/unit/steps/tasks/test_quality_scorer.py index e7ba165384..3929aaaedf 100644 --- a/tests/unit/steps/tasks/test_quality_scorer.py +++ b/tests/unit/steps/tasks/test_quality_scorer.py @@ -18,14 +18,14 @@ from distilabel.pipeline.local import Pipeline from distilabel.steps.tasks.quality_scorer import QualityScorer -from tests.unit.conftest import DummyLLM +from tests.unit.conftest import DummyAsyncLLM class TestQualityScorer: def test_format_input(self) -> None: task = QualityScorer( name="quality_scorer", - llm=DummyLLM(), + llm=DummyAsyncLLM(), pipeline=Pipeline(name="unit-test-pipeline"), ) task.load() @@ -81,7 +81,8 @@ def test_format_output( expected: Dict[str, Any], ) -> None: task = QualityScorer( - llm=DummyLLM(), use_default_structured_output=use_default_structured_output + llm=DummyAsyncLLM(), + use_default_structured_output=use_default_structured_output, ) task.load() diff --git a/tests/unit/steps/tasks/test_self_instruct.py b/tests/unit/steps/tasks/test_self_instruct.py index 1f539d0e3b..76a24497e2 100644 --- a/tests/unit/steps/tasks/test_self_instruct.py +++ b/tests/unit/steps/tasks/test_self_instruct.py @@ -14,14 +14,14 @@ from distilabel.pipeline.local import Pipeline from distilabel.steps.tasks.self_instruct import SelfInstruct -from tests.unit.conftest import DummyLLM +from tests.unit.conftest import DummyAsyncLLM class TestSelfInstruct: def test_format_input(self) -> None: task = SelfInstruct( name="self_instruct", - llm=DummyLLM(), + llm=DummyAsyncLLM(), pipeline=Pipeline(name="unit-test-pipeline"), ) task.load() @@ -37,7 +37,7 @@ def test_format_input(self) -> None: def test_format_output(self) -> None: task = SelfInstruct( name="self_instruct", - llm=DummyLLM(), + llm=DummyAsyncLLM(), pipeline=Pipeline(name="unit-test-pipeline"), ) task.load() diff --git a/tests/unit/steps/tasks/test_sentence_transformers.py b/tests/unit/steps/tasks/test_sentence_transformers.py index 82be67c6d4..9dc6b38ae1 100644 --- a/tests/unit/steps/tasks/test_sentence_transformers.py +++ b/tests/unit/steps/tasks/test_sentence_transformers.py @@ -24,7 +24,7 @@ GenerateSentencePair, GenerationAction, ) -from tests.unit.conftest import DummyLLM +from tests.unit.conftest import DummyAsyncLLM # from distilabel.llms.base import LLM, AsyncLLM @@ -172,7 +172,10 @@ def test_format_input( system_prompt: str, ) -> None: task = GenerateSentencePair( - llm=DummyLLM(), action=action, triplet=triplet, hard_negative=hard_negative + llm=DummyAsyncLLM(), + action=action, + triplet=triplet, + hard_negative=hard_negative, ) task.load() content = "## Anchor\n\nThis is a unit test\n" @@ -307,7 +310,7 @@ def test_format_input_with_context( ) -> None: context = "This is your context." task = GenerateSentencePair( - llm=DummyLLM(), + llm=DummyAsyncLLM(), action=action, triplet=triplet, context=context, @@ -391,7 +394,7 @@ def test_format_output( expected: Dict[str, Any], ) -> None: task = GenerateSentencePair( - llm=DummyLLM(), + llm=DummyAsyncLLM(), action="paraphrase", triplet=triplet, use_default_structured_output=use_default_structured_output, diff --git a/tests/unit/steps/tasks/test_text_generation.py b/tests/unit/steps/tasks/test_text_generation.py index 1c7054a7ca..84ed6edaa5 100644 --- a/tests/unit/steps/tasks/test_text_generation.py +++ b/tests/unit/steps/tasks/test_text_generation.py @@ -16,12 +16,12 @@ from distilabel.pipeline.local import Pipeline from distilabel.steps.tasks.text_generation import ChatGeneration, TextGeneration -from tests.unit.conftest import DummyLLM +from tests.unit.conftest import DummyAsyncLLM class TestTextGeneration: def test_format_input(self) -> None: - llm = DummyLLM() + llm = DummyAsyncLLM() task = TextGeneration(name="task", llm=llm, use_system_prompt=False) assert task.format_input({"instruction": "test", "system_prompt": "test"}) == [ @@ -30,7 +30,7 @@ def test_format_input(self) -> None: def test_format_input_with_system_prompt(self) -> None: pipeline = Pipeline(name="unit-test-pipeline") - llm = DummyLLM() + llm = DummyAsyncLLM() task = TextGeneration( name="task", llm=llm, @@ -45,7 +45,7 @@ def test_format_input_with_system_prompt(self) -> None: def test_format_input_errors(self) -> None: pipeline = Pipeline(name="unit-test-pipeline") - llm = DummyLLM() + llm = DummyAsyncLLM() task = TextGeneration( name="task", llm=llm, pipeline=pipeline, use_system_prompt=True ) @@ -61,17 +61,9 @@ def test_format_input_errors(self) -> None: ): task.format_input({"instruction": 1}) - with pytest.warns( - UserWarning, - match=r"\`use_system_prompt\` is set to \`True\`, but no \`system_prompt\` in input batch, so it will be ignored.", - ): - assert task.format_input({"instruction": "test"}) == [ - {"role": "user", "content": "test"} - ] - def test_process(self) -> None: pipeline = Pipeline(name="unit-test-pipeline") - llm = DummyLLM() + llm = DummyAsyncLLM() task = TextGeneration( name="task", llm=llm, pipeline=pipeline, add_raw_input=False ) @@ -91,7 +83,7 @@ def test_process(self) -> None: class TestChatGeneration: def test_format_input(self) -> None: pipeline = Pipeline(name="unit-test-pipeline") - llm = DummyLLM() + llm = DummyAsyncLLM() task = ChatGeneration(name="task", llm=llm, pipeline=pipeline) assert task.format_input( @@ -108,7 +100,7 @@ def test_format_input(self) -> None: def test_format_input_errors(self) -> None: pipeline = Pipeline(name="unit-test-pipeline") - llm = DummyLLM() + llm = DummyAsyncLLM() task = ChatGeneration(name="task", llm=llm, pipeline=pipeline) with pytest.raises(ValueError, match="The last message must be from the user"): @@ -123,7 +115,7 @@ def test_format_input_errors(self) -> None: def test_process(self) -> None: pipeline = Pipeline(name="unit-test-pipeline") - llm = DummyLLM() + llm = DummyAsyncLLM() task = ChatGeneration( name="task", llm=llm, pipeline=pipeline, add_raw_input=False ) diff --git a/tests/unit/steps/tasks/test_urial.py b/tests/unit/steps/tasks/test_urial.py index 055e1a669f..f31ac0e5e2 100644 --- a/tests/unit/steps/tasks/test_urial.py +++ b/tests/unit/steps/tasks/test_urial.py @@ -15,12 +15,12 @@ import pytest from distilabel.steps.tasks.urial import URIAL -from tests.unit.conftest import DummyLLM +from tests.unit.conftest import DummyAsyncLLM class TestURIAL: def test_format_input(self) -> None: - task = URIAL(llm=DummyLLM()) + task = URIAL(llm=DummyAsyncLLM()) task.load() assert task.format_input({"instruction": "test"}) == [ { @@ -30,7 +30,7 @@ def test_format_input(self) -> None: ] def test_format_input_with_conversation(self) -> None: - task = URIAL(llm=DummyLLM()) + task = URIAL(llm=DummyAsyncLLM()) task.load() assert task.format_input( { @@ -48,7 +48,7 @@ def test_format_input_with_conversation(self) -> None: ] def test_format_input_raise_valueerror(self) -> None: - task = URIAL(llm=DummyLLM()) + task = URIAL(llm=DummyAsyncLLM()) task.load() with pytest.raises(ValueError, match="The last message must be from the user."): @@ -62,7 +62,7 @@ def test_format_input_raise_valueerror(self) -> None: ) def test_format_output(self) -> None: - task = URIAL(llm=DummyLLM()) + task = URIAL(llm=DummyAsyncLLM()) task.load() assert task.format_output( From c8f4d6164d6a996fd1ce938b1cda6b31b1c27352 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Mart=C3=ADn=20Bl=C3=A1zquez?= Date: Mon, 2 Sep 2024 15:27:59 +0200 Subject: [PATCH 41/82] Fix applying input mapping when mapping overrides another column (#938) * Fix inputs rows overriden * Add unit test * Fix applying input mappings * Fix `overriden_inputs` * Fix unit test --- src/distilabel/steps/base.py | 89 ++++++++++++++++++++++++----------- tests/unit/steps/test_base.py | 63 ++++++++++++++++++++----- 2 files changed, 112 insertions(+), 40 deletions(-) diff --git a/src/distilabel/steps/base.py b/src/distilabel/steps/base.py index f36931d7d8..cc0c0b2e1a 100644 --- a/src/distilabel/steps/base.py +++ b/src/distilabel/steps/base.py @@ -628,7 +628,11 @@ def process_applying_mappings(self, *args: List[Dict[str, Any]]) -> "StepOutput" The output rows. """ - inputs = self._apply_input_mappings(args) if self.input_mappings else args + inputs, overriden_inputs = ( + self._apply_input_mappings(args) + if self.input_mappings + else (args, [{} for _ in range(len(args[0]))]) + ) # If the `Step` was built using the `@step` decorator, then we need to pass # the runtime parameters as kwargs, so they can be used within the processing @@ -641,47 +645,76 @@ def process_applying_mappings(self, *args: List[Dict[str, Any]]) -> "StepOutput" for output_rows in generator: yield [ - { - # Apply output mapping and revert input mapping - self.output_mappings.get(k, None) - or self.input_mappings.get(k, None) - or k: v - for k, v in row.items() - } - for row in output_rows + self._apply_mappings_and_restore_overriden(row, overriden_inputs[i]) + for i, row in enumerate(output_rows) ] - def _revert_input_mappings(self, input: Dict[str, Any]) -> Dict[str, Any]: - """Reverts the `input_mappings` of the step to the input row. + def _apply_input_mappings( + self, inputs: Tuple[List[Dict[str, Any]], ...] + ) -> Tuple[Tuple[List[Dict[str, Any]], ...], List[Dict[str, Any]]]: + """Applies the `input_mappings` to the input rows. Args: - input: The input row. + inputs: The input rows. Returns: - The input row with the `input_mappings` reverted. + The input rows with the `input_mappings` applied and the overriden values + that were replaced by the `input_mappings`. """ - return {self.input_mappings.get(k, k): v for k, v in input.items()} + reverted_input_mappings = {v: k for k, v in self.input_mappings.items()} - def _apply_input_mappings( - self, inputs: Tuple[List[Dict[str, Any]], ...] - ) -> List[List[Dict[str, Any]]]: - """Applies the `input_mappings` to the input rows. + renamed_inputs = [] + overriden_inputs = [] + for i, row_inputs in enumerate(inputs): + renamed_row_inputs = [] + for row in row_inputs: + overriden_keys = {} + renamed_row = {} + for k, v in row.items(): + renamed_key = reverted_input_mappings.get(k, k) + + if renamed_key not in renamed_row or k != renamed_key: + renamed_row[renamed_key] = v + + if k != renamed_key and renamed_key in row and len(inputs) == 1: + overriden_keys[renamed_key] = row[renamed_key] + + if i == 0: + overriden_inputs.append(overriden_keys) + renamed_row_inputs.append(renamed_row) + renamed_inputs.append(renamed_row_inputs) + return tuple(renamed_inputs), overriden_inputs + + def _apply_mappings_and_restore_overriden( + self, row: Dict[str, Any], overriden: Dict[str, Any] + ) -> Dict[str, Any]: + """Reverts the `input_mappings` applied to the input rows and applies the `output_mappings` + to the output rows. In addition, it restores the overriden values that were replaced + by the `input_mappings`. Args: - inputs: The input rows. + row: The output row. + overriden: The overriden values that were replaced by the `input_mappings`. Returns: - The input rows with the `input_mappings` applied. + The output row with the `output_mappings` applied and the overriden values + restored. """ - reverted_input_mappings = {v: k for k, v in self.input_mappings.items()} + result = {} + for k, v in row.items(): + mapped_key = ( + self.output_mappings.get(k, None) + or self.input_mappings.get(k, None) + or k + ) + result[mapped_key] = v - return [ - [ - {reverted_input_mappings.get(k, k): v for k, v in row.items()} - for row in row_inputs - ] - for row_inputs in inputs - ] + # Restore overriden values + for k, v in overriden.items(): + if k not in result: + result[k] = v + + return result class GeneratorStep(_Step, ABC): diff --git a/tests/unit/steps/test_base.py b/tests/unit/steps/test_base.py index 05fb4a5ea9..4791c5be28 100644 --- a/tests/unit/steps/test_base.py +++ b/tests/unit/steps/test_base.py @@ -214,18 +214,21 @@ def test_apply_input_mappings(self) -> None: ) ) - assert inputs == [ - [ - {"instruction": "hello 1"}, - {"instruction": "hello 2"}, - {"instruction": "hello 3"}, - ], - [ - {"instruction": "bye 1"}, - {"instruction": "bye 2"}, - {"instruction": "bye 3"}, - ], - ] + assert inputs == ( + ( + [ + {"instruction": "hello 1"}, + {"instruction": "hello 2"}, + {"instruction": "hello 3"}, + ], + [ + {"instruction": "bye 1"}, + {"instruction": "bye 2"}, + {"instruction": "bye 3"}, + ], + ), + [{}, {}, {}], + ) def test_process_applying_mappings(self) -> None: step = DummyStep( @@ -251,6 +254,42 @@ def test_process_applying_mappings(self) -> None: {"prompt": "hello 3", "generation": "unit test"}, ] + def test_process_applying_mappings_and_overriden_inputs(self) -> None: + step = DummyStep( + name="dummy", + pipeline=Pipeline(name="unit-test-pipeline"), + input_mappings={"instruction": "prompt"}, + output_mappings={"response": "generation"}, + ) + + outputs = next( + step.process_applying_mappings( + [ + {"prompt": "hello 1", "instruction": "overriden 1"}, + {"prompt": "hello 2", "instruction": "overriden 2"}, + {"prompt": "hello 3", "instruction": "overriden 3"}, + ] + ) + ) + + assert outputs == [ + { + "prompt": "hello 1", + "generation": "unit test", + "instruction": "overriden 1", + }, + { + "prompt": "hello 2", + "generation": "unit test", + "instruction": "overriden 2", + }, + { + "prompt": "hello 3", + "generation": "unit test", + "instruction": "overriden 3", + }, + ] + def test_connect(self) -> None: @step(inputs=["instruction"], outputs=["generation"]) def GenerationStep(input: StepInput): From 56b4036297868e109cf61d299829db0f926ee730 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Mart=C3=ADn=20Bl=C3=A1zquez?= Date: Mon, 2 Sep 2024 16:35:54 +0200 Subject: [PATCH 42/82] Fix all replicas had the same `_llm_identifier` for `CudaDevicePlacementMixin` (#941) * Fix CUDA device placement with multiple replicas * Print replica id * Copy `step` for each replica --- src/distilabel/llms/mixins/cuda_device_placement.py | 5 ----- src/distilabel/pipeline/base.py | 10 ++++++++-- src/distilabel/pipeline/step_wrapper.py | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/distilabel/llms/mixins/cuda_device_placement.py b/src/distilabel/llms/mixins/cuda_device_placement.py index 5642a10761..c7730940e4 100644 --- a/src/distilabel/llms/mixins/cuda_device_placement.py +++ b/src/distilabel/llms/mixins/cuda_device_placement.py @@ -207,11 +207,6 @@ def _get_cuda_device(self, device_map: Dict[str, List[int]]) -> Union[int, None] return device return None - raise RuntimeError( - "Couldn't find an available CUDA device automatically to be used by the LLM" - f" '{self._llm_identifier}'. For forcing the use of a specific device, set the" - " `cuda_devices` attribute to a list with the desired device(s)." - ) def _set_cuda_visible_devices(self) -> None: """Sets the `CUDA_VISIBLE_DEVICES` environment variable to the list of CUDA devices diff --git a/src/distilabel/pipeline/base.py b/src/distilabel/pipeline/base.py index 97ad4f00a8..ced081343b 100644 --- a/src/distilabel/pipeline/base.py +++ b/src/distilabel/pipeline/base.py @@ -1222,8 +1222,14 @@ def _run_steps(self, steps: Iterable[str]) -> None: step_num_replicas: int = step.resources.replicas if step.is_normal else 1 # type: ignore for replica in range(step_num_replicas): - self._logger.debug(f"Running 1 replica of step '{step.name}'...") - self._run_step(step=step, input_queue=input_queue, replica=replica) + self._logger.debug( + f"Running 1 replica of step '{step.name}' with ID {replica}..." + ) + self._run_step( + step=step.model_copy(deep=True), + input_queue=input_queue, + replica=replica, + ) def _add_batches_back_to_batch_manager(self) -> None: """Add the `Batch`es that were sent to a `Step` back to the `_BatchManager`. This diff --git a/src/distilabel/pipeline/step_wrapper.py b/src/distilabel/pipeline/step_wrapper.py index 5fe5572009..0e8473e58a 100644 --- a/src/distilabel/pipeline/step_wrapper.py +++ b/src/distilabel/pipeline/step_wrapper.py @@ -76,7 +76,7 @@ def _init_cuda_device_placement_mixin(attr: CudaDevicePlacementMixin) -> None: attr.disable_cuda_device_placement = True else: desired_num_gpus = self.step.resources.gpus or 1 - attr._llm_identifier = self.step.name + attr._llm_identifier = f"{self.step.name}-replica-{self.replica}" attr._desired_num_gpus = desired_num_gpus for field_name in self.step.model_fields_set: From ebd2bb7ab5b497b17e4609484363956309e9f94a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Mart=C3=ADn=20Bl=C3=A1zquez?= Date: Tue, 3 Sep 2024 13:17:48 +0200 Subject: [PATCH 43/82] Fix empty load stage when two `GlobalStep`s are chained (#945) --- src/distilabel/pipeline/_dag.py | 12 +++++++++--- tests/unit/pipeline/test_dag.py | 31 +++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/src/distilabel/pipeline/_dag.py b/src/distilabel/pipeline/_dag.py index 383703ccf4..3ca2b569df 100644 --- a/src/distilabel/pipeline/_dag.py +++ b/src/distilabel/pipeline/_dag.py @@ -294,13 +294,19 @@ def _get_stage_last_steps(stage_steps: List[str]) -> List[str]: current_stage = [] stages_last_steps = [] - for step_name in nx.topological_sort(self.G): + steps_sorted = list(nx.topological_sort(self.G)) + for i, step_name in enumerate(steps_sorted): step: "_Step" = self.get_step(step_name)[STEP_ATTR_NAME] if not step.is_global: current_stage.append(step_name) else: - stages.append(current_stage) - stages_last_steps.append(_get_stage_last_steps(current_stage)) + previous_step = None + if i > 0: + previous_step_name = steps_sorted[i - 1] + previous_step = self.get_step(previous_step_name)[STEP_ATTR_NAME] + if not previous_step or not previous_step.is_global: + stages.append(current_stage) + stages_last_steps.append(_get_stage_last_steps(current_stage)) stages.append([step_name]) stages_last_steps.append([step_name]) current_stage = [] diff --git a/tests/unit/pipeline/test_dag.py b/tests/unit/pipeline/test_dag.py index db0a90dcc6..b1e14cf836 100644 --- a/tests/unit/pipeline/test_dag.py +++ b/tests/unit/pipeline/test_dag.py @@ -276,6 +276,37 @@ def test_get_steps_load_stages(self) -> None: ], ) + def test_get_steps_load_stages_global_steps_chained(self) -> None: + with Pipeline(name="dummy") as pipeline: + generator = DummyGeneratorStep(name="dummy_generator_step") + dummies_0 = [DummyStep1(name=f"dummy_step_0_{i}") for i in range(3)] + global_0 = DummyGlobalStep(name="global_0") + global_1 = DummyGlobalStep(name="global_1") + + generator >> dummies_0 >> global_0 >> global_1 + + assert pipeline.dag.get_steps_load_stages() == ( + [ + [ + "dummy_generator_step", + "dummy_step_0_0", + "dummy_step_0_1", + "dummy_step_0_2", + ], + ["global_0"], + ["global_1"], + ], + [ + [ + "dummy_step_0_0", + "dummy_step_0_1", + "dummy_step_0_2", + ], + ["global_0"], + ["global_1"], + ], + ) + def test_get_steps_load_stages_simple(self) -> None: with Pipeline(name="dummy") as pipeline: generator = DummyGeneratorStep(name="dummy_generator_step") From 973e0fa564398a739515879605b6e76875d29e8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Mart=C3=ADn=20Bl=C3=A1zquez?= Date: Fri, 6 Sep 2024 15:52:38 +0200 Subject: [PATCH 44/82] Update `TextGeneration` to deprecate `use_system_prompt` and add (#950) `system_prompt` attribute --- src/distilabel/steps/tasks/text_generation.py | 30 ++++++++++++------ .../unit/steps/tasks/test_text_generation.py | 31 +++++++++++++++---- 2 files changed, 45 insertions(+), 16 deletions(-) diff --git a/src/distilabel/steps/tasks/text_generation.py b/src/distilabel/steps/tasks/text_generation.py index 33da5d980e..2042539a4f 100644 --- a/src/distilabel/steps/tasks/text_generation.py +++ b/src/distilabel/steps/tasks/text_generation.py @@ -14,6 +14,8 @@ from typing import TYPE_CHECKING, Any, Dict, List, Union +from pydantic import Field + from distilabel.errors import DistilabelUserError from distilabel.steps.tasks.base import Task from distilabel.utils.chat import is_openai_format @@ -31,9 +33,13 @@ class TextGeneration(Task): instruction. The model_name is also returned as part of the output in order to enhance it. Attributes: - use_system_prompt: Whether to use the system prompt in the generation. Defaults to `True`, - which means that if the column `system_prompt` is defined within the input batch, then - the `system_prompt` will be used, otherwise, it will be ignored. + system_prompt: The system prompt to use in the generation. If not provided, then + it will check if the input row has a column named `system_prompt` and use it. + If not, then no system prompt will be used. Defaults to `None`. + use_system_prompt: DEPRECATED. To be removed in 1.5.0. Whether to use the system + prompt in the generation. Defaults to `True`, which means that if the column + `system_prompt` is defined within the input batch, then the `system_prompt` + will be used, otherwise, it will be ignored. Input columns: - instruction (`str`): The instruction to generate text from. @@ -77,14 +83,15 @@ class TextGeneration(Task): ``` """ - use_system_prompt: bool = True + system_prompt: Union[str, None] = None + use_system_prompt: bool = Field(default=True, deprecated=True) _can_be_used_with_offline_batch_generation = True @property def inputs(self) -> "StepColumns": """The input for the task is the `instruction`.""" - return ["instruction"] + return {"instruction": True, "system_prompt": False} def format_input(self, input: Dict[str, Any]) -> "ChatType": """The input is formatted as a `ChatType` assuming that the instruction @@ -104,11 +111,14 @@ def format_input(self, input: Dict[str, Any]) -> "ChatType": ) messages = [{"role": "user", "content": input["instruction"]}] - if self.use_system_prompt: - if "system_prompt" in input: - messages.insert( - 0, {"role": "system", "content": input["system_prompt"]} - ) + + row_system_prompt = input.get("system_prompt") + if row_system_prompt: + messages.insert(0, {"role": "system", "content": row_system_prompt}) + + if self.system_prompt and not row_system_prompt: + messages.insert(0, {"role": "system", "content": self.system_prompt}) + return messages # type: ignore @property diff --git a/tests/unit/steps/tasks/test_text_generation.py b/tests/unit/steps/tasks/test_text_generation.py index 84ed6edaa5..9cadc4e960 100644 --- a/tests/unit/steps/tasks/test_text_generation.py +++ b/tests/unit/steps/tasks/test_text_generation.py @@ -22,9 +22,9 @@ class TestTextGeneration: def test_format_input(self) -> None: llm = DummyAsyncLLM() - task = TextGeneration(name="task", llm=llm, use_system_prompt=False) + task = TextGeneration(name="task", llm=llm) - assert task.format_input({"instruction": "test", "system_prompt": "test"}) == [ + assert task.format_input({"instruction": "test"}) == [ {"role": "user", "content": "test"} ] @@ -32,10 +32,29 @@ def test_format_input_with_system_prompt(self) -> None: pipeline = Pipeline(name="unit-test-pipeline") llm = DummyAsyncLLM() task = TextGeneration( - name="task", - llm=llm, - pipeline=pipeline, - use_system_prompt=True, + name="task", llm=llm, pipeline=pipeline, system_prompt="test" + ) + + assert task.format_input({"instruction": "test"}) == [ + {"role": "system", "content": "test"}, + {"role": "user", "content": "test"}, + ] + + def test_format_input_with_row_system_prompt(self) -> None: + pipeline = Pipeline(name="unit-test-pipeline") + llm = DummyAsyncLLM() + task = TextGeneration(name="task", llm=llm, pipeline=pipeline) + + assert task.format_input({"instruction": "test", "system_prompt": "test"}) == [ + {"role": "system", "content": "test"}, + {"role": "user", "content": "test"}, + ] + + def test_format_input_with_row_system_prompt_and_system_prompt(self) -> None: + pipeline = Pipeline(name="unit-test-pipeline") + llm = DummyAsyncLLM() + task = TextGeneration( + name="task", llm=llm, pipeline=pipeline, system_prompt="i won't be used" ) assert task.format_input({"instruction": "test", "system_prompt": "test"}) == [ From de2bed0c1de22aa9a1fd723e6cc0add6b0f5be39 Mon Sep 17 00:00:00 2001 From: Agus Date: Fri, 6 Sep 2024 17:08:46 +0200 Subject: [PATCH 45/82] Add step to deduplicate records based on embeddings (#946) * Redirect import * Add train_size argument to allow training indices * Fix error when retrieving info from a dataset fails creating a step from the make_generator_step helper * Add embedding dedup step * Add unit and integration tests for embedding dedup * Apply comments from code review --- src/distilabel/steps/__init__.py | 2 + .../steps/embeddings/nearest_neighbour.py | 11 + src/distilabel/steps/filtering/embedding.py | 192 ++++++++++++++++++ .../steps/generators/huggingface.py | 10 +- src/distilabel/steps/generators/utils.py | 8 +- tests/integration/test_embedding_dedup.py | 130 ++++++++++++ tests/unit/steps/filtering/test_embeddings.py | 104 ++++++++++ 7 files changed, 450 insertions(+), 7 deletions(-) create mode 100644 src/distilabel/steps/filtering/embedding.py create mode 100644 tests/integration/test_embedding_dedup.py create mode 100644 tests/unit/steps/filtering/test_embeddings.py diff --git a/src/distilabel/steps/__init__.py b/src/distilabel/steps/__init__.py index 835f354bd5..0d0f33d9a6 100644 --- a/src/distilabel/steps/__init__.py +++ b/src/distilabel/steps/__init__.py @@ -30,6 +30,7 @@ from distilabel.steps.deita import DeitaFiltering from distilabel.steps.embeddings.embedding_generation import EmbeddingGeneration from distilabel.steps.embeddings.nearest_neighbour import FaissNearestNeighbour +from distilabel.steps.filtering.embedding import EmbeddingDedup from distilabel.steps.filtering.minhash import MinHashDedup from distilabel.steps.formatting.conversation import ConversationTemplate from distilabel.steps.formatting.dpo import ( @@ -79,6 +80,7 @@ "LoadDataFromDisk", "LoadDataFromFileSystem", "LoadDataFromHub", + "EmbeddingDedup", "MinHashDedup", "make_generator_step", "PushToHub", diff --git a/src/distilabel/steps/embeddings/nearest_neighbour.py b/src/distilabel/steps/embeddings/nearest_neighbour.py index adf3fe6c04..98b646d9ee 100644 --- a/src/distilabel/steps/embeddings/nearest_neighbour.py +++ b/src/distilabel/steps/embeddings/nearest_neighbour.py @@ -46,6 +46,8 @@ class FaissNearestNeighbour(GlobalStep): search_batch_size: the number of rows to include in a search batch. The value can be adjusted to maximize the resources usage or to avoid OOM issues. Defaults to `50`. + train_size: If the index needs a training step, specifies how many vectors will be + used to train the index. Runtime parameters: - `device`: the CUDA device ID or a list of IDs to be used. If negative integer, @@ -60,6 +62,8 @@ class FaissNearestNeighbour(GlobalStep): - `search_batch_size`: the number of rows to include in a search batch. The value can be adjusted to maximize the resources usage or to avoid OOM issues. Defaults to `50`. + - `train_size`: If the index needs a training step, specifies how many vectors will + be used to train the index. Input columns: - embedding (`List[Union[float, int]]`): a sentence embedding. @@ -148,6 +152,10 @@ class FaissNearestNeighbour(GlobalStep): description="The number of rows to include in a search batch. The value can be adjusted" " to maximize the resources usage or to avoid OOM issues.", ) + train_size: Optional[RuntimeParameter[int]] = Field( + default=None, + description="If the index needs a training step, specifies how many vectors will be used to train the index.", + ) def load(self) -> None: super().load() @@ -176,11 +184,14 @@ def _build_index(self, inputs: List[Dict[str, Any]]) -> Dataset: The build `datasets.Dataset` with its `faiss` index. """ dataset = Dataset.from_list(inputs) + if self.train_size is not None and self.string_factory: + self._logger.info("🏋️‍♀️ Starting Faiss index training...") dataset.add_faiss_index( column="embedding", device=self.device, # type: ignore string_factory=self.string_factory, metric_type=self.metric_type, + train_size=self.train_size, ) return dataset diff --git a/src/distilabel/steps/filtering/embedding.py b/src/distilabel/steps/filtering/embedding.py new file mode 100644 index 0000000000..cb1e710374 --- /dev/null +++ b/src/distilabel/steps/filtering/embedding.py @@ -0,0 +1,192 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import TYPE_CHECKING, List, Optional + +import numpy as np +from pydantic import Field +from rich.progress import track +from typing_extensions import override + +from distilabel.mixins.runtime_parameters import RuntimeParameter +from distilabel.steps.base import GlobalStep, StepInput + +if TYPE_CHECKING: + from distilabel.steps.typing import StepOutput + + +class EmbeddingDedup(GlobalStep): + """Deduplicates text using embeddings. + + `EmbeddingDedup` is a Step that detects near-duplicates in datasets, using + embeddings to compare the similarity between the texts. The typical workflow with this step + would include having a dataset with embeddings precomputed, and then (possibly using the + `FaissNearestNeighbour`) using the `nn_indices` and `nn_scores`, determine the texts that + are duplicate. + + Attributes: + threshold: the threshold to consider 2 examples as duplicates. + It's dependent on the type of index that was used to generate the embeddings. + For example, if the embeddings were generated using cosine similarity, a threshold + of `0.9` would make all the texts with a cosine similarity above the value + duplicates. Higher values detect less duplicates in such an index, but that should + be taken into account when building it. Defaults to `0.9`. + + Runtime Parameters: + - `threshold`: the threshold to consider 2 examples as duplicates. + + Input columns: + - nn_indices (`List[int]`): a list containing the indices of the `k` nearest neighbours + in the inputs for the row. + - nn_scores (`List[float]`): a list containing the score or distance to each `k` + nearest neighbour in the inputs. + + Output columns: + - keep_row_after_embedding_filtering (`bool`): boolean indicating if the piece `text` is + not a duplicate i.e. this text should be kept. + + Categories: + - filtering + + Examples: + + Deduplicate a list of texts using embedding information: + + ```python + from distilabel.pipeline import Pipeline + from distilabel.steps import EmbeddingDedup + from distilabel.steps import LoadDataFromDicts + + with Pipeline() as pipeline: + data = LoadDataFromDicts( + data=[ + { + "persona": "A chemistry student or academic researcher interested in inorganic or physical chemistry, likely at an advanced undergraduate or graduate level, studying acid-base interactions and chemical bonding.", + "embedding": [ + 0.018477669046149742, + -0.03748236608841726, + 0.001919870620352492, + 0.024918478063770535, + 0.02348063521315178, + 0.0038251285566308375, + -0.01723884983037716, + 0.02881971942372201, + ], + "nn_indices": [0, 1], + "nn_scores": [ + 0.9164746999740601, + 0.782106876373291, + ], + }, + { + "persona": "A music teacher or instructor focused on theoretical and practical piano lessons.", + "embedding": [ + -0.0023464179614082125, + -0.07325472251663565, + -0.06058678419516501, + -0.02100326928586996, + -0.013462744792362657, + 0.027368447064244242, + -0.003916070100455717, + 0.01243614518480423, + ], + "nn_indices": [0, 2], + "nn_scores": [ + 0.7552462220191956, + 0.7261884808540344, + ], + }, + { + "persona": "A classical guitar teacher or instructor, likely with experience teaching beginners, who focuses on breaking down complex music notation into understandable steps for their students.", + "embedding": [ + -0.01630817942328242, + -0.023760151552345232, + -0.014249650090627883, + -0.005713686451446624, + -0.016033059279131567, + 0.0071440908501058786, + -0.05691099643425161, + 0.01597412704817784, + ], + "nn_indices": [1, 2], + "nn_scores": [ + 0.8107735514640808, + 0.7172299027442932, + ], + }, + ], + batch_size=batch_size, + ) + # In general you should do something like this before the deduplication step, to obtain the + # `nn_indices` and `nn_scores`. In this case the embeddings are already normalized, so there's + # no need for it. + # nn = FaissNearestNeighbour( + # k=30, + # metric_type=faiss.METRIC_INNER_PRODUCT, + # search_batch_size=50, + # train_size=len(dataset), # The number of embeddings to use for training + # string_factory="IVF300_HNSW32,Flat" # To use an index (optional, maybe required for big datasets) + # ) + # Read more about the `string_factory` here: + # https://github.com/facebookresearch/faiss/wiki/Guidelines-to-choose-an-index + + embedding_dedup = EmbeddingDedup( + threshold=0.8, + input_batch_size=batch_size, + ) + + data >> embedding_dedup + + if __name__ == "__main__": + distiset = pipeline.run(use_cache=False) + ds = distiset["default"]["train"] + # Filter out the duplicates + ds_dedup = ds.filter(lambda x: x["keep_row_after_embedding_filtering"]) + ``` + """ + + threshold: Optional[RuntimeParameter[float]] = Field( + default=0.9, + description="The threshold to consider 2 examples as duplicates. It's dependent " + "on the type of index that was used to generate the embeddings. For example, if " + "the embeddings were generated using cosine similarity, a threshold of `0.9` " + "would make all the texts with a cosine similarity above the value duplicates. " + "Higher values detect less duplicates in such an index, but that should be " + "taken into account when building it.", + ) + + @property + def inputs(self) -> List[str]: + return ["nn_scores", "nn_indices"] + + @property + def outputs(self) -> List[str]: + return ["keep_row_after_embedding_filtering"] + + @override + def process(self, inputs: StepInput) -> "StepOutput": # type: ignore + rows_to_remove = set() + + for input in track(inputs, description="Running Embedding deduplication..."): + input["keep_row_after_embedding_filtering"] = True + indices_scores = np.array(input["nn_scores"]) > self.threshold + indices = np.array(input["nn_indices"])[indices_scores] + if len(indices) > 0: # If there are any rows found over the threshold + rows_to_remove.update(list(indices)) + + # Remove duplicates and get the list of rows to remove + for idx in rows_to_remove: + inputs[idx]["keep_row_after_embedding_filtering"] = False + + yield inputs diff --git a/src/distilabel/steps/generators/huggingface.py b/src/distilabel/steps/generators/huggingface.py index 5f82f64731..ea72c9a0c6 100644 --- a/src/distilabel/steps/generators/huggingface.py +++ b/src/distilabel/steps/generators/huggingface.py @@ -243,20 +243,18 @@ def _dataset_info(self) -> Dict[str, DatasetInfo]: Returns: The dataset information. """ - repo_id = self.repo_id - config = self.config try: - return get_dataset_infos(repo_id) + return get_dataset_infos(self.repo_id) except Exception as e: # The previous could fail in case of a internet connection issues. # Assuming the dataset is already loaded and we can get the info from the loaded dataset, otherwise it will fail anyway. self._logger.warning( f"Failed to get dataset info from Hugging Face Hub, trying to get it loading the dataset. Error: {e}" ) - ds = load_dataset(repo_id, config=self.config, split=self.split) - if config: - return ds[config].info + ds = load_dataset(self.repo_id, config=self.config, split=self.split) + if self.config: + return ds[self.config].info return ds.info diff --git a/src/distilabel/steps/generators/utils.py b/src/distilabel/steps/generators/utils.py index de119ab2ef..27455119bd 100644 --- a/src/distilabel/steps/generators/utils.py +++ b/src/distilabel/steps/generators/utils.py @@ -32,6 +32,7 @@ def make_generator_step( input_mappings: Optional[Dict[str, str]] = None, output_mappings: Optional[Dict[str, str]] = None, resources: StepResources = StepResources(), + repo_id: str = "placeholder", ) -> "GeneratorStep": """Helper method to create a `GeneratorStep` from a dataset, to simplify @@ -42,6 +43,10 @@ def make_generator_step( input_mappings: Applies the same as any other step. Defaults to `None`. output_mappings: Applies the same as any other step. Defaults to `None`. resources: Applies the same as any other step. Defaults to `StepResources()`. + repo_id: The repository ID to use in the `LoadDataFromHub` step. + This shouldn't be necessary, but in case of error, the dataset will try to be loaded + using `load_dataset` internally. If that case happens, the `repo_id` will be used. + Defaults to `"placeholder"`. Raises: ValueError: If the format is different from the ones supported. @@ -74,12 +79,13 @@ def make_generator_step( loader = LoadDataFromHub( pipeline=pipeline, - repo_id="placeholder_name", + repo_id=repo_id, batch_size=batch_size, input_mappings=input_mappings or {}, output_mappings=output_mappings or {}, resources=resources, ) + super(loader.__class__, loader).load() # Ensure the logger is loaded loader._dataset = dataset loader.num_examples = len(dataset) loader._dataset_info = {"default": dataset.info} diff --git a/tests/integration/test_embedding_dedup.py b/tests/integration/test_embedding_dedup.py new file mode 100644 index 0000000000..7806cf6761 --- /dev/null +++ b/tests/integration/test_embedding_dedup.py @@ -0,0 +1,130 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import TYPE_CHECKING + +import faiss +import numpy as np + +from distilabel.pipeline import Pipeline +from distilabel.steps import FaissNearestNeighbour, LoadDataFromDicts, StepInput, step +from distilabel.steps.filtering.embedding import EmbeddingDedup + +if TYPE_CHECKING: + from distilabel.steps.typing import StepOutput + + +SAMPLE_DATA = [ + { + "text": "A chemistry student or academic researcher interested in inorganic or physical chemistry, likely at an advanced undergraduate or graduate level, studying acid-base interactions and chemical bonding.", + "embedding": [ + 0.018477669046149742, + -0.03748236608841726, + 0.001919870620352492, + 0.024918478063770535, + 0.02348063521315178, + 0.0038251285566308375, + -0.01723884983037716, + 0.02881971942372201, + ], + }, + { + "text": "A music teacher or instructor focused on theoretical and practical piano lessons.", + "embedding": [ + -0.0023464179614082125, + -0.07325472251663565, + -0.06058678419516501, + -0.02100326928586996, + -0.013462744792362657, + 0.027368447064244242, + -0.003916070100455717, + 0.01243614518480423, + ], + }, + { + "text": "A classical guitar teacher or instructor, likely with experience teaching beginners, who focuses on breaking down complex music notation into understandable steps for their students.", + "embedding": [ + -0.01630817942328242, + -0.023760151552345232, + -0.014249650090627883, + -0.005713686451446624, + -0.016033059279131567, + 0.0071440908501058786, + -0.05691099643425161, + 0.01597412704817784, + ], + }, + { + "text": "A classical guitar teacher or instructor, likely with experience teaching beginners, who focuses on breaking down complex music notation into understandable steps for their students.", + "embedding": [ + -0.01630817942328242, + -0.023760151552345232, + -0.014249650090627883, + -0.005713686451446624, + -0.016033059279131567, + 0.0071440908501058786, + -0.05691099643425161, + 0.01597412704817784, + ], + }, +] + + +@step(inputs=["embedding"], outputs=["embedding"]) +def NormalizeEmbeddings(inputs: StepInput) -> "StepOutput": + # Normalize a vector to have length 1 + for input in inputs: + norm = np.linalg.norm(input["embedding"]) + if norm == 0: + print("Cannot normalize a zero vector") + continue + input["embedding"] = input["embedding"] / norm + yield inputs + + +def test_embedding_deduplication() -> None: + with Pipeline() as pipeline: + loader = LoadDataFromDicts( + data=SAMPLE_DATA * 20, + batch_size=50, + ) + batch_size = 50 + + # NOTE: Guide to choose an index: https://github.com/facebookresearch/faiss/wiki/Guidelines-to-choose-an-index + nn = FaissNearestNeighbour( + k=3, + metric_type=faiss.METRIC_INNER_PRODUCT, + search_batch_size=50, + # string_factory="IVF300_HNSW32,Flat", + # train_size=len(dataset), + input_batch_size=batch_size, + ) + + embedding_dedup = EmbeddingDedup( + threshold=0.99, + input_batch_size=batch_size, + ) + normalize = NormalizeEmbeddings() + loader >> normalize >> nn >> embedding_dedup + + distiset = pipeline.run(use_cache=False) + + ds = distiset["default"]["train"] + ds_dedup = ds.filter(lambda x: x["keep_row_after_embedding_filtering"]) + print(len(ds_dedup)) + assert len(ds_dedup) == 71 + + +if __name__ == "__main__": + test_embedding_deduplication() diff --git a/tests/unit/steps/filtering/test_embeddings.py b/tests/unit/steps/filtering/test_embeddings.py new file mode 100644 index 0000000000..354777bd94 --- /dev/null +++ b/tests/unit/steps/filtering/test_embeddings.py @@ -0,0 +1,104 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import pytest + +from distilabel.steps.filtering.embedding import EmbeddingDedup + +SAMPLE_DATA = [ + { + "persona": "A chemistry student or academic researcher interested in inorganic or physical chemistry, likely at an advanced undergraduate or graduate level, studying acid-base interactions and chemical bonding.", + "embedding": [ + 0.018477669046149742, + -0.03748236608841726, + 0.001919870620352492, + 0.024918478063770535, + 0.02348063521315178, + 0.0038251285566308375, + -0.01723884983037716, + 0.02881971942372201, + ], + "nn_indices": [0, 1], + "nn_scores": [ + 0.9164746999740601, + 0.782106876373291, + ], + }, + { + "persona": "A music teacher or instructor focused on theoretical and practical piano lessons.", + "embedding": [ + -0.0023464179614082125, + -0.07325472251663565, + -0.06058678419516501, + -0.02100326928586996, + -0.013462744792362657, + 0.027368447064244242, + -0.003916070100455717, + 0.01243614518480423, + ], + "nn_indices": [0, 2], + "nn_scores": [ + 0.7552462220191956, + 0.7261884808540344, + ], + }, + { + "persona": "A classical guitar teacher or instructor, likely with experience teaching beginners, who focuses on breaking down complex music notation into understandable steps for their students.", + "embedding": [ + -0.01630817942328242, + -0.023760151552345232, + -0.014249650090627883, + -0.005713686451446624, + -0.016033059279131567, + 0.0071440908501058786, + -0.05691099643425161, + 0.01597412704817784, + ], + "nn_indices": [1, 2], + "nn_scores": [ + 0.8107735514640808, + 0.7172299027442932, + ], + }, + { + "persona": "A classical guitar teacher or instructor, likely with experience teaching beginners, who focuses on breaking down complex music notation into understandable steps for their students.", + "embedding": [ + -0.01630817942328242, + -0.023760151552345232, + -0.014249650090627883, + -0.005713686451446624, + -0.016033059279131567, + 0.0071440908501058786, + -0.05691099643425161, + 0.01597412704817784, + ], + "nn_indices": [], + "nn_scores": [], + }, +] + + +class TestEmbeddingDedup: + @pytest.mark.parametrize( + "threshold, keep_row_after_embedding_filtering", + [(0.1, 1), (0.9, 3), (0.99999, 4)], + ) + def test_process( + self, threshold: float, keep_row_after_embedding_filtering: int + ) -> None: + step = EmbeddingDedup(threshold=threshold) + step.load() + result = next(step.process(SAMPLE_DATA)) + duplicated = [r["keep_row_after_embedding_filtering"] for r in result] + assert sum(duplicated) == keep_row_after_embedding_filtering From eef8961f32e57c80281249da7e26a0d9680c7463 Mon Sep 17 00:00:00 2001 From: David Meikle Date: Mon, 9 Sep 2024 09:26:56 +0100 Subject: [PATCH 46/82] Updated `setup_logging` to use UTF-8 encoding in `FileHandler` (#952) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Updated setup_logging to use UTF-8 in FileHandler * Update src/distilabel/utils/logging.py --------- Co-authored-by: Gabriel Martín Blázquez --- src/distilabel/utils/logging.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/distilabel/utils/logging.py b/src/distilabel/utils/logging.py index 1fdaf2f3e7..9939527aa4 100644 --- a/src/distilabel/utils/logging.py +++ b/src/distilabel/utils/logging.py @@ -66,7 +66,7 @@ def setup_logging( if not Path(filename).parent.exists(): Path(filename).parent.mkdir(parents=True, exist_ok=True) - file_handler = FileHandler(filename, delay=True) + file_handler = FileHandler(filename, delay=True, encoding="utf-8") file_formatter = logging.Formatter( "[%(asctime)s] %(levelname)-8s %(message)s", datefmt="%Y-%m-%d %H:%M:%S" ) From 8e9cc8d70991f44086573ff2fda16d21de9c537b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Mart=C3=ADn=20Bl=C3=A1zquez?= Date: Tue, 10 Sep 2024 11:39:19 +0200 Subject: [PATCH 47/82] Add more generation parameters to `vLLM` (#955) --- src/distilabel/llms/vllm.py | 56 ++++++++++++++++++++++++++++++------- 1 file changed, 46 insertions(+), 10 deletions(-) diff --git a/src/distilabel/llms/vllm.py b/src/distilabel/llms/vllm.py index be08debf2c..19212755d4 100644 --- a/src/distilabel/llms/vllm.py +++ b/src/distilabel/llms/vllm.py @@ -44,6 +44,13 @@ from distilabel.steps.tasks.typing import StandardInput +LogitsProcessorFn = Union[ + Callable[[List[int], Any], Any], + Callable[[List[int], List[int], Any], Any], +] + +LogitsProcessors = List[LogitsProcessorFn] + class vLLM(LLM, MagpieChatTemplateMixin, CudaDevicePlacementMixin): """`vLLM` library LLM implementation. @@ -159,7 +166,7 @@ class User(BaseModel): _model: "_vLLM" = PrivateAttr(None) _tokenizer: "PreTrainedTokenizer" = PrivateAttr(None) - _logits_processor: Optional[Callable] = PrivateAttr(default=None) + _structured_output_logits_processor: Optional[Callable] = PrivateAttr(default=None) def load(self) -> None: """Loads the `vLLM` model using either the path or the Hugging Face Hub repository id. @@ -197,12 +204,14 @@ def load(self) -> None: self._tokenizer.chat_template = self.chat_template # type: ignore if self.structured_output: - self._logits_processor = self._prepare_structured_output( + self._structured_output_logits_processor = self._prepare_structured_output( self.structured_output ) def unload(self) -> None: """Unloads the `vLLM` model.""" + self._model = None # type: ignore + self._tokenizer = None # type: ignore CudaDevicePlacementMixin.unload(self) super().unload() @@ -283,11 +292,17 @@ def generate( # type: ignore inputs: List[FormattedInput], num_generations: int = 1, max_new_tokens: int = 128, - frequency_penalty: float = 0.0, presence_penalty: float = 0.0, + frequency_penalty: float = 0.0, + repetition_penalty: float = 1.0, temperature: float = 1.0, top_p: float = 1.0, top_k: int = -1, + min_p: float = 0.0, + stop: Optional[List[str]] = None, + stop_token_ids: Optional[List[int]] = None, + include_stop_str_in_output: bool = False, + logits_processors: Optional[LogitsProcessors] = None, extra_sampling_params: Optional[Dict[str, Any]] = None, ) -> List[GenerateOutput]: """Generates `num_generations` responses for each input. @@ -298,13 +313,24 @@ def generate( # type: ignore `1`. max_new_tokens: the maximum number of new tokens that the model will generate. Defaults to `128`. - frequency_penalty: the repetition penalty to use for the generation. Defaults - to `0.0`. presence_penalty: the presence penalty to use for the generation. Defaults to `0.0`. + frequency_penalty: the repetition penalty to use for the generation. Defaults + to `0.0`. + repetition_penalty: the repetition penalty to use for the generation Defaults to + `1.0`. temperature: the temperature to use for the generation. Defaults to `0.1`. top_p: the top-p value to use for the generation. Defaults to `1.0`. top_k: the top-k value to use for the generation. Defaults to `0`. + min_p: the minimum probability to use for the generation. Defaults to `0.0`. + stop: a list of strings that will be used to stop the generation when found. + Defaults to `None`. + stop_token_ids: a list of token ids that will be used to stop the generation + when found. Defaults to `None`. + include_stop_str_in_output: whether to include the stop string in the output. + Defaults to `False`. + logits_processors: a list of functions to process the logits before sampling. + Defaults to `None`. extra_sampling_params: dictionary with additional arguments to be passed to the `SamplingParams` class from `vllm`. @@ -313,8 +339,12 @@ def generate( # type: ignore """ from vllm import SamplingParams + if not logits_processors: + logits_processors = [] + if extra_sampling_params is None: extra_sampling_params = {} + structured_output = None if isinstance(inputs[0], tuple): @@ -324,25 +354,31 @@ def generate( # type: ignore prepared_batches = [([self.prepare_input(input) for input in inputs], None)] sorted_indices = None - # In case we have a single structured output for the dataset, we can - logits_processors = None - if self._logits_processor: - logits_processors = [self._logits_processor] + # Case in which we have a single structured output for the dataset + if self._structured_output_logits_processor: + logits_processors.append(self._structured_output_logits_processor) batched_outputs = [] for prepared_inputs, structured_output in prepared_batches: if structured_output: - logits_processors = [self._prepare_structured_output(structured_output)] + logits_processors.append( + self._prepare_structured_output(structured_output) + ) sampling_params = SamplingParams( # type: ignore n=num_generations, presence_penalty=presence_penalty, frequency_penalty=frequency_penalty, + repetition_penalty=repetition_penalty, temperature=temperature, top_p=top_p, top_k=top_k, + min_p=min_p, max_tokens=max_new_tokens, + stop=stop, + stop_token_ids=stop_token_ids, + include_stop_str_in_output=include_stop_str_in_output, logits_processors=logits_processors, **extra_sampling_params, ) From f207fab676358766888d17b4289d4ff6e00aa384 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Mart=C3=ADn=20Bl=C3=A1zquez?= Date: Tue, 10 Sep 2024 15:37:02 +0200 Subject: [PATCH 48/82] Fix `Magpie` generating different columns depending on `LLM` output (#965) --- src/distilabel/steps/tasks/magpie/base.py | 7 ++ tests/unit/steps/tasks/magpie/test_base.py | 89 ++++++++++++++++++++++ 2 files changed, 96 insertions(+) diff --git a/src/distilabel/steps/tasks/magpie/base.py b/src/distilabel/steps/tasks/magpie/base.py index 29d04c29a0..97e2903628 100644 --- a/src/distilabel/steps/tasks/magpie/base.py +++ b/src/distilabel/steps/tasks/magpie/base.py @@ -164,6 +164,13 @@ def _prepare_conversation_outputs( """ outputs = [] for conversation in conversations: + # Something went wrong with the `LLM` and it didn't generate any message + if len(conversation) == 0: + if self.n_turns == 1: + outputs.append({"instruction": None, "response": None}) + else: + outputs.append({"conversation": []}) + continue if not self.include_system_prompt and conversation[0]["role"] == "system": conversation.pop(0) if self.n_turns == 1 and len(conversation) == 2: diff --git a/tests/unit/steps/tasks/magpie/test_base.py b/tests/unit/steps/tasks/magpie/test_base.py index dcdc6d9165..798ce8c5b4 100644 --- a/tests/unit/steps/tasks/magpie/test_base.py +++ b/tests/unit/steps/tasks/magpie/test_base.py @@ -13,6 +13,7 @@ # limitations under the License. import random +from typing import Any, Dict from unittest import mock import pytest @@ -388,6 +389,94 @@ def test_process_only_instruction(self) -> None: }, ] + @pytest.mark.parametrize( + "conversation, include_system_prompt, n_turns, expected", + [ + ( + [ + {"role": "user", "content": "Hello Magpie"}, + {"role": "assistant", "content": "Hello user"}, + ], + False, + 1, + {"instruction": "Hello Magpie", "response": "Hello user"}, + ), + ( + [ + {"role": "user", "content": "Hello Magpie"}, + {"role": "assistant", "content": "Hello user"}, + ], + False, + 1, + {"instruction": "Hello Magpie", "response": "Hello user"}, + ), + ( + [ + {"role": "system", "content": "This is a system prompt."}, + {"role": "user", "content": "Hello Magpie"}, + {"role": "assistant", "content": "Hello user"}, + {"role": "user", "content": "How are you?"}, + {"role": "assistant", "content": "I'm fine thank you."}, + ], + True, + 2, + { + "conversation": [ + {"role": "system", "content": "This is a system prompt."}, + {"role": "user", "content": "Hello Magpie"}, + {"role": "assistant", "content": "Hello user"}, + {"role": "user", "content": "How are you?"}, + {"role": "assistant", "content": "I'm fine thank you."}, + ], + }, + ), + ( + [ + {"role": "system", "content": "This is a system prompt."}, + {"role": "user", "content": "Hello Magpie"}, + {"role": "assistant", "content": "Hello user"}, + {"role": "user", "content": "How are you?"}, + {"role": "assistant", "content": "I'm fine thank you."}, + ], + False, + 2, + { + "conversation": [ + {"role": "user", "content": "Hello Magpie"}, + {"role": "assistant", "content": "Hello user"}, + {"role": "user", "content": "How are you?"}, + {"role": "assistant", "content": "I'm fine thank you."}, + ], + }, + ), + ( + [], + False, + 1, + {"instruction": None, "response": None}, + ), + ( + [], + False, + 2, + {"conversation": []}, + ), + ], + ) + def test_prepare_conversation_outputs( + self, + conversation, + include_system_prompt: bool, + n_turns: int, + expected: Dict[str, Any], + ) -> None: + task = Magpie( + llm=DummyMagpieLLM(magpie_pre_query_template="llama3"), + n_turns=n_turns, + include_system_prompt=include_system_prompt, + ) + assert task._prepare_conversation_outputs([conversation]) == [expected] + def test_serialization(self) -> None: task = Magpie( llm=DummyMagpieLLM(magpie_pre_query_template="llama3"), From 6e2c9b1f30ce55c8155c92ebd81070a8d269c0ec Mon Sep 17 00:00:00 2001 From: David Berenstein Date: Wed, 11 Sep 2024 07:15:15 +0200 Subject: [PATCH 49/82] Docs/962 docs create a smoother transition from index installation quickstart (#968) * docs: swapped order of installation and quickstart * chore: remove pipeline png file --- docs/sections/getting_started/quickstart.md | 12 +++++++++++- mkdocs.yml | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/docs/sections/getting_started/quickstart.md b/docs/sections/getting_started/quickstart.md index bf3780739c..863af475fb 100644 --- a/docs/sections/getting_started/quickstart.md +++ b/docs/sections/getting_started/quickstart.md @@ -12,6 +12,16 @@ hide: To start off, `distilabel` is a framework for building pipelines for generating synthetic data using LLMs, that defines a [`Pipeline`][distilabel.pipeline.Pipeline] which orchestrates the execution of the [`Step`][distilabel.steps.base.Step] subclasses, and those will be connected as nodes in a Direct Acyclic Graph (DAG). +## Installation + +To install the latest release with `hf-inference-endpoints` extra of the package from PyPI you can use the following command: + +```sh +pip install distilabel[hf-inference-endpoints] --upgrade +``` + +## Define a pipeline + In this guide we will walk you through the process of creating a simple pipeline that uses the [`InferenceEndpointsLLM`][distilabel.llms.InferenceEndpointsLLM] class to generate text. The [`Pipeline`][distilabel.pipeline.Pipeline] will load a dataset that contains a column named `prompt` from the Hugging Face Hub via the step [`LoadDataFromHub`][distilabel.steps.LoadDataFromHub] and then use the [`InferenceEndpointsLLM`][distilabel.llms.InferenceEndpointsLLM] class to generate text based on the dataset using the [`TextGeneration`][distilabel.steps.tasks.TextGeneration] task. > You can check the available models in the [Hugging Face Model Hub](https://huggingface.co/models?pipeline_tag=text-generation&sort=trending) and filter by `Inference status`. @@ -92,7 +102,7 @@ with Pipeline() as pipeline: # (1) TextGeneration(llm=InferenceEndpointsLLM(model_id="meta-llama/Meta-Llama-3.1-8B-Instruct")) # (2) -if __name__ == "__main__": +if __name__ == "__main__": distiset = pipeline.run(dataset=dataset) # (3) distiset.push_to_hub(repo_id="distilabel-example") ``` diff --git a/mkdocs.yml b/mkdocs.yml index 54fd76d244..07dccfc4fb 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -168,8 +168,8 @@ plugins: nav: - Distilabel: "index.md" - Getting started: - - Installation: "sections/getting_started/installation.md" - Quickstart: "sections/getting_started/quickstart.md" + - Installation: "sections/getting_started/installation.md" - FAQ: "sections/getting_started/faq.md" - How-to guides: - "sections/how_to_guides/index.md" From ccea49a582404b3dbbb21131a21551d00ab945d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Mart=C3=ADn=20Bl=C3=A1zquez?= Date: Thu, 12 Sep 2024 10:02:44 +0200 Subject: [PATCH 50/82] Add `logging_handlers` argument (#969) * Add `logging_handlers` argument * Update call to super init --- src/distilabel/pipeline/base.py | 8 +++++++- src/distilabel/pipeline/local.py | 26 +++++++++++++++++++++----- src/distilabel/pipeline/ray.py | 14 ++++++++++---- src/distilabel/utils/logging.py | 15 +++++++++++---- 4 files changed, 49 insertions(+), 14 deletions(-) diff --git a/src/distilabel/pipeline/base.py b/src/distilabel/pipeline/base.py index ced081343b..cd6f35e59d 100644 --- a/src/distilabel/pipeline/base.py +++ b/src/distilabel/pipeline/base.py @@ -312,6 +312,7 @@ def run( storage_parameters: Optional[Dict[str, Any]] = None, use_fs_to_pass_data: bool = False, dataset: Optional["InputDataset"] = None, + logging_handlers: Optional[List[logging.Handler]] = None, ) -> "Distiset": # type: ignore """Run the pipeline. It will set the runtime parameters for the steps and validate the pipeline. @@ -338,6 +339,9 @@ def run( dataset: If given, it will be used to create a `GeneratorStep` and put it as the root step. Convenient method when you have already processed the dataset in your script and just want to pass it already processed. Defaults to `None`. + logging_handlers: A list of logging handlers that will be used to log the + output of the pipeline. This argument can be useful so the logging messages + can be extracted and used in a different context. Defaults to `None`. Returns: The `Distiset` created by the pipeline. @@ -356,7 +360,9 @@ def run( self._add_dataset_generator_step(dataset) setup_logging( - log_queue=self._log_queue, filename=str(self._cache_location["log_file"]) + log_queue=self._log_queue, + filename=str(self._cache_location["log_file"]), + logging_handlers=logging_handlers, ) # Set the name of the pipeline if it's the default one. This should be called diff --git a/src/distilabel/pipeline/local.py b/src/distilabel/pipeline/local.py index 1daa63944d..be7919d56d 100644 --- a/src/distilabel/pipeline/local.py +++ b/src/distilabel/pipeline/local.py @@ -16,7 +16,17 @@ import signal import sys from multiprocessing.pool import Pool -from typing import TYPE_CHECKING, Any, Callable, Dict, Iterable, Optional, Union, cast +from typing import ( + TYPE_CHECKING, + Any, + Callable, + Dict, + Iterable, + List, + Optional, + Union, + cast, +) import tblib @@ -30,6 +40,7 @@ from distilabel.utils.ray import script_executed_in_ray_cluster if TYPE_CHECKING: + import logging from queue import Queue from distilabel.distiset import Distiset @@ -141,6 +152,7 @@ def run( storage_parameters: Optional[Dict[str, Any]] = None, use_fs_to_pass_data: bool = False, dataset: Optional["InputDataset"] = None, + logging_handlers: Optional[List["logging.Handler"]] = None, ) -> "Distiset": """Runs the pipeline. @@ -163,6 +175,9 @@ def run( dataset: If given, it will be used to create a `GeneratorStep` and put it as the root step. Convenient method when you have already processed the dataset in your script and just want to pass it already processed. Defaults to `None`. + logging_handlers: A list of logging handlers that will be used to log the + output of the pipeline. This argument can be useful so the logging messages + can be extracted and used in a different context. Defaults to `None`. Returns: The `Distiset` created by the pipeline. @@ -183,11 +198,12 @@ def run( self._log_queue = cast("Queue[Any]", mp.Queue()) if distiset := super().run( - parameters, - use_cache, - storage_parameters, - use_fs_to_pass_data, + parameters=parameters, + use_cache=use_cache, + storage_parameters=storage_parameters, + use_fs_to_pass_data=use_fs_to_pass_data, dataset=dataset, + logging_handlers=logging_handlers, ): return distiset diff --git a/src/distilabel/pipeline/ray.py b/src/distilabel/pipeline/ray.py index bfdff96c64..cf9a26064a 100644 --- a/src/distilabel/pipeline/ray.py +++ b/src/distilabel/pipeline/ray.py @@ -25,6 +25,7 @@ from distilabel.utils.serialization import TYPE_INFO_KEY if TYPE_CHECKING: + import logging from os import PathLike from queue import Queue @@ -82,6 +83,7 @@ def run( storage_parameters: Optional[Dict[str, Any]] = None, use_fs_to_pass_data: bool = False, dataset: Optional["InputDataset"] = None, + logging_handlers: Optional[List["logging.Handler"]] = None, ) -> "Distiset": """Runs the pipeline in the Ray cluster. @@ -104,6 +106,9 @@ def run( dataset: If given, it will be used to create a `GeneratorStep` and put it as the root step. Convenient method when you have already processed the dataset in your script and just want to pass it already processed. Defaults to `None`. + logging_handlers: A list of logging handlers that will be used to log the + output of the pipeline. This argument can be useful so the logging messages + can be extracted and used in a different context. Defaults to `None`. Returns: The `Distiset` created by the pipeline. @@ -120,11 +125,12 @@ def run( ) if distiset := super().run( - parameters, - use_cache, - storage_parameters, - use_fs_to_pass_data, + parameters=parameters, + use_cache=use_cache, + storage_parameters=storage_parameters, + use_fs_to_pass_data=use_fs_to_pass_data, dataset=dataset, + logging_handlers=logging_handlers, ): return distiset diff --git a/src/distilabel/utils/logging.py b/src/distilabel/utils/logging.py index 9939527aa4..994c81e321 100644 --- a/src/distilabel/utils/logging.py +++ b/src/distilabel/utils/logging.py @@ -18,7 +18,7 @@ from logging import FileHandler from logging.handlers import QueueHandler, QueueListener from pathlib import Path -from typing import TYPE_CHECKING, Any, Optional, Union +from typing import TYPE_CHECKING, Any, List, Optional, Union from rich.logging import RichHandler @@ -47,7 +47,9 @@ def setup_logging( - log_queue: Optional["Queue[Any]"] = None, filename: Optional[str] = None + log_queue: Optional["Queue[Any]"] = None, + filename: Optional[str] = None, + logging_handlers: Optional[List[logging.Handler]] = None, ) -> None: """Sets up logging to use a queue across all processes.""" global queue_listener @@ -60,21 +62,26 @@ def setup_logging( # If the current process is the main process, set up a `QueueListener` # to handle logs from all subprocesses if mp.current_process().name == "MainProcess" and filename: + if logging_handlers is None: + logging_handlers = [] + formatter = logging.Formatter("['%(name)s'] %(message)s") handler = RichHandler(rich_tracebacks=True) handler.setFormatter(formatter) + logging_handlers.append(handler) + if not Path(filename).parent.exists(): Path(filename).parent.mkdir(parents=True, exist_ok=True) - file_handler = FileHandler(filename, delay=True, encoding="utf-8") file_formatter = logging.Formatter( "[%(asctime)s] %(levelname)-8s %(message)s", datefmt="%Y-%m-%d %H:%M:%S" ) file_handler.setFormatter(file_formatter) + logging_handlers.append(file_handler) if log_queue is not None: queue_listener = QueueListener( - log_queue, handler, file_handler, respect_handler_level=True + log_queue, *logging_handlers, respect_handler_level=True ) queue_listener.start() From 28ecbc414a01aad5f6726b568043033ddb5686f8 Mon Sep 17 00:00:00 2001 From: Agus Date: Fri, 13 Sep 2024 12:14:30 +0200 Subject: [PATCH 51/82] [DOCS] Add tips in the docs to avoid overloading Free Serverless Endpoints (#973) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Make the regular expression more general to capture extra characters in the headers of the examples' sections * Add tip in the example title for free inference endpoints * Add FAQ entry for input batch size in serverless endpoints * Change default LLM * Update docs/sections/getting_started/faq.md Co-authored-by: Gabriel Martín Blázquez * Fix wording for SEO purposes * Add installing libdnnl-dev --------- Co-authored-by: Gabriel Martín Blázquez --- docs/sections/getting_started/faq.md | 15 +++++++++++++++ scripts/install_cpu_vllm.sh | 2 +- .../llms/huggingface/inference_endpoints.py | 4 ++-- src/distilabel/utils/docstring.py | 3 ++- 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/docs/sections/getting_started/faq.md b/docs/sections/getting_started/faq.md index 88c6cd7526..7a78126c46 100644 --- a/docs/sections/getting_started/faq.md +++ b/docs/sections/getting_started/faq.md @@ -45,3 +45,18 @@ hide: ??? faq "Can `distilabel` be used with [OpenAI Batch API](https://platform.openai.com/docs/guides/batch)?" Yes, `distilabel` is integrated with OpenAI Batch API via [OpenAILLM][distilabel.llms.openai.OpenAILLM]. Check [LLMs - Offline Batch Generation](../how_to_guides/basic/llm/index.md#offline-batch-generation) for a small example on how to use it and [Advanced - Offline Batch Generation](../how_to_guides/advanced/offline_batch_generation.md) for a more detailed guide. + +??? faq "Prevent overloads on [Free Serverless Endpoints][distilabel.llms.huggingface.InferenceEndpointsLLM]" + When running a task using the [InferenceEndpointsLLM][distilabel.llms.huggingface.InferenceEndpointsLLM] with Free Serverless Endpoints, you may be facing some errors such as `Model is overloaded` if you let the batch size to the default (set at 50). To fix the issue, lower the value or even better set `input_batch_size=1` in your task. It may take a longer time to finish, but please remember this is a free service. + + ```python + from distilabel.llms.huggingface import InferenceEndpointsLLM + from distilabel.steps import TextGeneration + + TextGeneration( + llm=InferenceEndpointsLLM( + model_id="meta-llama/Meta-Llama-3.1-70B-Instruct", + ), + input_batch_size=1 + ) + ``` diff --git a/scripts/install_cpu_vllm.sh b/scripts/install_cpu_vllm.sh index 199413a1e2..7535c88213 100755 --- a/scripts/install_cpu_vllm.sh +++ b/scripts/install_cpu_vllm.sh @@ -4,7 +4,7 @@ set -e echo "Updating system and installing build dependencies..." sudo apt-get update -y -sudo apt-get install -y gcc-12 g++-12 libnuma-dev cmake +sudo apt-get install -y gcc-12 g++-12 libnuma-dev cmake libdnnl-dev sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-12 10 --slave /usr/bin/g++ g++ /usr/bin/g++-12 echo "Python version:" diff --git a/src/distilabel/llms/huggingface/inference_endpoints.py b/src/distilabel/llms/huggingface/inference_endpoints.py index f99593b8e8..42cf1b4345 100644 --- a/src/distilabel/llms/huggingface/inference_endpoints.py +++ b/src/distilabel/llms/huggingface/inference_endpoints.py @@ -74,13 +74,13 @@ class InferenceEndpointsLLM(AsyncLLM, MagpieChatTemplateMixin): `:hugging:` Examples: - Free serverless Inference API: + Free serverless Inference API, set the input_batch_size of the Task that uses this to avoid Model is overloaded: ```python from distilabel.llms.huggingface import InferenceEndpointsLLM llm = InferenceEndpointsLLM( - model_id="mistralai/Mistral-7B-Instruct-v0.2", + model_id="meta-llama/Meta-Llama-3.1-70B-Instruct", ) llm.load() diff --git a/src/distilabel/utils/docstring.py b/src/distilabel/utils/docstring.py index 913b94700a..6171b72b2e 100644 --- a/src/distilabel/utils/docstring.py +++ b/src/distilabel/utils/docstring.py @@ -165,7 +165,8 @@ def parse_google_docstring(func: Callable) -> Docstring: # noqa: C901 elif section_name == "examples": # Parse examples into a dictionary example_items = re.findall( - r"(\w[\w\s]*?):\s*\n?\s*```python\n(.*?)\n\s*```", + r"""([\w,()'][\w\s,()=`!'"]*?):\s*\n?\s*```python\n(.*?)\n\s*```""", + # r"(\w[\w\s]*?):\s*\n?\s*```python\n(.*?)\n\s*```", section_content, re.DOTALL, ) From f0067b8737b2cea4896014eb26d680e2c91fa00c Mon Sep 17 00:00:00 2001 From: Agus Date: Mon, 16 Sep 2024 06:22:35 +0200 Subject: [PATCH 52/82] Add `TextClassification`, `UMAP`, `DBSCAN` and `TextClustering` tasks (#948) * Redirect import of task * Add icon for text classification * Add text classification task * Add tests for text classification * Continue with this problematic thing until we merge it in one of the PRs * Port itertools.batched function for python<3.12 * Make more generic the template for text classification * Add tests for the extra flexibility in the template * Fix condition to determine the backend for the structured output * Simplify condition for json schema in structured output * Add folder for clustering related steps * Fix default structured output for inference endpoints * Added examples to the docstrings * Add icon for clustering steps/tasks * Add umap step * Add dbscan step * Redirect import of steps * Add text clustering task * Set default value for repo_id to avoid potential errors when loading the dataset * Change example dataset in docstrings as that has more information * Add unit tests for clustering steps * Remove extra log message unnecesary * Add tests for text clustering process * Update pyproject with dependencies of text_clustering * Set internal variables to None on unload to clean up --- pyproject.toml | 5 + scripts/install_dependencies.sh | 2 +- src/distilabel/steps/__init__.py | 6 + src/distilabel/steps/clustering/__init__.py | 14 + src/distilabel/steps/clustering/dbscan.py | 177 ++++++++ .../steps/clustering/text_clustering.py | 327 +++++++++++++++ src/distilabel/steps/clustering/umap.py | 164 ++++++++ src/distilabel/steps/generators/utils.py | 3 +- src/distilabel/steps/tasks/__init__.py | 2 + src/distilabel/steps/tasks/base.py | 7 +- .../steps/tasks/text_classification.py | 378 ++++++++++++++++++ src/distilabel/utils/itertools.py | 15 + .../utils/mkdocs/components_gallery.py | 2 + tests/unit/steps/clustering/__init__.py | 14 + tests/unit/steps/clustering/test_dbscan.py | 39 ++ .../steps/clustering/test_text_clustering.py | 75 ++++ tests/unit/steps/clustering/test_umap.py | 42 ++ .../steps/tasks/test_text_classification.py | 140 +++++++ 18 files changed, 1406 insertions(+), 6 deletions(-) create mode 100644 src/distilabel/steps/clustering/__init__.py create mode 100644 src/distilabel/steps/clustering/dbscan.py create mode 100644 src/distilabel/steps/clustering/text_clustering.py create mode 100644 src/distilabel/steps/clustering/umap.py create mode 100644 src/distilabel/steps/tasks/text_classification.py create mode 100644 tests/unit/steps/clustering/__init__.py create mode 100644 tests/unit/steps/clustering/test_dbscan.py create mode 100644 tests/unit/steps/clustering/test_text_clustering.py create mode 100644 tests/unit/steps/clustering/test_umap.py create mode 100644 tests/unit/steps/tasks/test_text_classification.py diff --git a/pyproject.toml b/pyproject.toml index d5f4e795ed..01f5e903b5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -95,6 +95,11 @@ vllm = [ sentence-transformers = ["sentence-transformers >= 3.0.0"] faiss-cpu = ["faiss-cpu >= 1.8.0"] faiss-gpu = ["faiss-gpu >= 1.7.2"] +text-clustering = [ + "umap-learn >= 0.5.6", + "scikit-learn >= 1.4.1", + "matplotlib >= 3.8.3" # For the figure (even though it's optional) +] # minhash minhash = ["datasketch >= 1.6.5", "nltk>3.8.1"] diff --git a/scripts/install_dependencies.sh b/scripts/install_dependencies.sh index 3f7669deec..767f6e6dd0 100755 --- a/scripts/install_dependencies.sh +++ b/scripts/install_dependencies.sh @@ -6,7 +6,7 @@ python_version=$(python -c "import sys; print(sys.version_info[:2])") python -m pip install uv -uv pip install --system -e ".[anthropic,argilla,cohere,groq,hf-inference-endpoints,hf-transformers,litellm,llama-cpp,ollama,openai,outlines,vertexai,mistralai,instructor,sentence-transformers,faiss-cpu,minhash]" +uv pip install --system -e ".[anthropic,argilla,cohere,groq,hf-inference-endpoints,hf-transformers,litellm,llama-cpp,ollama,openai,outlines,vertexai,mistralai,instructor,sentence-transformers,faiss-cpu,minhash,text-clustering]" if [ "${python_version}" != "(3, 12)" ]; then uv pip install --system -e .[ray] diff --git a/src/distilabel/steps/__init__.py b/src/distilabel/steps/__init__.py index 0d0f33d9a6..79c10a268e 100644 --- a/src/distilabel/steps/__init__.py +++ b/src/distilabel/steps/__init__.py @@ -21,6 +21,9 @@ StepInput, StepResources, ) +from distilabel.steps.clustering.dbscan import DBSCAN +from distilabel.steps.clustering.text_clustering import TextClustering +from distilabel.steps.clustering.umap import UMAP from distilabel.steps.columns.combine import CombineOutputs from distilabel.steps.columns.expand import ExpandColumns from distilabel.steps.columns.group import CombineColumns, GroupColumns @@ -67,6 +70,9 @@ "GroupColumns", "KeepColumns", "MergeColumns", + "DBSCAN", + "UMAP", + "TextClustering", "step", "DeitaFiltering", "EmbeddingGeneration", diff --git a/src/distilabel/steps/clustering/__init__.py b/src/distilabel/steps/clustering/__init__.py new file mode 100644 index 0000000000..20ce00bda7 --- /dev/null +++ b/src/distilabel/steps/clustering/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + diff --git a/src/distilabel/steps/clustering/dbscan.py b/src/distilabel/steps/clustering/dbscan.py new file mode 100644 index 0000000000..03ac5dcb3e --- /dev/null +++ b/src/distilabel/steps/clustering/dbscan.py @@ -0,0 +1,177 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import importlib.util +from typing import TYPE_CHECKING, Any, List, Optional + +import numpy as np +from pydantic import Field, PrivateAttr + +from distilabel.mixins.runtime_parameters import RuntimeParameter +from distilabel.steps import ( + GlobalStep, + StepInput, +) + +if TYPE_CHECKING: + from sklearn.cluster import DBSCAN as _DBSCAN + + from distilabel.steps.typing import StepOutput + + +class DBSCAN(GlobalStep): + r"""DBSCAN (Density-Based Spatial Clustering of Applications with Noise) finds core + samples in regions of high density and expands clusters from them. This algorithm + is good for data which contains clusters of similar density. + + This is a `GlobalStep` that clusters the embeddings using the DBSCAN algorithm + from `sklearn`. Visit `TextClustering` step for an example of use. + The trained model is saved as an artifact when creating a distiset + and pushing it to the Hugging Face Hub. + + Input columns: + - projection (`List[float]`): Vector representation of the text to cluster, + normally the output from the `UMAP` step. + + Output columns: + - cluster_label (`int`): Integer representing the label of a given cluster. -1 + means it wasn't clustered. + + Categories: + - clustering + - text-classification + + References: + - [`DBSCAN demo of sklearn`](https://scikit-learn.org/stable/auto_examples/cluster/plot_dbscan.html#demo-of-dbscan-clustering-algorithm) + - [`sklearn dbscan`](https://scikit-learn.org/stable/modules/clustering.html#dbscan) + + Attributes: + - eps: The maximum distance between two samples for one to be considered as in the + neighborhood of the other. This is not a maximum bound on the distances of + points within a cluster. This is the most important DBSCAN parameter to + choose appropriately for your data set and distance function. + - min_samples: The number of samples (or total weight) in a neighborhood for a point + to be considered as a core point. This includes the point itself. If `min_samples` + is set to a higher value, DBSCAN will find denser clusters, whereas if it is set + to a lower value, the found clusters will be more sparse. + - metric: The metric to use when calculating distance between instances in a feature + array. If metric is a string or callable, it must be one of the options allowed + by `sklearn.metrics.pairwise_distances` for its metric parameter. + - n_jobs: The number of parallel jobs to run. + + Runtime parameters: + - `eps`: The maximum distance between two samples for one to be considered as in the + neighborhood of the other. This is not a maximum bound on the distances of + points within a cluster. This is the most important DBSCAN parameter to + choose appropriately for your data set and distance function. + - `min_samples`: The number of samples (or total weight) in a neighborhood for a point + to be considered as a core point. This includes the point itself. If `min_samples` + is set to a higher value, DBSCAN will find denser clusters, whereas if it is set + to a lower value, the found clusters will be more sparse. + - `metric`: The metric to use when calculating distance between instances in a feature + array. If metric is a string or callable, it must be one of the options allowed + by `sklearn.metrics.pairwise_distances` for its metric parameter. + - `n_jobs`: The number of parallel jobs to run. + """ + + eps: Optional[RuntimeParameter[float]] = Field( + default=0.3, + description=( + "The maximum distance between two samples for one to be considered " + "as in the neighborhood of the other. This is not a maximum bound " + "on the distances of points within a cluster. This is the most " + "important DBSCAN parameter to choose appropriately for your data set " + "and distance function." + ), + ) + min_samples: Optional[RuntimeParameter[int]] = Field( + default=30, + description=( + "The number of samples (or total weight) in a neighborhood for a point to " + "be considered as a core point. This includes the point itself. If " + "`min_samples` is set to a higher value, DBSCAN will find denser clusters, " + "whereas if it is set to a lower value, the found clusters will be more " + "sparse." + ), + ) + metric: Optional[RuntimeParameter[str]] = Field( + default="euclidean", + description=( + "The metric to use when calculating distance between instances in a " + "feature array. If metric is a string or callable, it must be one of " + "the options allowed by `sklearn.metrics.pairwise_distances` for " + "its metric parameter." + ), + ) + n_jobs: Optional[RuntimeParameter[int]] = Field( + default=8, description="The number of parallel jobs to run." + ) + + _clusterer: Optional["_DBSCAN"] = PrivateAttr(None) + + def load(self) -> None: + super().load() + if importlib.util.find_spec("sklearn") is None: + raise ImportError( + "`sklearn` package is not installed. Please install it using `pip install scikit-learn`." + ) + from sklearn.cluster import DBSCAN as _DBSCAN + + self._clusterer = _DBSCAN( + eps=self.eps, + min_samples=self.min_samples, + metric=self.metric, + n_jobs=self.n_jobs, + ) + + def unload(self) -> None: + self._clusterer = None + + @property + def inputs(self) -> List[str]: + return ["projection"] + + @property + def outputs(self) -> List[str]: + return ["cluster_label"] + + def _save_model(self, model: Any) -> None: + import joblib + + def save_model(path): + with open(str(path / "DBSCAN.joblib"), "wb") as f: + joblib.dump(model, f) + + self.save_artifact( + name="DBSCAN_model", + write_function=lambda path: save_model(path), + metadata={ + "eps": self.eps, + "min_samples": self.min_samples, + "metric": self.metric, + }, + ) + + def process(self, inputs: StepInput) -> "StepOutput": # type: ignore + projections = np.array([input["projection"] for input in inputs]) + + self._logger.info("🏋️‍♀️ Start training DBSCAN...") + fitted_clusterer = self._clusterer.fit(projections) + cluster_labels = fitted_clusterer.labels_ + # Sets the cluster labels for each input, -1 means it wasn't clustered + for input, cluster_label in zip(inputs, cluster_labels): + input["cluster_label"] = cluster_label + self._logger.info(f"DBSCAN labels assigned: {len(set(cluster_labels))}") + self._save_model(fitted_clusterer) + yield inputs diff --git a/src/distilabel/steps/clustering/text_clustering.py b/src/distilabel/steps/clustering/text_clustering.py new file mode 100644 index 0000000000..4bf583c167 --- /dev/null +++ b/src/distilabel/steps/clustering/text_clustering.py @@ -0,0 +1,327 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import importlib.util +import json +from collections import defaultdict +from typing import TYPE_CHECKING, Dict, List, Optional, Tuple, Union + +import numpy as np +import pandas as pd +from pydantic import Field + +from distilabel.mixins.runtime_parameters import RuntimeParameter +from distilabel.steps import StepInput +from distilabel.steps.tasks import TextClassification +from distilabel.steps.tasks.base import GlobalTask +from distilabel.utils.itertools import batched + +if TYPE_CHECKING: + from distilabel.steps.typing import StepOutput + + +class TextClustering(TextClassification, GlobalTask): + """Task that clusters a set of texts and generates summary labels for each cluster. + + This is a `GlobalTask` that inherits from `TextClassification`, this means that all + the attributes from that class are available here. Also, in this case we deal + with all the inputs at once, instead of using batches. The `input_batch_size` is + used here to send the examples to the LLM in batches (a subtle difference with the + more common `Task` definitions). + The task looks in each cluster for a given number of representative examples (the number + is set by the `samples_per_cluster` attribute), and sends them to the LLM to get a label/s + that represent the cluster. The labels are then assigned to each text in the cluster. + The clusters and projections used in the step, are assumed to be obtained from the `UMAP` + + `DBSCAN` steps, but could be generated for similar steps, as long as they represent the + same concepts. + This step runs a pipeline like the one in this repository: + https://github.com/huggingface/text-clustering + + Input columns: + - text (`str`): The reference text we want to obtain labels for. + - projection (`List[float]`): Vector representation of the text to cluster, + normally the output from the `UMAP` step. + - cluster_label (`int`): Integer representing the label of a given cluster. -1 + means it wasn't clustered. + + Output columns: + - summary_label (`str`): The label or list of labels for the text. + - model_name (`str`): The name of the model used to generate the label/s. + + Categories: + - clustering + - text-classification + + References: + - [`text-clustering repository`](https://github.com/huggingface/text-clustering) + + Attributes: + - savefig: Whether to generate and save a figure with the clustering of the texts. + - samples_per_cluster: The number of examples to use in the LLM as a sample of the cluster. + + Examples: + Generate labels for a set of texts using clustering: + + ```python + from distilabel.llms import InferenceEndpointsLLM + from distilabel.steps import UMAP, DBSCAN, TextClustering + from distilabel.pipeline import Pipeline + + ds_name = "argilla-warehouse/personahub-fineweb-edu-4-clustering-100k" + + with Pipeline(name="Text clustering dataset") as pipeline: + batch_size = 500 + + ds = load_dataset(ds_name, split="train").select(range(10000)) + loader = make_generator_step(ds, batch_size=batch_size, repo_id=ds_name) + + umap = UMAP(n_components=2, metric="cosine") + dbscan = DBSCAN(eps=0.3, min_samples=30) + + text_clustering = TextClustering( + llm=InferenceEndpointsLLM( + model_id="meta-llama/Meta-Llama-3.1-70B-Instruct", + tokenizer_id="meta-llama/Meta-Llama-3.1-70B-Instruct", + ), + n=3, # 3 labels per example + query_title="Examples of Personas", + samples_per_cluster=10, + context=( + "Describe the main themes, topics, or categories that could describe the " + "following types of personas. All the examples of personas must share " + "the same set of labels." + ), + default_label="None", + savefig=True, + input_batch_size=8, + input_mappings={"text": "persona"}, + use_default_structured_output=True, + ) + + loader >> umap >> dbscan >> text_clustering + ``` + """ + + savefig: Optional[RuntimeParameter[bool]] = Field( + default=True, + description="Whether to generate and save a figure with the clustering of the texts.", + ) + samples_per_cluster: int = Field( + default=10, + description="The number of examples to use in the LLM as a sample of the cluster.", + ) + + @property + def inputs(self) -> List[str]: + """The input for the task are the same as those for `TextClassification` plus + the `projection` and `cluster_label` columns (which can be obtained from + UMAP + DBSCAN steps). + """ + return super().inputs + ["projection", "cluster_label"] + + @property + def outputs(self) -> List[str]: + """The output for the task is the `summary_label` and the `model_name`.""" + return ["summary_label", "model_name"] + + def load(self) -> None: + super().load() + if self.savefig and (importlib.util.find_spec("matplotlib") is None): + raise ImportError( + "`matplotlib` package is not installed. Please install it using `pip install matplotlib`." + ) + + def _save_figure( + self, + data: pd.DataFrame, + cluster_centers: Dict[str, Tuple[float, float]], + cluster_summaries: Dict[int, str], + ) -> None: + """Saves the figure starting from the dataframe, using matplotlib. + + Args: + data: pd.DataFrame with the columns 'X', 'Y' and 'labels' representing + the projections and the label of each text respectively. + cluster_centers: Dictionary mapping from each label the center of a cluster, + to help with the placement of the annotations. + cluster_summaries: The summaries of the clusters, obtained from the LLM. + """ + import matplotlib.pyplot as plt + + fig, ax = plt.subplots(figsize=(12, 8), dpi=300) + unique_labels = data["labels"].unique() + # Map of colors for each label (-1 is black) + colormap = dict( + zip(unique_labels, plt.cm.Spectral(np.linspace(0, 1, len(unique_labels)))) + ) + colormap[-1] = np.array([0, 0, 0, 0]) + data["color"] = data["labels"].map(colormap) + + data.plot( + kind="scatter", + x="X", + y="Y", + c="color", + s=0.75, + alpha=0.8, + linewidth=0.4, + ax=ax, + colorbar=False, + ) + + for label in cluster_summaries.keys(): + if label == -1: + continue + summary = str(cluster_summaries[label]) # These are obtained from the LLM + position = cluster_centers[label] + t = ax.text( + position[0], + position[1], + summary, + horizontalalignment="center", + verticalalignment="center", + fontsize=4, + ) + t.set_bbox( + { + "facecolor": "white", + "alpha": 0.9, + "linewidth": 0, + "boxstyle": "square,pad=0.1", + } + ) + + ax.set_axis_off() + # Save the plot as an artifact of the step + self.save_artifact( + name="Text clusters", + write_function=lambda path: fig.savefig(path / "figure_clustering.png"), + metadata={"type": "image", "library": "matplotlib"}, + ) + plt.close() + + def _create_figure( + self, + inputs: StepInput, + label2docs: Dict[int, List[str]], + cluster_summaries: Dict[int, str], + ) -> None: + """Creates a figure of the clustered texts and save it as an artifact. + + Args: + inputs: The inputs of the step, as we will extract information from them again. + label2docs: Map from each label to the list of documents (texts) that belong to that cluster. + cluster_summaries: The summaries of the clusters, obtained from the LLM. + labels: The labels of the clusters (integers representing each predicted class). + """ + self._logger.info("🖼️ Creating figure for the clusters...") + + labels = [] + projections = [] + id2cluster = {} + for i, input in enumerate(inputs): + label = input["cluster_label"] + id2cluster[i] = label + labels.append(label) + projections.append(input["projection"]) + + projections = np.array(projections) + + # Contains the placement of the cluster centers in the figure + cluster_centers: Dict[str, Tuple[float, float]] = {} + for label in label2docs.keys(): + x = np.mean([projections[doc, 0] for doc in label2docs[label]]) + y = np.mean([projections[doc, 1] for doc in label2docs[label]]) + cluster_centers[label] = (x, y) + + df = pd.DataFrame( + data={ + "X": projections[:, 0], + "Y": projections[:, 1], + "labels": labels, + } + ) + + self._save_figure( + df, cluster_centers=cluster_centers, cluster_summaries=cluster_summaries + ) + + def _prepare_input_texts( + self, + inputs: StepInput, + label2docs: Dict[int, List[int]], + unique_labels: List[int], + ) -> List[Dict[str, Union[str, int]]]: + """Prepares a batch of inputs to send to the LLM, with the examples of each cluster. + + Args: + inputs: Inputs from the step. + label2docs: Map from each label to the list of documents (texts) that + belong to that cluster. + unique_labels: The unique labels of the clusters. + + Returns: + The input texts to send to the LLM, with the examples of each cluster + prepared to be used in the prompt, and an additional key to store the + labels (that will be needed to find the data after the batches are + returned from the LLM). + """ + input_texts = [] + for label in range(unique_labels): # The label -1 is implicitly excluded + # Get the ids but remove possible duplicates, which could happen with bigger probability + # the bigger the number of examples requested, and the smaller the subset of examples + ids = set( + np.random.choice(label2docs[label], size=self.samples_per_cluster) + ) # Grab the number of examples + examples = [inputs[i]["text"] for i in ids] + input_text = { + "text": "\n\n".join( + [f"Example {i}:\n{t}" for i, t in enumerate(examples, start=1)] + ), + "__LABEL": label, + } + input_texts.append(input_text) + return input_texts + + def process(self, inputs: StepInput) -> "StepOutput": + labels = [input["cluster_label"] for input in inputs] + # -1 because -1 is the label for the unclassified + unique_labels = len(set(labels)) - 1 + # This will be the output of the LLM, the set of labels for each cluster + cluster_summaries: Dict[int, str] = {-1: self.default_label} + + # Map from label to list of documents, will use them to select examples from each cluster + label2docs = defaultdict(list) + for i, label in enumerate(labels): + label2docs[label].append(i) + + input_texts = self._prepare_input_texts(inputs, label2docs, unique_labels) + + # Send the texts in batches to the LLM, and get the labels for each cluster + for i, batched_inputs in enumerate(batched(input_texts, self.input_batch_size)): + self._logger.info(f"📦 Processing internal batch of inputs {i}...") + results = super().process(batched_inputs) + for result in next(results): # Extract the elements from the generator + cluster_summaries[result["__LABEL"]] = result["labels"] + + # Assign the labels to each text + for input in inputs: + input["summary_label"] = json.dumps( + cluster_summaries[input["cluster_label"]] + ) + + if self.savefig: + self._create_figure(inputs, label2docs, cluster_summaries) + + yield inputs diff --git a/src/distilabel/steps/clustering/umap.py b/src/distilabel/steps/clustering/umap.py new file mode 100644 index 0000000000..daeb37486d --- /dev/null +++ b/src/distilabel/steps/clustering/umap.py @@ -0,0 +1,164 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import importlib.util +from typing import TYPE_CHECKING, Any, List, Optional + +import numpy as np +from pydantic import Field, PrivateAttr + +from distilabel.mixins.runtime_parameters import RuntimeParameter +from distilabel.steps import ( + GlobalStep, + StepInput, +) + +if TYPE_CHECKING: + from umap import UMAP as _UMAP + + from distilabel.steps.typing import StepOutput + + +class UMAP(GlobalStep): + r"""UMAP is a general purpose manifold learning and dimension reduction algorithm. + + This is a `GlobalStep` that reduces the dimensionality of the embeddings using. Visit + the `TextClustering` step for an example of use. The trained model is saved as an artifact + when creating a distiset and pushing it to the Hugging Face Hub. + + Input columns: + - embedding (`List[float]`): The original embeddings we want to reduce the dimension. + + Output columns: + - projection (`List[float]`): Embedding reduced to the number of components specified, + the size of the new embeddings will be determined by the `n_components`. + + Categories: + - clustering + - text-classification + + References: + - [`UMAP repository`](https://github.com/lmcinnes/umap/tree/master) + - [`UMAP documentation`](https://umap-learn.readthedocs.io/en/latest/) + + Attributes: + - n_components: The dimension of the space to embed into. This defaults to 2 to + provide easy visualization (that's probably what you want), but can + reasonably be set to any integer value in the range 2 to 100. + - metric: The metric to use to compute distances in high dimensional space. + Visit UMAP's documentation for more information. Defaults to `euclidean`. + - n_jobs: The number of parallel jobs to run. Defaults to `8`. + - random_state: The random state to use for the UMAP algorithm. + + Runtime parameters: + - `n_components`: The dimension of the space to embed into. This defaults to 2 to + provide easy visualization (that's probably what you want), but can + reasonably be set to any integer value in the range 2 to 100. + - `metric`: The metric to use to compute distances in high dimensional space. + Visit UMAP's documentation for more information. Defaults to `euclidean`. + - `n_jobs`: The number of parallel jobs to run. Defaults to `8`. + - `random_state`: The random state to use for the UMAP algorithm. + + Citations: + ``` + @misc{mcinnes2020umapuniformmanifoldapproximation, + title={UMAP: Uniform Manifold Approximation and Projection for Dimension Reduction}, + author={Leland McInnes and John Healy and James Melville}, + year={2020}, + eprint={1802.03426}, + archivePrefix={arXiv}, + primaryClass={stat.ML}, + url={https://arxiv.org/abs/1802.03426}, + } + ``` + """ + + n_components: Optional[RuntimeParameter[int]] = Field( + default=2, + description=( + "The dimension of the space to embed into. This defaults to 2 to " + "provide easy visualization, but can reasonably be set to any " + "integer value in the range 2 to 100." + ), + ) + metric: Optional[RuntimeParameter[str]] = Field( + default="euclidean", + description=( + "The metric to use to compute distances in high dimensional space. " + "Visit UMAP's documentation for more information." + ), + ) + n_jobs: Optional[RuntimeParameter[int]] = Field( + default=8, description="The number of parallel jobs to run." + ) + random_state: Optional[RuntimeParameter[int]] = Field( + default=None, description="The random state to use for the UMAP algorithm." + ) + + _umap: Optional["_UMAP"] = PrivateAttr(None) + + def load(self) -> None: + super().load() + if importlib.util.find_spec("umap") is None: + raise ImportError( + "`umap` package is not installed. Please install it using `pip install umap-learn`." + ) + from umap import UMAP as _UMAP + + self._umap = _UMAP( + n_components=self.n_components, + metric=self.metric, + n_jobs=self.n_jobs, + random_state=self.random_state, + ) + + def unload(self) -> None: + self._umap = None + + @property + def inputs(self) -> List[str]: + return ["embedding"] + + @property + def outputs(self) -> List[str]: + return ["projection"] + + def _save_model(self, model: Any) -> None: + import joblib + + def save_model(path): + with open(str(path / "UMAP.joblib"), "wb") as f: + joblib.dump(model, f) + + self.save_artifact( + name="UMAP_model", + write_function=lambda path: save_model(path), + metadata={ + "n_components": self.n_components, + "metric": self.metric, + }, + ) + + def process(self, inputs: StepInput) -> "StepOutput": # type: ignore + # Shape of the embeddings is (n_samples, n_features) + embeddings = np.array([input["embedding"] for input in inputs]) + + self._logger.info("🏋️‍♀️ Start UMAP training...") + mapper = self._umap.fit(embeddings) + # Shape of the projection will be (n_samples, n_components) + for input, projection in zip(inputs, mapper.embedding_): + input["projection"] = projection + + self._save_model(mapper) + yield inputs diff --git a/src/distilabel/steps/generators/utils.py b/src/distilabel/steps/generators/utils.py index 27455119bd..49d27748b4 100644 --- a/src/distilabel/steps/generators/utils.py +++ b/src/distilabel/steps/generators/utils.py @@ -32,7 +32,7 @@ def make_generator_step( input_mappings: Optional[Dict[str, str]] = None, output_mappings: Optional[Dict[str, str]] = None, resources: StepResources = StepResources(), - repo_id: str = "placeholder", + repo_id: Optional[str] = "default_name", ) -> "GeneratorStep": """Helper method to create a `GeneratorStep` from a dataset, to simplify @@ -46,7 +46,6 @@ def make_generator_step( repo_id: The repository ID to use in the `LoadDataFromHub` step. This shouldn't be necessary, but in case of error, the dataset will try to be loaded using `load_dataset` internally. If that case happens, the `repo_id` will be used. - Defaults to `"placeholder"`. Raises: ValueError: If the format is different from the ones supported. diff --git a/src/distilabel/steps/tasks/__init__.py b/src/distilabel/steps/tasks/__init__.py index 7bd96c3ce0..eb90c6dbad 100644 --- a/src/distilabel/steps/tasks/__init__.py +++ b/src/distilabel/steps/tasks/__init__.py @@ -43,6 +43,7 @@ from distilabel.steps.tasks.self_instruct import SelfInstruct from distilabel.steps.tasks.sentence_transformers import GenerateSentencePair from distilabel.steps.tasks.structured_generation import StructuredGeneration +from distilabel.steps.tasks.text_classification import TextClassification from distilabel.steps.tasks.text_generation import ChatGeneration, TextGeneration from distilabel.steps.tasks.typing import ChatItem, ChatType from distilabel.steps.tasks.ultrafeedback import UltraFeedback @@ -75,6 +76,7 @@ "SelfInstruct", "GenerateSentencePair", "StructuredGeneration", + "TextClassification", "ChatGeneration", "TextGeneration", "ChatItem", diff --git a/src/distilabel/steps/tasks/base.py b/src/distilabel/steps/tasks/base.py index dcf704a807..a0afb74c32 100644 --- a/src/distilabel/steps/tasks/base.py +++ b/src/distilabel/steps/tasks/base.py @@ -235,10 +235,11 @@ def check_dependency(module_name: str) -> None: dependency = "outlines" structured_output = {"schema": schema} + if isinstance(self.llm, InferenceEndpointsLLM): + structured_output.update({"format": "json"}) # To determine instructor or outlines format - if not ( - isinstance(self.llm, AsyncLLM) - and not isinstance(self.llm, InferenceEndpointsLLM) + elif isinstance(self.llm, AsyncLLM) and not isinstance( + self.llm, InferenceEndpointsLLM ): dependency = "instructor" structured_output.update({"format": "json"}) diff --git a/src/distilabel/steps/tasks/text_classification.py b/src/distilabel/steps/tasks/text_classification.py new file mode 100644 index 0000000000..5d04b3b2db --- /dev/null +++ b/src/distilabel/steps/tasks/text_classification.py @@ -0,0 +1,378 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from textwrap import indent +from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union + +import orjson +from jinja2 import Template +from pydantic import BaseModel, Field, PositiveInt, PrivateAttr +from typing_extensions import override + +from distilabel.steps.tasks import Task + +if TYPE_CHECKING: + from distilabel.steps.tasks.typing import ChatType + + +TEXT_CLASSIFICATION_TEMPLATE: str = """\ +# Instruction +Please classify the {{ query_title.lower() }} by assigning the most appropriate labels. +Do not explain your reasoning or provide any additional commentary. +If the text is ambiguous or lacks sufficient information for classification, respond with "{{ default_label }}". +{{ labels_message }}{{ context}} +{{ available_labels }} +{{ examples }} + +## {{ query_title }} +``` +{{ text }} +``` + +## Output Format +Now, please give me the labels in JSON format, do not include any other text in your response: +``` +{ + "labels": {{ labels_format }} +} +``` +""".rstrip() + + +class TextClassification(Task): + r"""Classifies text into one or more categories or labels. + + This task can be used for text classification problems, where the goal is to assign + one or multiple labels to a given text. + It uses structured generation as per the reference paper by default, + it can help to generate more concise labels. See section 4.1 in the reference. + + Input columns: + - text (`str`): The reference text we want to obtain labels for. + + Output columns: + - labels (`Union[str, List[str]]`): The label or list of labels for the text. + - model_name (`str`): The name of the model used to generate the label/s. + + Categories: + - text-classification + + References: + - [`Let Me Speak Freely? A Study on the Impact of Format Restrictions on Performance of Large Language Models`](https://arxiv.org/abs/2408.02442) + + Attributes: + system_prompt: A prompt to display to the user before the task starts. Contains a default + message to make the model behave like a classifier specialist. + n: Number of labels to generate If only 1 is required, corresponds to a label + classification problem, if >1 it will intend return the "n" labels most representative + for the text. Defaults to 1. + context: Context to use when generating the labels. By default contains a generic message, + but can be used to customize the context for the task. + examples: List of examples to help the model understand the task, few shots. + available_labels: List of available labels to choose from when classifying the text, or + a dictionary with the labels and their descriptions. + default_label: Default label to use when the text is ambiguous or lacks sufficient information for + classification. Can be a list in case of multiple labels (n>1). + + Examples: + Assigning a sentiment to a text: + + ```python + from distilabel.steps.tasks import TextClassification + from distilabel.llms.huggingface import InferenceEndpointsLLM + + llm = InferenceEndpointsLLM( + model_id="meta-llama/Meta-Llama-3.1-70B-Instruct", + tokenizer_id="meta-llama/Meta-Llama-3.1-70B-Instruct", + ) + + text_classification = TextClassification( + llm=llm, + context="You are an AI system specialized in assigning sentiment to movies.", + available_labels=["positive", "negative"], + ) + + text_classification.load() + + result = next( + text_classification.process( + [{"text": "This was a masterpiece. Not completely faithful to the books, but enthralling from beginning to end. Might be my favorite of the three."}] + ) + ) + # result + # [{'text': 'This was a masterpiece. Not completely faithful to the books, but enthralling from beginning to end. Might be my favorite of the three.', + # 'labels': 'positive', + # 'distilabel_metadata': {'raw_output_text_classification_0': '{\n "labels": "positive"\n}', + # 'raw_input_text_classification_0': [{'role': 'system', + # 'content': 'You are an AI system specialized in generating labels to classify pieces of text. Your sole purpose is to analyze the given text and provide appropriate classification labels.'}, + # {'role': 'user', + # 'content': '# Instruction\nPlease classify the user query by assigning the most appropriate labels.\nDo not explain your reasoning or provide any additional commentary.\nIf the text is ambiguous or lacks sufficient information for classification, respond with "Unclassified".\nProvide the label that best describes the text.\nYou are an AI system specialized in assigning sentiment to movie the user queries.\n## Labeling the user input\nUse the available labels to classify the user query. Analyze the context of each label specifically:\navailable_labels = [\n "positive", # The text shows positive sentiment\n "negative", # The text shows negative sentiment\n]\n\n\n## User Query\n```\nThis was a masterpiece. Not completely faithful to the books, but enthralling from beginning to end. Might be my favorite of the three.\n```\n\n## Output Format\nNow, please give me the labels in JSON format, do not include any other text in your response:\n```\n{\n "labels": "label"\n}\n```'}]}, + # 'model_name': 'meta-llama/Meta-Llama-3.1-70B-Instruct'}] + ``` + + Assigning predefined labels with specified descriptions: + + ```python + from distilabel.steps.tasks import TextClassification + + text_classification = TextClassification( + llm=llm, + n=1, + context="Determine the intent of the text.", + available_labels={ + "complaint": "A statement expressing dissatisfaction or annoyance about a product, service, or experience. It's a negative expression of discontent, often with the intention of seeking a resolution or compensation.", + "inquiry": "A question or request for information about a product, service, or situation. It's a neutral or curious expression seeking clarification or details.", + "feedback": "A statement providing evaluation, opinion, or suggestion about a product, service, or experience. It can be positive, negative, or neutral, and is often intended to help improve or inform.", + "praise": "A statement expressing admiration, approval, or appreciation for a product, service, or experience. It's a positive expression of satisfaction or delight, often with the intention of encouraging or recommending." + }, + query_title="Customer Query", + ) + + text_classification.load() + + result = next( + text_classification.process( + [{"text": "Can you tell me more about your return policy?"}] + ) + ) + # result + # [{'text': 'Can you tell me more about your return policy?', + # 'labels': 'inquiry', + # 'distilabel_metadata': {'raw_output_text_classification_0': '{\n "labels": "inquiry"\n}', + # 'raw_input_text_classification_0': [{'role': 'system', + # 'content': 'You are an AI system specialized in generating labels to classify pieces of text. Your sole purpose is to analyze the given text and provide appropriate classification labels.'}, + # {'role': 'user', + # 'content': '# Instruction\nPlease classify the customer query by assigning the most appropriate labels.\nDo not explain your reasoning or provide any additional commentary.\nIf the text is ambiguous or lacks sufficient information for classification, respond with "Unclassified".\nProvide the label that best describes the text.\nDetermine the intent of the text.\n## Labeling the user input\nUse the available labels to classify the user query. Analyze the context of each label specifically:\navailable_labels = [\n "complaint", # A statement expressing dissatisfaction or annoyance about a product, service, or experience. It\'s a negative expression of discontent, often with the intention of seeking a resolution or compensation.\n "inquiry", # A question or request for information about a product, service, or situation. It\'s a neutral or curious expression seeking clarification or details.\n "feedback", # A statement providing evaluation, opinion, or suggestion about a product, service, or experience. It can be positive, negative, or neutral, and is often intended to help improve or inform.\n "praise", # A statement expressing admiration, approval, or appreciation for a product, service, or experience. It\'s a positive expression of satisfaction or delight, often with the intention of encouraging or recommending.\n]\n\n\n## Customer Query\n```\nCan you tell me more about your return policy?\n```\n\n## Output Format\nNow, please give me the labels in JSON format, do not include any other text in your response:\n```\n{\n "labels": "label"\n}\n```'}]}, + # 'model_name': 'meta-llama/Meta-Llama-3.1-70B-Instruct'}] + ``` + + Free multi label classification without predefined labels: + + ```python + from distilabel.steps.tasks import TextClassification + + text_classification = TextClassification( + llm=llm, + n=3, + context=( + "Describe the main themes, topics, or categories that could describe the " + "following type of persona." + ), + query_title="Example of Persona", + ) + + text_classification.load() + + result = next( + text_classification.process( + [{"text": "A historian or curator of Mexican-American history and culture focused on the cultural, social, and historical impact of the Mexican presence in the United States."}] + ) + ) + # result + # [{'text': 'A historian or curator of Mexican-American history and culture focused on the cultural, social, and historical impact of the Mexican presence in the United States.', + # 'labels': ['Historical Researcher', + # 'Cultural Specialist', + # 'Ethnic Studies Expert'], + # 'distilabel_metadata': {'raw_output_text_classification_0': '{\n "labels": ["Historical Researcher", "Cultural Specialist", "Ethnic Studies Expert"]\n}', + # 'raw_input_text_classification_0': [{'role': 'system', + # 'content': 'You are an AI system specialized in generating labels to classify pieces of text. Your sole purpose is to analyze the given text and provide appropriate classification labels.'}, + # {'role': 'user', + # 'content': '# Instruction\nPlease classify the example of persona by assigning the most appropriate labels.\nDo not explain your reasoning or provide any additional commentary.\nIf the text is ambiguous or lacks sufficient information for classification, respond with "Unclassified".\nProvide a list of 3 labels that best describe the text.\nDescribe the main themes, topics, or categories that could describe the following type of persona.\nUse clear, widely understood terms for labels.Avoid overly specific or obscure labels unless the text demands it.\n\n\n## Example of Persona\n```\nA historian or curator of Mexican-American history and culture focused on the cultural, social, and historical impact of the Mexican presence in the United States.\n```\n\n## Output Format\nNow, please give me the labels in JSON format, do not include any other text in your response:\n```\n{\n "labels": ["label_0", "label_1", "label_2"]\n}\n```'}]}, + # 'model_name': 'meta-llama/Meta-Llama-3.1-70B-Instruct'}] + ``` + """ + + system_prompt: Optional[str] = ( + "You are an AI system specialized in generating labels to classify pieces of text. " + "Your sole purpose is to analyze the given text and provide appropriate classification labels." + ) + n: PositiveInt = Field( + default=1, + description="Number of labels to generate. Defaults to 1.", + ) + context: Optional[str] = Field( + default="Generate concise, relevant labels that accurately represent the text's main themes, topics, or categories.", + description="Context to use when generating the labels.", + ) + examples: Optional[List[str]] = Field( + default=None, + description="List of examples to help the model understand the task, few shots.", + ) + available_labels: Optional[Union[List[str], Dict[str, str]]] = Field( + default=None, + description=( + "List of available labels to choose from when classifying the text, or " + "a dictionary with the labels and their descriptions." + ), + ) + default_label: Optional[Union[str, List[str]]] = Field( + default="Unclassified", + description=( + "Default label to use when the text is ambiguous or lacks sufficient information for " + "classification. Can be a list in case of multiple labels (n>1)." + ), + ) + query_title: str = Field( + default="User Query", + description="Title of the query used to show the example/s to classify.", + ) + use_default_structured_output: bool = True + + _template: Optional[Template] = PrivateAttr(default=None) + + def load(self) -> None: + super().load() + self._template = Template(TEXT_CLASSIFICATION_TEMPLATE) + self._labels_format: str = ( + '"label"' + if self.n == 1 + else "[" + ", ".join([f'"label_{i}"' for i in range(self.n)]) + "]" + ) + self._labels_message: str = ( + "Provide the label that best describes the text." + if self.n == 1 + else f"Provide a list of {self.n} labels that best describe the text." + ) + self._available_labels_message: str = self._get_available_labels_message() + self._examples: str = self._get_examples_message() + + def _get_available_labels_message(self) -> str: + """Prepares the message to display depending on the available labels (if any), + and whether the labels have a specific context. + """ + if self.available_labels is None: + return ( + "Use clear, widely understood terms for labels." + "Avoid overly specific or obscure labels unless the text demands it." + ) + + msg = ( + "## Labeling the user input\n" + "Use the available labels to classify the user query{label_context}:\n" + "available_labels = {available_labels}" + ) + if isinstance(self.available_labels, list): + specific_msg = ( + "[\n" + + indent( + "".join([f'"{label}",\n' for label in self.available_labels]), + prefix=" " * 4, + ) + + "]" + ) + return msg.format(label_context="", available_labels=specific_msg) + + elif isinstance(self.available_labels, dict): + specific_msg = "" + for label, description in self.available_labels.items(): + specific_msg += indent( + f'"{label}", # {description}' + "\n", prefix=" " * 4 + ) + + specific_msg = "[\n" + specific_msg + "]" + return msg.format( + label_context=". Analyze the context of each label specifically", + available_labels=specific_msg, + ) + + def _get_examples_message(self) -> str: + """Prepares the message to display depending on the examples provided.""" + if self.examples is None: + return "" + + examples_msg = "\n".join([f"- {ex}" for ex in self.examples]) + + return ( + "\n## Examples\n" + "Here are some examples to help you understand the task:\n" + f"{examples_msg}" + ) + + @property + def inputs(self) -> List[str]: + """The input for the task is the `instruction`.""" + return ["text"] + + @property + def outputs(self) -> List[str]: + """The output for the task is the `generation` and the `model_name`.""" + return ["labels", "model_name"] + + def format_input(self, input: Dict[str, Any]) -> "ChatType": + """The input is formatted as a `ChatType` assuming that the instruction + is the first interaction from the user within a conversation.""" + messages = [ + { + "role": "user", + "content": self._template.render( # type: ignore + context=f"\n{self.context}", + labels_message=self._labels_message, + available_labels=self._available_labels_message, + examples=self._examples, + default_label=self.default_label, + labels_format=self._labels_format, + query_title=self.query_title, + text=input["text"], + ), + }, + ] + if self.system_prompt: + messages.insert(0, {"role": "system", "content": self.system_prompt}) + return messages + + def format_output( + self, output: Union[str, None], input: Union[Dict[str, Any], None] = None + ) -> Dict[str, Any]: + """The output is formatted as a dictionary with the `generation`. The `model_name` + will be automatically included within the `process` method of `Task`.""" + return self._format_structured_output(output) + + @override + def get_structured_output(self) -> Dict[str, Any]: + """Creates the json schema to be passed to the LLM, to enforce generating + a dictionary with the output which can be directly parsed as a python dictionary. + + Returns: + JSON Schema of the response to enforce. + """ + if self.n > 1: + + class MultiLabelSchema(BaseModel): + labels: List[str] + + return MultiLabelSchema.model_json_schema() + + class SingleLabelSchema(BaseModel): + labels: str + + return SingleLabelSchema.model_json_schema() + + def _format_structured_output( + self, output: str + ) -> Dict[str, Union[str, List[str]]]: + """Parses the structured response, which should correspond to a dictionary + with the `labels`, and either a string or a list of strings with the labels. + + Args: + output: The output from the `LLM`. + + Returns: + Formatted output. + """ + try: + return orjson.loads(output) + except orjson.JSONDecodeError: + if self.n > 1: + return {"labels": [None for _ in range(self.n)]} + return {"labels": None} diff --git a/src/distilabel/utils/itertools.py b/src/distilabel/utils/itertools.py index 2555f3b262..34accced2b 100644 --- a/src/distilabel/utils/itertools.py +++ b/src/distilabel/utils/itertools.py @@ -12,11 +12,26 @@ # See the License for the specific language governing permissions and # limitations under the License. +import sys from itertools import zip_longest from typing import Any, Iterable, Literal, Tuple, TypeVar T = TypeVar("T") +# https://docs.python.org/3/library/itertools.html#itertools.batched +if sys.version_info >= (3, 12): + from itertools import batched +else: + from itertools import islice + + def batched(iterable: Iterable[T], n: int) -> Iterable[T]: + # batched('ABCDEFG', 3) → ABC DEF G + if n < 1: + raise ValueError("n must be at least one") + iterator = iter(iterable) + while batch := tuple(islice(iterator, n)): + yield batch + # Copy pasted from https://docs.python.org/3/library/itertools.html#itertools-recipes # Just added the type hints and use `if`s instead of `match` diff --git a/src/distilabel/utils/mkdocs/components_gallery.py b/src/distilabel/utils/mkdocs/components_gallery.py index a7dba7e7da..3798b6f90a 100644 --- a/src/distilabel/utils/mkdocs/components_gallery.py +++ b/src/distilabel/utils/mkdocs/components_gallery.py @@ -87,6 +87,8 @@ "text-generation": ":material-text-box-edit:", "text-manipulation": ":material-receipt-text-edit:", "columns": ":material-table-column:", + "text-classification": ":material-label:", + "clustering": ":material-scatter-plot:", } diff --git a/tests/unit/steps/clustering/__init__.py b/tests/unit/steps/clustering/__init__.py new file mode 100644 index 0000000000..20ce00bda7 --- /dev/null +++ b/tests/unit/steps/clustering/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + diff --git a/tests/unit/steps/clustering/test_dbscan.py b/tests/unit/steps/clustering/test_dbscan.py new file mode 100644 index 0000000000..d4f62a3fae --- /dev/null +++ b/tests/unit/steps/clustering/test_dbscan.py @@ -0,0 +1,39 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +from distilabel.steps.clustering.dbscan import DBSCAN + + +class TestDBSCAN: + def test_process(self) -> None: + step = DBSCAN(n_jobs=1, eps=0.5, min_samples=5) + step.load() + + results = next( + step.process( + inputs=[ + {"projection": [0.1, -0.4]}, + {"projection": [-0.3, 0.9]}, + {"projection": [0.6, 0.2]}, + {"projection": [-0.2, -0.6]}, + {"projection": [0.9, 0.1]}, + {"projection": [0.4, -0.7]}, + {"projection": [-0.5, 0.3]}, + {"projection": [0.7, 0.5]}, + {"projection": [-0.1, -0.9]}, + ] + ) + ) + assert all(result["cluster_label"] == -1 for result in results) diff --git a/tests/unit/steps/clustering/test_text_clustering.py b/tests/unit/steps/clustering/test_text_clustering.py new file mode 100644 index 0000000000..4b2da96d40 --- /dev/null +++ b/tests/unit/steps/clustering/test_text_clustering.py @@ -0,0 +1,75 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import json +from typing import TYPE_CHECKING + +import pytest + +from distilabel.steps.clustering.text_clustering import TextClustering +from tests.unit.conftest import DummyAsyncLLM + +if TYPE_CHECKING: + from distilabel.llms.typing import GenerateOutput + from distilabel.steps.tasks.typing import FormattedInput + + +class ClusteringLLM(DummyAsyncLLM): + n: int = 1 + + async def agenerate( # type: ignore + self, input: "FormattedInput", num_generations: int = 1 + ) -> "GenerateOutput": + if self.n == 1: + return [json.dumps({"labels": "label"}) for _ in range(num_generations)] + return [ + json.dumps({"labels": ["label" for _ in range(self.n)]}) + for _ in range(self.n) + ] + + +class TestTextClustering: + @pytest.mark.parametrize("n", [1, 3]) + def test_process(self, n: int) -> None: + step = TextClustering( + llm=ClusteringLLM(n=n), + n=n, + samples_per_cluster=2, + savefig=False, + ) + step.load() + + results = next( + step.process( + inputs=[ + {"projection": [0.1, -0.4], "cluster_label": -1, "text": "hello"}, + {"projection": [-0.3, 0.9], "cluster_label": -1, "text": "hello"}, + {"projection": [0.6, 0.2], "cluster_label": 0, "text": "hello"}, + {"projection": [-0.2, -0.6], "cluster_label": 0, "text": "hello"}, + {"projection": [0.9, 0.1], "cluster_label": 0, "text": "hello"}, + {"projection": [0.4, -0.7], "cluster_label": 1, "text": "hello"}, + {"projection": [-0.5, 0.3], "cluster_label": 1, "text": "hello"}, + {"projection": [0.7, 0.5], "cluster_label": 2, "text": "hello"}, + {"projection": [-0.1, -0.9], "cluster_label": 2, "text": "hello"}, + ] + ) + ) + for r in results: + if r["cluster_label"] == -1: + assert r["summary_label"] == json.dumps("Unclassified") + else: + if n == 1: + assert r["summary_label"] == json.dumps("label") + else: + assert r["summary_label"] == json.dumps(["label"] * n) diff --git a/tests/unit/steps/clustering/test_umap.py b/tests/unit/steps/clustering/test_umap.py new file mode 100644 index 0000000000..3ab252fd24 --- /dev/null +++ b/tests/unit/steps/clustering/test_umap.py @@ -0,0 +1,42 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import numpy as np + +from distilabel.steps.clustering.umap import UMAP + + +class TestUMAP: + def test_process(self) -> None: + n_components = 2 + step = UMAP(n_jobs=1, n_components=n_components) + step.load() + + results = next( + step.process( + inputs=[ + {"embedding": [0.1, -0.4, 0.7, 0.2]}, + {"embedding": [-0.3, 0.9, 0.1, -0.5]}, + {"embedding": [0.6, 0.2, -0.1, 0.8]}, + {"embedding": [-0.2, -0.6, 0.4, 0.3]}, + {"embedding": [0.9, 0.1, -0.3, -0.2]}, + {"embedding": [0.4, -0.7, 0.6, 0.1]}, + {"embedding": [-0.5, 0.3, -0.2, 0.9]}, + {"embedding": [0.7, 0.5, -0.4, -0.1]}, + {"embedding": [-0.1, -0.9, 0.8, 0.6]}, + ] + ) + ) + assert all(isinstance(result["projection"], np.ndarray) for result in results) + assert all(len(result["projection"]) == n_components for result in results) diff --git a/tests/unit/steps/tasks/test_text_classification.py b/tests/unit/steps/tasks/test_text_classification.py new file mode 100644 index 0000000000..e5af171b33 --- /dev/null +++ b/tests/unit/steps/tasks/test_text_classification.py @@ -0,0 +1,140 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import json +from typing import TYPE_CHECKING, Dict, List, Optional, Union + +import pytest + +from distilabel.steps.tasks.text_classification import TextClassification +from tests.unit.conftest import DummyAsyncLLM + +if TYPE_CHECKING: + from distilabel.llms.typing import GenerateOutput + from distilabel.steps.tasks.typing import FormattedInput + + +class TextClassificationLLM(DummyAsyncLLM): + n: int = 1 + + async def agenerate( # type: ignore + self, input: "FormattedInput", num_generations: int = 1 + ) -> "GenerateOutput": + if self.n == 1: + return [json.dumps({"labels": "label"}) for _ in range(num_generations)] + return [ + json.dumps({"labels": [f"label_{i}" for i in range(self.n)]}) + for _ in range(num_generations) + ] + + +class TestTextClassification: + @pytest.mark.parametrize( + "n, context, examples, available_labels, default_label, query_title", + [ + (1, "context", None, None, "Unclassified", "User Query"), + (1, "", ["example"], ["label1", "label2"], "default", "User Query"), + ( + 1, + "", + ["example"], + {"label1": "explanation 1", "label2": "explanation 2"}, + "default", + "User Query", + ), + ( + 3, + "", + ["example", "other example"], + None, + "default", + "User Query", + ), + ], + ) + def test_format_input( + self, + n: int, + context: str, + examples: Optional[List[str]], + available_labels: Optional[Union[List[str], Dict[str, str]]], + default_label: Optional[Union[str, List[str]]], + query_title: str, + ) -> None: + task = TextClassification( + llm=DummyAsyncLLM(), + n=n, + context=context, + examples=examples, + available_labels=available_labels, + default_label=default_label, + query_title=query_title, + ) + task.load() + + result = task.format_input({"text": "SAMPLE_TEXT"}) + content = result[1]["content"] + + assert f'respond with "{default_label}"' in content + assert "## User Query\n```\nSAMPLE_TEXT\n```" in content + assert f'respond with "{default_label}"' in content + if n == 1: + assert "Provide the label that best describes the text." in content + assert '```\n{\n "labels": "label"\n}\n```' in content + else: + assert ( + f"Provide a list of {n} labels that best describe the text." in content + ) + assert ( + '```\n{\n "labels": ["label_0", "label_1", "label_2"]\n}\n```' + in content + ) + if available_labels: + if isinstance(available_labels, list): + assert 'Use the available labels to classify the user query:\navailable_labels = [\n "label1",\n "label2"\n]' + if isinstance(available_labels, dict): + assert 'Use the available labels to classify the user query:\navailable_labels = [\n "label1", # explanation 1\n "label2", # explanation 2\n]' + + if examples: + assert ( + "## Examples\nHere are some examples to help you understand the task:\n- example\n" + in content + ) + else: + assert "## Examples" not in content + assert ( + f"Please classify the {query_title.lower()} by assigning the most appropriate labels." + in content + ) + assert f"## {query_title}" in content + + @pytest.mark.parametrize( + "n, expected", + [ + (1, json.dumps({"labels": "label"})), + (3, json.dumps({"labels": ["label_0", "label_1", "label_2"]})), + ], + ) + def test_process(self, n: int, expected: str) -> None: + task = TextClassification( + llm=TextClassificationLLM(n=n), n=n, use_default_structured_output=True + ) + task.load() + result = next(task.process([{"text": "SAMPLE_TEXT"}])) + assert result[0]["text"] == "SAMPLE_TEXT" + assert result[0]["labels"] == json.loads(expected)["labels"] + assert ( + result[0]["distilabel_metadata"]["raw_output_text_classification_0"] + == expected + ) From af08b5911ab5feaf4d6285cf6de06420a13ec831 Mon Sep 17 00:00:00 2001 From: Agus Date: Mon, 16 Sep 2024 06:26:36 +0200 Subject: [PATCH 53/82] [FEATURE] Simplify customizing the `TextGeneration` task with custom prompts (#974) * Simplify customization of TextGeneration * Update tests loading the task * Extra tests for the new functionality * Added examples and extra checks * Include missing attributes and info in docstrings * Fix model_post_init call to super * Force a template for the task * Trying to fix the pickling error * It's unused, but the argument of generate was wrongly spelled * Checking if works without an instance of Template * Remove template in unload to fix error on offline batch generation --- src/distilabel/steps/tasks/text_generation.py | 217 +++++++++++++++--- .../test_offline_batch_generation.py | 2 +- .../unit/steps/tasks/test_text_generation.py | 76 ++++++ 3 files changed, 268 insertions(+), 27 deletions(-) diff --git a/src/distilabel/steps/tasks/text_generation.py b/src/distilabel/steps/tasks/text_generation.py index 2042539a4f..a8b2048e54 100644 --- a/src/distilabel/steps/tasks/text_generation.py +++ b/src/distilabel/steps/tasks/text_generation.py @@ -12,9 +12,11 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import TYPE_CHECKING, Any, Dict, List, Union +import re +from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union -from pydantic import Field +from jinja2 import Template +from pydantic import Field, PrivateAttr from distilabel.errors import DistilabelUserError from distilabel.steps.tasks.base import Task @@ -26,23 +28,31 @@ class TextGeneration(Task): - """Simple text generation with an `LLM` given an instruction. + """Text generation with an `LLM` given a prompt. - `TextGeneration` is a pre-defined task that defines the `instruction` as the input - and `generation` as the output. This task is used to generate text based on the input - instruction. The model_name is also returned as part of the output in order to enhance it. + `TextGeneration` is a pre-defined task that allows passing a custom prompt using the + Jinja2 syntax. By default, a `instruction` is expected in the inputs, but the using + `template` and `columns` attributes one can define a custom prompt and columns expected + from the text. This task should be good enough for tasks that don't need post-processing + of the responses generated by the LLM. Attributes: system_prompt: The system prompt to use in the generation. If not provided, then it will check if the input row has a column named `system_prompt` and use it. If not, then no system prompt will be used. Defaults to `None`. + template: The template to use for the generation. It must follow the Jinja2 template + syntax. If not provided, it will assume the text passed is an instruction and + construct the appropriate template. + columns: A string with the column, or a list with columns expected in the template. + Take a look at the examples for more information. Defaults to `instruction`. use_system_prompt: DEPRECATED. To be removed in 1.5.0. Whether to use the system prompt in the generation. Defaults to `True`, which means that if the column `system_prompt` is defined within the input batch, then the `system_prompt` will be used, otherwise, it will be ignored. Input columns: - - instruction (`str`): The instruction to generate text from. + - dynamic (determined by `columns` attribute): By default will be set to `instruction`. + The columns can point both to a `str` or a `List[str]` to be used in the template. Output columns: - generation (`str`): The generated text. @@ -51,6 +61,9 @@ class TextGeneration(Task): Categories: - text-generation + References: + - [Jinja2 Template Designer Documentation](https://jinja.palletsprojects.com/en/3.1.x/templates/) + Examples: Generate text from an instruction: @@ -61,7 +74,7 @@ class TextGeneration(Task): # Consider this as a placeholder for your actual LLM. text_gen = TextGeneration( llm=InferenceEndpointsLLM( - model_id="mistralai/Mistral-7B-Instruct-v0.2", + model_id="meta-llama/Meta-Llama-3.1-70B-Instruct", ) ) @@ -76,41 +89,193 @@ class TextGeneration(Task): # [ # { # 'instruction': 'your instruction', - # 'model_name': 'mistralai/Mistral-7B-Instruct-v0.2', + # 'model_name': 'meta-llama/Meta-Llama-3.1-70B-Instruct', # 'generation': 'generation', # } # ] ``` + + Use a custom template to generate text: + + ```python + from distilabel.steps.tasks import TextGeneration + from distilabel.llms.huggingface import InferenceEndpointsLLM + + CUSTOM_TEMPLATE = '''Document: + {{ document }} + + Question: {{ question }} + + Please provide a clear and concise answer to the question based on the information in the document and your general knowledge: + '''.rstrip() + + text_gen = TextGeneration( + llm=InferenceEndpointsLLM( + model_id="meta-llama/Meta-Llama-3.1-70B-Instruct", + ), + system_prompt="You are a helpful AI assistant. Your task is to answer the following question based on the provided document. If the answer is not explicitly stated in the document, use your knowledge to provide the most relevant and accurate answer possible. If you cannot answer the question based on the given information, state that clearly.", + template=CUSTOM_TEMPLATE, + columns=["document", "question"], + ) + + text_gen.load() + + result = next( + text_gen.process( + [ + { + "document": "The Great Barrier Reef, located off the coast of Australia, is the world's largest coral reef system. It stretches over 2,300 kilometers and is home to a diverse array of marine life, including over 1,500 species of fish. However, in recent years, the reef has faced significant challenges due to climate change, with rising sea temperatures causing coral bleaching events.", + "question": "What is the main threat to the Great Barrier Reef mentioned in the document?" + } + ] + ) + ) + # result + # [ + # { + # 'document': 'The Great Barrier Reef, located off the coast of Australia, is the world's largest coral reef system. It stretches over 2,300 kilometers and is home to a diverse array of marine life, including over 1,500 species of fish. However, in recent years, the reef has faced significant challenges due to climate change, with rising sea temperatures causing coral bleaching events.', + # 'question': 'What is the main threat to the Great Barrier Reef mentioned in the document?', + # 'model_name': 'meta-llama/Meta-Llama-3.1-70B-Instruct', + # 'generation': 'According to the document, the main threat to the Great Barrier Reef is climate change, specifically rising sea temperatures causing coral bleaching events.', + # } + # ] + ``` + + Few shot learning with different system prompts: + + ```python + from distilabel.steps.tasks import TextGeneration + from distilabel.llms.huggingface import InferenceEndpointsLLM + + CUSTOM_TEMPLATE = '''Generate a clear, single-sentence instruction based on the following examples: + + {% for example in examples %} + Example {{ loop.index }}: + Instruction: {{ example }} + + {% endfor %} + Now, generate a new instruction in a similar style: + '''.rstrip() + + text_gen = TextGeneration( + llm=InferenceEndpointsLLM( + model_id="meta-llama/Meta-Llama-3.1-70B-Instruct", + ), + template=CUSTOM_TEMPLATE, + columns="examples", + ) + + text_gen.load() + + result = next( + text_gen.process( + [ + { + "examples": ["This is an example", "Another relevant example"], + "system_prompt": "You are an AI assistant specialised in cybersecurity and computing in general, you make your point clear without any explanations." + } + ] + ) + ) + # result + # [ + # { + # 'examples': ['This is an example', 'Another relevant example'], + # 'system_prompt': 'You are an AI assistant specialised in cybersecurity and computing in general, you make your point clear without any explanations.', + # 'model_name': 'meta-llama/Meta-Llama-3.1-70B-Instruct', + # 'generation': 'Disable the firewall on the router', + # } + # ] + ``` """ system_prompt: Union[str, None] = None use_system_prompt: bool = Field(default=True, deprecated=True) + template: str = Field( + default="{{ instruction }}", + description=( + "This is a template or prompt to use for the generation. " + "If not provided, it is assumed a `instruction` is placed in the inputs, " + "to be used as is." + ), + ) + columns: Union[str, List[str]] = Field( + default="instruction", + description=( + "Custom column or list of columns to include in the input. " + "If a `template` is provided which needs custom column names, " + "then they should be provided here. By default it will use `instruction`." + ), + ) _can_be_used_with_offline_batch_generation = True + _template: Optional["Template"] = PrivateAttr(default=...) + + def model_post_init(self, __context: Any) -> None: + self.columns = [self.columns] if isinstance(self.columns, str) else self.columns + super().model_post_init(__context) + + def load(self) -> None: + super().load() + + def check_column_in_template(column, template): + pattern = ( + r"(?:{%.*?\b" + + re.escape(column) + + r"\b.*?%}|{{\s*" + + re.escape(column) + + r"\s*}})" + ) + if not re.search(pattern, template): + raise DistilabelUserError( + ( + f"You required column name '{column}', but is not present in the template, " + "ensure the 'columns' match with the 'template' to avoid errors." + ), + page="components-gallery/tasks/textgeneration/", + ) + + for column in self.columns: + check_column_in_template(column, self.template) + + self._template = Template(self.template) + + def unload(self) -> None: + super().unload() + self._template = None @property def inputs(self) -> "StepColumns": - """The input for the task is the `instruction`.""" - return {"instruction": True, "system_prompt": False} + """The input for the task is the `instruction` by default, or the `columns` given as input.""" + columns = {column: True for column in self.columns} + columns["system_prompt"] = False + return columns + + def _prepare_message_content(self, input: Dict[str, Any]) -> "ChatType": + """Prepares the content for the template and returns the formatted messages.""" + fields = {column: input[column] for column in self.columns} + return [{"role": "user", "content": self._template.render(**fields)}] def format_input(self, input: Dict[str, Any]) -> "ChatType": """The input is formatted as a `ChatType` assuming that the instruction is the first interaction from the user within a conversation.""" - - if is_openai_format(input["instruction"]): - raise DistilabelUserError( - "Providing `instruction` formatted as an OpenAI chat / conversation is" - " deprecated, you should use `ChatGeneration` with `messages` as input instead.", - page="components-gallery/tasks/textgeneration/", - ) - - if not isinstance(input["instruction"], str): - raise DistilabelUserError( - f"Input `instruction` must be a string. Got: {input['instruction']}.", - page="components-gallery/tasks/textgeneration/", - ) - - messages = [{"role": "user", "content": input["instruction"]}] + # Handle the previous expected errors, in case of custom columns there's more freedom + # and we cannot check it so easily. + if self.columns == ["instruction"]: + if is_openai_format(input["instruction"]): + raise DistilabelUserError( + "Providing `instruction` formatted as an OpenAI chat / conversation is" + " deprecated, you should use `ChatGeneration` with `messages` as input instead.", + page="components-gallery/tasks/textgeneration/", + ) + + if not isinstance(input["instruction"], str): + raise DistilabelUserError( + f"Input `instruction` must be a string. Got: {input['instruction']}.", + page="components-gallery/tasks/textgeneration/", + ) + + messages = self._prepare_message_content(input) row_system_prompt = input.get("system_prompt") if row_system_prompt: diff --git a/tests/integration/test_offline_batch_generation.py b/tests/integration/test_offline_batch_generation.py index d7afb0a47f..a9fe880ff7 100644 --- a/tests/integration/test_offline_batch_generation.py +++ b/tests/integration/test_offline_batch_generation.py @@ -35,7 +35,7 @@ def model_name(self) -> str: return "test" def generate( # type: ignore - self, input: "FormattedInput", num_generations: int = 1 + self, inputs: "FormattedInput", num_generations: int = 1 ) -> "GenerateOutput": return ["output" for _ in range(num_generations)] diff --git a/tests/unit/steps/tasks/test_text_generation.py b/tests/unit/steps/tasks/test_text_generation.py index 9cadc4e960..2a6abefb22 100644 --- a/tests/unit/steps/tasks/test_text_generation.py +++ b/tests/unit/steps/tasks/test_text_generation.py @@ -12,8 +12,11 @@ # See the License for the specific language governing permissions and # limitations under the License. +from typing import Any, Dict, List, Union + import pytest +from distilabel.errors import DistilabelUserError from distilabel.pipeline.local import Pipeline from distilabel.steps.tasks.text_generation import ChatGeneration, TextGeneration from tests.unit.conftest import DummyAsyncLLM @@ -23,6 +26,7 @@ class TestTextGeneration: def test_format_input(self) -> None: llm = DummyAsyncLLM() task = TextGeneration(name="task", llm=llm) + task.load() assert task.format_input({"instruction": "test"}) == [ {"role": "user", "content": "test"} @@ -34,6 +38,7 @@ def test_format_input_with_system_prompt(self) -> None: task = TextGeneration( name="task", llm=llm, pipeline=pipeline, system_prompt="test" ) + task.load() assert task.format_input({"instruction": "test"}) == [ {"role": "system", "content": "test"}, @@ -44,6 +49,7 @@ def test_format_input_with_row_system_prompt(self) -> None: pipeline = Pipeline(name="unit-test-pipeline") llm = DummyAsyncLLM() task = TextGeneration(name="task", llm=llm, pipeline=pipeline) + task.load() assert task.format_input({"instruction": "test", "system_prompt": "test"}) == [ {"role": "system", "content": "test"}, @@ -56,6 +62,7 @@ def test_format_input_with_row_system_prompt_and_system_prompt(self) -> None: task = TextGeneration( name="task", llm=llm, pipeline=pipeline, system_prompt="i won't be used" ) + task.load() assert task.format_input({"instruction": "test", "system_prompt": "test"}) == [ {"role": "system", "content": "test"}, @@ -68,6 +75,7 @@ def test_format_input_errors(self) -> None: task = TextGeneration( name="task", llm=llm, pipeline=pipeline, use_system_prompt=True ) + task.load() with pytest.raises( ValueError, @@ -86,6 +94,7 @@ def test_process(self) -> None: task = TextGeneration( name="task", llm=llm, pipeline=pipeline, add_raw_input=False ) + task.load() assert next(task.process([{"instruction": "test"}])) == [ { @@ -98,6 +107,73 @@ def test_process(self) -> None: } ] + @pytest.mark.parametrize( + "template, columns, sample", + [ + ("{{ instruction }}", "instruction", {"instruction": "INSTRUCTION"}), + ( + "Document:\n{{ document }}\n\nQuestion: {{ question }}\n\nPlease provide a clear and concise answer to the question based on the information in the document and your general knowledge:", + ["document", "question"], + {"document": "DOCUMENT", "question": "QUESTION"}, + ), + ( + "Generate a clear, single-sentence instruction based on the following examples:\n\n{% for example in examples %}\nExample {{ loop.index }}:\nInstruction: {{ example }}\n\n{% endfor %}\nNow, generate a new instruction in a similar style:\n", + "examples", + {"examples": ["example1", "example2"]}, + ), + ], + ) + def test_format_input_custom_columns( + self, + template: str, + columns: Union[str, List[str]], + sample: Dict[str, Any], + ) -> None: + task = TextGeneration( + llm=DummyAsyncLLM(), + system_prompt=None, + template=template, + columns=columns, + add_raw_input=False, + add_raw_output=False, + ) + task.load() + + # Check the input from the sample are present in the formatted input + result = task.format_input(sample)[0]["content"] + values = list(sample.values()) + + if isinstance(values[0], list): + values = values[0] + assert all(v in result for v in values) + + @pytest.mark.parametrize( + "template, columns, sample", + [ + ( + "This is a {{ custom }} template", + "instruction", + {"other": "INSTRUCTION"}, + ), + ], + ) + def test_format_input_custom_columns_expected_errors( + self, + template: str, + columns: Union[str, List[str]], + sample: Dict[str, Any], + ) -> None: + task = TextGeneration( + llm=DummyAsyncLLM(), + system_prompt=None, + template=template, + columns=columns, + add_raw_input=False, + add_raw_output=False, + ) + with pytest.raises(DistilabelUserError): + task.load() + class TestChatGeneration: def test_format_input(self) -> None: From e1253a6a90e5b61b2a92b8b2de2d8422a48fd012 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Mart=C3=ADn=20Bl=C3=A1zquez?= Date: Mon, 16 Sep 2024 15:22:58 +0200 Subject: [PATCH 54/82] Update `system_prompt` attribute for adding probabilities in `MagpieBase` (#981) * Update `system_prompt` so it can be a `Dict[str, Any]` * Update docstrings and tests * Update tests --- src/distilabel/steps/tasks/magpie/base.py | 126 +++++++++++++---- .../steps/tasks/magpie/generator.py | 82 ++++++++--- tests/unit/steps/tasks/magpie/test_base.py | 132 ++++++++++++++++-- .../unit/steps/tasks/magpie/test_generator.py | 37 +++-- 4 files changed, 301 insertions(+), 76 deletions(-) diff --git a/src/distilabel/steps/tasks/magpie/base.py b/src/distilabel/steps/tasks/magpie/base.py index 97e2903628..13c31f00ef 100644 --- a/src/distilabel/steps/tasks/magpie/base.py +++ b/src/distilabel/steps/tasks/magpie/base.py @@ -15,7 +15,7 @@ import random from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union -from pydantic import Field, PositiveInt +from pydantic import Field, PositiveInt, field_validator from distilabel.errors import DistilabelUserError from distilabel.llms.base import LLM @@ -78,15 +78,44 @@ class MagpieBase(RuntimeParametersMixin): description="Whether to generate only the instruction. If this argument" " is `True`, then `n_turns` will be ignored.", ) - system_prompt: Optional[RuntimeParameter[Union[List[str], str]]] = Field( + system_prompt: Optional[ + RuntimeParameter[ + Union[List[str], Dict[str, str], Dict[str, Tuple[str, float]], str] + ] + ] = Field( default=None, - description="An optional system prompt or list of system prompts that can be used" - " to steer the LLM to generate content of certain topic, guide the style, etc.", + description="An optional system prompt, or a list of system prompts from which a" + " random one will be chosen, or a dictionary of system prompts from which a random" + " one will be choosen, or a dictionary of system prompts with their probability of" + " being chosen. The random system prompt will be chosen per input/output batch." + " This system prompt can be used to guide the generation of the instruct LLM and" + " steer it to generate instructions of a certain topic.", ) + @field_validator("system_prompt", mode="after") + @classmethod + def system_prompts_weights_validator( + cls, + system_prompts: Union[ + List[str], Dict[str, str], Dict[str, Tuple[str, float]], str + ], + ) -> Union[List[str], Dict[str, str], Dict[str, Tuple[str, float]], str]: + """Validates that the sum of the weights of the system prompts is equal to 1.0.""" + if isinstance(system_prompts, dict): + system_prompts_values = list(system_prompts.values()) + if isinstance(system_prompts_values[0], tuple): + weights_sum = sum(weight for _, weight in system_prompts_values) # type: ignore + if weights_sum != 1.0: + raise DistilabelUserError( + "If `system_prompts` attribute is a dictionary containing tuples with" + " the system prompts and their probability of being chosen, then the" + " sum of the weights must be equal to 1.0." + ) + return system_prompts + def _prepare_inputs_for_instruction_generation( self, inputs: List[Dict[str, Any]] - ) -> List["ChatType"]: + ) -> Tuple[List["ChatType"], Union[str, None]]: """Prepares the inputs adding the system (if required) prompt provided in each row, or if the conversations to generate have more than one turn, then adding the system prompt for multi-turn conversation from the paper. @@ -98,6 +127,7 @@ def _prepare_inputs_for_instruction_generation( The prepared inputs. """ prepared_inputs = [] + system_prompt_key = None for input in inputs: conversation = [] if "system_prompt" in input: @@ -106,7 +136,19 @@ def _prepare_inputs_for_instruction_generation( ) elif self.system_prompt is not None: if isinstance(self.system_prompt, list): - system_prompt = random.choice(self.system_prompt) + system_prompt = random.choices(self.system_prompt, k=1)[0] + elif isinstance(self.system_prompt, dict): + system_prompts_keys = list(self.system_prompt.keys()) + system_prompts_values = list(self.system_prompt.values()) + weights: Union[List[float], None] = None + if isinstance(system_prompts_values[0], tuple): + weights = [weight for _, weight in system_prompts_values] # type: ignore + system_prompt_key = random.choices( + system_prompts_keys, weights, k=1 + )[0] + system_prompt = self.system_prompt[system_prompt_key] + if isinstance(system_prompt, tuple): + system_prompt = system_prompt[0] else: system_prompt = self.system_prompt conversation.append({"role": "system", "content": system_prompt}) @@ -117,7 +159,7 @@ def _prepare_inputs_for_instruction_generation( prepared_inputs.append(conversation) - return prepared_inputs + return prepared_inputs, system_prompt_key def _append_messages_to_conversations( self, role: str, messages: List[str], conversations: List["ChatType"] @@ -140,16 +182,24 @@ def _append_messages_to_conversations( def _generate_instruction( self, inputs: List[Dict[str, Any]] ) -> List[Dict[str, Any]]: - prepared_inputs = self._prepare_inputs_for_instruction_generation(inputs) + prepared_inputs, system_prompt_key = ( + self._prepare_inputs_for_instruction_generation(inputs) + ) outputs = self.llm.generate( inputs=prepared_inputs, num_generations=1, **self.llm.generation_kwargs, # type: ignore ) - return [{"instruction": output[0]} for output in outputs] + rows = [] + for output in outputs: + row = {"instruction": output[0]} + if system_prompt_key is not None: + row["system_prompt_key"] = system_prompt_key + rows.append(row) + return rows def _prepare_conversation_outputs( - self, conversations: List["ChatType"] + self, conversations: List["ChatType"], system_prompt_key: Optional[str] = None ) -> List[Dict[str, Any]]: """Prepare the output conversation removing the system prompt if necessary. If `n_turns==1`, then it will return a dictionary with "instruction" and "response" @@ -157,6 +207,7 @@ def _prepare_conversation_outputs( Args: conversations: the list of generated conversations. + system_prompt_key: the key of the system prompt used to generate the conversation. Returns: A list of dictionaries containing a "conversation" key or "instruction" and @@ -174,14 +225,15 @@ def _prepare_conversation_outputs( if not self.include_system_prompt and conversation[0]["role"] == "system": conversation.pop(0) if self.n_turns == 1 and len(conversation) == 2: - outputs.append( - { - "instruction": conversation[0]["content"], - "response": conversation[1]["content"], - } - ) + output: Dict[str, Any] = { + "instruction": conversation[0]["content"], + "response": conversation[1]["content"], + } else: - outputs.append({"conversation": conversation}) + output = {"conversation": conversation} + if system_prompt_key is not None: + output["system_prompt_key"] = system_prompt_key + outputs.append(output) return outputs def _generate_conversation_turn( @@ -213,7 +265,7 @@ def _generate_conversation_turn( def _generate_multi_turn_conversation( self, inputs: List[Dict[str, Any]] ) -> List[Dict[str, Any]]: - conversations: List["ChatType"] = ( + conversations, system_prompt_key = ( self._prepare_inputs_for_instruction_generation(inputs) ) # Keep track of the active conversations, as it could happen that for some conversation @@ -291,12 +343,12 @@ class Magpie(Task, MagpieBase): conversation. Defaults to `False`. only_instruction: whether to generate only the instruction. If this argument is `True`, then `n_turns` will be ignored. Defaults to `False`. - system_prompt: an optional system prompt or list of system prompts that can - be used to steer the LLM to generate content of certain topic, guide the style, - etc. If it's a list of system prompts, then a random system prompt will be chosen - per input/output batch. If the provided inputs contains a `system_prompt` column, - then this runtime parameter will be ignored and the one from the column will - be used. Defaults to `None`. + system_prompt: an optional system prompt, or a list of system prompts from which + a random one will be chosen, or a dictionary of system prompts from which a + random one will be choosen, or a dictionary of system prompts with their probability + of being chosen. The random system prompt will be chosen per input/output batch. + This system prompt can be used to guide the generation of the instruct LLM and + steer it to generate instructions of a certain topic. Defaults to `None`. Runtime parameters: - `n_turns`: the number of turns that the generated conversation will have. Defaults @@ -313,6 +365,12 @@ class Magpie(Task, MagpieBase): per input/output batch. If the provided inputs contains a `system_prompt` column, then this runtime parameter will be ignored and the one from the column will be used. Defaults to `None`. + - `system_prompt`: an optional system prompt, or a list of system prompts from which + a random one will be chosen, or a dictionary of system prompts from which a + random one will be choosen, or a dictionary of system prompts with their probability + of being chosen. The random system prompt will be chosen per input/output batch. + This system prompt can be used to guide the generation of the instruct LLM and + steer it to generate instructions of a certain topic. Input columns: - system_prompt (`str`, optional): an optional system prompt that can be provided @@ -324,6 +382,8 @@ class Magpie(Task, MagpieBase): items with a role and a message. Only if `only_instruction=False`. - instruction (`str`): the generated instructions if `only_instruction=True` or `n_turns==1`. - response (`str`): the generated response if `n_turns==1`. + - system_prompt_key (`str`, optional): the key of the system prompt used to generate + the conversation or instruction. Only if `system_prompt` is a dictionary. - model_name (`str`): The model name used to generate the `conversation` or `instruction`. Categories: @@ -465,11 +525,21 @@ def format_input(self, input: Dict[str, Any]) -> "ChatType": @property def outputs(self) -> "StepColumns": """Either a multi-turn conversation or the instruction generated.""" + outputs = [] + if self.only_instruction: - return ["instruction", "model_name"] - if self.n_turns == 1: - return ["instruction", "response", "model_name"] - return ["conversation", "model_name"] + outputs.append("instruction") + elif self.n_turns == 1: + outputs.extend(["instruction", "response"]) + else: + outputs.append("conversation") + + if isinstance(self.system_prompt, dict): + outputs.append("system_prompt_key") + + outputs.append("model_name") + + return outputs def format_output( self, diff --git a/src/distilabel/steps/tasks/magpie/generator.py b/src/distilabel/steps/tasks/magpie/generator.py index dc4fe99387..2df8bf6217 100644 --- a/src/distilabel/steps/tasks/magpie/generator.py +++ b/src/distilabel/steps/tasks/magpie/generator.py @@ -50,12 +50,12 @@ class MagpieGenerator(GeneratorTask, MagpieBase): conversation. Defaults to `False`. only_instruction: whether to generate only the instruction. If this argument is `True`, then `n_turns` will be ignored. Defaults to `False`. - system_prompt: an optional system prompt or list of system prompts that can - be used to steer the LLM to generate content of certain topic, guide the style, - etc. If it's a list of system prompts, then a random system prompt will be chosen - per input/output batch. If the provided inputs contains a `system_prompt` column, - then this runtime parameter will be ignored and the one from the column will - be used. Defaults to `None`. + system_prompt: an optional system prompt, or a list of system prompts from which + a random one will be chosen, or a dictionary of system prompts from which a + random one will be choosen, or a dictionary of system prompts with their probability + of being chosen. The random system prompt will be chosen per input/output batch. + This system prompt can be used to guide the generation of the instruct LLM and + steer it to generate instructions of a certain topic. Defaults to `None`. num_rows: the number of rows to be generated. Runtime parameters: @@ -67,12 +67,12 @@ class MagpieGenerator(GeneratorTask, MagpieBase): conversation. Defaults to `False`. - `only_instruction`: whether to generate only the instruction. If this argument is `True`, then `n_turns` will be ignored. Defaults to `False`. - - `system_prompt`: an optional system prompt or list of system prompts that can - be used to steer the LLM to generate content of certain topic, guide the style, - etc. If it's a list of system prompts, then a random system prompt will be chosen - per input/output batch. If the provided inputs contains a `system_prompt` column, - then this runtime parameter will be ignored and the one from the column will - be used. Defaults to `None`. + - `system_prompt`: an optional system prompt, or a list of system prompts from which + a random one will be chosen, or a dictionary of system prompts from which a + random one will be choosen, or a dictionary of system prompts with their probability + of being chosen. The random system prompt will be chosen per input/output batch. + This system prompt can be used to guide the generation of the instruct LLM and + steer it to generate instructions of a certain topic. - `num_rows`: the number of rows to be generated. Output columns: @@ -80,6 +80,8 @@ class MagpieGenerator(GeneratorTask, MagpieBase): items with a role and a message. - instruction (`str`): the generated instructions if `only_instruction=True`. - response (`str`): the generated response if `n_turns==1`. + - system_prompt_key (`str`, optional): the key of the system prompt used to generate + the conversation or instruction. Only if `system_prompt` is a dictionary. - model_name (`str`): The model name used to generate the `conversation` or `instruction`. Categories: @@ -203,6 +205,34 @@ class MagpieGenerator(GeneratorTask, MagpieBase): # ) ``` + Generating with system prompts with probabilities: + + ```python + from distilabel.llms import InferenceEndpointsLLM + from distilabel.steps.tasks import MagpieGenerator + + magpie = MagpieGenerator( + llm=InferenceEndpointsLLM( + model_id="meta-llama/Meta-Llama-3-8B-Instruct", + tokenizer_id="meta-llama/Meta-Llama-3-8B-Instruct", + magpie_pre_query_template="llama3", + generation_kwargs={ + "temperature": 0.8, + "max_new_tokens": 256, + }, + ), + n_turns=2, + system_prompt={ + "math": ("You're an expert AI assistant.", 0.8), + "writing": ("You're an expert writing assistant.", 0.2), + }, + ) + + magpie.load() + + result = next(magpie.process()) + ``` + Citations: ``` @misc{xu2024magpiealignmentdatasynthesis, @@ -235,6 +265,25 @@ def model_post_init(self, __context: Any) -> None: self.llm.use_magpie_template = True + @property + def outputs(self) -> "StepColumns": + """Either a multi-turn conversation or the instruction generated.""" + outputs = [] + + if self.only_instruction: + outputs.append("instruction") + elif self.n_turns == 1: + outputs.extend(["instruction", "response"]) + else: + outputs.append("conversation") + + if isinstance(self.system_prompt, dict): + outputs.append("system_prompt_key") + + outputs.append("model_name") + + return outputs + def format_output( self, output: Union[str, None], @@ -243,15 +292,6 @@ def format_output( """Does nothing.""" return {} - @property - def outputs(self) -> "StepColumns": - """Either a multi-turn conversation or the instruction generated.""" - if self.only_instruction: - return ["instruction", "model_name"] - if self.n_turns == 1: - return ["instruction", "response", "model_name"] - return ["conversation", "model_name"] - def process(self, offset: int = 0) -> "GeneratorStepOutput": """Generates the desired number of instructions or conversations using Magpie. diff --git a/tests/unit/steps/tasks/magpie/test_base.py b/tests/unit/steps/tasks/magpie/test_base.py index 798ce8c5b4..2e00404e09 100644 --- a/tests/unit/steps/tasks/magpie/test_base.py +++ b/tests/unit/steps/tasks/magpie/test_base.py @@ -31,7 +31,28 @@ def test_raise_value_error_llm_no_magpie_mixin(self) -> None: ): Magpie(llm=OpenAILLM(model="gpt-4", api_key="fake")) # type: ignore + def test_raise_error_if_system_prompts_weights_do_not_sum_to_one(self) -> None: + with pytest.raises( + ValueError, + match="`*If `system_prompts` attribute is a dictionary containing tuples with" + " the system prompts and their probability of being chosen", + ): + Magpie( + llm=DummyMagpieLLM(magpie_pre_query_template="llama3"), + system_prompt={ + "system_prompt_1": ("system_prompt", 0.5), + "system_prompt_2": ("system_prompt", 0.4), + }, + ) + def test_outputs(self) -> None: + task = Magpie( + llm=DummyMagpieLLM(magpie_pre_query_template="llama3"), + only_instruction=True, + ) + + assert task.outputs == ["instruction", "model_name"] + task = Magpie(llm=DummyMagpieLLM(magpie_pre_query_template="llama3"), n_turns=1) assert task.outputs == ["instruction", "response", "model_name"] @@ -42,10 +63,18 @@ def test_outputs(self) -> None: task = Magpie( llm=DummyMagpieLLM(magpie_pre_query_template="llama3"), - only_instruction=True, + system_prompt={ + "system_prompt_1": ("system_prompt", 0.5), + "system_prompt_2": ("system_prompt", 0.5), + }, ) - assert task.outputs == ["instruction", "model_name"] + assert task.outputs == [ + "instruction", + "response", + "system_prompt_key", + "model_name", + ] def test_process(self) -> None: task = Magpie(llm=DummyMagpieLLM(magpie_pre_query_template="llama3"), n_turns=1) @@ -131,7 +160,7 @@ def test_process_with_several_system_prompts(self) -> None: assert next(task.process(inputs=[{}, {}, {}])) == [ { "conversation": [ - {"role": "system", "content": "This is a system prompt."}, + {"role": "system", "content": "This is another system prompt."}, {"role": "user", "content": "Hello Magpie"}, {"role": "assistant", "content": "Hello Magpie"}, {"role": "user", "content": "Hello Magpie"}, @@ -151,7 +180,7 @@ def test_process_with_several_system_prompts(self) -> None: }, { "conversation": [ - {"role": "system", "content": "This is another system prompt."}, + {"role": "system", "content": "This is a system prompt."}, {"role": "user", "content": "Hello Magpie"}, {"role": "assistant", "content": "Hello Magpie"}, {"role": "user", "content": "Hello Magpie"}, @@ -477,6 +506,85 @@ def test_prepare_conversation_outputs( ) assert task._prepare_conversation_outputs([conversation]) == [expected] + @pytest.mark.parametrize( + "system_prompt, n_turns, inputs, random_choices_return, expected_prepared_inputs, expected_system_prompt_key", + [ + ( + None, + 1, + [{"system_prompt": "Custom system prompt."}], + None, + [[{"role": "system", "content": "Custom system prompt."}]], + None, + ), + ( + ["Prompt A", "Prompt B"], + 1, + [{}], + ["Prompt A"], + [[{"role": "system", "content": "Prompt A"}]], + None, + ), + ( + {"Key1": "Prompt 1", "Key2": "Prompt 2"}, + 1, + [{}], + ["Key1"], + [[{"role": "system", "content": "Prompt 1"}]], + "Key1", + ), + ( + {"Key1": ("Prompt 1", 0.7), "Key2": ("Prompt 2", 0.3)}, + 1, + [{}], + ["Key1"], + [[{"role": "system", "content": "Prompt 1"}]], + "Key1", + ), + ( + None, + 2, + [{}], + None, + [[{"role": "system", "content": MAGPIE_MULTI_TURN_SYSTEM_PROMPT}]], + None, + ), + ( + None, + 1, + [{}], + None, + [[]], + None, + ), + ], + ) + def test_prepare_inputs_for_instruction_generation( + self, + system_prompt, + n_turns, + inputs, + random_choices_return, + expected_prepared_inputs, + expected_system_prompt_key, + ): + task = Magpie( + llm=DummyMagpieLLM(magpie_pre_query_template="llama3"), + n_turns=n_turns, + system_prompt=system_prompt, + ) + + with mock.patch("random.choices") as mock_choices: + if random_choices_return is not None: + mock_choices.return_value = random_choices_return + + prepared_inputs, system_prompt_key = ( + task._prepare_inputs_for_instruction_generation(inputs) + ) + + assert prepared_inputs == expected_prepared_inputs + assert system_prompt_key == expected_system_prompt_key + def test_serialization(self) -> None: task = Magpie( llm=DummyMagpieLLM(magpie_pre_query_template="llama3"), @@ -488,9 +596,9 @@ def test_serialization(self) -> None: "use_magpie_template": True, "magpie_pre_query_template": "<|begin_of_text|><|start_header_id|>user<|end_header_id|>\n\n", "generation_kwargs": {}, - "jobs_ids": None, - "offline_batch_generation_block_until_done": None, "use_offline_batch_generation": False, + "offline_batch_generation_block_until_done": None, + "jobs_ids": None, "type_info": { "module": "tests.unit.conftest", "name": "DummyMagpieLLM", @@ -527,18 +635,14 @@ def test_serialization(self) -> None: "keys": [{"name": "kwargs", "optional": False}], }, { - "description": "Whether to use the `offline_batch_generate` method to " - "generate the responses.", "name": "use_offline_batch_generation", "optional": True, + "description": "Whether to use the `offline_batch_generate` method to generate the responses.", }, { - "description": "If provided, then polling will be done until the " - "`ofline_batch_generate` method is able to retrieve the " - "results. The value indicate the time to wait between each " - "polling.", "name": "offline_batch_generation_block_until_done", "optional": True, + "description": "If provided, then polling will be done until the `ofline_batch_generate` method is able to retrieve the results. The value indicate the time to wait between each polling.", }, ], }, @@ -565,7 +669,7 @@ def test_serialization(self) -> None: { "name": "system_prompt", "optional": True, - "description": "An optional system prompt or list of system prompts that can be used to steer the LLM to generate content of certain topic, guide the style, etc.", + "description": "An optional system prompt, or a list of system prompts from which a random one will be chosen, or a dictionary of system prompts from which a random one will be choosen, or a dictionary of system prompts with their probability of being chosen. The random system prompt will be chosen per input/output batch. This system prompt can be used to guide the generation of the instruct LLM and steer it to generate instructions of a certain topic.", }, { "name": "resources", @@ -608,9 +712,9 @@ def test_serialization(self) -> None: "description": "Whether to include the raw output of the LLM in the key `raw_output_` of the `distilabel_metadata` dictionary output column", }, { - "description": "Whether to include the raw input of the LLM in the key `raw_input_` of the `distilabel_metadata` dictionary column", "name": "add_raw_input", "optional": True, + "description": "Whether to include the raw input of the LLM in the key `raw_input_` of the `distilabel_metadata` dictionary column", }, { "name": "num_generations", diff --git a/tests/unit/steps/tasks/magpie/test_generator.py b/tests/unit/steps/tasks/magpie/test_generator.py index 3edd91f9b2..b5e41c3554 100644 --- a/tests/unit/steps/tasks/magpie/test_generator.py +++ b/tests/unit/steps/tasks/magpie/test_generator.py @@ -28,6 +28,13 @@ def test_raise_value_error_llm_no_magpie_mixin(self) -> None: MagpieGenerator(llm=OpenAILLM(model="gpt-4", api_key="fake")) # type: ignore def test_outputs(self) -> None: + task = MagpieGenerator( + llm=DummyMagpieLLM(magpie_pre_query_template="llama3"), + only_instruction=True, + ) + + assert task.outputs == ["instruction", "model_name"] + task = MagpieGenerator( llm=DummyMagpieLLM(magpie_pre_query_template="llama3"), n_turns=1 ) @@ -42,10 +49,18 @@ def test_outputs(self) -> None: task = MagpieGenerator( llm=DummyMagpieLLM(magpie_pre_query_template="llama3"), - only_instruction=True, + system_prompt={ + "system_prompt_1": ("system_prompt", 0.5), + "system_prompt_2": ("system_prompt", 0.5), + }, ) - assert task.outputs == ["instruction", "model_name"] + assert task.outputs == [ + "instruction", + "response", + "system_prompt_key", + "model_name", + ] def test_serialization(self) -> None: task = MagpieGenerator(llm=DummyMagpieLLM(magpie_pre_query_template="llama3")) @@ -55,9 +70,9 @@ def test_serialization(self) -> None: "use_magpie_template": True, "magpie_pre_query_template": "<|begin_of_text|><|start_header_id|>user<|end_header_id|>\n\n", "generation_kwargs": {}, - "jobs_ids": None, - "offline_batch_generation_block_until_done": None, "use_offline_batch_generation": False, + "offline_batch_generation_block_until_done": None, + "jobs_ids": None, "type_info": { "module": "tests.unit.conftest", "name": "DummyMagpieLLM", @@ -83,8 +98,8 @@ def test_serialization(self) -> None: "add_raw_output": True, "add_raw_input": True, "num_generations": 1, - "num_rows": None, "use_default_structured_output": False, + "num_rows": None, "runtime_parameters_info": [ { "name": "llm", @@ -95,18 +110,14 @@ def test_serialization(self) -> None: "keys": [{"name": "kwargs", "optional": False}], }, { - "description": "Whether to use the `offline_batch_generate` method to " - "generate the responses.", "name": "use_offline_batch_generation", "optional": True, + "description": "Whether to use the `offline_batch_generate` method to generate the responses.", }, { - "description": "If provided, then polling will be done until the " - "`ofline_batch_generate` method is able to retrieve the " - "results. The value indicate the time to wait between each " - "polling.", "name": "offline_batch_generation_block_until_done", "optional": True, + "description": "If provided, then polling will be done until the `ofline_batch_generate` method is able to retrieve the results. The value indicate the time to wait between each polling.", }, ], }, @@ -133,7 +144,7 @@ def test_serialization(self) -> None: { "name": "system_prompt", "optional": True, - "description": "An optional system prompt or list of system prompts that can be used to steer the LLM to generate content of certain topic, guide the style, etc.", + "description": "An optional system prompt, or a list of system prompts from which a random one will be chosen, or a dictionary of system prompts from which a random one will be choosen, or a dictionary of system prompts with their probability of being chosen. The random system prompt will be chosen per input/output batch. This system prompt can be used to guide the generation of the instruct LLM and steer it to generate instructions of a certain topic.", }, { "name": "resources", @@ -176,9 +187,9 @@ def test_serialization(self) -> None: "description": "Whether to include the raw output of the LLM in the key `raw_output_` of the `distilabel_metadata` dictionary output column", }, { - "description": "Whether to include the raw input of the LLM in the key `raw_input_` of the `distilabel_metadata` dictionary column", "name": "add_raw_input", "optional": True, + "description": "Whether to include the raw input of the LLM in the key `raw_input_` of the `distilabel_metadata` dictionary column", }, { "name": "num_generations", From 75e34e12abc9f54daa5e92c170f75dfce70c8202 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Mart=C3=ADn=20Bl=C3=A1zquez?= Date: Mon, 16 Sep 2024 16:10:21 +0200 Subject: [PATCH 55/82] Send as many `None`s as replicas in the step (#982) --- src/distilabel/pipeline/base.py | 3 ++- src/distilabel/pipeline/step_wrapper.py | 9 +++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/distilabel/pipeline/base.py b/src/distilabel/pipeline/base.py index cd6f35e59d..a854806651 100644 --- a/src/distilabel/pipeline/base.py +++ b/src/distilabel/pipeline/base.py @@ -1496,7 +1496,8 @@ def _notify_steps_to_stop(self) -> None: with self._steps_load_status_lock: for step_name, replicas in self._steps_load_status.items(): if replicas > 0: - self._send_to_step(step_name, None) + for _ in range(replicas): + self._send_to_step(step_name, None) def _get_successors(self, batch: "_Batch") -> Tuple[List[str], List[str], bool]: """Gets the successors and the successors to which the batch has to be routed. diff --git a/src/distilabel/pipeline/step_wrapper.py b/src/distilabel/pipeline/step_wrapper.py index 0e8473e58a..661f99b1f7 100644 --- a/src/distilabel/pipeline/step_wrapper.py +++ b/src/distilabel/pipeline/step_wrapper.py @@ -134,14 +134,23 @@ def run(self) -> str: def _notify_load(self) -> None: """Notifies that the step has finished executing its `load` function successfully.""" + self.step._logger.debug( + f"Notifying load of step '{self.step.name}' (replica ID {self.replica})..." + ) self.load_queue.put({"name": self.step.name, "status": "loaded"}) # type: ignore def _notify_unload(self) -> None: """Notifies that the step has been unloaded.""" + self.step._logger.debug( + f"Notifying unload of step '{self.step.name}' (replica ID {self.replica})..." + ) self.load_queue.put({"name": self.step.name, "status": "unloaded"}) # type: ignore def _notify_load_failed(self) -> None: """Notifies that the step failed to load.""" + self.step._logger.debug( + f"Notifying load failed of step '{self.step.name}' (replica ID {self.replica})..." + ) self.load_queue.put({"name": self.step.name, "status": "load_failed"}) # type: ignore def _generator_step_process_loop(self) -> None: From b2d8eb5e6ea83f4b77fa3a33a58fc32078d1b062 Mon Sep 17 00:00:00 2001 From: David Berenstein Date: Mon, 16 Sep 2024 18:42:08 +0200 Subject: [PATCH 56/82] docs: 960 docs add a glossary concept section (#970) * feat: add _STEP_CATEGORY_TO_DESCRIPTION * docs: emphasize structured generation * docs: add pipeline visualizations * docs: add concepts page outline * Apply suggestions from code review Co-authored-by: Natalia Elvira <126158523+nataliaElv@users.noreply.github.com> * docs: processed comments Natalia * chore: add tabulate dependency * feat: add task overview * docs: remove task overview from API definition * docs: update naming tutorials * docs: update naming --------- Co-authored-by: Natalia Elvira <126158523+nataliaElv@users.noreply.github.com> --- docs/assets/pipelines/arena-hard.png | Bin 0 -> 24962 bytes docs/assets/pipelines/clean-dataset.png | Bin 0 -> 9851 bytes docs/assets/pipelines/deepseek.png | Bin 0 -> 11721 bytes docs/assets/pipelines/deita.png | Bin 0 -> 15376 bytes .../pipelines/generate-preference-dataset.png | Bin 0 -> 23539 bytes .../pipelines/instruction_backtranslation.png | Bin 0 -> 9947 bytes docs/assets/pipelines/knowledge_graphs.png | Bin 0 -> 5716 bytes docs/assets/pipelines/prometheus.png | Bin 0 -> 7159 bytes .../assets/pipelines/sentence-transformer.png | Bin 0 -> 11939 bytes docs/assets/pipelines/ultrafeedback.png | Bin 0 -> 51739 bytes docs/sections/getting_started/quickstart.md | 38 +- .../sections/how_to_guides/basic/llm/index.md | 2 +- .../how_to_guides/basic/step/index.md | 2 +- .../basic/task/generator_task.md | 2 +- .../how_to_guides/basic/task/index.md | 2 +- .../examples/benchmarking_with_distilabel.md | 4 +- .../examples/llama_cpp_with_outlines.md | 4 +- .../examples/mistralai_with_instructor.md | 4 +- docs/sections/pipeline_samples/index.md | 10 +- .../papers/deepseek_prover.md | 4 +- .../sections/pipeline_samples/papers/deita.md | 2 + .../papers/instruction_backtranslation.md | 2 + .../pipeline_samples/papers/prometheus.md | 2 + .../pipeline_samples/papers/ultrafeedback.md | 2 + .../tutorials/GenerateSentencePair.ipynb | 2 + .../tutorials/clean_existing_dataset.ipynb | 4 +- .../generate_preference_dataset.ipynb | 4 +- mkdocs.yml | 20 +- pdm.lock | 2874 +++++++++++++++++ pyproject.toml | 3 + .../utils/mkdocs/components_gallery.py | 38 +- 31 files changed, 2970 insertions(+), 55 deletions(-) create mode 100644 docs/assets/pipelines/arena-hard.png create mode 100644 docs/assets/pipelines/clean-dataset.png create mode 100644 docs/assets/pipelines/deepseek.png create mode 100644 docs/assets/pipelines/deita.png create mode 100644 docs/assets/pipelines/generate-preference-dataset.png create mode 100644 docs/assets/pipelines/instruction_backtranslation.png create mode 100644 docs/assets/pipelines/knowledge_graphs.png create mode 100644 docs/assets/pipelines/prometheus.png create mode 100644 docs/assets/pipelines/sentence-transformer.png create mode 100644 docs/assets/pipelines/ultrafeedback.png create mode 100644 pdm.lock diff --git a/docs/assets/pipelines/arena-hard.png b/docs/assets/pipelines/arena-hard.png new file mode 100644 index 0000000000000000000000000000000000000000..a6a208c3733f0a91fb24b369500a13cf9d48b5de GIT binary patch literal 24962 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 literal 0 HcmV?d00001 diff --git a/docs/assets/pipelines/clean-dataset.png b/docs/assets/pipelines/clean-dataset.png new file mode 100644 index 0000000000000000000000000000000000000000..1f73e19ce68f3f9f35975c273ada199318fac798 GIT binary patch literal 9851 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 literal 0 HcmV?d00001 diff --git a/docs/assets/pipelines/deita.png b/docs/assets/pipelines/deita.png new file mode 100644 index 0000000000000000000000000000000000000000..b552cf0c4bcd9e19c2c11d248c459e71281239d1 GIT binary patch literal 15376 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 S#s#6UI$L{Lf)0V(P3k_M5M66u!i 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 z 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 M%Bsp#NSTHH4<)c3dH?_b literal 0 HcmV?d00001 diff --git a/docs/assets/pipelines/knowledge_graphs.png b/docs/assets/pipelines/knowledge_graphs.png new file mode 100644 index 0000000000000000000000000000000000000000..f142e76c8f33331fa45f256aa25c42ef0115478c GIT binary patch literal 5716 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 literal 0 HcmV?d00001 diff --git a/docs/assets/pipelines/sentence-transformer.png b/docs/assets/pipelines/sentence-transformer.png new file mode 100644 index 0000000000000000000000000000000000000000..690dc9379d0713983c1325d878c90650a26b94a8 GIT binary patch literal 11939 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 literal 0 HcmV?d00001 diff --git a/docs/assets/pipelines/ultrafeedback.png b/docs/assets/pipelines/ultrafeedback.png new file mode 100644 index 0000000000000000000000000000000000000000..852edc4c366505fe677a4e96a54ca373681ea169 GIT binary patch literal 51739 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%0A-DMV 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 IhRx0Y0<`b;4FCWD literal 0 HcmV?d00001 diff --git a/docs/sections/getting_started/quickstart.md b/docs/sections/getting_started/quickstart.md index 863af475fb..04d84d9786 100644 --- a/docs/sections/getting_started/quickstart.md +++ b/docs/sections/getting_started/quickstart.md @@ -10,7 +10,15 @@ hide: # Quickstart -To start off, `distilabel` is a framework for building pipelines for generating synthetic data using LLMs, that defines a [`Pipeline`][distilabel.pipeline.Pipeline] which orchestrates the execution of the [`Step`][distilabel.steps.base.Step] subclasses, and those will be connected as nodes in a Direct Acyclic Graph (DAG). +Distilabel provides all the tools you need to your scalable and reliable pipelines for synthetic data generation and AI-feedback. Pipelines are used to generate data, evaluate models, manipulate data, or any other general task. They are made up of different components: Steps, Tasks and LLMs, which are chained together in a directed acyclic graph (DAG). + +- **Steps**: These are the building blocks of your pipeline. Normal steps are used for basic executions like loading data, applying some transformations, or any other general task. +- **Tasks**: These are steps that rely on LLMs and prompts to perform generative tasks. For example, they can be used to generate data, evaluate models or manipulate data. +- **LLMs**: These are the models that will perform the task. They can be local or remote models, and open-source or commercial models. + +Pipelines are designed to be scalable and reliable. They can be executed in a distributed manner, and they can be cached and recovered. This is useful when dealing with large datasets or when you want to ensure that your pipeline is reproducible. + +Besides that, pipelines are designed to be modular and flexible. You can easily add new steps, tasks, or LLMs to your pipeline, and you can also easily modify or remove them. An example architecture of a pipeline to generate a dataset of preferences is the following: ## Installation @@ -84,31 +92,3 @@ if __name__ == "__main__": 7. We run the pipeline with the parameters for the `load_dataset` and `text_generation` steps. The `load_dataset` step will use the repository `distilabel-internal-testing/instruction-dataset-mini` and the `test` split, and the `text_generation` task will use the `generation_kwargs` with the `temperature` set to `0.7` and the `max_new_tokens` set to `512`. 8. Optionally, we can push the generated [`Distiset`][distilabel.distiset.Distiset] to the Hugging Face Hub repository `distilabel-example`. This will allow you to share the generated dataset with others and use it in other pipelines. - -## Minimal example - -`distilabel` gives a lot of flexibility to create your pipelines, but to start right away, you can omit a lot of the details and let default values: - -```python -from distilabel.llms import InferenceEndpointsLLM -from distilabel.pipeline import Pipeline -from distilabel.steps.tasks import TextGeneration -from datasets import load_dataset - - -dataset = load_dataset("distilabel-internal-testing/instruction-dataset-mini", split="test") - -with Pipeline() as pipeline: # (1) - TextGeneration(llm=InferenceEndpointsLLM(model_id="meta-llama/Meta-Llama-3.1-8B-Instruct")) # (2) - - -if __name__ == "__main__": - distiset = pipeline.run(dataset=dataset) # (3) - distiset.push_to_hub(repo_id="distilabel-example") -``` - -1. The [`Pipeline`][distilabel.pipeline.Pipeline] can take no arguments and generate a default name on it's own that will be tracked internally. - -2. Just as with the [`Pipeline`][distilabel.pipeline.Pipeline], the [`Step`][distilabel.steps.base.Step]s don't explicitly need a name. - -3. You can generate the dataset as you would normally do with Hugging Face and pass the dataset to the run method. diff --git a/docs/sections/how_to_guides/basic/llm/index.md b/docs/sections/how_to_guides/basic/llm/index.md index 46716e4352..f9dec754ae 100644 --- a/docs/sections/how_to_guides/basic/llm/index.md +++ b/docs/sections/how_to_guides/basic/llm/index.md @@ -1,4 +1,4 @@ -# Define LLMs as local or remote models +# Executing Tasks with LLMs ## Working with LLMs diff --git a/docs/sections/how_to_guides/basic/step/index.md b/docs/sections/how_to_guides/basic/step/index.md index e487cc953b..18388b8f4a 100644 --- a/docs/sections/how_to_guides/basic/step/index.md +++ b/docs/sections/how_to_guides/basic/step/index.md @@ -1,4 +1,4 @@ -# Define Steps for your Pipeline +# Steps for processing data ## Working with Steps diff --git a/docs/sections/how_to_guides/basic/task/generator_task.md b/docs/sections/how_to_guides/basic/task/generator_task.md index 040af877d9..613d8deb17 100644 --- a/docs/sections/how_to_guides/basic/task/generator_task.md +++ b/docs/sections/how_to_guides/basic/task/generator_task.md @@ -1,4 +1,4 @@ -# GeneratorTask +# GeneratorTask that produces output ## Working with GeneratorTasks diff --git a/docs/sections/how_to_guides/basic/task/index.md b/docs/sections/how_to_guides/basic/task/index.md index 0bf90c3aad..5104599700 100644 --- a/docs/sections/how_to_guides/basic/task/index.md +++ b/docs/sections/how_to_guides/basic/task/index.md @@ -1,4 +1,4 @@ -# Define Tasks that rely on LLMs +# Tasks for generating and judging with LLMs ## Working with Tasks diff --git a/docs/sections/pipeline_samples/examples/benchmarking_with_distilabel.md b/docs/sections/pipeline_samples/examples/benchmarking_with_distilabel.md index 6d5e594aa8..5f775f604e 100644 --- a/docs/sections/pipeline_samples/examples/benchmarking_with_distilabel.md +++ b/docs/sections/pipeline_samples/examples/benchmarking_with_distilabel.md @@ -1,12 +1,14 @@ --- hide: toc --- -# Benchmarking with `distilabel`: Arena Hard +# Benchmarking with `distilabel` Benchmark LLMs with `distilabel`: reproducing the Arena Hard benchmark. The script below first defines both the `ArenaHard` and the `ArenaHardResults` tasks, so as to generate responses for a given collection of prompts/questions with up to two LLMs, and then calculate the results as per the original implementation, respectively. Additionally, the second part of the example builds a `Pipeline` to run the generation on top of the prompts with `InferenceEndpointsLLM` while streaming the rest of the generations from a pre-computed set of GPT-4 generations, and then evaluate one against the other with `OpenAILLM` generating an alternate response, a comparison between the responses, and a result as A>>B, A>B, B>A, B>>A, or tie. +![Arena Hard](../../../assets/pipelines/arena-hard.png) + To run this example you will first need to install the Arena Hard optional dependencies, being `pandas`, `scikit-learn`, and `numpy`. ??? Run diff --git a/docs/sections/pipeline_samples/examples/llama_cpp_with_outlines.md b/docs/sections/pipeline_samples/examples/llama_cpp_with_outlines.md index bbd9c97ef0..9ff0bdff8f 100644 --- a/docs/sections/pipeline_samples/examples/llama_cpp_with_outlines.md +++ b/docs/sections/pipeline_samples/examples/llama_cpp_with_outlines.md @@ -1,12 +1,14 @@ --- hide: toc --- -# llama.cpp with `outlines` +# Structured generation with `outlines` Generate RPG characters following a `pydantic.BaseModel` with `outlines` in `distilabel`. This script makes use of [`LlamaCppLLM`][distilabel.llms.llamacpp.LlamaCppLLM] and the structured output capabilities thanks to [`outlines`](https://outlines-dev.github.io/outlines/welcome/) to generate RPG characters that adhere to a JSON schema. +![Arena Hard](../../../assets/pipelines/knowledge_graphs.png) + It makes use of a local model which can be downloaded using curl (explained in the script itself), and can be exchanged with other `LLMs` like [`vLLM`][distilabel.llms.vllm.vLLM]. ??? Run diff --git a/docs/sections/pipeline_samples/examples/mistralai_with_instructor.md b/docs/sections/pipeline_samples/examples/mistralai_with_instructor.md index 9d862a0056..7e081ab222 100644 --- a/docs/sections/pipeline_samples/examples/mistralai_with_instructor.md +++ b/docs/sections/pipeline_samples/examples/mistralai_with_instructor.md @@ -1,12 +1,14 @@ --- hide: toc --- -# MistralAI with `instructor` +# Structured generation with `instructor` Answer instructions with knowledge graphs defined as `pydantic.BaseModel` objects using `instructor` in `distilabel`. This script makes use of [`MistralLLM`][distilabel.llms.mistral.MistralLLM] and the structured output capabilities thanks to [`instructor`](https://python.useinstructor.com/) to generate knowledge graphs from complex topics. +![Knowledge graph figure](../../../assets/pipelines/knowledge_graphs.png) + This example is translated from this [awesome example](https://python.useinstructor.com/examples/knowledge_graph/) from `instructor` cookbook. ??? Run diff --git a/docs/sections/pipeline_samples/index.md b/docs/sections/pipeline_samples/index.md index d983f16822..ed2f516864 100644 --- a/docs/sections/pipeline_samples/index.md +++ b/docs/sections/pipeline_samples/index.md @@ -1,13 +1,13 @@ --- hide: toc --- -# Pipeline Samples +# Tutorials -- **Tutorials** provide detailed step-by-step explanations and the code used for end-to-end workflows. +- **End-to-end tutorials** provide detailed step-by-step explanations and the code used for end-to-end workflows. - **Paper implementations** provide reproductions of fundamental papers in the synthetic data domain. - **Examples** don't provide explenations but simply show code for different tasks. -## Tutorials +## End-to-end tutorials
@@ -97,7 +97,7 @@ hide: toc [:octicons-arrow-right-24: Example](examples/benchmarking_with_distilabel.md) -- __llama.cpp with outlines__ +- __Structured generation with outlines__ --- @@ -105,7 +105,7 @@ hide: toc [:octicons-arrow-right-24: Example](examples/llama_cpp_with_outlines.md) -- __MistralAI with instructor__ +- __Structured generation with instructor__ --- diff --git a/docs/sections/pipeline_samples/papers/deepseek_prover.md b/docs/sections/pipeline_samples/papers/deepseek_prover.md index 29e86c64cf..c7ecfcc32d 100644 --- a/docs/sections/pipeline_samples/papers/deepseek_prover.md +++ b/docs/sections/pipeline_samples/papers/deepseek_prover.md @@ -8,6 +8,8 @@ The authors propose a method for generating [Lean 4](https://github.com/leanprov Here we show how to deal with steps 1 and 2, but the authors ensure the theorems are checked using the [lean4](https://github.com/leanprover/lean4) program on the generated proofs, and iterate for a series of steps, fine-tuning a model on the synthetic data (DeepSeek prover 7B), regenerating the dataset, and continue the process until no further improvement is found. +![DEITA pipeline overview](../../../assets/pipelines/deepseek.png) + ### Replication !!! Note @@ -32,7 +34,7 @@ There are three components we needed to define for this pipeline, for the differ !!! Note We will use the same `LLM` for all the tasks, so we will define once and reuse it for the different tasks: - + ```python llm = InferenceEndpointsLLM( model_id="meta-llama/Meta-Llama-3-70B-Instruct", diff --git a/docs/sections/pipeline_samples/papers/deita.md b/docs/sections/pipeline_samples/papers/deita.md index d3ff1da59b..b9d3e9eea6 100644 --- a/docs/sections/pipeline_samples/papers/deita.md +++ b/docs/sections/pipeline_samples/papers/deita.md @@ -10,6 +10,8 @@ The strategy utilizes **LLMs to replace human effort in time-intensive data qual You can see that we see again the dataset of instructions/responses and we kind of reproducing the second step when we learn how to optimize the responses according to an instruction by comparing several possibilities. +![DEITA pipeline overview](../../../assets/pipelines/deita.png) + ### Datasets and budget We will dive deeper into the whole process. We will investigate each stage to efficiently select the final dataset used for supervised fine-tuning with a budget constraint. We will tackle technical challenges by explaining exactly how you would assess good data as presented in the paper. diff --git a/docs/sections/pipeline_samples/papers/instruction_backtranslation.md b/docs/sections/pipeline_samples/papers/instruction_backtranslation.md index 588fc50480..b3a6b20d68 100644 --- a/docs/sections/pipeline_samples/papers/instruction_backtranslation.md +++ b/docs/sections/pipeline_samples/papers/instruction_backtranslation.md @@ -2,6 +2,8 @@ ["Self Alignment with Instruction Backtranslation"](https://arxiv.org/abs/2308.06259) presents a scalable method to build high-quality instruction following a language model by automatically labeling human-written text with corresponding instructions. Their approach, named instruction backtranslation, starts with a language model finetuned on a small amount of seed data, and a given web corpus. The seed model is used to construct training examples by generating instruction prompts for web documents (self-augmentation), and then selecting high-quality examples from among these candidates (self-curation). This data is then used to finetune a stronger model. +![Instruction Backtranslation pipeline overview](../../../assets/pipelines/instruction_backtranslation.png) + Their self-training approach assumes access to a base language model, a small amount of seed data, and a collection of unlabelled examples, e.g. a web corpus. The unlabelled data is a large, diverse set of human-written documents that includes writing about all manner of topics humans are interested in – but crucially is not paired with instructions. A first key assumption is that there exists some subset of this very large human-written text that would be suitable as gold generations for some user instructions. A second key assumption is that they can predict instructions for these candidate gold answers that can be used as high-quality example pairs to train an instruction-following model. diff --git a/docs/sections/pipeline_samples/papers/prometheus.md b/docs/sections/pipeline_samples/papers/prometheus.md index c86ed39309..c8a3fb16c5 100644 --- a/docs/sections/pipeline_samples/papers/prometheus.md +++ b/docs/sections/pipeline_samples/papers/prometheus.md @@ -2,6 +2,8 @@ ["Prometheus 2: An Open Source Language Model Specialized in Evaluating Other Language Models"](https://arxiv.org/pdf/2405.01535) presents Prometheus 2, a new and more powerful evaluator LLM compared to Prometheus (its predecessor) presented in ["Prometheus: Inducing Fine-grained Evaluation Capability in Language Models"](https://arxiv.org/abs/2310.08491); since GPT-4, as well as other proprietary LLMs, are commonly used to assess the quality of the responses for various LLMs, but there are concerns about transparency, controllability, and affordability, that motivate the need of open-source LLMs specialized in evaluations. +![Prometheus 2 pipeline overview](../../../assets/pipelines/prometheus.png) + Existing open evaluator LMs exhibit critical shortcomings: 1. They issue scores that significantly diverge from those assigned by humans. diff --git a/docs/sections/pipeline_samples/papers/ultrafeedback.md b/docs/sections/pipeline_samples/papers/ultrafeedback.md index afa21a5717..83acc9f335 100644 --- a/docs/sections/pipeline_samples/papers/ultrafeedback.md +++ b/docs/sections/pipeline_samples/papers/ultrafeedback.md @@ -4,6 +4,8 @@ UltraFeedback collects about 64k prompts from diverse resources (including UltraChat, ShareGPT, Evol-Instruct, TruthfulQA, FalseQA, and FLAN), then they use these prompts to query multiple LLMs (commercial models, Llama models ranging 7B to 70B, and non-Llama models) and generate four different responses for each prompt, resulting in a total of 256k samples i.e. the UltraFeedback will rate four responses on every OpenAI request. +![UltraFeedback pipeline overview](../../../assets/pipelines/ultrafeedback.png) + To collect high-quality preference and textual feedback, they design a fine-grained annotation instruction, which contains four different aspects, namely instruction-following, truthfulness, honesty and helpfulness (even though within the paper they also mention a fifth one named verbalized calibration). Finally, GPT-4 is used to generate the ratings for the generated responses to the given prompt using the previously mentioned aspects. ## Replication diff --git a/docs/sections/pipeline_samples/tutorials/GenerateSentencePair.ipynb b/docs/sections/pipeline_samples/tutorials/GenerateSentencePair.ipynb index 0994c4ba4f..0779a53eb9 100644 --- a/docs/sections/pipeline_samples/tutorials/GenerateSentencePair.ipynb +++ b/docs/sections/pipeline_samples/tutorials/GenerateSentencePair.ipynb @@ -10,6 +10,8 @@ "- **Libraries**: [argilla](https://github.com/argilla-io/argilla), [hf-inference-endpoints](https://github.com/huggingface/huggingface_hub), [sentence-transformers](https://github.com/UKPLab/sentence-transformers)\n", "- **Components**: [LoadDataFromHub](https://distilabel.argilla.io/latest/components-gallery/steps/loaddatafromhub/), [GenerateSentencePair](https://distilabel.argilla.io/latest/components-gallery/tasks/generatesentencepair/), [InferenceEndpointsLLM](https://distilabel.argilla.io/latest/components-gallery/llms/inferenceendpointsllm/)\n", "\n", + "![GenerateSentencePair pipeline overview](../../../assets/pipelines/sentence-transformer.png)\n", + "\n", "!!! note\n", " For a comprehensive overview on optimizing the retrieval performance in a RAG pipeline, check this [guide](https://docs.zenml.io/user-guide/llmops-guide/finetuning-embeddings) in collaboration with [ZenML](https://github.com/zenml-io/zenml), an open-source MLOps framework designed for building portable and production-ready machine learning pipelines." ] diff --git a/docs/sections/pipeline_samples/tutorials/clean_existing_dataset.ipynb b/docs/sections/pipeline_samples/tutorials/clean_existing_dataset.ipynb index 55daf6cd99..de1e9fd264 100644 --- a/docs/sections/pipeline_samples/tutorials/clean_existing_dataset.ipynb +++ b/docs/sections/pipeline_samples/tutorials/clean_existing_dataset.ipynb @@ -13,7 +13,9 @@ "source": [ "- **Goal**: Clean an existing preference dataset by providing AI feedback on the quality of the data.\n", "- **Libraries**: [argilla](https://github.com/argilla-io/argilla), [hf-inference-endpoints](https://github.com/huggingface/huggingface_hub)\n", - "- **Components**: [LoadDataFromDicts](https://distilabel.argilla.io/dev/components-gallery/steps/loaddatafromdicts/), [UltraFeedback](https://distilabel.argilla.io/latest/components-gallery/tasks/ultrafeedback/), [KeepColumns](https://distilabel.argilla.io/latest/components-gallery/steps/groupcolumns/), [PreferenceToArgilla](https://distilabel.argilla.io/latest/components-gallery/steps/textgenerationtoargilla/), [InferenceEndpointsLLM](https://distilabel.argilla.io/latest/components-gallery/llms/inferenceendpointsllm/), [GlobalStep](../../how_to_guides/basic/step/global_step.md)" + "- **Components**: [LoadDataFromDicts](https://distilabel.argilla.io/dev/components-gallery/steps/loaddatafromdicts/), [UltraFeedback](https://distilabel.argilla.io/latest/components-gallery/tasks/ultrafeedback/), [KeepColumns](https://distilabel.argilla.io/latest/components-gallery/steps/groupcolumns/), [PreferenceToArgilla](https://distilabel.argilla.io/latest/components-gallery/steps/textgenerationtoargilla/), [InferenceEndpointsLLM](https://distilabel.argilla.io/latest/components-gallery/llms/inferenceendpointsllm/), [GlobalStep](../../how_to_guides/basic/step/global_step.md)\n", + "\n", + "![Knowledge graph figure](../../../assets/pipelines/clean-dataset.png)" ] }, { diff --git a/docs/sections/pipeline_samples/tutorials/generate_preference_dataset.ipynb b/docs/sections/pipeline_samples/tutorials/generate_preference_dataset.ipynb index 187de7420d..a81e8051ad 100644 --- a/docs/sections/pipeline_samples/tutorials/generate_preference_dataset.ipynb +++ b/docs/sections/pipeline_samples/tutorials/generate_preference_dataset.ipynb @@ -13,7 +13,9 @@ "source": [ "- **Goal**: Generate a synthetic preference dataset for DPO/ORPO.\n", "- **Libraries**: [argilla](https://github.com/argilla-io/argilla), [hf-inference-endpoints](https://github.com/huggingface/huggingface_hub)\n", - "- **Components**: [LoadDataFromHub](https://distilabel.argilla.io/latest/components-gallery/steps/loaddatafromhub/), [TextGeneration](https://distilabel.argilla.io/latest/components-gallery/tasks/textgeneration/), [UltraFeedback](https://distilabel.argilla.io/latest/components-gallery/tasks/ultrafeedback/), [GroupColumns](https://distilabel.argilla.io/latest/components-gallery/steps/groupcolumns/), [FormatTextGenerationDPO](https://distilabel.argilla.io/latest/components-gallery/steps/formattextgenerationdpo/), [PreferenceToArgilla](https://distilabel.argilla.io/latest/components-gallery/steps/textgenerationtoargilla/), [InferenceEndpointsLLM](https://distilabel.argilla.io/latest/components-gallery/llms/inferenceendpointsllm/)" + "- **Components**: [LoadDataFromHub](https://distilabel.argilla.io/latest/components-gallery/steps/loaddatafromhub/), [TextGeneration](https://distilabel.argilla.io/latest/components-gallery/tasks/textgeneration/), [UltraFeedback](https://distilabel.argilla.io/latest/components-gallery/tasks/ultrafeedback/), [GroupColumns](https://distilabel.argilla.io/latest/components-gallery/steps/groupcolumns/), [FormatTextGenerationDPO](https://distilabel.argilla.io/latest/components-gallery/steps/formattextgenerationdpo/), [PreferenceToArgilla](https://distilabel.argilla.io/latest/components-gallery/steps/textgenerationtoargilla/), [InferenceEndpointsLLM](https://distilabel.argilla.io/latest/components-gallery/llms/inferenceendpointsllm/)\n", + "\n", + "![Knowledge graph figure](../../../assets/pipelines/generate-preference-dataset.png)" ] }, { diff --git a/mkdocs.yml b/mkdocs.yml index 07dccfc4fb..3f53b4d4a8 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -174,29 +174,29 @@ nav: - How-to guides: - "sections/how_to_guides/index.md" - Basic: - - Define Steps for your Pipeline: + - Steps for processing data: - "sections/how_to_guides/basic/step/index.md" - GeneratorStep: "sections/how_to_guides/basic/step/generator_step.md" - GlobalStep: "sections/how_to_guides/basic/step/global_step.md" - - Define Tasks that rely on LLMs: + - Tasks for generating and judging with LLMs: - "sections/how_to_guides/basic/task/index.md" - GeneratorTask: "sections/how_to_guides/basic/task/generator_task.md" - - Define LLMs as local or remote models: "sections/how_to_guides/basic/llm/index.md" + - Executing Tasks with LLMs: "sections/how_to_guides/basic/llm/index.md" - Execute Steps and Tasks in a Pipeline: "sections/how_to_guides/basic/pipeline/index.md" - Advanced: - - Using the Distiset dataset object: "sections/how_to_guides/advanced/distiset.md" - - Cache and recover pipeline executions: "sections/how_to_guides/advanced/caching.md" - - Export data to Argilla: "sections/how_to_guides/advanced/argilla.md" + - The Distiset dataset object: "sections/how_to_guides/advanced/distiset.md" + - Cachinc and recovering pipelines: "sections/how_to_guides/advanced/caching.md" + - Exporting data to Argilla: "sections/how_to_guides/advanced/argilla.md" - Structured data generation: "sections/how_to_guides/advanced/structured_generation.md" - Offline Batch Generation: "sections/how_to_guides/advanced/offline_batch_generation.md" - - Specify requirements for pipelines and steps: "sections/how_to_guides/advanced/pipeline_requirements.md" + - Specifying requirements for pipelines and steps: "sections/how_to_guides/advanced/pipeline_requirements.md" - Using CLI to explore and re-run existing Pipelines: "sections/how_to_guides/advanced/cli/index.md" - Using a file system to pass data of batches between steps: "sections/how_to_guides/advanced/fs_to_pass_data.md" - Assigning resources to a step: "sections/how_to_guides/advanced/assigning_resources_to_step.md" - Saving step generated artifacts: "sections/how_to_guides/advanced/saving_step_generated_artifacts.md" - Serving an LLM for sharing it between several tasks: "sections/how_to_guides/advanced/serving_an_llm_for_reuse.md" - Scaling and distributing a pipeline with Ray: "sections/how_to_guides/advanced/scaling_with_ray.md" - - Pipeline Samples: + - Tutorials: - "sections/pipeline_samples/index.md" - Tutorials: - Generate a preference dataset: "sections/pipeline_samples/tutorials/generate_preference_dataset.ipynb" @@ -210,8 +210,8 @@ nav: - UltraFeedback: "sections/pipeline_samples/papers/ultrafeedback.md" - Examples: - Benchmarking with distilabel: "sections/pipeline_samples/examples/benchmarking_with_distilabel.md" - - Llama cpp with outlines: "sections/pipeline_samples/examples/llama_cpp_with_outlines.md" - - MistralAI with instructor: "sections/pipeline_samples/examples/mistralai_with_instructor.md" + - Structured generation with outlines: "sections/pipeline_samples/examples/llama_cpp_with_outlines.md" + - Structured generation with instructor: "sections/pipeline_samples/examples/mistralai_with_instructor.md" - API Reference: - Step: - "api/step/index.md" diff --git a/pdm.lock b/pdm.lock new file mode 100644 index 0000000000..453da56d45 --- /dev/null +++ b/pdm.lock @@ -0,0 +1,2874 @@ +# This file is @generated by PDM. +# It is not intended for manual editing. + +[metadata] +groups = ["default", "docs"] +strategy = ["inherit_metadata"] +lock_version = "4.5.0" +content_hash = "sha256:40f8a1dc7b5bcf50262cca3a8692ff149c88af6bfab71c1469310a98f38b0643" + +[[metadata.targets]] +requires_python = ">=3.9" + +[[package]] +name = "aiohappyeyeballs" +version = "2.4.0" +requires_python = ">=3.8" +summary = "Happy Eyeballs for asyncio" +groups = ["default"] +files = [ + {file = "aiohappyeyeballs-2.4.0-py3-none-any.whl", hash = "sha256:7ce92076e249169a13c2f49320d1967425eaf1f407522d707d59cac7628d62bd"}, + {file = "aiohappyeyeballs-2.4.0.tar.gz", hash = "sha256:55a1714f084e63d49639800f95716da97a1f173d46a16dfcfda0016abb93b6b2"}, +] + +[[package]] +name = "aiohttp" +version = "3.10.5" +requires_python = ">=3.8" +summary = "Async http client/server framework (asyncio)" +groups = ["default"] +dependencies = [ + "aiohappyeyeballs>=2.3.0", + "aiosignal>=1.1.2", + "async-timeout<5.0,>=4.0; python_version < \"3.11\"", + "attrs>=17.3.0", + "frozenlist>=1.1.1", + "multidict<7.0,>=4.5", + "yarl<2.0,>=1.0", +] +files = [ + {file = "aiohttp-3.10.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:18a01eba2574fb9edd5f6e5fb25f66e6ce061da5dab5db75e13fe1558142e0a3"}, + {file = "aiohttp-3.10.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:94fac7c6e77ccb1ca91e9eb4cb0ac0270b9fb9b289738654120ba8cebb1189c6"}, + {file = "aiohttp-3.10.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2f1f1c75c395991ce9c94d3e4aa96e5c59c8356a15b1c9231e783865e2772699"}, + {file = "aiohttp-3.10.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f7acae3cf1a2a2361ec4c8e787eaaa86a94171d2417aae53c0cca6ca3118ff6"}, + {file = "aiohttp-3.10.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:94c4381ffba9cc508b37d2e536b418d5ea9cfdc2848b9a7fea6aebad4ec6aac1"}, + {file = "aiohttp-3.10.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c31ad0c0c507894e3eaa843415841995bf8de4d6b2d24c6e33099f4bc9fc0d4f"}, + {file = "aiohttp-3.10.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0912b8a8fadeb32ff67a3ed44249448c20148397c1ed905d5dac185b4ca547bb"}, + {file = "aiohttp-3.10.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d93400c18596b7dc4794d48a63fb361b01a0d8eb39f28800dc900c8fbdaca91"}, + {file = "aiohttp-3.10.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d00f3c5e0d764a5c9aa5a62d99728c56d455310bcc288a79cab10157b3af426f"}, + {file = "aiohttp-3.10.5-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:d742c36ed44f2798c8d3f4bc511f479b9ceef2b93f348671184139e7d708042c"}, + {file = "aiohttp-3.10.5-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:814375093edae5f1cb31e3407997cf3eacefb9010f96df10d64829362ae2df69"}, + {file = "aiohttp-3.10.5-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8224f98be68a84b19f48e0bdc14224b5a71339aff3a27df69989fa47d01296f3"}, + {file = "aiohttp-3.10.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d9a487ef090aea982d748b1b0d74fe7c3950b109df967630a20584f9a99c0683"}, + {file = "aiohttp-3.10.5-cp310-cp310-win32.whl", hash = "sha256:d9ef084e3dc690ad50137cc05831c52b6ca428096e6deb3c43e95827f531d5ef"}, + {file = "aiohttp-3.10.5-cp310-cp310-win_amd64.whl", hash = "sha256:66bf9234e08fe561dccd62083bf67400bdbf1c67ba9efdc3dac03650e97c6088"}, + {file = "aiohttp-3.10.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8c6a4e5e40156d72a40241a25cc226051c0a8d816610097a8e8f517aeacd59a2"}, + {file = "aiohttp-3.10.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2c634a3207a5445be65536d38c13791904fda0748b9eabf908d3fe86a52941cf"}, + {file = "aiohttp-3.10.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4aff049b5e629ef9b3e9e617fa6e2dfeda1bf87e01bcfecaf3949af9e210105e"}, + {file = "aiohttp-3.10.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1942244f00baaacaa8155eca94dbd9e8cc7017deb69b75ef67c78e89fdad3c77"}, + {file = "aiohttp-3.10.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e04a1f2a65ad2f93aa20f9ff9f1b672bf912413e5547f60749fa2ef8a644e061"}, + {file = "aiohttp-3.10.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7f2bfc0032a00405d4af2ba27f3c429e851d04fad1e5ceee4080a1c570476697"}, + {file = "aiohttp-3.10.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:424ae21498790e12eb759040bbb504e5e280cab64693d14775c54269fd1d2bb7"}, + {file = "aiohttp-3.10.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:975218eee0e6d24eb336d0328c768ebc5d617609affaca5dbbd6dd1984f16ed0"}, + {file = "aiohttp-3.10.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4120d7fefa1e2d8fb6f650b11489710091788de554e2b6f8347c7a20ceb003f5"}, + {file = "aiohttp-3.10.5-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:b90078989ef3fc45cf9221d3859acd1108af7560c52397ff4ace8ad7052a132e"}, + {file = "aiohttp-3.10.5-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:ba5a8b74c2a8af7d862399cdedce1533642fa727def0b8c3e3e02fcb52dca1b1"}, + {file = "aiohttp-3.10.5-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:02594361128f780eecc2a29939d9dfc870e17b45178a867bf61a11b2a4367277"}, + {file = "aiohttp-3.10.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:8fb4fc029e135859f533025bc82047334e24b0d489e75513144f25408ecaf058"}, + {file = "aiohttp-3.10.5-cp311-cp311-win32.whl", hash = "sha256:e1ca1ef5ba129718a8fc827b0867f6aa4e893c56eb00003b7367f8a733a9b072"}, + {file = "aiohttp-3.10.5-cp311-cp311-win_amd64.whl", hash = "sha256:349ef8a73a7c5665cca65c88ab24abe75447e28aa3bc4c93ea5093474dfdf0ff"}, + {file = "aiohttp-3.10.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:305be5ff2081fa1d283a76113b8df7a14c10d75602a38d9f012935df20731487"}, + {file = "aiohttp-3.10.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3a1c32a19ee6bbde02f1cb189e13a71b321256cc1d431196a9f824050b160d5a"}, + {file = "aiohttp-3.10.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:61645818edd40cc6f455b851277a21bf420ce347baa0b86eaa41d51ef58ba23d"}, + {file = "aiohttp-3.10.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c225286f2b13bab5987425558baa5cbdb2bc925b2998038fa028245ef421e75"}, + {file = "aiohttp-3.10.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8ba01ebc6175e1e6b7275c907a3a36be48a2d487549b656aa90c8a910d9f3178"}, + {file = "aiohttp-3.10.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8eaf44ccbc4e35762683078b72bf293f476561d8b68ec8a64f98cf32811c323e"}, + {file = "aiohttp-3.10.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1c43eb1ab7cbf411b8e387dc169acb31f0ca0d8c09ba63f9eac67829585b44f"}, + {file = "aiohttp-3.10.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:de7a5299827253023c55ea549444e058c0eb496931fa05d693b95140a947cb73"}, + {file = "aiohttp-3.10.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4790f0e15f00058f7599dab2b206d3049d7ac464dc2e5eae0e93fa18aee9e7bf"}, + {file = "aiohttp-3.10.5-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:44b324a6b8376a23e6ba25d368726ee3bc281e6ab306db80b5819999c737d820"}, + {file = "aiohttp-3.10.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:0d277cfb304118079e7044aad0b76685d30ecb86f83a0711fc5fb257ffe832ca"}, + {file = "aiohttp-3.10.5-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:54d9ddea424cd19d3ff6128601a4a4d23d54a421f9b4c0fff740505813739a91"}, + {file = "aiohttp-3.10.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4f1c9866ccf48a6df2b06823e6ae80573529f2af3a0992ec4fe75b1a510df8a6"}, + {file = "aiohttp-3.10.5-cp312-cp312-win32.whl", hash = "sha256:dc4826823121783dccc0871e3f405417ac116055bf184ac04c36f98b75aacd12"}, + {file = "aiohttp-3.10.5-cp312-cp312-win_amd64.whl", hash = "sha256:22c0a23a3b3138a6bf76fc553789cb1a703836da86b0f306b6f0dc1617398abc"}, + {file = "aiohttp-3.10.5-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7f6b639c36734eaa80a6c152a238242bedcee9b953f23bb887e9102976343092"}, + {file = "aiohttp-3.10.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f29930bc2921cef955ba39a3ff87d2c4398a0394ae217f41cb02d5c26c8b1b77"}, + {file = "aiohttp-3.10.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f489a2c9e6455d87eabf907ac0b7d230a9786be43fbe884ad184ddf9e9c1e385"}, + {file = "aiohttp-3.10.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:123dd5b16b75b2962d0fff566effb7a065e33cd4538c1692fb31c3bda2bfb972"}, + {file = "aiohttp-3.10.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b98e698dc34966e5976e10bbca6d26d6724e6bdea853c7c10162a3235aba6e16"}, + {file = "aiohttp-3.10.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3b9162bab7e42f21243effc822652dc5bb5e8ff42a4eb62fe7782bcbcdfacf6"}, + {file = "aiohttp-3.10.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1923a5c44061bffd5eebeef58cecf68096e35003907d8201a4d0d6f6e387ccaa"}, + {file = "aiohttp-3.10.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d55f011da0a843c3d3df2c2cf4e537b8070a419f891c930245f05d329c4b0689"}, + {file = "aiohttp-3.10.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:afe16a84498441d05e9189a15900640a2d2b5e76cf4efe8cbb088ab4f112ee57"}, + {file = "aiohttp-3.10.5-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f8112fb501b1e0567a1251a2fd0747baae60a4ab325a871e975b7bb67e59221f"}, + {file = "aiohttp-3.10.5-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:1e72589da4c90337837fdfe2026ae1952c0f4a6e793adbbfbdd40efed7c63599"}, + {file = "aiohttp-3.10.5-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:4d46c7b4173415d8e583045fbc4daa48b40e31b19ce595b8d92cf639396c15d5"}, + {file = "aiohttp-3.10.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:33e6bc4bab477c772a541f76cd91e11ccb6d2efa2b8d7d7883591dfb523e5987"}, + {file = "aiohttp-3.10.5-cp313-cp313-win32.whl", hash = "sha256:c58c6837a2c2a7cf3133983e64173aec11f9c2cd8e87ec2fdc16ce727bcf1a04"}, + {file = "aiohttp-3.10.5-cp313-cp313-win_amd64.whl", hash = "sha256:38172a70005252b6893088c0f5e8a47d173df7cc2b2bd88650957eb84fcf5022"}, + {file = "aiohttp-3.10.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7e2fe37ac654032db1f3499fe56e77190282534810e2a8e833141a021faaab0e"}, + {file = "aiohttp-3.10.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f5bf3ead3cb66ab990ee2561373b009db5bc0e857549b6c9ba84b20bc462e172"}, + {file = "aiohttp-3.10.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1b2c16a919d936ca87a3c5f0e43af12a89a3ce7ccbce59a2d6784caba945b68b"}, + {file = "aiohttp-3.10.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad146dae5977c4dd435eb31373b3fe9b0b1bf26858c6fc452bf6af394067e10b"}, + {file = "aiohttp-3.10.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8c5c6fa16412b35999320f5c9690c0f554392dc222c04e559217e0f9ae244b92"}, + {file = "aiohttp-3.10.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:95c4dc6f61d610bc0ee1edc6f29d993f10febfe5b76bb470b486d90bbece6b22"}, + {file = "aiohttp-3.10.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da452c2c322e9ce0cfef392e469a26d63d42860f829026a63374fde6b5c5876f"}, + {file = "aiohttp-3.10.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:898715cf566ec2869d5cb4d5fb4be408964704c46c96b4be267442d265390f32"}, + {file = "aiohttp-3.10.5-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:391cc3a9c1527e424c6865e087897e766a917f15dddb360174a70467572ac6ce"}, + {file = "aiohttp-3.10.5-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:380f926b51b92d02a34119d072f178d80bbda334d1a7e10fa22d467a66e494db"}, + {file = "aiohttp-3.10.5-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ce91db90dbf37bb6fa0997f26574107e1b9d5ff939315247b7e615baa8ec313b"}, + {file = "aiohttp-3.10.5-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:9093a81e18c45227eebe4c16124ebf3e0d893830c6aca7cc310bfca8fe59d857"}, + {file = "aiohttp-3.10.5-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:ee40b40aa753d844162dcc80d0fe256b87cba48ca0054f64e68000453caead11"}, + {file = "aiohttp-3.10.5-cp39-cp39-win32.whl", hash = "sha256:03f2645adbe17f274444953bdea69f8327e9d278d961d85657cb0d06864814c1"}, + {file = "aiohttp-3.10.5-cp39-cp39-win_amd64.whl", hash = "sha256:d17920f18e6ee090bdd3d0bfffd769d9f2cb4c8ffde3eb203777a3895c128862"}, + {file = "aiohttp-3.10.5.tar.gz", hash = "sha256:f071854b47d39591ce9a17981c46790acb30518e2f83dfca8db2dfa091178691"}, +] + +[[package]] +name = "aiosignal" +version = "1.3.1" +requires_python = ">=3.7" +summary = "aiosignal: a list of registered asynchronous callbacks" +groups = ["default"] +dependencies = [ + "frozenlist>=1.1.0", +] +files = [ + {file = "aiosignal-1.3.1-py3-none-any.whl", hash = "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17"}, + {file = "aiosignal-1.3.1.tar.gz", hash = "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc"}, +] + +[[package]] +name = "annotated-types" +version = "0.7.0" +requires_python = ">=3.8" +summary = "Reusable constraint types to use with typing.Annotated" +groups = ["default"] +dependencies = [ + "typing-extensions>=4.0.0; python_version < \"3.9\"", +] +files = [ + {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, + {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, +] + +[[package]] +name = "anyio" +version = "4.4.0" +requires_python = ">=3.8" +summary = "High level compatibility layer for multiple asynchronous event loop implementations" +groups = ["default"] +dependencies = [ + "exceptiongroup>=1.0.2; python_version < \"3.11\"", + "idna>=2.8", + "sniffio>=1.1", + "typing-extensions>=4.1; python_version < \"3.11\"", +] +files = [ + {file = "anyio-4.4.0-py3-none-any.whl", hash = "sha256:c1b2d8f46a8a812513012e1107cb0e68c17159a7a594208005a57dc776e1bdc7"}, + {file = "anyio-4.4.0.tar.gz", hash = "sha256:5aadc6a1bbb7cdb0bede386cac5e2940f5e2ff3aa20277e991cf028e0585ce94"}, +] + +[[package]] +name = "async-timeout" +version = "4.0.3" +requires_python = ">=3.7" +summary = "Timeout context manager for asyncio programs" +groups = ["default"] +marker = "python_version < \"3.11\"" +dependencies = [ + "typing-extensions>=3.6.5; python_version < \"3.8\"", +] +files = [ + {file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"}, + {file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"}, +] + +[[package]] +name = "attrs" +version = "24.2.0" +requires_python = ">=3.7" +summary = "Classes Without Boilerplate" +groups = ["default", "docs"] +dependencies = [ + "importlib-metadata; python_version < \"3.8\"", +] +files = [ + {file = "attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2"}, + {file = "attrs-24.2.0.tar.gz", hash = "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346"}, +] + +[[package]] +name = "babel" +version = "2.16.0" +requires_python = ">=3.8" +summary = "Internationalization utilities" +groups = ["docs"] +dependencies = [ + "pytz>=2015.7; python_version < \"3.9\"", +] +files = [ + {file = "babel-2.16.0-py3-none-any.whl", hash = "sha256:368b5b98b37c06b7daf6696391c3240c938b37767d4584413e8438c5c435fa8b"}, + {file = "babel-2.16.0.tar.gz", hash = "sha256:d1f3554ca26605fe173f3de0c65f750f5a42f924499bf134de6423582298e316"}, +] + +[[package]] +name = "beautifulsoup4" +version = "4.12.3" +requires_python = ">=3.6.0" +summary = "Screen-scraping library" +groups = ["docs"] +dependencies = [ + "soupsieve>1.2", +] +files = [ + {file = "beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed"}, + {file = "beautifulsoup4-4.12.3.tar.gz", hash = "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051"}, +] + +[[package]] +name = "bleach" +version = "6.1.0" +requires_python = ">=3.8" +summary = "An easy safelist-based HTML-sanitizing tool." +groups = ["docs"] +dependencies = [ + "six>=1.9.0", + "webencodings", +] +files = [ + {file = "bleach-6.1.0-py3-none-any.whl", hash = "sha256:3225f354cfc436b9789c66c4ee030194bee0568fbf9cbdad3bc8b5c26c5f12b6"}, + {file = "bleach-6.1.0.tar.gz", hash = "sha256:0a31f1837963c41d46bbf1331b8778e1308ea0791db03cc4e7357b97cf42a8fe"}, +] + +[[package]] +name = "cairocffi" +version = "1.7.1" +requires_python = ">=3.8" +summary = "cffi-based cairo bindings for Python" +groups = ["docs"] +dependencies = [ + "cffi>=1.1.0", +] +files = [ + {file = "cairocffi-1.7.1-py3-none-any.whl", hash = "sha256:9803a0e11f6c962f3b0ae2ec8ba6ae45e957a146a004697a1ac1bbf16b073b3f"}, + {file = "cairocffi-1.7.1.tar.gz", hash = "sha256:2e48ee864884ec4a3a34bfa8c9ab9999f688286eb714a15a43ec9d068c36557b"}, +] + +[[package]] +name = "cairosvg" +version = "2.7.1" +requires_python = ">=3.5" +summary = "A Simple SVG Converter based on Cairo" +groups = ["docs"] +dependencies = [ + "cairocffi", + "cssselect2", + "defusedxml", + "pillow", + "tinycss2", +] +files = [ + {file = "CairoSVG-2.7.1-py3-none-any.whl", hash = "sha256:8a5222d4e6c3f86f1f7046b63246877a63b49923a1cd202184c3a634ef546b3b"}, + {file = "CairoSVG-2.7.1.tar.gz", hash = "sha256:432531d72347291b9a9ebfb6777026b607563fd8719c46ee742db0aef7271ba0"}, +] + +[[package]] +name = "certifi" +version = "2024.8.30" +requires_python = ">=3.6" +summary = "Python package for providing Mozilla's CA Bundle." +groups = ["default", "docs"] +files = [ + {file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"}, + {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, +] + +[[package]] +name = "cffi" +version = "1.17.1" +requires_python = ">=3.8" +summary = "Foreign Function Interface for Python calling C code." +groups = ["docs"] +dependencies = [ + "pycparser", +] +files = [ + {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"}, + {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6"}, + {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17"}, + {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8"}, + {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e"}, + {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be"}, + {file = "cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c"}, + {file = "cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15"}, + {file = "cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401"}, + {file = "cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6"}, + {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d"}, + {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6"}, + {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f"}, + {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b"}, + {file = "cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655"}, + {file = "cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0"}, + {file = "cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4"}, + {file = "cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99"}, + {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93"}, + {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3"}, + {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8"}, + {file = "cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65"}, + {file = "cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903"}, + {file = "cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e"}, + {file = "cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4"}, + {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd"}, + {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed"}, + {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9"}, + {file = "cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d"}, + {file = "cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a"}, + {file = "cffi-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16"}, + {file = "cffi-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0"}, + {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3"}, + {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595"}, + {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a"}, + {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e"}, + {file = "cffi-1.17.1-cp39-cp39-win32.whl", hash = "sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7"}, + {file = "cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662"}, + {file = "cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824"}, +] + +[[package]] +name = "charset-normalizer" +version = "3.3.2" +requires_python = ">=3.7.0" +summary = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +groups = ["default", "docs"] +files = [ + {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, + {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, +] + +[[package]] +name = "click" +version = "8.1.7" +requires_python = ">=3.7" +summary = "Composable command line interface toolkit" +groups = ["default", "docs"] +dependencies = [ + "colorama; platform_system == \"Windows\"", + "importlib-metadata; python_version < \"3.8\"", +] +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[[package]] +name = "colorama" +version = "0.4.6" +requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +summary = "Cross-platform colored terminal text." +groups = ["default", "docs"] +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "cssselect2" +version = "0.7.0" +requires_python = ">=3.7" +summary = "CSS selectors for Python ElementTree" +groups = ["docs"] +dependencies = [ + "tinycss2", + "webencodings", +] +files = [ + {file = "cssselect2-0.7.0-py3-none-any.whl", hash = "sha256:fd23a65bfd444595913f02fc71f6b286c29261e354c41d722ca7a261a49b5969"}, + {file = "cssselect2-0.7.0.tar.gz", hash = "sha256:1ccd984dab89fc68955043aca4e1b03e0cf29cad9880f6e28e3ba7a74b14aa5a"}, +] + +[[package]] +name = "datasets" +version = "3.0.0" +requires_python = ">=3.8.0" +summary = "HuggingFace community-driven open-source library of datasets" +groups = ["default"] +dependencies = [ + "aiohttp", + "dill<0.3.9,>=0.3.0", + "filelock", + "fsspec[http]<=2024.6.1,>=2023.1.0", + "huggingface-hub>=0.22.0", + "multiprocess", + "numpy>=1.17", + "packaging", + "pandas", + "pyarrow>=15.0.0", + "pyyaml>=5.1", + "requests>=2.32.2", + "tqdm>=4.66.3", + "xxhash", +] +files = [ + {file = "datasets-3.0.0-py3-none-any.whl", hash = "sha256:c23fefb6c953dcb1cd5f6deb6c502729c733ef98791e0c3f2d80c7ca2d9a01dd"}, + {file = "datasets-3.0.0.tar.gz", hash = "sha256:592317eb137f0fc5aac068ff283ba13c3c66d10c9c034d44bc8aa584126cf3e2"}, +] + +[[package]] +name = "defusedxml" +version = "0.7.1" +requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +summary = "XML bomb protection for Python stdlib modules" +groups = ["docs"] +files = [ + {file = "defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61"}, + {file = "defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69"}, +] + +[[package]] +name = "dill" +version = "0.3.8" +requires_python = ">=3.8" +summary = "serialize all of Python" +groups = ["default"] +files = [ + {file = "dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"}, + {file = "dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca"}, +] + +[[package]] +name = "exceptiongroup" +version = "1.2.2" +requires_python = ">=3.7" +summary = "Backport of PEP 654 (exception groups)" +groups = ["default"] +marker = "python_version < \"3.11\"" +files = [ + {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, + {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, +] + +[[package]] +name = "fastjsonschema" +version = "2.20.0" +summary = "Fastest Python implementation of JSON schema" +groups = ["docs"] +files = [ + {file = "fastjsonschema-2.20.0-py3-none-any.whl", hash = "sha256:5875f0b0fa7a0043a91e93a9b8f793bcbbba9691e7fd83dca95c28ba26d21f0a"}, + {file = "fastjsonschema-2.20.0.tar.gz", hash = "sha256:3d48fc5300ee96f5d116f10fe6f28d938e6008f59a6a025c2649475b87f76a23"}, +] + +[[package]] +name = "filelock" +version = "3.16.0" +requires_python = ">=3.8" +summary = "A platform independent file lock." +groups = ["default"] +files = [ + {file = "filelock-3.16.0-py3-none-any.whl", hash = "sha256:f6ed4c963184f4c84dd5557ce8fece759a3724b37b80c6c4f20a2f63a4dc6609"}, + {file = "filelock-3.16.0.tar.gz", hash = "sha256:81de9eb8453c769b63369f87f11131a7ab04e367f8d97ad39dc230daa07e3bec"}, +] + +[[package]] +name = "frozenlist" +version = "1.4.1" +requires_python = ">=3.8" +summary = "A list-like structure which implements collections.abc.MutableSequence" +groups = ["default"] +files = [ + {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f9aa1878d1083b276b0196f2dfbe00c9b7e752475ed3b682025ff20c1c1f51ac"}, + {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:29acab3f66f0f24674b7dc4736477bcd4bc3ad4b896f5f45379a67bce8b96868"}, + {file = "frozenlist-1.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:74fb4bee6880b529a0c6560885fce4dc95936920f9f20f53d99a213f7bf66776"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:590344787a90ae57d62511dd7c736ed56b428f04cd8c161fcc5e7232c130c69a"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:068b63f23b17df8569b7fdca5517edef76171cf3897eb68beb01341131fbd2ad"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c849d495bf5154cd8da18a9eb15db127d4dba2968d88831aff6f0331ea9bd4c"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9750cc7fe1ae3b1611bb8cfc3f9ec11d532244235d75901fb6b8e42ce9229dfe"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9b2de4cf0cdd5bd2dee4c4f63a653c61d2408055ab77b151c1957f221cabf2a"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0633c8d5337cb5c77acbccc6357ac49a1770b8c487e5b3505c57b949b4b82e98"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:27657df69e8801be6c3638054e202a135c7f299267f1a55ed3a598934f6c0d75"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:f9a3ea26252bd92f570600098783d1371354d89d5f6b7dfd87359d669f2109b5"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:4f57dab5fe3407b6c0c1cc907ac98e8a189f9e418f3b6e54d65a718aaafe3950"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e02a0e11cf6597299b9f3bbd3f93d79217cb90cfd1411aec33848b13f5c656cc"}, + {file = "frozenlist-1.4.1-cp310-cp310-win32.whl", hash = "sha256:a828c57f00f729620a442881cc60e57cfcec6842ba38e1b19fd3e47ac0ff8dc1"}, + {file = "frozenlist-1.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:f56e2333dda1fe0f909e7cc59f021eba0d2307bc6f012a1ccf2beca6ba362439"}, + {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a0cb6f11204443f27a1628b0e460f37fb30f624be6051d490fa7d7e26d4af3d0"}, + {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b46c8ae3a8f1f41a0d2ef350c0b6e65822d80772fe46b653ab6b6274f61d4a49"}, + {file = "frozenlist-1.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fde5bd59ab5357e3853313127f4d3565fc7dad314a74d7b5d43c22c6a5ed2ced"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:722e1124aec435320ae01ee3ac7bec11a5d47f25d0ed6328f2273d287bc3abb0"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2471c201b70d58a0f0c1f91261542a03d9a5e088ed3dc6c160d614c01649c106"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c757a9dd70d72b076d6f68efdbb9bc943665ae954dad2801b874c8c69e185068"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f146e0911cb2f1da549fc58fc7bcd2b836a44b79ef871980d605ec392ff6b0d2"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f9c515e7914626b2a2e1e311794b4c35720a0be87af52b79ff8e1429fc25f19"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c302220494f5c1ebeb0912ea782bcd5e2f8308037b3c7553fad0e48ebad6ad82"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:442acde1e068288a4ba7acfe05f5f343e19fac87bfc96d89eb886b0363e977ec"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:1b280e6507ea8a4fa0c0a7150b4e526a8d113989e28eaaef946cc77ffd7efc0a"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:fe1a06da377e3a1062ae5fe0926e12b84eceb8a50b350ddca72dc85015873f74"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:db9e724bebd621d9beca794f2a4ff1d26eed5965b004a97f1f1685a173b869c2"}, + {file = "frozenlist-1.4.1-cp311-cp311-win32.whl", hash = "sha256:e774d53b1a477a67838a904131c4b0eef6b3d8a651f8b138b04f748fccfefe17"}, + {file = "frozenlist-1.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:fb3c2db03683b5767dedb5769b8a40ebb47d6f7f45b1b3e3b4b51ec8ad9d9825"}, + {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1979bc0aeb89b33b588c51c54ab0161791149f2461ea7c7c946d95d5f93b56ae"}, + {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cc7b01b3754ea68a62bd77ce6020afaffb44a590c2289089289363472d13aedb"}, + {file = "frozenlist-1.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c9c92be9fd329ac801cc420e08452b70e7aeab94ea4233a4804f0915c14eba9b"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c3894db91f5a489fc8fa6a9991820f368f0b3cbdb9cd8849547ccfab3392d86"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba60bb19387e13597fb059f32cd4d59445d7b18b69a745b8f8e5db0346f33480"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8aefbba5f69d42246543407ed2461db31006b0f76c4e32dfd6f42215a2c41d09"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780d3a35680ced9ce682fbcf4cb9c2bad3136eeff760ab33707b71db84664e3a"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9acbb16f06fe7f52f441bb6f413ebae6c37baa6ef9edd49cdd567216da8600cd"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:23b701e65c7b36e4bf15546a89279bd4d8675faabc287d06bbcfac7d3c33e1e6"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3e0153a805a98f5ada7e09826255ba99fb4f7524bb81bf6b47fb702666484ae1"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:dd9b1baec094d91bf36ec729445f7769d0d0cf6b64d04d86e45baf89e2b9059b"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:1a4471094e146b6790f61b98616ab8e44f72661879cc63fa1049d13ef711e71e"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5667ed53d68d91920defdf4035d1cdaa3c3121dc0b113255124bcfada1cfa1b8"}, + {file = "frozenlist-1.4.1-cp312-cp312-win32.whl", hash = "sha256:beee944ae828747fd7cb216a70f120767fc9f4f00bacae8543c14a6831673f89"}, + {file = "frozenlist-1.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:64536573d0a2cb6e625cf309984e2d873979709f2cf22839bf2d61790b448ad5"}, + {file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bfa4a17e17ce9abf47a74ae02f32d014c5e9404b6d9ac7f729e01562bbee601e"}, + {file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b7e3ed87d4138356775346e6845cccbe66cd9e207f3cd11d2f0b9fd13681359d"}, + {file = "frozenlist-1.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c99169d4ff810155ca50b4da3b075cbde79752443117d89429595c2e8e37fed8"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edb678da49d9f72c9f6c609fbe41a5dfb9a9282f9e6a2253d5a91e0fc382d7c0"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6db4667b187a6742b33afbbaf05a7bc551ffcf1ced0000a571aedbb4aa42fc7b"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55fdc093b5a3cb41d420884cdaf37a1e74c3c37a31f46e66286d9145d2063bd0"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82e8211d69a4f4bc360ea22cd6555f8e61a1bd211d1d5d39d3d228b48c83a897"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89aa2c2eeb20957be2d950b85974b30a01a762f3308cd02bb15e1ad632e22dc7"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9d3e0c25a2350080e9319724dede4f31f43a6c9779be48021a7f4ebde8b2d742"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7268252af60904bf52c26173cbadc3a071cece75f873705419c8681f24d3edea"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:0c250a29735d4f15321007fb02865f0e6b6a41a6b88f1f523ca1596ab5f50bd5"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:96ec70beabbd3b10e8bfe52616a13561e58fe84c0101dd031dc78f250d5128b9"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:23b2d7679b73fe0e5a4560b672a39f98dfc6f60df63823b0a9970525325b95f6"}, + {file = "frozenlist-1.4.1-cp39-cp39-win32.whl", hash = "sha256:a7496bfe1da7fb1a4e1cc23bb67c58fab69311cc7d32b5a99c2007b4b2a0e932"}, + {file = "frozenlist-1.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:e6a20a581f9ce92d389a8c7d7c3dd47c81fd5d6e655c8dddf341e14aa48659d0"}, + {file = "frozenlist-1.4.1-py3-none-any.whl", hash = "sha256:04ced3e6a46b4cfffe20f9ae482818e34eba9b5fb0ce4056e4cc9b6e212d09b7"}, + {file = "frozenlist-1.4.1.tar.gz", hash = "sha256:c037a86e8513059a2613aaba4d817bb90b9d9b6b69aace3ce9c877e8c8ed402b"}, +] + +[[package]] +name = "fsspec" +version = "2024.6.1" +requires_python = ">=3.8" +summary = "File-system specification" +groups = ["default"] +files = [ + {file = "fsspec-2024.6.1-py3-none-any.whl", hash = "sha256:3cb443f8bcd2efb31295a5b9fdb02aee81d8452c80d28f97a6d0959e6cee101e"}, + {file = "fsspec-2024.6.1.tar.gz", hash = "sha256:fad7d7e209dd4c1208e3bbfda706620e0da5142bebbd9c384afb95b07e798e49"}, +] + +[[package]] +name = "fsspec" +version = "2024.6.1" +extras = ["http"] +requires_python = ">=3.8" +summary = "File-system specification" +groups = ["default"] +dependencies = [ + "aiohttp!=4.0.0a0,!=4.0.0a1", + "fsspec==2024.6.1", +] +files = [ + {file = "fsspec-2024.6.1-py3-none-any.whl", hash = "sha256:3cb443f8bcd2efb31295a5b9fdb02aee81d8452c80d28f97a6d0959e6cee101e"}, + {file = "fsspec-2024.6.1.tar.gz", hash = "sha256:fad7d7e209dd4c1208e3bbfda706620e0da5142bebbd9c384afb95b07e798e49"}, +] + +[[package]] +name = "ghp-import" +version = "2.1.0" +summary = "Copy your docs directly to the gh-pages branch." +groups = ["docs"] +dependencies = [ + "python-dateutil>=2.8.1", +] +files = [ + {file = "ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343"}, + {file = "ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619"}, +] + +[[package]] +name = "gitdb" +version = "4.0.11" +requires_python = ">=3.7" +summary = "Git Object Database" +groups = ["docs"] +dependencies = [ + "smmap<6,>=3.0.1", +] +files = [ + {file = "gitdb-4.0.11-py3-none-any.whl", hash = "sha256:81a3407ddd2ee8df444cbacea00e2d038e40150acfa3001696fe0dcf1d3adfa4"}, + {file = "gitdb-4.0.11.tar.gz", hash = "sha256:bf5421126136d6d0af55bc1e7c1af1c397a34f5b7bd79e776cd3e89785c2b04b"}, +] + +[[package]] +name = "gitpython" +version = "3.1.43" +requires_python = ">=3.7" +summary = "GitPython is a Python library used to interact with Git repositories" +groups = ["docs"] +dependencies = [ + "gitdb<5,>=4.0.1", + "typing-extensions>=3.7.4.3; python_version < \"3.8\"", +] +files = [ + {file = "GitPython-3.1.43-py3-none-any.whl", hash = "sha256:eec7ec56b92aad751f9912a73404bc02ba212a23adb2c7098ee668417051a1ff"}, + {file = "GitPython-3.1.43.tar.gz", hash = "sha256:35f314a9f878467f5453cc1fee295c3e18e52f1b99f10f6cf5b1682e968a9e7c"}, +] + +[[package]] +name = "griffe" +version = "1.3.1" +requires_python = ">=3.8" +summary = "Signatures for entire Python programs. Extract the structure, the frame, the skeleton of your project, to generate API documentation or find breaking changes in your API." +groups = ["docs"] +dependencies = [ + "astunparse>=1.6; python_version < \"3.9\"", + "colorama>=0.4", +] +files = [ + {file = "griffe-1.3.1-py3-none-any.whl", hash = "sha256:940aeb630bc3054b4369567f150b6365be6f11eef46b0ed8623aea96e6d17b19"}, + {file = "griffe-1.3.1.tar.gz", hash = "sha256:3f86a716b631a4c0f96a43cb75d05d3c85975003c20540426c0eba3b0581c56a"}, +] + +[[package]] +name = "h11" +version = "0.14.0" +requires_python = ">=3.7" +summary = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" +groups = ["default"] +dependencies = [ + "typing-extensions; python_version < \"3.8\"", +] +files = [ + {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, + {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, +] + +[[package]] +name = "httpcore" +version = "1.0.5" +requires_python = ">=3.8" +summary = "A minimal low-level HTTP client." +groups = ["default"] +dependencies = [ + "certifi", + "h11<0.15,>=0.13", +] +files = [ + {file = "httpcore-1.0.5-py3-none-any.whl", hash = "sha256:421f18bac248b25d310f3cacd198d55b8e6125c107797b609ff9b7a6ba7991b5"}, + {file = "httpcore-1.0.5.tar.gz", hash = "sha256:34a38e2f9291467ee3b44e89dd52615370e152954ba21721378a87b2960f7a61"}, +] + +[[package]] +name = "httpx" +version = "0.27.2" +requires_python = ">=3.8" +summary = "The next generation HTTP client." +groups = ["default"] +dependencies = [ + "anyio", + "certifi", + "httpcore==1.*", + "idna", + "sniffio", +] +files = [ + {file = "httpx-0.27.2-py3-none-any.whl", hash = "sha256:7bb2708e112d8fdd7829cd4243970f0c223274051cb35ee80c03301ee29a3df0"}, + {file = "httpx-0.27.2.tar.gz", hash = "sha256:f7c2be1d2f3c3c3160d441802406b206c2b76f5947b11115e6df10c6c65e66c2"}, +] + +[[package]] +name = "huggingface-hub" +version = "0.24.7" +requires_python = ">=3.8.0" +summary = "Client library to download and publish models, datasets and other repos on the huggingface.co hub" +groups = ["default"] +dependencies = [ + "filelock", + "fsspec>=2023.5.0", + "packaging>=20.9", + "pyyaml>=5.1", + "requests", + "tqdm>=4.42.1", + "typing-extensions>=3.7.4.3", +] +files = [ + {file = "huggingface_hub-0.24.7-py3-none-any.whl", hash = "sha256:a212c555324c8a7b1ffdd07266bb7e7d69ca71aa238d27b7842d65e9a26ac3e5"}, + {file = "huggingface_hub-0.24.7.tar.gz", hash = "sha256:0ad8fb756e2831da0ac0491175b960f341fe06ebcf80ed6f8728313f95fc0207"}, +] + +[[package]] +name = "idna" +version = "3.10" +requires_python = ">=3.6" +summary = "Internationalized Domain Names in Applications (IDNA)" +groups = ["default", "docs"] +files = [ + {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, + {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, +] + +[[package]] +name = "importlib-metadata" +version = "8.5.0" +requires_python = ">=3.8" +summary = "Read metadata from Python packages" +groups = ["docs"] +dependencies = [ + "typing-extensions>=3.6.4; python_version < \"3.8\"", + "zipp>=3.20", +] +files = [ + {file = "importlib_metadata-8.5.0-py3-none-any.whl", hash = "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b"}, + {file = "importlib_metadata-8.5.0.tar.gz", hash = "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7"}, +] + +[[package]] +name = "importlib-resources" +version = "6.4.5" +requires_python = ">=3.8" +summary = "Read resources from Python packages" +groups = ["docs"] +dependencies = [ + "zipp>=3.1.0; python_version < \"3.10\"", +] +files = [ + {file = "importlib_resources-6.4.5-py3-none-any.whl", hash = "sha256:ac29d5f956f01d5e4bb63102a5a19957f1b9175e45649977264a1416783bb717"}, + {file = "importlib_resources-6.4.5.tar.gz", hash = "sha256:980862a1d16c9e147a59603677fa2aa5fd82b87f223b6cb870695bcfce830065"}, +] + +[[package]] +name = "jinja2" +version = "3.1.4" +requires_python = ">=3.7" +summary = "A very fast and expressive template engine." +groups = ["default", "docs"] +dependencies = [ + "MarkupSafe>=2.0", +] +files = [ + {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"}, + {file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"}, +] + +[[package]] +name = "jsonschema" +version = "4.23.0" +requires_python = ">=3.8" +summary = "An implementation of JSON Schema validation for Python" +groups = ["docs"] +dependencies = [ + "attrs>=22.2.0", + "importlib-resources>=1.4.0; python_version < \"3.9\"", + "jsonschema-specifications>=2023.03.6", + "pkgutil-resolve-name>=1.3.10; python_version < \"3.9\"", + "referencing>=0.28.4", + "rpds-py>=0.7.1", +] +files = [ + {file = "jsonschema-4.23.0-py3-none-any.whl", hash = "sha256:fbadb6f8b144a8f8cf9f0b89ba94501d143e50411a1278633f56a7acf7fd5566"}, + {file = "jsonschema-4.23.0.tar.gz", hash = "sha256:d71497fef26351a33265337fa77ffeb82423f3ea21283cd9467bb03999266bc4"}, +] + +[[package]] +name = "jsonschema-specifications" +version = "2023.12.1" +requires_python = ">=3.8" +summary = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" +groups = ["docs"] +dependencies = [ + "importlib-resources>=1.4.0; python_version < \"3.9\"", + "referencing>=0.31.0", +] +files = [ + {file = "jsonschema_specifications-2023.12.1-py3-none-any.whl", hash = "sha256:87e4fdf3a94858b8a2ba2778d9ba57d8a9cafca7c7489c46ba0d30a8bc6a9c3c"}, + {file = "jsonschema_specifications-2023.12.1.tar.gz", hash = "sha256:48a76787b3e70f5ed53f1160d2b81f586e4ca6d1548c5de7085d1682674764cc"}, +] + +[[package]] +name = "jupyter-client" +version = "8.6.2" +requires_python = ">=3.8" +summary = "Jupyter protocol implementation and client libraries" +groups = ["docs"] +dependencies = [ + "importlib-metadata>=4.8.3; python_version < \"3.10\"", + "jupyter-core!=5.0.*,>=4.12", + "python-dateutil>=2.8.2", + "pyzmq>=23.0", + "tornado>=6.2", + "traitlets>=5.3", +] +files = [ + {file = "jupyter_client-8.6.2-py3-none-any.whl", hash = "sha256:50cbc5c66fd1b8f65ecb66bc490ab73217993632809b6e505687de18e9dea39f"}, + {file = "jupyter_client-8.6.2.tar.gz", hash = "sha256:2bda14d55ee5ba58552a8c53ae43d215ad9868853489213f37da060ced54d8df"}, +] + +[[package]] +name = "jupyter-core" +version = "5.7.2" +requires_python = ">=3.8" +summary = "Jupyter core package. A base package on which Jupyter projects rely." +groups = ["docs"] +dependencies = [ + "platformdirs>=2.5", + "pywin32>=300; sys_platform == \"win32\" and platform_python_implementation != \"PyPy\"", + "traitlets>=5.3", +] +files = [ + {file = "jupyter_core-5.7.2-py3-none-any.whl", hash = "sha256:4f7315d2f6b4bcf2e3e7cb6e46772eba760ae459cd1f59d29eb57b0a01bd7409"}, + {file = "jupyter_core-5.7.2.tar.gz", hash = "sha256:aa5f8d32bbf6b431ac830496da7392035d6f61b4f54872f15c4bd2a9c3f536d9"}, +] + +[[package]] +name = "jupyterlab-pygments" +version = "0.3.0" +requires_python = ">=3.8" +summary = "Pygments theme using JupyterLab CSS variables" +groups = ["docs"] +files = [ + {file = "jupyterlab_pygments-0.3.0-py3-none-any.whl", hash = "sha256:841a89020971da1d8693f1a99997aefc5dc424bb1b251fd6322462a1b8842780"}, + {file = "jupyterlab_pygments-0.3.0.tar.gz", hash = "sha256:721aca4d9029252b11cfa9d185e5b5af4d54772bb8072f9b7036f4170054d35d"}, +] + +[[package]] +name = "markdown" +version = "3.7" +requires_python = ">=3.8" +summary = "Python implementation of John Gruber's Markdown." +groups = ["docs"] +dependencies = [ + "importlib-metadata>=4.4; python_version < \"3.10\"", +] +files = [ + {file = "Markdown-3.7-py3-none-any.whl", hash = "sha256:7eb6df5690b81a1d7942992c97fad2938e956e79df20cbc6186e9c3a77b1c803"}, + {file = "markdown-3.7.tar.gz", hash = "sha256:2ae2471477cfd02dbbf038d5d9bc226d40def84b4fe2986e49b59b6b472bbed2"}, +] + +[[package]] +name = "markdown-it-py" +version = "3.0.0" +requires_python = ">=3.8" +summary = "Python port of markdown-it. Markdown parsing, done right!" +groups = ["default"] +dependencies = [ + "mdurl~=0.1", +] +files = [ + {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, + {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, +] + +[[package]] +name = "markupsafe" +version = "2.1.5" +requires_python = ">=3.7" +summary = "Safely add untrusted strings to HTML/XML markup." +groups = ["default", "docs"] +files = [ + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, + {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, +] + +[[package]] +name = "material-plausible-plugin" +version = "0.2.0" +requires_python = ">=3.7" +summary = "Plausible Analytics implementation for Material for MkDocs" +groups = ["docs"] +dependencies = [ + "mkdocs", + "mkdocs-material", +] +files = [ + {file = "material_plausible_plugin-0.2.0-py3-none-any.whl", hash = "sha256:b7dc866b358475d940c5c61f56f86c400b9c1e73ffa2b06819207df38f34fcf4"}, + {file = "material_plausible_plugin-0.2.0.tar.gz", hash = "sha256:9416d6313ff9ddeec25e14e9c4baac1341511263706a5997bd714d552992d752"}, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +requires_python = ">=3.7" +summary = "Markdown URL utilities" +groups = ["default"] +files = [ + {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, + {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, +] + +[[package]] +name = "mergedeep" +version = "1.3.4" +requires_python = ">=3.6" +summary = "A deep merge function for 🐍." +groups = ["docs"] +files = [ + {file = "mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307"}, + {file = "mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8"}, +] + +[[package]] +name = "mike" +version = "2.1.3" +summary = "Manage multiple versions of your MkDocs-powered documentation" +groups = ["docs"] +dependencies = [ + "importlib-metadata", + "importlib-resources", + "jinja2>=2.7", + "mkdocs>=1.0", + "pyparsing>=3.0", + "pyyaml-env-tag", + "pyyaml>=5.1", + "verspec", +] +files = [ + {file = "mike-2.1.3-py3-none-any.whl", hash = "sha256:d90c64077e84f06272437b464735130d380703a76a5738b152932884c60c062a"}, + {file = "mike-2.1.3.tar.gz", hash = "sha256:abd79b8ea483fb0275b7972825d3082e5ae67a41820f8d8a0dc7a3f49944e810"}, +] + +[[package]] +name = "mistune" +version = "3.0.2" +requires_python = ">=3.7" +summary = "A sane and fast Markdown parser with useful plugins and renderers" +groups = ["docs"] +files = [ + {file = "mistune-3.0.2-py3-none-any.whl", hash = "sha256:71481854c30fdbc938963d3605b72501f5c10a9320ecd412c121c163a1c7d205"}, + {file = "mistune-3.0.2.tar.gz", hash = "sha256:fc7f93ded930c92394ef2cb6f04a8aabab4117a91449e72dcc8dfa646a508be8"}, +] + +[[package]] +name = "mkdocs" +version = "1.6.1" +requires_python = ">=3.8" +summary = "Project documentation with Markdown." +groups = ["docs"] +dependencies = [ + "click>=7.0", + "colorama>=0.4; platform_system == \"Windows\"", + "ghp-import>=1.0", + "importlib-metadata>=4.4; python_version < \"3.10\"", + "jinja2>=2.11.1", + "markdown>=3.3.6", + "markupsafe>=2.0.1", + "mergedeep>=1.3.4", + "mkdocs-get-deps>=0.2.0", + "packaging>=20.5", + "pathspec>=0.11.1", + "pyyaml-env-tag>=0.1", + "pyyaml>=5.1", + "watchdog>=2.0", +] +files = [ + {file = "mkdocs-1.6.1-py3-none-any.whl", hash = "sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e"}, + {file = "mkdocs-1.6.1.tar.gz", hash = "sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2"}, +] + +[[package]] +name = "mkdocs-autorefs" +version = "1.2.0" +requires_python = ">=3.8" +summary = "Automatically link across pages in MkDocs." +groups = ["docs"] +dependencies = [ + "Markdown>=3.3", + "markupsafe>=2.0.1", + "mkdocs>=1.1", +] +files = [ + {file = "mkdocs_autorefs-1.2.0-py3-none-any.whl", hash = "sha256:d588754ae89bd0ced0c70c06f58566a4ee43471eeeee5202427da7de9ef85a2f"}, + {file = "mkdocs_autorefs-1.2.0.tar.gz", hash = "sha256:a86b93abff653521bda71cf3fc5596342b7a23982093915cb74273f67522190f"}, +] + +[[package]] +name = "mkdocs-gen-files" +version = "0.5.0" +requires_python = ">=3.7" +summary = "MkDocs plugin to programmatically generate documentation pages during the build" +groups = ["docs"] +dependencies = [ + "mkdocs>=1.0.3", +] +files = [ + {file = "mkdocs_gen_files-0.5.0-py3-none-any.whl", hash = "sha256:7ac060096f3f40bd19039e7277dd3050be9a453c8ac578645844d4d91d7978ea"}, + {file = "mkdocs_gen_files-0.5.0.tar.gz", hash = "sha256:4c7cf256b5d67062a788f6b1d035e157fc1a9498c2399be9af5257d4ff4d19bc"}, +] + +[[package]] +name = "mkdocs-get-deps" +version = "0.2.0" +requires_python = ">=3.8" +summary = "MkDocs extension that lists all dependencies according to a mkdocs.yml file" +groups = ["docs"] +dependencies = [ + "importlib-metadata>=4.3; python_version < \"3.10\"", + "mergedeep>=1.3.4", + "platformdirs>=2.2.0", + "pyyaml>=5.1", +] +files = [ + {file = "mkdocs_get_deps-0.2.0-py3-none-any.whl", hash = "sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134"}, + {file = "mkdocs_get_deps-0.2.0.tar.gz", hash = "sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c"}, +] + +[[package]] +name = "mkdocs-literate-nav" +version = "0.6.1" +requires_python = ">=3.7" +summary = "MkDocs plugin to specify the navigation in Markdown instead of YAML" +groups = ["docs"] +dependencies = [ + "mkdocs>=1.0.3", +] +files = [ + {file = "mkdocs_literate_nav-0.6.1-py3-none-any.whl", hash = "sha256:e70bdc4a07050d32da79c0b697bd88e9a104cf3294282e9cb20eec94c6b0f401"}, + {file = "mkdocs_literate_nav-0.6.1.tar.gz", hash = "sha256:78a7ab6d878371728acb0cdc6235c9b0ffc6e83c997b037f4a5c6ff7cef7d759"}, +] + +[[package]] +name = "mkdocs-material" +version = "9.5.34" +requires_python = ">=3.8" +summary = "Documentation that simply works" +groups = ["docs"] +dependencies = [ + "babel~=2.10", + "colorama~=0.4", + "jinja2~=3.0", + "markdown~=3.2", + "mkdocs-material-extensions~=1.3", + "mkdocs~=1.6", + "paginate~=0.5", + "pygments~=2.16", + "pymdown-extensions~=10.2", + "regex>=2022.4", + "requests~=2.26", +] +files = [ + {file = "mkdocs_material-9.5.34-py3-none-any.whl", hash = "sha256:54caa8be708de2b75167fd4d3b9f3d949579294f49cb242515d4653dbee9227e"}, + {file = "mkdocs_material-9.5.34.tar.gz", hash = "sha256:1e60ddf716cfb5679dfd65900b8a25d277064ed82d9a53cd5190e3f894df7840"}, +] + +[[package]] +name = "mkdocs-material-extensions" +version = "1.3.1" +requires_python = ">=3.8" +summary = "Extension pack for Python Markdown and MkDocs Material." +groups = ["docs"] +files = [ + {file = "mkdocs_material_extensions-1.3.1-py3-none-any.whl", hash = "sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31"}, + {file = "mkdocs_material_extensions-1.3.1.tar.gz", hash = "sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443"}, +] + +[[package]] +name = "mkdocs-section-index" +version = "0.3.9" +requires_python = ">=3.8" +summary = "MkDocs plugin to allow clickable sections that lead to an index page" +groups = ["docs"] +dependencies = [ + "mkdocs>=1.2", +] +files = [ + {file = "mkdocs_section_index-0.3.9-py3-none-any.whl", hash = "sha256:5e5eb288e8d7984d36c11ead5533f376fdf23498f44e903929d72845b24dfe34"}, + {file = "mkdocs_section_index-0.3.9.tar.gz", hash = "sha256:b66128d19108beceb08b226ee1ba0981840d14baf8a652b6c59e650f3f92e4f8"}, +] + +[[package]] +name = "mkdocstrings" +version = "0.26.1" +requires_python = ">=3.8" +summary = "Automatic documentation from sources, for MkDocs." +groups = ["docs"] +dependencies = [ + "Jinja2>=2.11.1", + "Markdown>=3.6", + "MarkupSafe>=1.1", + "click>=7.0", + "importlib-metadata>=4.6; python_version < \"3.10\"", + "mkdocs-autorefs>=1.2", + "mkdocs>=1.4", + "platformdirs>=2.2", + "pymdown-extensions>=6.3", + "typing-extensions>=4.1; python_version < \"3.10\"", +] +files = [ + {file = "mkdocstrings-0.26.1-py3-none-any.whl", hash = "sha256:29738bfb72b4608e8e55cc50fb8a54f325dc7ebd2014e4e3881a49892d5983cf"}, + {file = "mkdocstrings-0.26.1.tar.gz", hash = "sha256:bb8b8854d6713d5348ad05b069a09f3b79edbc6a0f33a34c6821141adb03fe33"}, +] + +[[package]] +name = "mkdocstrings-python" +version = "1.11.1" +requires_python = ">=3.8" +summary = "A Python handler for mkdocstrings." +groups = ["docs"] +dependencies = [ + "griffe>=0.49", + "mkdocs-autorefs>=1.2", + "mkdocstrings>=0.26", +] +files = [ + {file = "mkdocstrings_python-1.11.1-py3-none-any.whl", hash = "sha256:a21a1c05acef129a618517bb5aae3e33114f569b11588b1e7af3e9d4061a71af"}, + {file = "mkdocstrings_python-1.11.1.tar.gz", hash = "sha256:8824b115c5359304ab0b5378a91f6202324a849e1da907a3485b59208b797322"}, +] + +[[package]] +name = "mkdocstrings" +version = "0.26.1" +extras = ["python"] +requires_python = ">=3.8" +summary = "Automatic documentation from sources, for MkDocs." +groups = ["docs"] +dependencies = [ + "mkdocstrings-python>=0.5.2", + "mkdocstrings==0.26.1", +] +files = [ + {file = "mkdocstrings-0.26.1-py3-none-any.whl", hash = "sha256:29738bfb72b4608e8e55cc50fb8a54f325dc7ebd2014e4e3881a49892d5983cf"}, + {file = "mkdocstrings-0.26.1.tar.gz", hash = "sha256:bb8b8854d6713d5348ad05b069a09f3b79edbc6a0f33a34c6821141adb03fe33"}, +] + +[[package]] +name = "mknotebooks" +version = "0.8.0" +summary = "Plugin for mkdocs to generate markdown documents from jupyter notebooks." +groups = ["docs"] +dependencies = [ + "gitpython", + "jupyter-client", + "markdown>=3.3.3", + "mkdocs>=1.5.0", + "nbconvert>=6.0.0", +] +files = [ + {file = "mknotebooks-0.8.0-py3-none-any.whl", hash = "sha256:4a9b998260c09bcc311455a19a44cc395a30ee82dc1e86e3316dd09f2445ebd3"}, +] + +[[package]] +name = "multidict" +version = "6.1.0" +requires_python = ">=3.8" +summary = "multidict implementation" +groups = ["default"] +dependencies = [ + "typing-extensions>=4.1.0; python_version < \"3.11\"", +] +files = [ + {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3380252550e372e8511d49481bd836264c009adb826b23fefcc5dd3c69692f60"}, + {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:99f826cbf970077383d7de805c0681799491cb939c25450b9b5b3ced03ca99f1"}, + {file = "multidict-6.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a114d03b938376557927ab23f1e950827c3b893ccb94b62fd95d430fd0e5cf53"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1c416351ee6271b2f49b56ad7f308072f6f44b37118d69c2cad94f3fa8a40d5"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6b5d83030255983181005e6cfbac1617ce9746b219bc2aad52201ad121226581"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3e97b5e938051226dc025ec80980c285b053ffb1e25a3db2a3aa3bc046bf7f56"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d618649d4e70ac6efcbba75be98b26ef5078faad23592f9b51ca492953012429"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10524ebd769727ac77ef2278390fb0068d83f3acb7773792a5080f2b0abf7748"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ff3827aef427c89a25cc96ded1759271a93603aba9fb977a6d264648ebf989db"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:06809f4f0f7ab7ea2cabf9caca7d79c22c0758b58a71f9d32943ae13c7ace056"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f179dee3b863ab1c59580ff60f9d99f632f34ccb38bf67a33ec6b3ecadd0fd76"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:aaed8b0562be4a0876ee3b6946f6869b7bcdb571a5d1496683505944e268b160"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3c8b88a2ccf5493b6c8da9076fb151ba106960a2df90c2633f342f120751a9e7"}, + {file = "multidict-6.1.0-cp310-cp310-win32.whl", hash = "sha256:4a9cb68166a34117d6646c0023c7b759bf197bee5ad4272f420a0141d7eb03a0"}, + {file = "multidict-6.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:20b9b5fbe0b88d0bdef2012ef7dee867f874b72528cf1d08f1d59b0e3850129d"}, + {file = "multidict-6.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3efe2c2cb5763f2f1b275ad2bf7a287d3f7ebbef35648a9726e3b69284a4f3d6"}, + {file = "multidict-6.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c7053d3b0353a8b9de430a4f4b4268ac9a4fb3481af37dfe49825bf45ca24156"}, + {file = "multidict-6.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:27e5fc84ccef8dfaabb09d82b7d179c7cf1a3fbc8a966f8274fcb4ab2eb4cadb"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e2b90b43e696f25c62656389d32236e049568b39320e2735d51f08fd362761b"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d83a047959d38a7ff552ff94be767b7fd79b831ad1cd9920662db05fec24fe72"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d1a9dd711d0877a1ece3d2e4fea11a8e75741ca21954c919406b44e7cf971304"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec2abea24d98246b94913b76a125e855eb5c434f7c46546046372fe60f666351"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4867cafcbc6585e4b678876c489b9273b13e9fff9f6d6d66add5e15d11d926cb"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5b48204e8d955c47c55b72779802b219a39acc3ee3d0116d5080c388970b76e3"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:d8fff389528cad1618fb4b26b95550327495462cd745d879a8c7c2115248e399"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a7a9541cd308eed5e30318430a9c74d2132e9a8cb46b901326272d780bf2d423"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:da1758c76f50c39a2efd5e9859ce7d776317eb1dd34317c8152ac9251fc574a3"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c943a53e9186688b45b323602298ab727d8865d8c9ee0b17f8d62d14b56f0753"}, + {file = "multidict-6.1.0-cp311-cp311-win32.whl", hash = "sha256:90f8717cb649eea3504091e640a1b8568faad18bd4b9fcd692853a04475a4b80"}, + {file = "multidict-6.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:82176036e65644a6cc5bd619f65f6f19781e8ec2e5330f51aa9ada7504cc1926"}, + {file = "multidict-6.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b04772ed465fa3cc947db808fa306d79b43e896beb677a56fb2347ca1a49c1fa"}, + {file = "multidict-6.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6180c0ae073bddeb5a97a38c03f30c233e0a4d39cd86166251617d1bbd0af436"}, + {file = "multidict-6.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:071120490b47aa997cca00666923a83f02c7fbb44f71cf7f136df753f7fa8761"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50b3a2710631848991d0bf7de077502e8994c804bb805aeb2925a981de58ec2e"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b58c621844d55e71c1b7f7c498ce5aa6985d743a1a59034c57a905b3f153c1ef"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55b6d90641869892caa9ca42ff913f7ff1c5ece06474fbd32fb2cf6834726c95"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b820514bfc0b98a30e3d85462084779900347e4d49267f747ff54060cc33925"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10a9b09aba0c5b48c53761b7c720aaaf7cf236d5fe394cd399c7ba662d5f9966"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e16bf3e5fc9f44632affb159d30a437bfe286ce9e02754759be5536b169b305"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:76f364861c3bfc98cbbcbd402d83454ed9e01a5224bb3a28bf70002a230f73e2"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:820c661588bd01a0aa62a1283f20d2be4281b086f80dad9e955e690c75fb54a2"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:0e5f362e895bc5b9e67fe6e4ded2492d8124bdf817827f33c5b46c2fe3ffaca6"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3ec660d19bbc671e3a6443325f07263be452c453ac9e512f5eb935e7d4ac28b3"}, + {file = "multidict-6.1.0-cp312-cp312-win32.whl", hash = "sha256:58130ecf8f7b8112cdb841486404f1282b9c86ccb30d3519faf301b2e5659133"}, + {file = "multidict-6.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:188215fc0aafb8e03341995e7c4797860181562380f81ed0a87ff455b70bf1f1"}, + {file = "multidict-6.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d569388c381b24671589335a3be6e1d45546c2988c2ebe30fdcada8457a31008"}, + {file = "multidict-6.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:052e10d2d37810b99cc170b785945421141bf7bb7d2f8799d431e7db229c385f"}, + {file = "multidict-6.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f90c822a402cb865e396a504f9fc8173ef34212a342d92e362ca498cad308e28"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b225d95519a5bf73860323e633a664b0d85ad3d5bede6d30d95b35d4dfe8805b"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:23bfd518810af7de1116313ebd9092cb9aa629beb12f6ed631ad53356ed6b86c"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c09fcfdccdd0b57867577b719c69e347a436b86cd83747f179dbf0cc0d4c1f3"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf6bea52ec97e95560af5ae576bdac3aa3aae0b6758c6efa115236d9e07dae44"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57feec87371dbb3520da6192213c7d6fc892d5589a93db548331954de8248fd2"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0c3f390dc53279cbc8ba976e5f8035eab997829066756d811616b652b00a23a3"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:59bfeae4b25ec05b34f1956eaa1cb38032282cd4dfabc5056d0a1ec4d696d3aa"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b2f59caeaf7632cc633b5cf6fc449372b83bbdf0da4ae04d5be36118e46cc0aa"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:37bb93b2178e02b7b618893990941900fd25b6b9ac0fa49931a40aecdf083fe4"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4e9f48f58c2c523d5a06faea47866cd35b32655c46b443f163d08c6d0ddb17d6"}, + {file = "multidict-6.1.0-cp313-cp313-win32.whl", hash = "sha256:3a37ffb35399029b45c6cc33640a92bef403c9fd388acce75cdc88f58bd19a81"}, + {file = "multidict-6.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:e9aa71e15d9d9beaad2c6b9319edcdc0a49a43ef5c0a4c8265ca9ee7d6c67774"}, + {file = "multidict-6.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:4e18b656c5e844539d506a0a06432274d7bd52a7487e6828c63a63d69185626c"}, + {file = "multidict-6.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a185f876e69897a6f3325c3f19f26a297fa058c5e456bfcff8015e9a27e83ae1"}, + {file = "multidict-6.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ab7c4ceb38d91570a650dba194e1ca87c2b543488fe9309b4212694174fd539c"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e617fb6b0b6953fffd762669610c1c4ffd05632c138d61ac7e14ad187870669c"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:16e5f4bf4e603eb1fdd5d8180f1a25f30056f22e55ce51fb3d6ad4ab29f7d96f"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4c035da3f544b1882bac24115f3e2e8760f10a0107614fc9839fd232200b875"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:957cf8e4b6e123a9eea554fa7ebc85674674b713551de587eb318a2df3e00255"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:483a6aea59cb89904e1ceabd2b47368b5600fb7de78a6e4a2c2987b2d256cf30"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:87701f25a2352e5bf7454caa64757642734da9f6b11384c1f9d1a8e699758057"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:682b987361e5fd7a139ed565e30d81fd81e9629acc7d925a205366877d8c8657"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ce2186a7df133a9c895dea3331ddc5ddad42cdd0d1ea2f0a51e5d161e4762f28"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:9f636b730f7e8cb19feb87094949ba54ee5357440b9658b2a32a5ce4bce53972"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:73eae06aa53af2ea5270cc066dcaf02cc60d2994bbb2c4ef5764949257d10f43"}, + {file = "multidict-6.1.0-cp39-cp39-win32.whl", hash = "sha256:1ca0083e80e791cffc6efce7660ad24af66c8d4079d2a750b29001b53ff59ada"}, + {file = "multidict-6.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:aa466da5b15ccea564bdab9c89175c762bc12825f4659c11227f515cee76fa4a"}, + {file = "multidict-6.1.0-py3-none-any.whl", hash = "sha256:48e171e52d1c4d33888e529b999e5900356b9ae588c2f09a52dcefb158b27506"}, + {file = "multidict-6.1.0.tar.gz", hash = "sha256:22ae2ebf9b0c69d206c003e2f6a914ea33f0a932d4aa16f236afc049d9958f4a"}, +] + +[[package]] +name = "multiprocess" +version = "0.70.16" +requires_python = ">=3.8" +summary = "better multiprocessing and multithreading in Python" +groups = ["default"] +dependencies = [ + "dill>=0.3.8", +] +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"}, + {file = "multiprocess-0.70.16-pp39-pypy39_pp73-macosx_10_13_x86_64.whl", hash = "sha256:0dfd078c306e08d46d7a8d06fb120313d87aa43af60d66da43ffff40b44d2f41"}, + {file = "multiprocess-0.70.16-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e7b9d0f307cd9bd50851afaac0dba2cb6c44449efff697df7c7645f7d3f2be3a"}, + {file = "multiprocess-0.70.16-py310-none-any.whl", hash = "sha256:c4a9944c67bd49f823687463660a2d6daae94c289adff97e0f9d696ba6371d02"}, + {file = "multiprocess-0.70.16-py311-none-any.whl", hash = "sha256:af4cabb0dac72abfb1e794fa7855c325fd2b55a10a44628a3c1ad3311c04127a"}, + {file = "multiprocess-0.70.16-py312-none-any.whl", hash = "sha256:fc0544c531920dde3b00c29863377f87e1632601092ea2daca74e4beb40faa2e"}, + {file = "multiprocess-0.70.16-py39-none-any.whl", hash = "sha256:a0bafd3ae1b732eac64be2e72038231c1ba97724b60b09400d68f229fcc2fbf3"}, + {file = "multiprocess-0.70.16.tar.gz", hash = "sha256:161af703d4652a0e1410be6abccecde4a7ddffd19341be0a7011b94aeb171ac1"}, +] + +[[package]] +name = "nbclient" +version = "0.10.0" +requires_python = ">=3.8.0" +summary = "A client library for executing notebooks. Formerly nbconvert's ExecutePreprocessor." +groups = ["docs"] +dependencies = [ + "jupyter-client>=6.1.12", + "jupyter-core!=5.0.*,>=4.12", + "nbformat>=5.1", + "traitlets>=5.4", +] +files = [ + {file = "nbclient-0.10.0-py3-none-any.whl", hash = "sha256:f13e3529332a1f1f81d82a53210322476a168bb7090a0289c795fe9cc11c9d3f"}, + {file = "nbclient-0.10.0.tar.gz", hash = "sha256:4b3f1b7dba531e498449c4db4f53da339c91d449dc11e9af3a43b4eb5c5abb09"}, +] + +[[package]] +name = "nbconvert" +version = "7.16.4" +requires_python = ">=3.8" +summary = "Converting Jupyter Notebooks (.ipynb files) to other formats. Output formats include asciidoc, html, latex, markdown, pdf, py, rst, script. nbconvert can be used both as a Python library (`import nbconvert`) or as a command line tool (invoked as `jupyter nbconvert ...`)." +groups = ["docs"] +dependencies = [ + "beautifulsoup4", + "bleach!=5.0.0", + "defusedxml", + "importlib-metadata>=3.6; python_version < \"3.10\"", + "jinja2>=3.0", + "jupyter-core>=4.7", + "jupyterlab-pygments", + "markupsafe>=2.0", + "mistune<4,>=2.0.3", + "nbclient>=0.5.0", + "nbformat>=5.7", + "packaging", + "pandocfilters>=1.4.1", + "pygments>=2.4.1", + "tinycss2", + "traitlets>=5.1", +] +files = [ + {file = "nbconvert-7.16.4-py3-none-any.whl", hash = "sha256:05873c620fe520b6322bf8a5ad562692343fe3452abda5765c7a34b7d1aa3eb3"}, + {file = "nbconvert-7.16.4.tar.gz", hash = "sha256:86ca91ba266b0a448dc96fa6c5b9d98affabde2867b363258703536807f9f7f4"}, +] + +[[package]] +name = "nbformat" +version = "5.10.4" +requires_python = ">=3.8" +summary = "The Jupyter Notebook format" +groups = ["docs"] +dependencies = [ + "fastjsonschema>=2.15", + "jsonschema>=2.6", + "jupyter-core!=5.0.*,>=4.12", + "traitlets>=5.1", +] +files = [ + {file = "nbformat-5.10.4-py3-none-any.whl", hash = "sha256:3b48d6c8fbca4b299bf3982ea7db1af21580e4fec269ad087b9e81588891200b"}, + {file = "nbformat-5.10.4.tar.gz", hash = "sha256:322168b14f937a5d11362988ecac2a4952d3d8e3a2cbeb2319584631226d5b3a"}, +] + +[[package]] +name = "nest-asyncio" +version = "1.6.0" +requires_python = ">=3.5" +summary = "Patch asyncio to allow nested event loops" +groups = ["default"] +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"}, +] + +[[package]] +name = "networkx" +version = "3.2.1" +requires_python = ">=3.9" +summary = "Python package for creating and manipulating graphs and networks" +groups = ["default"] +files = [ + {file = "networkx-3.2.1-py3-none-any.whl", hash = "sha256:f18c69adc97877c42332c170849c96cefa91881c99a7cb3e95b7c659ebdc1ec2"}, + {file = "networkx-3.2.1.tar.gz", hash = "sha256:9f1bb5cf3409bf324e0a722c20bdb4c20ee39bf1c30ce8ae499c8502b0b5e0c6"}, +] + +[[package]] +name = "numpy" +version = "2.0.2" +requires_python = ">=3.9" +summary = "Fundamental package for array computing in Python" +groups = ["default", "docs"] +files = [ + {file = "numpy-2.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:51129a29dbe56f9ca83438b706e2e69a39892b5eda6cedcb6b0c9fdc9b0d3ece"}, + {file = "numpy-2.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f15975dfec0cf2239224d80e32c3170b1d168335eaedee69da84fbe9f1f9cd04"}, + {file = "numpy-2.0.2-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:8c5713284ce4e282544c68d1c3b2c7161d38c256d2eefc93c1d683cf47683e66"}, + {file = "numpy-2.0.2-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:becfae3ddd30736fe1889a37f1f580e245ba79a5855bff5f2a29cb3ccc22dd7b"}, + {file = "numpy-2.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2da5960c3cf0df7eafefd806d4e612c5e19358de82cb3c343631188991566ccd"}, + {file = "numpy-2.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:496f71341824ed9f3d2fd36cf3ac57ae2e0165c143b55c3a035ee219413f3318"}, + {file = "numpy-2.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a61ec659f68ae254e4d237816e33171497e978140353c0c2038d46e63282d0c8"}, + {file = "numpy-2.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d731a1c6116ba289c1e9ee714b08a8ff882944d4ad631fd411106a30f083c326"}, + {file = "numpy-2.0.2-cp310-cp310-win32.whl", hash = "sha256:984d96121c9f9616cd33fbd0618b7f08e0cfc9600a7ee1d6fd9b239186d19d97"}, + {file = "numpy-2.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:c7b0be4ef08607dd04da4092faee0b86607f111d5ae68036f16cc787e250a131"}, + {file = "numpy-2.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:49ca4decb342d66018b01932139c0961a8f9ddc7589611158cb3c27cbcf76448"}, + {file = "numpy-2.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:11a76c372d1d37437857280aa142086476136a8c0f373b2e648ab2c8f18fb195"}, + {file = "numpy-2.0.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:807ec44583fd708a21d4a11d94aedf2f4f3c3719035c76a2bbe1fe8e217bdc57"}, + {file = "numpy-2.0.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:8cafab480740e22f8d833acefed5cc87ce276f4ece12fdaa2e8903db2f82897a"}, + {file = "numpy-2.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a15f476a45e6e5a3a79d8a14e62161d27ad897381fecfa4a09ed5322f2085669"}, + {file = "numpy-2.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13e689d772146140a252c3a28501da66dfecd77490b498b168b501835041f951"}, + {file = "numpy-2.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9ea91dfb7c3d1c56a0e55657c0afb38cf1eeae4544c208dc465c3c9f3a7c09f9"}, + {file = "numpy-2.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c1c9307701fec8f3f7a1e6711f9089c06e6284b3afbbcd259f7791282d660a15"}, + {file = "numpy-2.0.2-cp311-cp311-win32.whl", hash = "sha256:a392a68bd329eafac5817e5aefeb39038c48b671afd242710b451e76090e81f4"}, + {file = "numpy-2.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:286cd40ce2b7d652a6f22efdfc6d1edf879440e53e76a75955bc0c826c7e64dc"}, + {file = "numpy-2.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:df55d490dea7934f330006d0f81e8551ba6010a5bf035a249ef61a94f21c500b"}, + {file = "numpy-2.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8df823f570d9adf0978347d1f926b2a867d5608f434a7cff7f7908c6570dcf5e"}, + {file = "numpy-2.0.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:9a92ae5c14811e390f3767053ff54eaee3bf84576d99a2456391401323f4ec2c"}, + {file = "numpy-2.0.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:a842d573724391493a97a62ebbb8e731f8a5dcc5d285dfc99141ca15a3302d0c"}, + {file = "numpy-2.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c05e238064fc0610c840d1cf6a13bf63d7e391717d247f1bf0318172e759e692"}, + {file = "numpy-2.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0123ffdaa88fa4ab64835dcbde75dcdf89c453c922f18dced6e27c90d1d0ec5a"}, + {file = "numpy-2.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:96a55f64139912d61de9137f11bf39a55ec8faec288c75a54f93dfd39f7eb40c"}, + {file = "numpy-2.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ec9852fb39354b5a45a80bdab5ac02dd02b15f44b3804e9f00c556bf24b4bded"}, + {file = "numpy-2.0.2-cp312-cp312-win32.whl", hash = "sha256:671bec6496f83202ed2d3c8fdc486a8fc86942f2e69ff0e986140339a63bcbe5"}, + {file = "numpy-2.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:cfd41e13fdc257aa5778496b8caa5e856dc4896d4ccf01841daee1d96465467a"}, + {file = "numpy-2.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9059e10581ce4093f735ed23f3b9d283b9d517ff46009ddd485f1747eb22653c"}, + {file = "numpy-2.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:423e89b23490805d2a5a96fe40ec507407b8ee786d66f7328be214f9679df6dd"}, + {file = "numpy-2.0.2-cp39-cp39-macosx_14_0_arm64.whl", hash = "sha256:2b2955fa6f11907cf7a70dab0d0755159bca87755e831e47932367fc8f2f2d0b"}, + {file = "numpy-2.0.2-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:97032a27bd9d8988b9a97a8c4d2c9f2c15a81f61e2f21404d7e8ef00cb5be729"}, + {file = "numpy-2.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e795a8be3ddbac43274f18588329c72939870a16cae810c2b73461c40718ab1"}, + {file = "numpy-2.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f26b258c385842546006213344c50655ff1555a9338e2e5e02a0756dc3e803dd"}, + {file = "numpy-2.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5fec9451a7789926bcf7c2b8d187292c9f93ea30284802a0ab3f5be8ab36865d"}, + {file = "numpy-2.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9189427407d88ff25ecf8f12469d4d39d35bee1db5d39fc5c168c6f088a6956d"}, + {file = "numpy-2.0.2-cp39-cp39-win32.whl", hash = "sha256:905d16e0c60200656500c95b6b8dca5d109e23cb24abc701d41c02d74c6b3afa"}, + {file = "numpy-2.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:a3f4ab0caa7f053f6797fcd4e1e25caee367db3112ef2b6ef82d749530768c73"}, + {file = "numpy-2.0.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7f0a0c6f12e07fa94133c8a67404322845220c06a9e80e85999afe727f7438b8"}, + {file = "numpy-2.0.2-pp39-pypy39_pp73-macosx_14_0_x86_64.whl", hash = "sha256:312950fdd060354350ed123c0e25a71327d3711584beaef30cdaa93320c392d4"}, + {file = "numpy-2.0.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26df23238872200f63518dd2aa984cfca675d82469535dc7162dc2ee52d9dd5c"}, + {file = "numpy-2.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a46288ec55ebbd58947d31d72be2c63cbf839f0a63b49cb755022310792a3385"}, + {file = "numpy-2.0.2.tar.gz", hash = "sha256:883c987dee1880e2a864ab0dc9892292582510604156762362d9326444636e78"}, +] + +[[package]] +name = "orjson" +version = "3.10.7" +requires_python = ">=3.8" +summary = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" +groups = ["default"] +files = [ + {file = "orjson-3.10.7-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:74f4544f5a6405b90da8ea724d15ac9c36da4d72a738c64685003337401f5c12"}, + {file = "orjson-3.10.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34a566f22c28222b08875b18b0dfbf8a947e69df21a9ed5c51a6bf91cfb944ac"}, + {file = "orjson-3.10.7-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bf6ba8ebc8ef5792e2337fb0419f8009729335bb400ece005606336b7fd7bab7"}, + {file = "orjson-3.10.7-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac7cf6222b29fbda9e3a472b41e6a5538b48f2c8f99261eecd60aafbdb60690c"}, + {file = "orjson-3.10.7-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:de817e2f5fc75a9e7dd350c4b0f54617b280e26d1631811a43e7e968fa71e3e9"}, + {file = "orjson-3.10.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:348bdd16b32556cf8d7257b17cf2bdb7ab7976af4af41ebe79f9796c218f7e91"}, + {file = "orjson-3.10.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:479fd0844ddc3ca77e0fd99644c7fe2de8e8be1efcd57705b5c92e5186e8a250"}, + {file = "orjson-3.10.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:fdf5197a21dd660cf19dfd2a3ce79574588f8f5e2dbf21bda9ee2d2b46924d84"}, + {file = "orjson-3.10.7-cp310-none-win32.whl", hash = "sha256:d374d36726746c81a49f3ff8daa2898dccab6596864ebe43d50733275c629175"}, + {file = "orjson-3.10.7-cp310-none-win_amd64.whl", hash = "sha256:cb61938aec8b0ffb6eef484d480188a1777e67b05d58e41b435c74b9d84e0b9c"}, + {file = "orjson-3.10.7-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:7db8539039698ddfb9a524b4dd19508256107568cdad24f3682d5773e60504a2"}, + {file = "orjson-3.10.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:480f455222cb7a1dea35c57a67578848537d2602b46c464472c995297117fa09"}, + {file = "orjson-3.10.7-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8a9c9b168b3a19e37fe2778c0003359f07822c90fdff8f98d9d2a91b3144d8e0"}, + {file = "orjson-3.10.7-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8de062de550f63185e4c1c54151bdddfc5625e37daf0aa1e75d2a1293e3b7d9a"}, + {file = "orjson-3.10.7-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6b0dd04483499d1de9c8f6203f8975caf17a6000b9c0c54630cef02e44ee624e"}, + {file = "orjson-3.10.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b58d3795dafa334fc8fd46f7c5dc013e6ad06fd5b9a4cc98cb1456e7d3558bd6"}, + {file = "orjson-3.10.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:33cfb96c24034a878d83d1a9415799a73dc77480e6c40417e5dda0710d559ee6"}, + {file = "orjson-3.10.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e724cebe1fadc2b23c6f7415bad5ee6239e00a69f30ee423f319c6af70e2a5c0"}, + {file = "orjson-3.10.7-cp311-none-win32.whl", hash = "sha256:82763b46053727a7168d29c772ed5c870fdae2f61aa8a25994c7984a19b1021f"}, + {file = "orjson-3.10.7-cp311-none-win_amd64.whl", hash = "sha256:eb8d384a24778abf29afb8e41d68fdd9a156cf6e5390c04cc07bbc24b89e98b5"}, + {file = "orjson-3.10.7-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:44a96f2d4c3af51bfac6bc4ef7b182aa33f2f054fd7f34cc0ee9a320d051d41f"}, + {file = "orjson-3.10.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76ac14cd57df0572453543f8f2575e2d01ae9e790c21f57627803f5e79b0d3c3"}, + {file = "orjson-3.10.7-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bdbb61dcc365dd9be94e8f7df91975edc9364d6a78c8f7adb69c1cdff318ec93"}, + {file = "orjson-3.10.7-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b48b3db6bb6e0a08fa8c83b47bc169623f801e5cc4f24442ab2b6617da3b5313"}, + {file = "orjson-3.10.7-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:23820a1563a1d386414fef15c249040042b8e5d07b40ab3fe3efbfbbcbcb8864"}, + {file = "orjson-3.10.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0c6a008e91d10a2564edbb6ee5069a9e66df3fbe11c9a005cb411f441fd2c09"}, + {file = "orjson-3.10.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d352ee8ac1926d6193f602cbe36b1643bbd1bbcb25e3c1a657a4390f3000c9a5"}, + {file = "orjson-3.10.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d2d9f990623f15c0ae7ac608103c33dfe1486d2ed974ac3f40b693bad1a22a7b"}, + {file = "orjson-3.10.7-cp312-none-win32.whl", hash = "sha256:7c4c17f8157bd520cdb7195f75ddbd31671997cbe10aee559c2d613592e7d7eb"}, + {file = "orjson-3.10.7-cp312-none-win_amd64.whl", hash = "sha256:1d9c0e733e02ada3ed6098a10a8ee0052dd55774de3d9110d29868d24b17faa1"}, + {file = "orjson-3.10.7-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:77d325ed866876c0fa6492598ec01fe30e803272a6e8b10e992288b009cbe149"}, + {file = "orjson-3.10.7-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ea2c232deedcb605e853ae1db2cc94f7390ac776743b699b50b071b02bea6fe"}, + {file = "orjson-3.10.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3dcfbede6737fdbef3ce9c37af3fb6142e8e1ebc10336daa05872bfb1d87839c"}, + {file = "orjson-3.10.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:11748c135f281203f4ee695b7f80bb1358a82a63905f9f0b794769483ea854ad"}, + {file = "orjson-3.10.7-cp313-none-win32.whl", hash = "sha256:a7e19150d215c7a13f39eb787d84db274298d3f83d85463e61d277bbd7f401d2"}, + {file = "orjson-3.10.7-cp313-none-win_amd64.whl", hash = "sha256:eef44224729e9525d5261cc8d28d6b11cafc90e6bd0be2157bde69a52ec83024"}, + {file = "orjson-3.10.7-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:e76be12658a6fa376fcd331b1ea4e58f5a06fd0220653450f0d415b8fd0fbe20"}, + {file = "orjson-3.10.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed350d6978d28b92939bfeb1a0570c523f6170efc3f0a0ef1f1df287cd4f4960"}, + {file = "orjson-3.10.7-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:144888c76f8520e39bfa121b31fd637e18d4cc2f115727865fdf9fa325b10412"}, + {file = "orjson-3.10.7-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09b2d92fd95ad2402188cf51573acde57eb269eddabaa60f69ea0d733e789fe9"}, + {file = "orjson-3.10.7-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5b24a579123fa884f3a3caadaed7b75eb5715ee2b17ab5c66ac97d29b18fe57f"}, + {file = "orjson-3.10.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e72591bcfe7512353bd609875ab38050efe3d55e18934e2f18950c108334b4ff"}, + {file = "orjson-3.10.7-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f4db56635b58cd1a200b0a23744ff44206ee6aa428185e2b6c4a65b3197abdcd"}, + {file = "orjson-3.10.7-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0fa5886854673222618638c6df7718ea7fe2f3f2384c452c9ccedc70b4a510a5"}, + {file = "orjson-3.10.7-cp39-none-win32.whl", hash = "sha256:8272527d08450ab16eb405f47e0f4ef0e5ff5981c3d82afe0efd25dcbef2bcd2"}, + {file = "orjson-3.10.7-cp39-none-win_amd64.whl", hash = "sha256:974683d4618c0c7dbf4f69c95a979734bf183d0658611760017f6e70a145af58"}, + {file = "orjson-3.10.7.tar.gz", hash = "sha256:75ef0640403f945f3a1f9f6400686560dbfb0fb5b16589ad62cd477043c4eee3"}, +] + +[[package]] +name = "packaging" +version = "24.1" +requires_python = ">=3.8" +summary = "Core utilities for Python packages" +groups = ["default", "docs"] +files = [ + {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, + {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, +] + +[[package]] +name = "paginate" +version = "0.5.7" +summary = "Divides large result sets into pages for easier browsing" +groups = ["docs"] +files = [ + {file = "paginate-0.5.7-py2.py3-none-any.whl", hash = "sha256:b885e2af73abcf01d9559fd5216b57ef722f8c42affbb63942377668e35c7591"}, + {file = "paginate-0.5.7.tar.gz", hash = "sha256:22bd083ab41e1a8b4f3690544afb2c60c25e5c9a63a30fa2f483f6c60c8e5945"}, +] + +[[package]] +name = "pandas" +version = "2.2.2" +requires_python = ">=3.9" +summary = "Powerful data structures for data analysis, time series, and statistics" +groups = ["default", "docs"] +dependencies = [ + "numpy>=1.22.4; python_version < \"3.11\"", + "numpy>=1.23.2; python_version == \"3.11\"", + "numpy>=1.26.0; python_version >= \"3.12\"", + "python-dateutil>=2.8.2", + "pytz>=2020.1", + "tzdata>=2022.7", +] +files = [ + {file = "pandas-2.2.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:90c6fca2acf139569e74e8781709dccb6fe25940488755716d1d354d6bc58bce"}, + {file = "pandas-2.2.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c7adfc142dac335d8c1e0dcbd37eb8617eac386596eb9e1a1b77791cf2498238"}, + {file = "pandas-2.2.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4abfe0be0d7221be4f12552995e58723c7422c80a659da13ca382697de830c08"}, + {file = "pandas-2.2.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8635c16bf3d99040fdf3ca3db669a7250ddf49c55dc4aa8fe0ae0fa8d6dcc1f0"}, + {file = "pandas-2.2.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:40ae1dffb3967a52203105a077415a86044a2bea011b5f321c6aa64b379a3f51"}, + {file = "pandas-2.2.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8e5a0b00e1e56a842f922e7fae8ae4077aee4af0acb5ae3622bd4b4c30aedf99"}, + {file = "pandas-2.2.2-cp310-cp310-win_amd64.whl", hash = "sha256:ddf818e4e6c7c6f4f7c8a12709696d193976b591cc7dc50588d3d1a6b5dc8772"}, + {file = "pandas-2.2.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:696039430f7a562b74fa45f540aca068ea85fa34c244d0deee539cb6d70aa288"}, + {file = "pandas-2.2.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8e90497254aacacbc4ea6ae5e7a8cd75629d6ad2b30025a4a8b09aa4faf55151"}, + {file = "pandas-2.2.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58b84b91b0b9f4bafac2a0ac55002280c094dfc6402402332c0913a59654ab2b"}, + {file = "pandas-2.2.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2123dc9ad6a814bcdea0f099885276b31b24f7edf40f6cdbc0912672e22eee"}, + {file = "pandas-2.2.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:2925720037f06e89af896c70bca73459d7e6a4be96f9de79e2d440bd499fe0db"}, + {file = "pandas-2.2.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0cace394b6ea70c01ca1595f839cf193df35d1575986e484ad35c4aeae7266c1"}, + {file = "pandas-2.2.2-cp311-cp311-win_amd64.whl", hash = "sha256:873d13d177501a28b2756375d59816c365e42ed8417b41665f346289adc68d24"}, + {file = "pandas-2.2.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9dfde2a0ddef507a631dc9dc4af6a9489d5e2e740e226ad426a05cabfbd7c8ef"}, + {file = "pandas-2.2.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e9b79011ff7a0f4b1d6da6a61aa1aa604fb312d6647de5bad20013682d1429ce"}, + {file = "pandas-2.2.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cb51fe389360f3b5a4d57dbd2848a5f033350336ca3b340d1c53a1fad33bcad"}, + {file = "pandas-2.2.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eee3a87076c0756de40b05c5e9a6069c035ba43e8dd71c379e68cab2c20f16ad"}, + {file = "pandas-2.2.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3e374f59e440d4ab45ca2fffde54b81ac3834cf5ae2cdfa69c90bc03bde04d76"}, + {file = "pandas-2.2.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:43498c0bdb43d55cb162cdc8c06fac328ccb5d2eabe3cadeb3529ae6f0517c32"}, + {file = "pandas-2.2.2-cp312-cp312-win_amd64.whl", hash = "sha256:d187d355ecec3629624fccb01d104da7d7f391db0311145817525281e2804d23"}, + {file = "pandas-2.2.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0ca6377b8fca51815f382bd0b697a0814c8bda55115678cbc94c30aacbb6eff2"}, + {file = "pandas-2.2.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9057e6aa78a584bc93a13f0a9bf7e753a5e9770a30b4d758b8d5f2a62a9433cd"}, + {file = "pandas-2.2.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:001910ad31abc7bf06f49dcc903755d2f7f3a9186c0c040b827e522e9cef0863"}, + {file = "pandas-2.2.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66b479b0bd07204e37583c191535505410daa8df638fd8e75ae1b383851fe921"}, + {file = "pandas-2.2.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a77e9d1c386196879aa5eb712e77461aaee433e54c68cf253053a73b7e49c33a"}, + {file = "pandas-2.2.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:92fd6b027924a7e178ac202cfbe25e53368db90d56872d20ffae94b96c7acc57"}, + {file = "pandas-2.2.2-cp39-cp39-win_amd64.whl", hash = "sha256:640cef9aa381b60e296db324337a554aeeb883ead99dc8f6c18e81a93942f5f4"}, + {file = "pandas-2.2.2.tar.gz", hash = "sha256:9e79019aba43cb4fda9e4d983f8e88ca0373adbb697ae9c6c43093218de28b54"}, +] + +[[package]] +name = "pandocfilters" +version = "1.5.1" +requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +summary = "Utilities for writing pandoc filters in python" +groups = ["docs"] +files = [ + {file = "pandocfilters-1.5.1-py2.py3-none-any.whl", hash = "sha256:93be382804a9cdb0a7267585f157e5d1731bbe5545a85b268d6f5fe6232de2bc"}, + {file = "pandocfilters-1.5.1.tar.gz", hash = "sha256:002b4a555ee4ebc03f8b66307e287fa492e4a77b4ea14d3f934328297bb4939e"}, +] + +[[package]] +name = "pathspec" +version = "0.12.1" +requires_python = ">=3.8" +summary = "Utility library for gitignore style pattern matching of file paths." +groups = ["docs"] +files = [ + {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, + {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, +] + +[[package]] +name = "pillow" +version = "10.4.0" +requires_python = ">=3.8" +summary = "Python Imaging Library (Fork)" +groups = ["docs"] +files = [ + {file = "pillow-10.4.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:4d9667937cfa347525b319ae34375c37b9ee6b525440f3ef48542fcf66f2731e"}, + {file = "pillow-10.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:543f3dc61c18dafb755773efc89aae60d06b6596a63914107f75459cf984164d"}, + {file = "pillow-10.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7928ecbf1ece13956b95d9cbcfc77137652b02763ba384d9ab508099a2eca856"}, + {file = "pillow-10.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4d49b85c4348ea0b31ea63bc75a9f3857869174e2bf17e7aba02945cd218e6f"}, + {file = "pillow-10.4.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:6c762a5b0997f5659a5ef2266abc1d8851ad7749ad9a6a5506eb23d314e4f46b"}, + {file = "pillow-10.4.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a985e028fc183bf12a77a8bbf36318db4238a3ded7fa9df1b9a133f1cb79f8fc"}, + {file = "pillow-10.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:812f7342b0eee081eaec84d91423d1b4650bb9828eb53d8511bcef8ce5aecf1e"}, + {file = "pillow-10.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ac1452d2fbe4978c2eec89fb5a23b8387aba707ac72810d9490118817d9c0b46"}, + {file = "pillow-10.4.0-cp310-cp310-win32.whl", hash = "sha256:bcd5e41a859bf2e84fdc42f4edb7d9aba0a13d29a2abadccafad99de3feff984"}, + {file = "pillow-10.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:ecd85a8d3e79cd7158dec1c9e5808e821feea088e2f69a974db5edf84dc53141"}, + {file = "pillow-10.4.0-cp310-cp310-win_arm64.whl", hash = "sha256:ff337c552345e95702c5fde3158acb0625111017d0e5f24bf3acdb9cc16b90d1"}, + {file = "pillow-10.4.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:0a9ec697746f268507404647e531e92889890a087e03681a3606d9b920fbee3c"}, + {file = "pillow-10.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfe91cb65544a1321e631e696759491ae04a2ea11d36715eca01ce07284738be"}, + {file = "pillow-10.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5dc6761a6efc781e6a1544206f22c80c3af4c8cf461206d46a1e6006e4429ff3"}, + {file = "pillow-10.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e84b6cc6a4a3d76c153a6b19270b3526a5a8ed6b09501d3af891daa2a9de7d6"}, + {file = "pillow-10.4.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:bbc527b519bd3aa9d7f429d152fea69f9ad37c95f0b02aebddff592688998abe"}, + {file = "pillow-10.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:76a911dfe51a36041f2e756b00f96ed84677cdeb75d25c767f296c1c1eda1319"}, + {file = "pillow-10.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:59291fb29317122398786c2d44427bbd1a6d7ff54017075b22be9d21aa59bd8d"}, + {file = "pillow-10.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:416d3a5d0e8cfe4f27f574362435bc9bae57f679a7158e0096ad2beb427b8696"}, + {file = "pillow-10.4.0-cp311-cp311-win32.whl", hash = "sha256:7086cc1d5eebb91ad24ded9f58bec6c688e9f0ed7eb3dbbf1e4800280a896496"}, + {file = "pillow-10.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cbed61494057c0f83b83eb3a310f0bf774b09513307c434d4366ed64f4128a91"}, + {file = "pillow-10.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:f5f0c3e969c8f12dd2bb7e0b15d5c468b51e5017e01e2e867335c81903046a22"}, + {file = "pillow-10.4.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:673655af3eadf4df6b5457033f086e90299fdd7a47983a13827acf7459c15d94"}, + {file = "pillow-10.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:866b6942a92f56300012f5fbac71f2d610312ee65e22f1aa2609e491284e5597"}, + {file = "pillow-10.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29dbdc4207642ea6aad70fbde1a9338753d33fb23ed6956e706936706f52dd80"}, + {file = "pillow-10.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf2342ac639c4cf38799a44950bbc2dfcb685f052b9e262f446482afaf4bffca"}, + {file = "pillow-10.4.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:f5b92f4d70791b4a67157321c4e8225d60b119c5cc9aee8ecf153aace4aad4ef"}, + {file = "pillow-10.4.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:86dcb5a1eb778d8b25659d5e4341269e8590ad6b4e8b44d9f4b07f8d136c414a"}, + {file = "pillow-10.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:780c072c2e11c9b2c7ca37f9a2ee8ba66f44367ac3e5c7832afcfe5104fd6d1b"}, + {file = "pillow-10.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:37fb69d905be665f68f28a8bba3c6d3223c8efe1edf14cc4cfa06c241f8c81d9"}, + {file = "pillow-10.4.0-cp312-cp312-win32.whl", hash = "sha256:7dfecdbad5c301d7b5bde160150b4db4c659cee2b69589705b6f8a0c509d9f42"}, + {file = "pillow-10.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:1d846aea995ad352d4bdcc847535bd56e0fd88d36829d2c90be880ef1ee4668a"}, + {file = "pillow-10.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:e553cad5179a66ba15bb18b353a19020e73a7921296a7979c4a2b7f6a5cd57f9"}, + {file = "pillow-10.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8bc1a764ed8c957a2e9cacf97c8b2b053b70307cf2996aafd70e91a082e70df3"}, + {file = "pillow-10.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6209bb41dc692ddfee4942517c19ee81b86c864b626dbfca272ec0f7cff5d9fb"}, + {file = "pillow-10.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bee197b30783295d2eb680b311af15a20a8b24024a19c3a26431ff83eb8d1f70"}, + {file = "pillow-10.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ef61f5dd14c300786318482456481463b9d6b91ebe5ef12f405afbba77ed0be"}, + {file = "pillow-10.4.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:297e388da6e248c98bc4a02e018966af0c5f92dfacf5a5ca22fa01cb3179bca0"}, + {file = "pillow-10.4.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:e4db64794ccdf6cb83a59d73405f63adbe2a1887012e308828596100a0b2f6cc"}, + {file = "pillow-10.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bd2880a07482090a3bcb01f4265f1936a903d70bc740bfcb1fd4e8a2ffe5cf5a"}, + {file = "pillow-10.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b35b21b819ac1dbd1233317adeecd63495f6babf21b7b2512d244ff6c6ce309"}, + {file = "pillow-10.4.0-cp313-cp313-win32.whl", hash = "sha256:551d3fd6e9dc15e4c1eb6fc4ba2b39c0c7933fa113b220057a34f4bb3268a060"}, + {file = "pillow-10.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:030abdbe43ee02e0de642aee345efa443740aa4d828bfe8e2eb11922ea6a21ea"}, + {file = "pillow-10.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:5b001114dd152cfd6b23befeb28d7aee43553e2402c9f159807bf55f33af8a8d"}, + {file = "pillow-10.4.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:0ae24a547e8b711ccaaf99c9ae3cd975470e1a30caa80a6aaee9a2f19c05701d"}, + {file = "pillow-10.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:298478fe4f77a4408895605f3482b6cc6222c018b2ce565c2b6b9c354ac3229b"}, + {file = "pillow-10.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:134ace6dc392116566980ee7436477d844520a26a4b1bd4053f6f47d096997fd"}, + {file = "pillow-10.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:930044bb7679ab003b14023138b50181899da3f25de50e9dbee23b61b4de2126"}, + {file = "pillow-10.4.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:c76e5786951e72ed3686e122d14c5d7012f16c8303a674d18cdcd6d89557fc5b"}, + {file = "pillow-10.4.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:b2724fdb354a868ddf9a880cb84d102da914e99119211ef7ecbdc613b8c96b3c"}, + {file = "pillow-10.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dbc6ae66518ab3c5847659e9988c3b60dc94ffb48ef9168656e0019a93dbf8a1"}, + {file = "pillow-10.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:06b2f7898047ae93fad74467ec3d28fe84f7831370e3c258afa533f81ef7f3df"}, + {file = "pillow-10.4.0-cp39-cp39-win32.whl", hash = "sha256:7970285ab628a3779aecc35823296a7869f889b8329c16ad5a71e4901a3dc4ef"}, + {file = "pillow-10.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:961a7293b2457b405967af9c77dcaa43cc1a8cd50d23c532e62d48ab6cdd56f5"}, + {file = "pillow-10.4.0-cp39-cp39-win_arm64.whl", hash = "sha256:32cda9e3d601a52baccb2856b8ea1fc213c90b340c542dcef77140dfa3278a9e"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5b4815f2e65b30f5fbae9dfffa8636d992d49705723fe86a3661806e069352d4"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8f0aef4ef59694b12cadee839e2ba6afeab89c0f39a3adc02ed51d109117b8da"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f4727572e2918acaa9077c919cbbeb73bd2b3ebcfe033b72f858fc9fbef0026"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff25afb18123cea58a591ea0244b92eb1e61a1fd497bf6d6384f09bc3262ec3e"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:dc3e2db6ba09ffd7d02ae9141cfa0ae23393ee7687248d46a7507b75d610f4f5"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:02a2be69f9c9b8c1e97cf2713e789d4e398c751ecfd9967c18d0ce304efbf885"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:0755ffd4a0c6f267cccbae2e9903d95477ca2f77c4fcf3a3a09570001856c8a5"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:a02364621fe369e06200d4a16558e056fe2805d3468350df3aef21e00d26214b"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:1b5dea9831a90e9d0721ec417a80d4cbd7022093ac38a568db2dd78363b00908"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b885f89040bb8c4a1573566bbb2f44f5c505ef6e74cec7ab9068c900047f04b"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87dd88ded2e6d74d31e1e0a99a726a6765cda32d00ba72dc37f0651f306daaa8"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:2db98790afc70118bd0255c2eeb465e9767ecf1f3c25f9a1abb8ffc8cfd1fe0a"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f7baece4ce06bade126fb84b8af1c33439a76d8a6fd818970215e0560ca28c27"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:cfdd747216947628af7b259d274771d84db2268ca062dd5faf373639d00113a3"}, + {file = "pillow-10.4.0.tar.gz", hash = "sha256:166c1cd4d24309b30d61f79f4a9114b7b2313d7450912277855ff5dfd7cd4a06"}, +] + +[[package]] +name = "platformdirs" +version = "4.3.3" +requires_python = ">=3.8" +summary = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." +groups = ["docs"] +files = [ + {file = "platformdirs-4.3.3-py3-none-any.whl", hash = "sha256:50a5450e2e84f44539718293cbb1da0a0885c9d14adf21b77bae4e66fc99d9b5"}, + {file = "platformdirs-4.3.3.tar.gz", hash = "sha256:d4e0b7d8ec176b341fb03cb11ca12d0276faa8c485f9cd218f613840463fc2c0"}, +] + +[[package]] +name = "portalocker" +version = "2.10.1" +requires_python = ">=3.8" +summary = "Wraps the portalocker recipe for easy usage" +groups = ["default"] +dependencies = [ + "pywin32>=226; platform_system == \"Windows\"", +] +files = [ + {file = "portalocker-2.10.1-py3-none-any.whl", hash = "sha256:53a5984ebc86a025552264b459b46a2086e269b21823cb572f8f28ee759e45bf"}, + {file = "portalocker-2.10.1.tar.gz", hash = "sha256:ef1bf844e878ab08aee7e40184156e1151f228f103aa5c6bd0724cc330960f8f"}, +] + +[[package]] +name = "pyarrow" +version = "17.0.0" +requires_python = ">=3.8" +summary = "Python library for Apache Arrow" +groups = ["default"] +dependencies = [ + "numpy>=1.16.6", +] +files = [ + {file = "pyarrow-17.0.0-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:a5c8b238d47e48812ee577ee20c9a2779e6a5904f1708ae240f53ecbee7c9f07"}, + {file = "pyarrow-17.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:db023dc4c6cae1015de9e198d41250688383c3f9af8f565370ab2b4cb5f62655"}, + {file = "pyarrow-17.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da1e060b3876faa11cee287839f9cc7cdc00649f475714b8680a05fd9071d545"}, + {file = "pyarrow-17.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75c06d4624c0ad6674364bb46ef38c3132768139ddec1c56582dbac54f2663e2"}, + {file = "pyarrow-17.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:fa3c246cc58cb5a4a5cb407a18f193354ea47dd0648194e6265bd24177982fe8"}, + {file = "pyarrow-17.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:f7ae2de664e0b158d1607699a16a488de3d008ba99b3a7aa5de1cbc13574d047"}, + {file = "pyarrow-17.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:5984f416552eea15fd9cee03da53542bf4cddaef5afecefb9aa8d1010c335087"}, + {file = "pyarrow-17.0.0-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:1c8856e2ef09eb87ecf937104aacfa0708f22dfeb039c363ec99735190ffb977"}, + {file = "pyarrow-17.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2e19f569567efcbbd42084e87f948778eb371d308e137a0f97afe19bb860ccb3"}, + {file = "pyarrow-17.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b244dc8e08a23b3e352899a006a26ae7b4d0da7bb636872fa8f5884e70acf15"}, + {file = "pyarrow-17.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b72e87fe3e1db343995562f7fff8aee354b55ee83d13afba65400c178ab2597"}, + {file = "pyarrow-17.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:dc5c31c37409dfbc5d014047817cb4ccd8c1ea25d19576acf1a001fe07f5b420"}, + {file = "pyarrow-17.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:e3343cb1e88bc2ea605986d4b94948716edc7a8d14afd4e2c097232f729758b4"}, + {file = "pyarrow-17.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:a27532c38f3de9eb3e90ecab63dfda948a8ca859a66e3a47f5f42d1e403c4d03"}, + {file = "pyarrow-17.0.0-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:9b8a823cea605221e61f34859dcc03207e52e409ccf6354634143e23af7c8d22"}, + {file = "pyarrow-17.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f1e70de6cb5790a50b01d2b686d54aaf73da01266850b05e3af2a1bc89e16053"}, + {file = "pyarrow-17.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0071ce35788c6f9077ff9ecba4858108eebe2ea5a3f7cf2cf55ebc1dbc6ee24a"}, + {file = "pyarrow-17.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:757074882f844411fcca735e39aae74248a1531367a7c80799b4266390ae51cc"}, + {file = "pyarrow-17.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:9ba11c4f16976e89146781a83833df7f82077cdab7dc6232c897789343f7891a"}, + {file = "pyarrow-17.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:b0c6ac301093b42d34410b187bba560b17c0330f64907bfa4f7f7f2444b0cf9b"}, + {file = "pyarrow-17.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:392bc9feabc647338e6c89267635e111d71edad5fcffba204425a7c8d13610d7"}, + {file = "pyarrow-17.0.0-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:13d7a460b412f31e4c0efa1148e1d29bdf18ad1411eb6757d38f8fbdcc8645fb"}, + {file = "pyarrow-17.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9b564a51fbccfab5a04a80453e5ac6c9954a9c5ef2890d1bcf63741909c3f8df"}, + {file = "pyarrow-17.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32503827abbc5aadedfa235f5ece8c4f8f8b0a3cf01066bc8d29de7539532687"}, + {file = "pyarrow-17.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a155acc7f154b9ffcc85497509bcd0d43efb80d6f733b0dc3bb14e281f131c8b"}, + {file = "pyarrow-17.0.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:dec8d129254d0188a49f8a1fc99e0560dc1b85f60af729f47de4046015f9b0a5"}, + {file = "pyarrow-17.0.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:a48ddf5c3c6a6c505904545c25a4ae13646ae1f8ba703c4df4a1bfe4f4006bda"}, + {file = "pyarrow-17.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:42bf93249a083aca230ba7e2786c5f673507fa97bbd9725a1e2754715151a204"}, + {file = "pyarrow-17.0.0.tar.gz", hash = "sha256:4beca9521ed2c0921c1023e68d097d0299b62c362639ea315572a58f3f50fd28"}, +] + +[[package]] +name = "pycparser" +version = "2.22" +requires_python = ">=3.8" +summary = "C parser in Python" +groups = ["docs"] +files = [ + {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, + {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, +] + +[[package]] +name = "pydantic" +version = "2.9.1" +requires_python = ">=3.8" +summary = "Data validation using Python type hints" +groups = ["default"] +dependencies = [ + "annotated-types>=0.6.0", + "pydantic-core==2.23.3", + "typing-extensions>=4.12.2; python_version >= \"3.13\"", + "typing-extensions>=4.6.1; python_version < \"3.13\"", +] +files = [ + {file = "pydantic-2.9.1-py3-none-any.whl", hash = "sha256:7aff4db5fdf3cf573d4b3c30926a510a10e19a0774d38fc4967f78beb6deb612"}, + {file = "pydantic-2.9.1.tar.gz", hash = "sha256:1363c7d975c7036df0db2b4a61f2e062fbc0aa5ab5f2772e0ffc7191a4f4bce2"}, +] + +[[package]] +name = "pydantic-core" +version = "2.23.3" +requires_python = ">=3.8" +summary = "Core functionality for Pydantic validation and serialization" +groups = ["default"] +dependencies = [ + "typing-extensions!=4.7.0,>=4.6.0", +] +files = [ + {file = "pydantic_core-2.23.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:7f10a5d1b9281392f1bf507d16ac720e78285dfd635b05737c3911637601bae6"}, + {file = "pydantic_core-2.23.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3c09a7885dd33ee8c65266e5aa7fb7e2f23d49d8043f089989726391dd7350c5"}, + {file = "pydantic_core-2.23.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6470b5a1ec4d1c2e9afe928c6cb37eb33381cab99292a708b8cb9aa89e62429b"}, + {file = "pydantic_core-2.23.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9172d2088e27d9a185ea0a6c8cebe227a9139fd90295221d7d495944d2367700"}, + {file = "pydantic_core-2.23.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86fc6c762ca7ac8fbbdff80d61b2c59fb6b7d144aa46e2d54d9e1b7b0e780e01"}, + {file = "pydantic_core-2.23.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0cb80fd5c2df4898693aa841425ea1727b1b6d2167448253077d2a49003e0ed"}, + {file = "pydantic_core-2.23.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03667cec5daf43ac4995cefa8aaf58f99de036204a37b889c24a80927b629cec"}, + {file = "pydantic_core-2.23.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:047531242f8e9c2db733599f1c612925de095e93c9cc0e599e96cf536aaf56ba"}, + {file = "pydantic_core-2.23.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5499798317fff7f25dbef9347f4451b91ac2a4330c6669821c8202fd354c7bee"}, + {file = "pydantic_core-2.23.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bbb5e45eab7624440516ee3722a3044b83fff4c0372efe183fd6ba678ff681fe"}, + {file = "pydantic_core-2.23.3-cp310-none-win32.whl", hash = "sha256:8b5b3ed73abb147704a6e9f556d8c5cb078f8c095be4588e669d315e0d11893b"}, + {file = "pydantic_core-2.23.3-cp310-none-win_amd64.whl", hash = "sha256:2b603cde285322758a0279995b5796d64b63060bfbe214b50a3ca23b5cee3e83"}, + {file = "pydantic_core-2.23.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:c889fd87e1f1bbeb877c2ee56b63bb297de4636661cc9bbfcf4b34e5e925bc27"}, + {file = "pydantic_core-2.23.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ea85bda3189fb27503af4c45273735bcde3dd31c1ab17d11f37b04877859ef45"}, + {file = "pydantic_core-2.23.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a7f7f72f721223f33d3dc98a791666ebc6a91fa023ce63733709f4894a7dc611"}, + {file = "pydantic_core-2.23.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b2b55b0448e9da68f56b696f313949cda1039e8ec7b5d294285335b53104b61"}, + {file = "pydantic_core-2.23.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c24574c7e92e2c56379706b9a3f07c1e0c7f2f87a41b6ee86653100c4ce343e5"}, + {file = "pydantic_core-2.23.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2b05e6ccbee333a8f4b8f4d7c244fdb7a979e90977ad9c51ea31261e2085ce0"}, + {file = "pydantic_core-2.23.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2c409ce1c219c091e47cb03feb3c4ed8c2b8e004efc940da0166aaee8f9d6c8"}, + {file = "pydantic_core-2.23.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d965e8b325f443ed3196db890d85dfebbb09f7384486a77461347f4adb1fa7f8"}, + {file = "pydantic_core-2.23.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f56af3a420fb1ffaf43ece3ea09c2d27c444e7c40dcb7c6e7cf57aae764f2b48"}, + {file = "pydantic_core-2.23.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5b01a078dd4f9a52494370af21aa52964e0a96d4862ac64ff7cea06e0f12d2c5"}, + {file = "pydantic_core-2.23.3-cp311-none-win32.whl", hash = "sha256:560e32f0df04ac69b3dd818f71339983f6d1f70eb99d4d1f8e9705fb6c34a5c1"}, + {file = "pydantic_core-2.23.3-cp311-none-win_amd64.whl", hash = "sha256:c744fa100fdea0d000d8bcddee95213d2de2e95b9c12be083370b2072333a0fa"}, + {file = "pydantic_core-2.23.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:e0ec50663feedf64d21bad0809f5857bac1ce91deded203efc4a84b31b2e4305"}, + {file = "pydantic_core-2.23.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:db6e6afcb95edbe6b357786684b71008499836e91f2a4a1e55b840955b341dbb"}, + {file = "pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98ccd69edcf49f0875d86942f4418a4e83eb3047f20eb897bffa62a5d419c8fa"}, + {file = "pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a678c1ac5c5ec5685af0133262103defb427114e62eafeda12f1357a12140162"}, + {file = "pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:01491d8b4d8db9f3391d93b0df60701e644ff0894352947f31fff3e52bd5c801"}, + {file = "pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fcf31facf2796a2d3b7fe338fe8640aa0166e4e55b4cb108dbfd1058049bf4cb"}, + {file = "pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7200fd561fb3be06827340da066df4311d0b6b8eb0c2116a110be5245dceb326"}, + {file = "pydantic_core-2.23.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dc1636770a809dee2bd44dd74b89cc80eb41172bcad8af75dd0bc182c2666d4c"}, + {file = "pydantic_core-2.23.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:67a5def279309f2e23014b608c4150b0c2d323bd7bccd27ff07b001c12c2415c"}, + {file = "pydantic_core-2.23.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:748bdf985014c6dd3e1e4cc3db90f1c3ecc7246ff5a3cd4ddab20c768b2f1dab"}, + {file = "pydantic_core-2.23.3-cp312-none-win32.whl", hash = "sha256:255ec6dcb899c115f1e2a64bc9ebc24cc0e3ab097775755244f77360d1f3c06c"}, + {file = "pydantic_core-2.23.3-cp312-none-win_amd64.whl", hash = "sha256:40b8441be16c1e940abebed83cd006ddb9e3737a279e339dbd6d31578b802f7b"}, + {file = "pydantic_core-2.23.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:6daaf5b1ba1369a22c8b050b643250e3e5efc6a78366d323294aee54953a4d5f"}, + {file = "pydantic_core-2.23.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d015e63b985a78a3d4ccffd3bdf22b7c20b3bbd4b8227809b3e8e75bc37f9cb2"}, + {file = "pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3fc572d9b5b5cfe13f8e8a6e26271d5d13f80173724b738557a8c7f3a8a3791"}, + {file = "pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f6bd91345b5163ee7448bee201ed7dd601ca24f43f439109b0212e296eb5b423"}, + {file = "pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc379c73fd66606628b866f661e8785088afe2adaba78e6bbe80796baf708a63"}, + {file = "pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fbdce4b47592f9e296e19ac31667daed8753c8367ebb34b9a9bd89dacaa299c9"}, + {file = "pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc3cf31edf405a161a0adad83246568647c54404739b614b1ff43dad2b02e6d5"}, + {file = "pydantic_core-2.23.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8e22b477bf90db71c156f89a55bfe4d25177b81fce4aa09294d9e805eec13855"}, + {file = "pydantic_core-2.23.3-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:0a0137ddf462575d9bce863c4c95bac3493ba8e22f8c28ca94634b4a1d3e2bb4"}, + {file = "pydantic_core-2.23.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:203171e48946c3164fe7691fc349c79241ff8f28306abd4cad5f4f75ed80bc8d"}, + {file = "pydantic_core-2.23.3-cp313-none-win32.whl", hash = "sha256:76bdab0de4acb3f119c2a4bff740e0c7dc2e6de7692774620f7452ce11ca76c8"}, + {file = "pydantic_core-2.23.3-cp313-none-win_amd64.whl", hash = "sha256:37ba321ac2a46100c578a92e9a6aa33afe9ec99ffa084424291d84e456f490c1"}, + {file = "pydantic_core-2.23.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:82da2f4703894134a9f000e24965df73cc103e31e8c31906cc1ee89fde72cbd8"}, + {file = "pydantic_core-2.23.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:dd9be0a42de08f4b58a3cc73a123f124f65c24698b95a54c1543065baca8cf0e"}, + {file = "pydantic_core-2.23.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89b731f25c80830c76fdb13705c68fef6a2b6dc494402987c7ea9584fe189f5d"}, + {file = "pydantic_core-2.23.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c6de1ec30c4bb94f3a69c9f5f2182baeda5b809f806676675e9ef6b8dc936f28"}, + {file = "pydantic_core-2.23.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb68b41c3fa64587412b104294b9cbb027509dc2f6958446c502638d481525ef"}, + {file = "pydantic_core-2.23.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c3980f2843de5184656aab58698011b42763ccba11c4a8c35936c8dd6c7068c"}, + {file = "pydantic_core-2.23.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94f85614f2cba13f62c3c6481716e4adeae48e1eaa7e8bac379b9d177d93947a"}, + {file = "pydantic_core-2.23.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:510b7fb0a86dc8f10a8bb43bd2f97beb63cffad1203071dc434dac26453955cd"}, + {file = "pydantic_core-2.23.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:1eba2f7ce3e30ee2170410e2171867ea73dbd692433b81a93758ab2de6c64835"}, + {file = "pydantic_core-2.23.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4b259fd8409ab84b4041b7b3f24dcc41e4696f180b775961ca8142b5b21d0e70"}, + {file = "pydantic_core-2.23.3-cp39-none-win32.whl", hash = "sha256:40d9bd259538dba2f40963286009bf7caf18b5112b19d2b55b09c14dde6db6a7"}, + {file = "pydantic_core-2.23.3-cp39-none-win_amd64.whl", hash = "sha256:5a8cd3074a98ee70173a8633ad3c10e00dcb991ecec57263aacb4095c5efb958"}, + {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f399e8657c67313476a121a6944311fab377085ca7f490648c9af97fc732732d"}, + {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:6b5547d098c76e1694ba85f05b595720d7c60d342f24d5aad32c3049131fa5c4"}, + {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0dda0290a6f608504882d9f7650975b4651ff91c85673341789a476b1159f211"}, + {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65b6e5da855e9c55a0c67f4db8a492bf13d8d3316a59999cfbaf98cc6e401961"}, + {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:09e926397f392059ce0afdcac920df29d9c833256354d0c55f1584b0b70cf07e"}, + {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:87cfa0ed6b8c5bd6ae8b66de941cece179281239d482f363814d2b986b79cedc"}, + {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e61328920154b6a44d98cabcb709f10e8b74276bc709c9a513a8c37a18786cc4"}, + {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ce3317d155628301d649fe5e16a99528d5680af4ec7aa70b90b8dacd2d725c9b"}, + {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:e89513f014c6be0d17b00a9a7c81b1c426f4eb9224b15433f3d98c1a071f8433"}, + {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:4f62c1c953d7ee375df5eb2e44ad50ce2f5aff931723b398b8bc6f0ac159791a"}, + {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2718443bc671c7ac331de4eef9b673063b10af32a0bb385019ad61dcf2cc8f6c"}, + {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0d90e08b2727c5d01af1b5ef4121d2f0c99fbee692c762f4d9d0409c9da6541"}, + {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2b676583fc459c64146debea14ba3af54e540b61762dfc0613dc4e98c3f66eeb"}, + {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:50e4661f3337977740fdbfbae084ae5693e505ca2b3130a6d4eb0f2281dc43b8"}, + {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:68f4cf373f0de6abfe599a38307f4417c1c867ca381c03df27c873a9069cda25"}, + {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:59d52cf01854cb26c46958552a21acb10dd78a52aa34c86f284e66b209db8cab"}, + {file = "pydantic_core-2.23.3.tar.gz", hash = "sha256:3cb0f65d8b4121c1b015c60104a685feb929a29d7cf204387c7f2688c7974690"}, +] + +[[package]] +name = "pygments" +version = "2.18.0" +requires_python = ">=3.8" +summary = "Pygments is a syntax highlighting package written in Python." +groups = ["default", "docs"] +files = [ + {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"}, + {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"}, +] + +[[package]] +name = "pymdown-extensions" +version = "10.9" +requires_python = ">=3.8" +summary = "Extension pack for Python Markdown." +groups = ["docs"] +dependencies = [ + "markdown>=3.6", + "pyyaml", +] +files = [ + {file = "pymdown_extensions-10.9-py3-none-any.whl", hash = "sha256:d323f7e90d83c86113ee78f3fe62fc9dee5f56b54d912660703ea1816fed5626"}, + {file = "pymdown_extensions-10.9.tar.gz", hash = "sha256:6ff740bcd99ec4172a938970d42b96128bdc9d4b9bcad72494f29921dc69b753"}, +] + +[[package]] +name = "pyparsing" +version = "3.1.4" +requires_python = ">=3.6.8" +summary = "pyparsing module - Classes and methods to define and execute parsing grammars" +groups = ["docs"] +files = [ + {file = "pyparsing-3.1.4-py3-none-any.whl", hash = "sha256:a6a7ee4235a3f944aa1fa2249307708f893fe5717dc603503c6c7969c070fb7c"}, + {file = "pyparsing-3.1.4.tar.gz", hash = "sha256:f86ec8d1a83f11977c9a6ea7598e8c27fc5cddfa5b07ea2241edbbde1d7bc032"}, +] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +summary = "Extensions to the standard Python datetime module" +groups = ["default", "docs"] +dependencies = [ + "six>=1.5", +] +files = [ + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] + +[[package]] +name = "pytz" +version = "2024.2" +summary = "World timezone definitions, modern and historical" +groups = ["default", "docs"] +files = [ + {file = "pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725"}, + {file = "pytz-2024.2.tar.gz", hash = "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a"}, +] + +[[package]] +name = "pywin32" +version = "306" +summary = "Python for Window Extensions" +groups = ["default", "docs"] +marker = "sys_platform == \"win32\" and platform_python_implementation != \"PyPy\" or platform_system == \"Windows\"" +files = [ + {file = "pywin32-306-cp310-cp310-win32.whl", hash = "sha256:06d3420a5155ba65f0b72f2699b5bacf3109f36acbe8923765c22938a69dfc8d"}, + {file = "pywin32-306-cp310-cp310-win_amd64.whl", hash = "sha256:84f4471dbca1887ea3803d8848a1616429ac94a4a8d05f4bc9c5dcfd42ca99c8"}, + {file = "pywin32-306-cp311-cp311-win32.whl", hash = "sha256:e65028133d15b64d2ed8f06dd9fbc268352478d4f9289e69c190ecd6818b6407"}, + {file = "pywin32-306-cp311-cp311-win_amd64.whl", hash = "sha256:a7639f51c184c0272e93f244eb24dafca9b1855707d94c192d4a0b4c01e1100e"}, + {file = "pywin32-306-cp311-cp311-win_arm64.whl", hash = "sha256:70dba0c913d19f942a2db25217d9a1b726c278f483a919f1abfed79c9cf64d3a"}, + {file = "pywin32-306-cp312-cp312-win32.whl", hash = "sha256:383229d515657f4e3ed1343da8be101000562bf514591ff383ae940cad65458b"}, + {file = "pywin32-306-cp312-cp312-win_amd64.whl", hash = "sha256:37257794c1ad39ee9be652da0462dc2e394c8159dfd913a8a4e8eb6fd346da0e"}, + {file = "pywin32-306-cp312-cp312-win_arm64.whl", hash = "sha256:5821ec52f6d321aa59e2db7e0a35b997de60c201943557d108af9d4ae1ec7040"}, + {file = "pywin32-306-cp39-cp39-win32.whl", hash = "sha256:e25fd5b485b55ac9c057f67d94bc203f3f6595078d1fb3b458c9c28b7153a802"}, + {file = "pywin32-306-cp39-cp39-win_amd64.whl", hash = "sha256:39b61c15272833b5c329a2989999dcae836b1eed650252ab1b7bfbe1d59f30f4"}, +] + +[[package]] +name = "pyyaml" +version = "6.0.2" +requires_python = ">=3.8" +summary = "YAML parser and emitter for Python" +groups = ["default", "docs"] +files = [ + {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, + {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"}, + {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"}, + {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"}, + {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"}, + {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"}, + {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"}, + {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"}, + {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"}, + {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"}, + {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"}, + {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"}, + {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"}, + {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"}, + {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"}, + {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"}, + {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"}, + {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"}, + {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"}, + {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"}, + {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"}, + {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"}, + {file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"}, + {file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"}, + {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"}, + {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"}, + {file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"}, + {file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"}, + {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, +] + +[[package]] +name = "pyyaml-env-tag" +version = "0.1" +requires_python = ">=3.6" +summary = "A custom YAML tag for referencing environment variables in YAML files. " +groups = ["docs"] +dependencies = [ + "pyyaml", +] +files = [ + {file = "pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069"}, + {file = "pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb"}, +] + +[[package]] +name = "pyzmq" +version = "26.2.0" +requires_python = ">=3.7" +summary = "Python bindings for 0MQ" +groups = ["docs"] +dependencies = [ + "cffi; implementation_name == \"pypy\"", +] +files = [ + {file = "pyzmq-26.2.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:ddf33d97d2f52d89f6e6e7ae66ee35a4d9ca6f36eda89c24591b0c40205a3629"}, + {file = "pyzmq-26.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dacd995031a01d16eec825bf30802fceb2c3791ef24bcce48fa98ce40918c27b"}, + {file = "pyzmq-26.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89289a5ee32ef6c439086184529ae060c741334b8970a6855ec0b6ad3ff28764"}, + {file = "pyzmq-26.2.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5506f06d7dc6ecf1efacb4a013b1f05071bb24b76350832c96449f4a2d95091c"}, + {file = "pyzmq-26.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ea039387c10202ce304af74def5021e9adc6297067f3441d348d2b633e8166a"}, + {file = "pyzmq-26.2.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a2224fa4a4c2ee872886ed00a571f5e967c85e078e8e8c2530a2fb01b3309b88"}, + {file = "pyzmq-26.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:28ad5233e9c3b52d76196c696e362508959741e1a005fb8fa03b51aea156088f"}, + {file = "pyzmq-26.2.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:1c17211bc037c7d88e85ed8b7d8f7e52db6dc8eca5590d162717c654550f7282"}, + {file = "pyzmq-26.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b8f86dd868d41bea9a5f873ee13bf5551c94cf6bc51baebc6f85075971fe6eea"}, + {file = "pyzmq-26.2.0-cp310-cp310-win32.whl", hash = "sha256:46a446c212e58456b23af260f3d9fb785054f3e3653dbf7279d8f2b5546b21c2"}, + {file = "pyzmq-26.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:49d34ab71db5a9c292a7644ce74190b1dd5a3475612eefb1f8be1d6961441971"}, + {file = "pyzmq-26.2.0-cp310-cp310-win_arm64.whl", hash = "sha256:bfa832bfa540e5b5c27dcf5de5d82ebc431b82c453a43d141afb1e5d2de025fa"}, + {file = "pyzmq-26.2.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:8f7e66c7113c684c2b3f1c83cdd3376103ee0ce4c49ff80a648643e57fb22218"}, + {file = "pyzmq-26.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3a495b30fc91db2db25120df5847d9833af237546fd59170701acd816ccc01c4"}, + {file = "pyzmq-26.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77eb0968da535cba0470a5165468b2cac7772cfb569977cff92e240f57e31bef"}, + {file = "pyzmq-26.2.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ace4f71f1900a548f48407fc9be59c6ba9d9aaf658c2eea6cf2779e72f9f317"}, + {file = "pyzmq-26.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:92a78853d7280bffb93df0a4a6a2498cba10ee793cc8076ef797ef2f74d107cf"}, + {file = "pyzmq-26.2.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:689c5d781014956a4a6de61d74ba97b23547e431e9e7d64f27d4922ba96e9d6e"}, + {file = "pyzmq-26.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0aca98bc423eb7d153214b2df397c6421ba6373d3397b26c057af3c904452e37"}, + {file = "pyzmq-26.2.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1f3496d76b89d9429a656293744ceca4d2ac2a10ae59b84c1da9b5165f429ad3"}, + {file = "pyzmq-26.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5c2b3bfd4b9689919db068ac6c9911f3fcb231c39f7dd30e3138be94896d18e6"}, + {file = "pyzmq-26.2.0-cp311-cp311-win32.whl", hash = "sha256:eac5174677da084abf378739dbf4ad245661635f1600edd1221f150b165343f4"}, + {file = "pyzmq-26.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:5a509df7d0a83a4b178d0f937ef14286659225ef4e8812e05580776c70e155d5"}, + {file = "pyzmq-26.2.0-cp311-cp311-win_arm64.whl", hash = "sha256:c0e6091b157d48cbe37bd67233318dbb53e1e6327d6fc3bb284afd585d141003"}, + {file = "pyzmq-26.2.0-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:ded0fc7d90fe93ae0b18059930086c51e640cdd3baebdc783a695c77f123dcd9"}, + {file = "pyzmq-26.2.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:17bf5a931c7f6618023cdacc7081f3f266aecb68ca692adac015c383a134ca52"}, + {file = "pyzmq-26.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55cf66647e49d4621a7e20c8d13511ef1fe1efbbccf670811864452487007e08"}, + {file = "pyzmq-26.2.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4661c88db4a9e0f958c8abc2b97472e23061f0bc737f6f6179d7a27024e1faa5"}, + {file = "pyzmq-26.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea7f69de383cb47522c9c208aec6dd17697db7875a4674c4af3f8cfdac0bdeae"}, + {file = "pyzmq-26.2.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:7f98f6dfa8b8ccaf39163ce872bddacca38f6a67289116c8937a02e30bbe9711"}, + {file = "pyzmq-26.2.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e3e0210287329272539eea617830a6a28161fbbd8a3271bf4150ae3e58c5d0e6"}, + {file = "pyzmq-26.2.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:6b274e0762c33c7471f1a7471d1a2085b1a35eba5cdc48d2ae319f28b6fc4de3"}, + {file = "pyzmq-26.2.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:29c6a4635eef69d68a00321e12a7d2559fe2dfccfa8efae3ffb8e91cd0b36a8b"}, + {file = "pyzmq-26.2.0-cp312-cp312-win32.whl", hash = "sha256:989d842dc06dc59feea09e58c74ca3e1678c812a4a8a2a419046d711031f69c7"}, + {file = "pyzmq-26.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:2a50625acdc7801bc6f74698c5c583a491c61d73c6b7ea4dee3901bb99adb27a"}, + {file = "pyzmq-26.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:4d29ab8592b6ad12ebbf92ac2ed2bedcfd1cec192d8e559e2e099f648570e19b"}, + {file = "pyzmq-26.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9dd8cd1aeb00775f527ec60022004d030ddc51d783d056e3e23e74e623e33726"}, + {file = "pyzmq-26.2.0-cp313-cp313-macosx_10_15_universal2.whl", hash = "sha256:28c812d9757fe8acecc910c9ac9dafd2ce968c00f9e619db09e9f8f54c3a68a3"}, + {file = "pyzmq-26.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d80b1dd99c1942f74ed608ddb38b181b87476c6a966a88a950c7dee118fdf50"}, + {file = "pyzmq-26.2.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8c997098cc65e3208eca09303630e84d42718620e83b733d0fd69543a9cab9cb"}, + {file = "pyzmq-26.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ad1bc8d1b7a18497dda9600b12dc193c577beb391beae5cd2349184db40f187"}, + {file = "pyzmq-26.2.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:bea2acdd8ea4275e1278350ced63da0b166421928276c7c8e3f9729d7402a57b"}, + {file = "pyzmq-26.2.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:23f4aad749d13698f3f7b64aad34f5fc02d6f20f05999eebc96b89b01262fb18"}, + {file = "pyzmq-26.2.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:a4f96f0d88accc3dbe4a9025f785ba830f968e21e3e2c6321ccdfc9aef755115"}, + {file = "pyzmq-26.2.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ced65e5a985398827cc9276b93ef6dfabe0273c23de8c7931339d7e141c2818e"}, + {file = "pyzmq-26.2.0-cp313-cp313-win32.whl", hash = "sha256:31507f7b47cc1ead1f6e86927f8ebb196a0bab043f6345ce070f412a59bf87b5"}, + {file = "pyzmq-26.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:70fc7fcf0410d16ebdda9b26cbd8bf8d803d220a7f3522e060a69a9c87bf7bad"}, + {file = "pyzmq-26.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:c3789bd5768ab5618ebf09cef6ec2b35fed88709b104351748a63045f0ff9797"}, + {file = "pyzmq-26.2.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:034da5fc55d9f8da09015d368f519478a52675e558c989bfcb5cf6d4e16a7d2a"}, + {file = "pyzmq-26.2.0-cp313-cp313t-macosx_10_15_universal2.whl", hash = "sha256:c92d73464b886931308ccc45b2744e5968cbaade0b1d6aeb40d8ab537765f5bc"}, + {file = "pyzmq-26.2.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:794a4562dcb374f7dbbfb3f51d28fb40123b5a2abadee7b4091f93054909add5"}, + {file = "pyzmq-26.2.0-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aee22939bb6075e7afededabad1a56a905da0b3c4e3e0c45e75810ebe3a52672"}, + {file = "pyzmq-26.2.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ae90ff9dad33a1cfe947d2c40cb9cb5e600d759ac4f0fd22616ce6540f72797"}, + {file = "pyzmq-26.2.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:43a47408ac52647dfabbc66a25b05b6a61700b5165807e3fbd40063fcaf46386"}, + {file = "pyzmq-26.2.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:25bf2374a2a8433633c65ccb9553350d5e17e60c8eb4de4d92cc6bd60f01d306"}, + {file = "pyzmq-26.2.0-cp313-cp313t-musllinux_1_1_i686.whl", hash = "sha256:007137c9ac9ad5ea21e6ad97d3489af654381324d5d3ba614c323f60dab8fae6"}, + {file = "pyzmq-26.2.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:470d4a4f6d48fb34e92d768b4e8a5cc3780db0d69107abf1cd7ff734b9766eb0"}, + {file = "pyzmq-26.2.0-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:b1d464cb8d72bfc1a3adc53305a63a8e0cac6bc8c5a07e8ca190ab8d3faa43c2"}, + {file = "pyzmq-26.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4da04c48873a6abdd71811c5e163bd656ee1b957971db7f35140a2d573f6949c"}, + {file = "pyzmq-26.2.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:d049df610ac811dcffdc147153b414147428567fbbc8be43bb8885f04db39d98"}, + {file = "pyzmq-26.2.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:05590cdbc6b902101d0e65d6a4780af14dc22914cc6ab995d99b85af45362cc9"}, + {file = "pyzmq-26.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c811cfcd6a9bf680236c40c6f617187515269ab2912f3d7e8c0174898e2519db"}, + {file = "pyzmq-26.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6835dd60355593de10350394242b5757fbbd88b25287314316f266e24c61d073"}, + {file = "pyzmq-26.2.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc6bee759a6bddea5db78d7dcd609397449cb2d2d6587f48f3ca613b19410cfc"}, + {file = "pyzmq-26.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c530e1eecd036ecc83c3407f77bb86feb79916d4a33d11394b8234f3bd35b940"}, + {file = "pyzmq-26.2.0-cp39-cp39-win32.whl", hash = "sha256:367b4f689786fca726ef7a6c5ba606958b145b9340a5e4808132cc65759abd44"}, + {file = "pyzmq-26.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:e6fa2e3e683f34aea77de8112f6483803c96a44fd726d7358b9888ae5bb394ec"}, + {file = "pyzmq-26.2.0-cp39-cp39-win_arm64.whl", hash = "sha256:7445be39143a8aa4faec43b076e06944b8f9d0701b669df4af200531b21e40bb"}, + {file = "pyzmq-26.2.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:706e794564bec25819d21a41c31d4df2d48e1cc4b061e8d345d7fb4dd3e94072"}, + {file = "pyzmq-26.2.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b435f2753621cd36e7c1762156815e21c985c72b19135dac43a7f4f31d28dd1"}, + {file = "pyzmq-26.2.0-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:160c7e0a5eb178011e72892f99f918c04a131f36056d10d9c1afb223fc952c2d"}, + {file = "pyzmq-26.2.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c4a71d5d6e7b28a47a394c0471b7e77a0661e2d651e7ae91e0cab0a587859ca"}, + {file = "pyzmq-26.2.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:90412f2db8c02a3864cbfc67db0e3dcdbda336acf1c469526d3e869394fe001c"}, + {file = "pyzmq-26.2.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:cdeabcff45d1c219636ee2e54d852262e5c2e085d6cb476d938aee8d921356b3"}, + {file = "pyzmq-26.2.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35cffef589bcdc587d06f9149f8d5e9e8859920a071df5a2671de2213bef592a"}, + {file = "pyzmq-26.2.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18c8dc3b7468d8b4bdf60ce9d7141897da103c7a4690157b32b60acb45e333e6"}, + {file = "pyzmq-26.2.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7133d0a1677aec369d67dd78520d3fa96dd7f3dcec99d66c1762870e5ea1a50a"}, + {file = "pyzmq-26.2.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:6a96179a24b14fa6428cbfc08641c779a53f8fcec43644030328f44034c7f1f4"}, + {file = "pyzmq-26.2.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:4f78c88905461a9203eac9faac157a2a0dbba84a0fd09fd29315db27be40af9f"}, + {file = "pyzmq-26.2.0.tar.gz", hash = "sha256:070672c258581c8e4f640b5159297580a9974b026043bd4ab0470be9ed324f1f"}, +] + +[[package]] +name = "referencing" +version = "0.35.1" +requires_python = ">=3.8" +summary = "JSON Referencing + Python" +groups = ["docs"] +dependencies = [ + "attrs>=22.2.0", + "rpds-py>=0.7.0", +] +files = [ + {file = "referencing-0.35.1-py3-none-any.whl", hash = "sha256:eda6d3234d62814d1c64e305c1331c9a3a6132da475ab6382eaa997b21ee75de"}, + {file = "referencing-0.35.1.tar.gz", hash = "sha256:25b42124a6c8b632a425174f24087783efb348a6f1e0008e63cd4466fedf703c"}, +] + +[[package]] +name = "regex" +version = "2024.9.11" +requires_python = ">=3.8" +summary = "Alternative regular expression module, to replace re." +groups = ["docs"] +files = [ + {file = "regex-2024.9.11-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1494fa8725c285a81d01dc8c06b55287a1ee5e0e382d8413adc0a9197aac6408"}, + {file = "regex-2024.9.11-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0e12c481ad92d129c78f13a2a3662317e46ee7ef96c94fd332e1c29131875b7d"}, + {file = "regex-2024.9.11-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:16e13a7929791ac1216afde26f712802e3df7bf0360b32e4914dca3ab8baeea5"}, + {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46989629904bad940bbec2106528140a218b4a36bb3042d8406980be1941429c"}, + {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a906ed5e47a0ce5f04b2c981af1c9acf9e8696066900bf03b9d7879a6f679fc8"}, + {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e9a091b0550b3b0207784a7d6d0f1a00d1d1c8a11699c1a4d93db3fbefc3ad35"}, + {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ddcd9a179c0a6fa8add279a4444015acddcd7f232a49071ae57fa6e278f1f71"}, + {file = "regex-2024.9.11-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6b41e1adc61fa347662b09398e31ad446afadff932a24807d3ceb955ed865cc8"}, + {file = "regex-2024.9.11-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ced479f601cd2f8ca1fd7b23925a7e0ad512a56d6e9476f79b8f381d9d37090a"}, + {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:635a1d96665f84b292e401c3d62775851aedc31d4f8784117b3c68c4fcd4118d"}, + {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c0256beda696edcf7d97ef16b2a33a8e5a875affd6fa6567b54f7c577b30a137"}, + {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:3ce4f1185db3fbde8ed8aa223fc9620f276c58de8b0d4f8cc86fd1360829edb6"}, + {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:09d77559e80dcc9d24570da3745ab859a9cf91953062e4ab126ba9d5993688ca"}, + {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7a22ccefd4db3f12b526eccb129390942fe874a3a9fdbdd24cf55773a1faab1a"}, + {file = "regex-2024.9.11-cp310-cp310-win32.whl", hash = "sha256:f745ec09bc1b0bd15cfc73df6fa4f726dcc26bb16c23a03f9e3367d357eeedd0"}, + {file = "regex-2024.9.11-cp310-cp310-win_amd64.whl", hash = "sha256:01c2acb51f8a7d6494c8c5eafe3d8e06d76563d8a8a4643b37e9b2dd8a2ff623"}, + {file = "regex-2024.9.11-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2cce2449e5927a0bf084d346da6cd5eb016b2beca10d0013ab50e3c226ffc0df"}, + {file = "regex-2024.9.11-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3b37fa423beefa44919e009745ccbf353d8c981516e807995b2bd11c2c77d268"}, + {file = "regex-2024.9.11-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:64ce2799bd75039b480cc0360907c4fb2f50022f030bf9e7a8705b636e408fad"}, + {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a4cc92bb6db56ab0c1cbd17294e14f5e9224f0cc6521167ef388332604e92679"}, + {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d05ac6fa06959c4172eccd99a222e1fbf17b5670c4d596cb1e5cde99600674c4"}, + {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:040562757795eeea356394a7fb13076ad4f99d3c62ab0f8bdfb21f99a1f85664"}, + {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6113c008a7780792efc80f9dfe10ba0cd043cbf8dc9a76ef757850f51b4edc50"}, + {file = "regex-2024.9.11-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8e5fb5f77c8745a60105403a774fe2c1759b71d3e7b4ca237a5e67ad066c7199"}, + {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:54d9ff35d4515debf14bc27f1e3b38bfc453eff3220f5bce159642fa762fe5d4"}, + {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:df5cbb1fbc74a8305b6065d4ade43b993be03dbe0f8b30032cced0d7740994bd"}, + {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:7fb89ee5d106e4a7a51bce305ac4efb981536301895f7bdcf93ec92ae0d91c7f"}, + {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:a738b937d512b30bf75995c0159c0ddf9eec0775c9d72ac0202076c72f24aa96"}, + {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e28f9faeb14b6f23ac55bfbbfd3643f5c7c18ede093977f1df249f73fd22c7b1"}, + {file = "regex-2024.9.11-cp311-cp311-win32.whl", hash = "sha256:18e707ce6c92d7282dfce370cd205098384b8ee21544e7cb29b8aab955b66fa9"}, + {file = "regex-2024.9.11-cp311-cp311-win_amd64.whl", hash = "sha256:313ea15e5ff2a8cbbad96ccef6be638393041b0a7863183c2d31e0c6116688cf"}, + {file = "regex-2024.9.11-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b0d0a6c64fcc4ef9c69bd5b3b3626cc3776520a1637d8abaa62b9edc147a58f7"}, + {file = "regex-2024.9.11-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:49b0e06786ea663f933f3710a51e9385ce0cba0ea56b67107fd841a55d56a231"}, + {file = "regex-2024.9.11-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5b513b6997a0b2f10e4fd3a1313568e373926e8c252bd76c960f96fd039cd28d"}, + {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee439691d8c23e76f9802c42a95cfeebf9d47cf4ffd06f18489122dbb0a7ad64"}, + {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a8f877c89719d759e52783f7fe6e1c67121076b87b40542966c02de5503ace42"}, + {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:23b30c62d0f16827f2ae9f2bb87619bc4fba2044911e2e6c2eb1af0161cdb766"}, + {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85ab7824093d8f10d44330fe1e6493f756f252d145323dd17ab6b48733ff6c0a"}, + {file = "regex-2024.9.11-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8dee5b4810a89447151999428fe096977346cf2f29f4d5e29609d2e19e0199c9"}, + {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:98eeee2f2e63edae2181c886d7911ce502e1292794f4c5ee71e60e23e8d26b5d"}, + {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:57fdd2e0b2694ce6fc2e5ccf189789c3e2962916fb38779d3e3521ff8fe7a822"}, + {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:d552c78411f60b1fdaafd117a1fca2f02e562e309223b9d44b7de8be451ec5e0"}, + {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:a0b2b80321c2ed3fcf0385ec9e51a12253c50f146fddb2abbb10f033fe3d049a"}, + {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:18406efb2f5a0e57e3a5881cd9354c1512d3bb4f5c45d96d110a66114d84d23a"}, + {file = "regex-2024.9.11-cp312-cp312-win32.whl", hash = "sha256:e464b467f1588e2c42d26814231edecbcfe77f5ac414d92cbf4e7b55b2c2a776"}, + {file = "regex-2024.9.11-cp312-cp312-win_amd64.whl", hash = "sha256:9e8719792ca63c6b8340380352c24dcb8cd7ec49dae36e963742a275dfae6009"}, + {file = "regex-2024.9.11-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c157bb447303070f256e084668b702073db99bbb61d44f85d811025fcf38f784"}, + {file = "regex-2024.9.11-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4db21ece84dfeefc5d8a3863f101995de646c6cb0536952c321a2650aa202c36"}, + {file = "regex-2024.9.11-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:220e92a30b426daf23bb67a7962900ed4613589bab80382be09b48896d211e92"}, + {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb1ae19e64c14c7ec1995f40bd932448713d3c73509e82d8cd7744dc00e29e86"}, + {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f47cd43a5bfa48f86925fe26fbdd0a488ff15b62468abb5d2a1e092a4fb10e85"}, + {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9d4a76b96f398697fe01117093613166e6aa8195d63f1b4ec3f21ab637632963"}, + {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ea51dcc0835eea2ea31d66456210a4e01a076d820e9039b04ae8d17ac11dee6"}, + {file = "regex-2024.9.11-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7aaa315101c6567a9a45d2839322c51c8d6e81f67683d529512f5bcfb99c802"}, + {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c57d08ad67aba97af57a7263c2d9006d5c404d721c5f7542f077f109ec2a4a29"}, + {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f8404bf61298bb6f8224bb9176c1424548ee1181130818fcd2cbffddc768bed8"}, + {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:dd4490a33eb909ef5078ab20f5f000087afa2a4daa27b4c072ccb3cb3050ad84"}, + {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:eee9130eaad130649fd73e5cd92f60e55708952260ede70da64de420cdcad554"}, + {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6a2644a93da36c784e546de579ec1806bfd2763ef47babc1b03d765fe560c9f8"}, + {file = "regex-2024.9.11-cp313-cp313-win32.whl", hash = "sha256:e997fd30430c57138adc06bba4c7c2968fb13d101e57dd5bb9355bf8ce3fa7e8"}, + {file = "regex-2024.9.11-cp313-cp313-win_amd64.whl", hash = "sha256:042c55879cfeb21a8adacc84ea347721d3d83a159da6acdf1116859e2427c43f"}, + {file = "regex-2024.9.11-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:07f45f287469039ffc2c53caf6803cd506eb5f5f637f1d4acb37a738f71dd066"}, + {file = "regex-2024.9.11-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4838e24ee015101d9f901988001038f7f0d90dc0c3b115541a1365fb439add62"}, + {file = "regex-2024.9.11-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6edd623bae6a737f10ce853ea076f56f507fd7726bee96a41ee3d68d347e4d16"}, + {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c69ada171c2d0e97a4b5aa78fbb835e0ffbb6b13fc5da968c09811346564f0d3"}, + {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:02087ea0a03b4af1ed6ebab2c54d7118127fee8d71b26398e8e4b05b78963199"}, + {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:69dee6a020693d12a3cf892aba4808fe168d2a4cef368eb9bf74f5398bfd4ee8"}, + {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:297f54910247508e6e5cae669f2bc308985c60540a4edd1c77203ef19bfa63ca"}, + {file = "regex-2024.9.11-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ecea58b43a67b1b79805f1a0255730edaf5191ecef84dbc4cc85eb30bc8b63b9"}, + {file = "regex-2024.9.11-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:eab4bb380f15e189d1313195b062a6aa908f5bd687a0ceccd47c8211e9cf0d4a"}, + {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0cbff728659ce4bbf4c30b2a1be040faafaa9eca6ecde40aaff86f7889f4ab39"}, + {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:54c4a097b8bc5bb0dfc83ae498061d53ad7b5762e00f4adaa23bee22b012e6ba"}, + {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:73d6d2f64f4d894c96626a75578b0bf7d9e56dcda8c3d037a2118fdfe9b1c664"}, + {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:e53b5fbab5d675aec9f0c501274c467c0f9a5d23696cfc94247e1fb56501ed89"}, + {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0ffbcf9221e04502fc35e54d1ce9567541979c3fdfb93d2c554f0ca583a19b35"}, + {file = "regex-2024.9.11-cp39-cp39-win32.whl", hash = "sha256:e4c22e1ac1f1ec1e09f72e6c44d8f2244173db7eb9629cc3a346a8d7ccc31142"}, + {file = "regex-2024.9.11-cp39-cp39-win_amd64.whl", hash = "sha256:faa3c142464efec496967359ca99696c896c591c56c53506bac1ad465f66e919"}, + {file = "regex-2024.9.11.tar.gz", hash = "sha256:6c188c307e8433bcb63dc1915022deb553b4203a70722fc542c363bf120a01fd"}, +] + +[[package]] +name = "requests" +version = "2.32.3" +requires_python = ">=3.8" +summary = "Python HTTP for Humans." +groups = ["default", "docs"] +dependencies = [ + "certifi>=2017.4.17", + "charset-normalizer<4,>=2", + "idna<4,>=2.5", + "urllib3<3,>=1.21.1", +] +files = [ + {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, + {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, +] + +[[package]] +name = "rich" +version = "13.8.1" +requires_python = ">=3.7.0" +summary = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +groups = ["default"] +dependencies = [ + "markdown-it-py>=2.2.0", + "pygments<3.0.0,>=2.13.0", + "typing-extensions<5.0,>=4.0.0; python_version < \"3.9\"", +] +files = [ + {file = "rich-13.8.1-py3-none-any.whl", hash = "sha256:1760a3c0848469b97b558fc61c85233e3dafb69c7a071b4d60c38099d3cd4c06"}, + {file = "rich-13.8.1.tar.gz", hash = "sha256:8260cda28e3db6bf04d2d1ef4dbc03ba80a824c88b0e7668a0f23126a424844a"}, +] + +[[package]] +name = "rpds-py" +version = "0.20.0" +requires_python = ">=3.8" +summary = "Python bindings to Rust's persistent data structures (rpds)" +groups = ["docs"] +files = [ + {file = "rpds_py-0.20.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3ad0fda1635f8439cde85c700f964b23ed5fc2d28016b32b9ee5fe30da5c84e2"}, + {file = "rpds_py-0.20.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9bb4a0d90fdb03437c109a17eade42dfbf6190408f29b2744114d11586611d6f"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6377e647bbfd0a0b159fe557f2c6c602c159fc752fa316572f012fc0bf67150"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb851b7df9dda52dc1415ebee12362047ce771fc36914586b2e9fcbd7d293b3e"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1e0f80b739e5a8f54837be5d5c924483996b603d5502bfff79bf33da06164ee2"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a8c94dad2e45324fc74dce25e1645d4d14df9a4e54a30fa0ae8bad9a63928e3"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8e604fe73ba048c06085beaf51147eaec7df856824bfe7b98657cf436623daf"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:df3de6b7726b52966edf29663e57306b23ef775faf0ac01a3e9f4012a24a4140"}, + {file = "rpds_py-0.20.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf258ede5bc22a45c8e726b29835b9303c285ab46fc7c3a4cc770736b5304c9f"}, + {file = "rpds_py-0.20.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:55fea87029cded5df854ca7e192ec7bdb7ecd1d9a3f63d5c4eb09148acf4a7ce"}, + {file = "rpds_py-0.20.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ae94bd0b2f02c28e199e9bc51485d0c5601f58780636185660f86bf80c89af94"}, + {file = "rpds_py-0.20.0-cp310-none-win32.whl", hash = "sha256:28527c685f237c05445efec62426d285e47a58fb05ba0090a4340b73ecda6dee"}, + {file = "rpds_py-0.20.0-cp310-none-win_amd64.whl", hash = "sha256:238a2d5b1cad28cdc6ed15faf93a998336eb041c4e440dd7f902528b8891b399"}, + {file = "rpds_py-0.20.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:ac2f4f7a98934c2ed6505aead07b979e6f999389f16b714448fb39bbaa86a489"}, + {file = "rpds_py-0.20.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:220002c1b846db9afd83371d08d239fdc865e8f8c5795bbaec20916a76db3318"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d7919548df3f25374a1f5d01fbcd38dacab338ef5f33e044744b5c36729c8db"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:758406267907b3781beee0f0edfe4a179fbd97c0be2e9b1154d7f0a1279cf8e5"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3d61339e9f84a3f0767b1995adfb171a0d00a1185192718a17af6e124728e0f5"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1259c7b3705ac0a0bd38197565a5d603218591d3f6cee6e614e380b6ba61c6f6"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c1dc0f53856b9cc9a0ccca0a7cc61d3d20a7088201c0937f3f4048c1718a209"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7e60cb630f674a31f0368ed32b2a6b4331b8350d67de53c0359992444b116dd3"}, + {file = "rpds_py-0.20.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dbe982f38565bb50cb7fb061ebf762c2f254ca3d8c20d4006878766e84266272"}, + {file = "rpds_py-0.20.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:514b3293b64187172bc77c8fb0cdae26981618021053b30d8371c3a902d4d5ad"}, + {file = "rpds_py-0.20.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d0a26ffe9d4dd35e4dfdd1e71f46401cff0181c75ac174711ccff0459135fa58"}, + {file = "rpds_py-0.20.0-cp311-none-win32.whl", hash = "sha256:89c19a494bf3ad08c1da49445cc5d13d8fefc265f48ee7e7556839acdacf69d0"}, + {file = "rpds_py-0.20.0-cp311-none-win_amd64.whl", hash = "sha256:c638144ce971df84650d3ed0096e2ae7af8e62ecbbb7b201c8935c370df00a2c"}, + {file = "rpds_py-0.20.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a84ab91cbe7aab97f7446652d0ed37d35b68a465aeef8fc41932a9d7eee2c1a6"}, + {file = "rpds_py-0.20.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:56e27147a5a4c2c21633ff8475d185734c0e4befd1c989b5b95a5d0db699b21b"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2580b0c34583b85efec8c5c5ec9edf2dfe817330cc882ee972ae650e7b5ef739"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b80d4a7900cf6b66bb9cee5c352b2d708e29e5a37fe9bf784fa97fc11504bf6c"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50eccbf054e62a7b2209b28dc7a22d6254860209d6753e6b78cfaeb0075d7bee"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:49a8063ea4296b3a7e81a5dfb8f7b2d73f0b1c20c2af401fb0cdf22e14711a96"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea438162a9fcbee3ecf36c23e6c68237479f89f962f82dae83dc15feeceb37e4"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:18d7585c463087bddcfa74c2ba267339f14f2515158ac4db30b1f9cbdb62c8ef"}, + {file = "rpds_py-0.20.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d4c7d1a051eeb39f5c9547e82ea27cbcc28338482242e3e0b7768033cb083821"}, + {file = "rpds_py-0.20.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e4df1e3b3bec320790f699890d41c59d250f6beda159ea3c44c3f5bac1976940"}, + {file = "rpds_py-0.20.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2cf126d33a91ee6eedc7f3197b53e87a2acdac63602c0f03a02dd69e4b138174"}, + {file = "rpds_py-0.20.0-cp312-none-win32.whl", hash = "sha256:8bc7690f7caee50b04a79bf017a8d020c1f48c2a1077ffe172abec59870f1139"}, + {file = "rpds_py-0.20.0-cp312-none-win_amd64.whl", hash = "sha256:0e13e6952ef264c40587d510ad676a988df19adea20444c2b295e536457bc585"}, + {file = "rpds_py-0.20.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:aa9a0521aeca7d4941499a73ad7d4f8ffa3d1affc50b9ea11d992cd7eff18a29"}, + {file = "rpds_py-0.20.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4a1f1d51eccb7e6c32ae89243cb352389228ea62f89cd80823ea7dd1b98e0b91"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a86a9b96070674fc88b6f9f71a97d2c1d3e5165574615d1f9168ecba4cecb24"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6c8ef2ebf76df43f5750b46851ed1cdf8f109d7787ca40035fe19fbdc1acc5a7"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b74b25f024b421d5859d156750ea9a65651793d51b76a2e9238c05c9d5f203a9"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57eb94a8c16ab08fef6404301c38318e2c5a32216bf5de453e2714c964c125c8"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1940dae14e715e2e02dfd5b0f64a52e8374a517a1e531ad9412319dc3ac7879"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d20277fd62e1b992a50c43f13fbe13277a31f8c9f70d59759c88f644d66c619f"}, + {file = "rpds_py-0.20.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:06db23d43f26478303e954c34c75182356ca9aa7797d22c5345b16871ab9c45c"}, + {file = "rpds_py-0.20.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b2a5db5397d82fa847e4c624b0c98fe59d2d9b7cf0ce6de09e4d2e80f8f5b3f2"}, + {file = "rpds_py-0.20.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5a35df9f5548fd79cb2f52d27182108c3e6641a4feb0f39067911bf2adaa3e57"}, + {file = "rpds_py-0.20.0-cp313-none-win32.whl", hash = "sha256:fd2d84f40633bc475ef2d5490b9c19543fbf18596dcb1b291e3a12ea5d722f7a"}, + {file = "rpds_py-0.20.0-cp313-none-win_amd64.whl", hash = "sha256:9bc2d153989e3216b0559251b0c260cfd168ec78b1fac33dd485750a228db5a2"}, + {file = "rpds_py-0.20.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:3fde368e9140312b6e8b6c09fb9f8c8c2f00999d1823403ae90cc00480221b22"}, + {file = "rpds_py-0.20.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9824fb430c9cf9af743cf7aaf6707bf14323fb51ee74425c380f4c846ea70789"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11ef6ce74616342888b69878d45e9f779b95d4bd48b382a229fe624a409b72c5"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c52d3f2f82b763a24ef52f5d24358553e8403ce05f893b5347098014f2d9eff2"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d35cef91e59ebbeaa45214861874bc6f19eb35de96db73e467a8358d701a96c"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d72278a30111e5b5525c1dd96120d9e958464316f55adb030433ea905866f4de"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4c29cbbba378759ac5786730d1c3cb4ec6f8ababf5c42a9ce303dc4b3d08cda"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6632f2d04f15d1bd6fe0eedd3b86d9061b836ddca4c03d5cf5c7e9e6b7c14580"}, + {file = "rpds_py-0.20.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:d0b67d87bb45ed1cd020e8fbf2307d449b68abc45402fe1a4ac9e46c3c8b192b"}, + {file = "rpds_py-0.20.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ec31a99ca63bf3cd7f1a5ac9fe95c5e2d060d3c768a09bc1d16e235840861420"}, + {file = "rpds_py-0.20.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:22e6c9976e38f4d8c4a63bd8a8edac5307dffd3ee7e6026d97f3cc3a2dc02a0b"}, + {file = "rpds_py-0.20.0-cp39-none-win32.whl", hash = "sha256:569b3ea770c2717b730b61998b6c54996adee3cef69fc28d444f3e7920313cf7"}, + {file = "rpds_py-0.20.0-cp39-none-win_amd64.whl", hash = "sha256:e6900ecdd50ce0facf703f7a00df12374b74bbc8ad9fe0f6559947fb20f82364"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:617c7357272c67696fd052811e352ac54ed1d9b49ab370261a80d3b6ce385045"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9426133526f69fcaba6e42146b4e12d6bc6c839b8b555097020e2b78ce908dcc"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:deb62214c42a261cb3eb04d474f7155279c1a8a8c30ac89b7dcb1721d92c3c02"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fcaeb7b57f1a1e071ebd748984359fef83ecb026325b9d4ca847c95bc7311c92"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d454b8749b4bd70dd0a79f428731ee263fa6995f83ccb8bada706e8d1d3ff89d"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d807dc2051abe041b6649681dce568f8e10668e3c1c6543ebae58f2d7e617855"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3c20f0ddeb6e29126d45f89206b8291352b8c5b44384e78a6499d68b52ae511"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b7f19250ceef892adf27f0399b9e5afad019288e9be756d6919cb58892129f51"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:4f1ed4749a08379555cebf4650453f14452eaa9c43d0a95c49db50c18b7da075"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:dcedf0b42bcb4cfff4101d7771a10532415a6106062f005ab97d1d0ab5681c60"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:39ed0d010457a78f54090fafb5d108501b5aa5604cc22408fc1c0c77eac14344"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:bb273176be34a746bdac0b0d7e4e2c467323d13640b736c4c477881a3220a989"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f918a1a130a6dfe1d7fe0f105064141342e7dd1611f2e6a21cd2f5c8cb1cfb3e"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:f60012a73aa396be721558caa3a6fd49b3dd0033d1675c6d59c4502e870fcf0c"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d2b1ad682a3dfda2a4e8ad8572f3100f95fad98cb99faf37ff0ddfe9cbf9d03"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:614fdafe9f5f19c63ea02817fa4861c606a59a604a77c8cdef5aa01d28b97921"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fa518bcd7600c584bf42e6617ee8132869e877db2f76bcdc281ec6a4113a53ab"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0475242f447cc6cb8a9dd486d68b2ef7fbee84427124c232bff5f63b1fe11e5"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f90a4cd061914a60bd51c68bcb4357086991bd0bb93d8aa66a6da7701370708f"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:def7400461c3a3f26e49078302e1c1b38f6752342c77e3cf72ce91ca69fb1bc1"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:65794e4048ee837494aea3c21a28ad5fc080994dfba5b036cf84de37f7ad5074"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:faefcc78f53a88f3076b7f8be0a8f8d35133a3ecf7f3770895c25f8813460f08"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:5b4f105deeffa28bbcdff6c49b34e74903139afa690e35d2d9e3c2c2fba18cec"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:fdfc3a892927458d98f3d55428ae46b921d1f7543b89382fdb483f5640daaec8"}, + {file = "rpds_py-0.20.0.tar.gz", hash = "sha256:d72a210824facfdaf8768cf2d7ca25a042c30320b3020de2fa04640920d4e121"}, +] + +[[package]] +name = "scipy" +version = "1.13.1" +requires_python = ">=3.9" +summary = "Fundamental algorithms for scientific computing in Python" +groups = ["default"] +dependencies = [ + "numpy<2.3,>=1.22.4", +] +files = [ + {file = "scipy-1.13.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:20335853b85e9a49ff7572ab453794298bcf0354d8068c5f6775a0eabf350aca"}, + {file = "scipy-1.13.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:d605e9c23906d1994f55ace80e0125c587f96c020037ea6aa98d01b4bd2e222f"}, + {file = "scipy-1.13.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cfa31f1def5c819b19ecc3a8b52d28ffdcc7ed52bb20c9a7589669dd3c250989"}, + {file = "scipy-1.13.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f26264b282b9da0952a024ae34710c2aff7d27480ee91a2e82b7b7073c24722f"}, + {file = "scipy-1.13.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:eccfa1906eacc02de42d70ef4aecea45415f5be17e72b61bafcfd329bdc52e94"}, + {file = "scipy-1.13.1-cp310-cp310-win_amd64.whl", hash = "sha256:2831f0dc9c5ea9edd6e51e6e769b655f08ec6db6e2e10f86ef39bd32eb11da54"}, + {file = "scipy-1.13.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:27e52b09c0d3a1d5b63e1105f24177e544a222b43611aaf5bc44d4a0979e32f9"}, + {file = "scipy-1.13.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:54f430b00f0133e2224c3ba42b805bfd0086fe488835effa33fa291561932326"}, + {file = "scipy-1.13.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e89369d27f9e7b0884ae559a3a956e77c02114cc60a6058b4e5011572eea9299"}, + {file = "scipy-1.13.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a78b4b3345f1b6f68a763c6e25c0c9a23a9fd0f39f5f3d200efe8feda560a5fa"}, + {file = "scipy-1.13.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:45484bee6d65633752c490404513b9ef02475b4284c4cfab0ef946def50b3f59"}, + {file = "scipy-1.13.1-cp311-cp311-win_amd64.whl", hash = "sha256:5713f62f781eebd8d597eb3f88b8bf9274e79eeabf63afb4a737abc6c84ad37b"}, + {file = "scipy-1.13.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5d72782f39716b2b3509cd7c33cdc08c96f2f4d2b06d51e52fb45a19ca0c86a1"}, + {file = "scipy-1.13.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:017367484ce5498445aade74b1d5ab377acdc65e27095155e448c88497755a5d"}, + {file = "scipy-1.13.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:949ae67db5fa78a86e8fa644b9a6b07252f449dcf74247108c50e1d20d2b4627"}, + {file = "scipy-1.13.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de3ade0e53bc1f21358aa74ff4830235d716211d7d077e340c7349bc3542e884"}, + {file = "scipy-1.13.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2ac65fb503dad64218c228e2dc2d0a0193f7904747db43014645ae139c8fad16"}, + {file = "scipy-1.13.1-cp312-cp312-win_amd64.whl", hash = "sha256:cdd7dacfb95fea358916410ec61bbc20440f7860333aee6d882bb8046264e949"}, + {file = "scipy-1.13.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:436bbb42a94a8aeef855d755ce5a465479c721e9d684de76bf61a62e7c2b81d5"}, + {file = "scipy-1.13.1-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:8335549ebbca860c52bf3d02f80784e91a004b71b059e3eea9678ba994796a24"}, + {file = "scipy-1.13.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d533654b7d221a6a97304ab63c41c96473ff04459e404b83275b60aa8f4b7004"}, + {file = "scipy-1.13.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:637e98dcf185ba7f8e663e122ebf908c4702420477ae52a04f9908707456ba4d"}, + {file = "scipy-1.13.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a014c2b3697bde71724244f63de2476925596c24285c7a637364761f8710891c"}, + {file = "scipy-1.13.1-cp39-cp39-win_amd64.whl", hash = "sha256:392e4ec766654852c25ebad4f64e4e584cf19820b980bc04960bca0b0cd6eaa2"}, + {file = "scipy-1.13.1.tar.gz", hash = "sha256:095a87a0312b08dfd6a6155cbbd310a8c51800fc931b8c0b84003014b874ed3c"}, +] + +[[package]] +name = "shellingham" +version = "1.5.4" +requires_python = ">=3.7" +summary = "Tool to Detect Surrounding Shell" +groups = ["default"] +files = [ + {file = "shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686"}, + {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"}, +] + +[[package]] +name = "six" +version = "1.16.0" +requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +summary = "Python 2 and 3 compatibility utilities" +groups = ["default", "docs"] +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + +[[package]] +name = "smmap" +version = "5.0.1" +requires_python = ">=3.7" +summary = "A pure Python implementation of a sliding window memory map manager" +groups = ["docs"] +files = [ + {file = "smmap-5.0.1-py3-none-any.whl", hash = "sha256:e6d8668fa5f93e706934a62d7b4db19c8d9eb8cf2adbb75ef1b675aa332b69da"}, + {file = "smmap-5.0.1.tar.gz", hash = "sha256:dceeb6c0028fdb6734471eb07c0cd2aae706ccaecab45965ee83f11c8d3b1f62"}, +] + +[[package]] +name = "sniffio" +version = "1.3.1" +requires_python = ">=3.7" +summary = "Sniff out which async library your code is running under" +groups = ["default"] +files = [ + {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, + {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, +] + +[[package]] +name = "soupsieve" +version = "2.6" +requires_python = ">=3.8" +summary = "A modern CSS selector implementation for Beautiful Soup." +groups = ["docs"] +files = [ + {file = "soupsieve-2.6-py3-none-any.whl", hash = "sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9"}, + {file = "soupsieve-2.6.tar.gz", hash = "sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb"}, +] + +[[package]] +name = "tabulate" +version = "0.9.0" +requires_python = ">=3.7" +summary = "Pretty-print tabular data" +groups = ["docs"] +files = [ + {file = "tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f"}, + {file = "tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c"}, +] + +[[package]] +name = "tblib" +version = "3.0.0" +requires_python = ">=3.8" +summary = "Traceback serialization library." +groups = ["default"] +files = [ + {file = "tblib-3.0.0-py3-none-any.whl", hash = "sha256:80a6c77e59b55e83911e1e607c649836a69c103963c5f28a46cbeef44acf8129"}, + {file = "tblib-3.0.0.tar.gz", hash = "sha256:93622790a0a29e04f0346458face1e144dc4d32f493714c6c3dff82a4adb77e6"}, +] + +[[package]] +name = "tinycss2" +version = "1.3.0" +requires_python = ">=3.8" +summary = "A tiny CSS parser" +groups = ["docs"] +dependencies = [ + "webencodings>=0.4", +] +files = [ + {file = "tinycss2-1.3.0-py3-none-any.whl", hash = "sha256:54a8dbdffb334d536851be0226030e9505965bb2f30f21a4a82c55fb2a80fae7"}, + {file = "tinycss2-1.3.0.tar.gz", hash = "sha256:152f9acabd296a8375fbca5b84c961ff95971fcfc32e79550c8df8e29118c54d"}, +] + +[[package]] +name = "tornado" +version = "6.4.1" +requires_python = ">=3.8" +summary = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." +groups = ["docs"] +files = [ + {file = "tornado-6.4.1-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:163b0aafc8e23d8cdc3c9dfb24c5368af84a81e3364745ccb4427669bf84aec8"}, + {file = "tornado-6.4.1-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:6d5ce3437e18a2b66fbadb183c1d3364fb03f2be71299e7d10dbeeb69f4b2a14"}, + {file = "tornado-6.4.1-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2e20b9113cd7293f164dc46fffb13535266e713cdb87bd2d15ddb336e96cfc4"}, + {file = "tornado-6.4.1-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ae50a504a740365267b2a8d1a90c9fbc86b780a39170feca9bcc1787ff80842"}, + {file = "tornado-6.4.1-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:613bf4ddf5c7a95509218b149b555621497a6cc0d46ac341b30bd9ec19eac7f3"}, + {file = "tornado-6.4.1-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:25486eb223babe3eed4b8aecbac33b37e3dd6d776bc730ca14e1bf93888b979f"}, + {file = "tornado-6.4.1-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:454db8a7ecfcf2ff6042dde58404164d969b6f5d58b926da15e6b23817950fc4"}, + {file = "tornado-6.4.1-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a02a08cc7a9314b006f653ce40483b9b3c12cda222d6a46d4ac63bb6c9057698"}, + {file = "tornado-6.4.1-cp38-abi3-win32.whl", hash = "sha256:d9a566c40b89757c9aa8e6f032bcdb8ca8795d7c1a9762910c722b1635c9de4d"}, + {file = "tornado-6.4.1-cp38-abi3-win_amd64.whl", hash = "sha256:b24b8982ed444378d7f21d563f4180a2de31ced9d8d84443907a0a64da2072e7"}, + {file = "tornado-6.4.1.tar.gz", hash = "sha256:92d3ab53183d8c50f8204a51e6f91d18a15d5ef261e84d452800d4ff6fc504e9"}, +] + +[[package]] +name = "tqdm" +version = "4.66.5" +requires_python = ">=3.7" +summary = "Fast, Extensible Progress Meter" +groups = ["default"] +dependencies = [ + "colorama; platform_system == \"Windows\"", +] +files = [ + {file = "tqdm-4.66.5-py3-none-any.whl", hash = "sha256:90279a3770753eafc9194a0364852159802111925aa30eb3f9d85b0e805ac7cd"}, + {file = "tqdm-4.66.5.tar.gz", hash = "sha256:e1020aef2e5096702d8a025ac7d16b1577279c9d63f8375b63083e9a5f0fcbad"}, +] + +[[package]] +name = "traitlets" +version = "5.14.3" +requires_python = ">=3.8" +summary = "Traitlets Python configuration system" +groups = ["docs"] +files = [ + {file = "traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f"}, + {file = "traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7"}, +] + +[[package]] +name = "typer" +version = "0.12.5" +requires_python = ">=3.7" +summary = "Typer, build great CLIs. Easy to code. Based on Python type hints." +groups = ["default"] +dependencies = [ + "click>=8.0.0", + "rich>=10.11.0", + "shellingham>=1.3.0", + "typing-extensions>=3.7.4.3", +] +files = [ + {file = "typer-0.12.5-py3-none-any.whl", hash = "sha256:62fe4e471711b147e3365034133904df3e235698399bc4de2b36c8579298d52b"}, + {file = "typer-0.12.5.tar.gz", hash = "sha256:f592f089bedcc8ec1b974125d64851029c3b1af145f04aca64d69410f0c9b722"}, +] + +[[package]] +name = "typing-extensions" +version = "4.12.2" +requires_python = ">=3.8" +summary = "Backported and Experimental Type Hints for Python 3.8+" +groups = ["default", "docs"] +files = [ + {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, + {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, +] + +[[package]] +name = "tzdata" +version = "2024.1" +requires_python = ">=2" +summary = "Provider of IANA time zone data" +groups = ["default", "docs"] +files = [ + {file = "tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252"}, + {file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"}, +] + +[[package]] +name = "universal-pathlib" +version = "0.2.5" +requires_python = ">=3.8" +summary = "pathlib api extended to use fsspec backends" +groups = ["default"] +dependencies = [ + "fsspec!=2024.3.1,>=2022.1.0", +] +files = [ + {file = "universal_pathlib-0.2.5-py3-none-any.whl", hash = "sha256:a634f700eca827b4ad03bfa0267e51161560dd1de83b051cf0fccf39b3e56b32"}, + {file = "universal_pathlib-0.2.5.tar.gz", hash = "sha256:ea5d4fb8178c2ab469cf4fa46d0ceb16ccb378da46dbbc28a8b9c1eebdccc655"}, +] + +[[package]] +name = "urllib3" +version = "2.2.3" +requires_python = ">=3.8" +summary = "HTTP library with thread-safe connection pooling, file post, and more." +groups = ["default", "docs"] +files = [ + {file = "urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac"}, + {file = "urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9"}, +] + +[[package]] +name = "verspec" +version = "0.1.0" +summary = "Flexible version handling" +groups = ["docs"] +files = [ + {file = "verspec-0.1.0-py3-none-any.whl", hash = "sha256:741877d5633cc9464c45a469ae2a31e801e6dbbaa85b9675d481cda100f11c31"}, + {file = "verspec-0.1.0.tar.gz", hash = "sha256:c4504ca697b2056cdb4bfa7121461f5a0e81809255b41c03dda4ba823637c01e"}, +] + +[[package]] +name = "watchdog" +version = "5.0.2" +requires_python = ">=3.9" +summary = "Filesystem events monitoring" +groups = ["docs"] +files = [ + {file = "watchdog-5.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d961f4123bb3c447d9fcdcb67e1530c366f10ab3a0c7d1c0c9943050936d4877"}, + {file = "watchdog-5.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72990192cb63872c47d5e5fefe230a401b87fd59d257ee577d61c9e5564c62e5"}, + {file = "watchdog-5.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6bec703ad90b35a848e05e1b40bf0050da7ca28ead7ac4be724ae5ac2653a1a0"}, + {file = "watchdog-5.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:dae7a1879918f6544201d33666909b040a46421054a50e0f773e0d870ed7438d"}, + {file = "watchdog-5.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c4a440f725f3b99133de610bfec93d570b13826f89616377715b9cd60424db6e"}, + {file = "watchdog-5.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f8b2918c19e0d48f5f20df458c84692e2a054f02d9df25e6c3c930063eca64c1"}, + {file = "watchdog-5.0.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:aa9cd6e24126d4afb3752a3e70fce39f92d0e1a58a236ddf6ee823ff7dba28ee"}, + {file = "watchdog-5.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f627c5bf5759fdd90195b0c0431f99cff4867d212a67b384442c51136a098ed7"}, + {file = "watchdog-5.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d7594a6d32cda2b49df3fd9abf9b37c8d2f3eab5df45c24056b4a671ac661619"}, + {file = "watchdog-5.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba32efcccfe2c58f4d01115440d1672b4eb26cdd6fc5b5818f1fb41f7c3e1889"}, + {file = "watchdog-5.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:963f7c4c91e3f51c998eeff1b3fb24a52a8a34da4f956e470f4b068bb47b78ee"}, + {file = "watchdog-5.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8c47150aa12f775e22efff1eee9f0f6beee542a7aa1a985c271b1997d340184f"}, + {file = "watchdog-5.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:14dd4ed023d79d1f670aa659f449bcd2733c33a35c8ffd88689d9d243885198b"}, + {file = "watchdog-5.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b84bff0391ad4abe25c2740c7aec0e3de316fdf7764007f41e248422a7760a7f"}, + {file = "watchdog-5.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3e8d5ff39f0a9968952cce548e8e08f849141a4fcc1290b1c17c032ba697b9d7"}, + {file = "watchdog-5.0.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:fb223456db6e5f7bd9bbd5cd969f05aae82ae21acc00643b60d81c770abd402b"}, + {file = "watchdog-5.0.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9814adb768c23727a27792c77812cf4e2fd9853cd280eafa2bcfa62a99e8bd6e"}, + {file = "watchdog-5.0.2-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:901ee48c23f70193d1a7bc2d9ee297df66081dd5f46f0ca011be4f70dec80dab"}, + {file = "watchdog-5.0.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:638bcca3d5b1885c6ec47be67bf712b00a9ab3d4b22ec0881f4889ad870bc7e8"}, + {file = "watchdog-5.0.2-py3-none-manylinux2014_aarch64.whl", hash = "sha256:5597c051587f8757798216f2485e85eac583c3b343e9aa09127a3a6f82c65ee8"}, + {file = "watchdog-5.0.2-py3-none-manylinux2014_armv7l.whl", hash = "sha256:53ed1bf71fcb8475dd0ef4912ab139c294c87b903724b6f4a8bd98e026862e6d"}, + {file = "watchdog-5.0.2-py3-none-manylinux2014_i686.whl", hash = "sha256:29e4a2607bd407d9552c502d38b45a05ec26a8e40cc7e94db9bb48f861fa5abc"}, + {file = "watchdog-5.0.2-py3-none-manylinux2014_ppc64.whl", hash = "sha256:b6dc8f1d770a8280997e4beae7b9a75a33b268c59e033e72c8a10990097e5fde"}, + {file = "watchdog-5.0.2-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:d2ab34adc9bf1489452965cdb16a924e97d4452fcf88a50b21859068b50b5c3b"}, + {file = "watchdog-5.0.2-py3-none-manylinux2014_s390x.whl", hash = "sha256:7d1aa7e4bb0f0c65a1a91ba37c10e19dabf7eaaa282c5787e51371f090748f4b"}, + {file = "watchdog-5.0.2-py3-none-manylinux2014_x86_64.whl", hash = "sha256:726eef8f8c634ac6584f86c9c53353a010d9f311f6c15a034f3800a7a891d941"}, + {file = "watchdog-5.0.2-py3-none-win32.whl", hash = "sha256:bda40c57115684d0216556671875e008279dea2dc00fcd3dde126ac8e0d7a2fb"}, + {file = "watchdog-5.0.2-py3-none-win_amd64.whl", hash = "sha256:d010be060c996db725fbce7e3ef14687cdcc76f4ca0e4339a68cc4532c382a73"}, + {file = "watchdog-5.0.2-py3-none-win_ia64.whl", hash = "sha256:3960136b2b619510569b90f0cd96408591d6c251a75c97690f4553ca88889769"}, + {file = "watchdog-5.0.2.tar.gz", hash = "sha256:dcebf7e475001d2cdeb020be630dc5b687e9acdd60d16fea6bb4508e7b94cf76"}, +] + +[[package]] +name = "webencodings" +version = "0.5.1" +summary = "Character encoding aliases for legacy web content" +groups = ["docs"] +files = [ + {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"}, + {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"}, +] + +[[package]] +name = "xxhash" +version = "3.5.0" +requires_python = ">=3.7" +summary = "Python binding for xxHash" +groups = ["default"] +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"}, + {file = "xxhash-3.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c5d3e570ef46adaf93fc81b44aca6002b5a4d8ca11bd0580c07eac537f36680"}, + {file = "xxhash-3.5.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7cb29a034301e2982df8b1fe6328a84f4b676106a13e9135a0d7e0c3e9f806da"}, + {file = "xxhash-3.5.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5d0d307d27099bb0cbeea7260eb39ed4fdb99c5542e21e94bb6fd29e49c57a23"}, + {file = "xxhash-3.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0342aafd421795d740e514bc9858ebddfc705a75a8c5046ac56d85fe97bf196"}, + {file = "xxhash-3.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3dbbd9892c5ebffeca1ed620cf0ade13eb55a0d8c84e0751a6653adc6ac40d0c"}, + {file = "xxhash-3.5.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4cc2d67fdb4d057730c75a64c5923abfa17775ae234a71b0200346bfb0a7f482"}, + {file = "xxhash-3.5.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:ec28adb204b759306a3d64358a5e5c07d7b1dd0ccbce04aa76cb9377b7b70296"}, + {file = "xxhash-3.5.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:1328f6d8cca2b86acb14104e381225a3d7b42c92c4b86ceae814e5c400dbb415"}, + {file = "xxhash-3.5.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8d47ebd9f5d9607fd039c1fbf4994e3b071ea23eff42f4ecef246ab2b7334198"}, + {file = "xxhash-3.5.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b96d559e0fcddd3343c510a0fe2b127fbff16bf346dd76280b82292567523442"}, + {file = "xxhash-3.5.0-cp310-cp310-win32.whl", hash = "sha256:61c722ed8d49ac9bc26c7071eeaa1f6ff24053d553146d5df031802deffd03da"}, + {file = "xxhash-3.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:9bed5144c6923cc902cd14bb8963f2d5e034def4486ab0bbe1f58f03f042f9a9"}, + {file = "xxhash-3.5.0-cp310-cp310-win_arm64.whl", hash = "sha256:893074d651cf25c1cc14e3bea4fceefd67f2921b1bb8e40fcfeba56820de80c6"}, + {file = "xxhash-3.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:02c2e816896dc6f85922ced60097bcf6f008dedfc5073dcba32f9c8dd786f3c1"}, + {file = "xxhash-3.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6027dcd885e21581e46d3c7f682cfb2b870942feeed58a21c29583512c3f09f8"}, + {file = "xxhash-3.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1308fa542bbdbf2fa85e9e66b1077eea3a88bef38ee8a06270b4298a7a62a166"}, + {file = "xxhash-3.5.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c28b2fdcee797e1c1961cd3bcd3d545cab22ad202c846235197935e1df2f8ef7"}, + {file = "xxhash-3.5.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:924361811732ddad75ff23e90efd9ccfda4f664132feecb90895bade6a1b4623"}, + {file = "xxhash-3.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89997aa1c4b6a5b1e5b588979d1da048a3c6f15e55c11d117a56b75c84531f5a"}, + {file = "xxhash-3.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:685c4f4e8c59837de103344eb1c8a3851f670309eb5c361f746805c5471b8c88"}, + {file = "xxhash-3.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dbd2ecfbfee70bc1a4acb7461fa6af7748ec2ab08ac0fa298f281c51518f982c"}, + {file = "xxhash-3.5.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:25b5a51dc3dfb20a10833c8eee25903fd2e14059e9afcd329c9da20609a307b2"}, + {file = "xxhash-3.5.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a8fb786fb754ef6ff8c120cb96629fb518f8eb5a61a16aac3a979a9dbd40a084"}, + {file = "xxhash-3.5.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:a905ad00ad1e1c34fe4e9d7c1d949ab09c6fa90c919860c1534ff479f40fd12d"}, + {file = "xxhash-3.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:963be41bcd49f53af6d795f65c0da9b4cc518c0dd9c47145c98f61cb464f4839"}, + {file = "xxhash-3.5.0-cp311-cp311-win32.whl", hash = "sha256:109b436096d0a2dd039c355fa3414160ec4d843dfecc64a14077332a00aeb7da"}, + {file = "xxhash-3.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:b702f806693201ad6c0a05ddbbe4c8f359626d0b3305f766077d51388a6bac58"}, + {file = "xxhash-3.5.0-cp311-cp311-win_arm64.whl", hash = "sha256:c4dcb4120d0cc3cc448624147dba64e9021b278c63e34a38789b688fd0da9bf3"}, + {file = "xxhash-3.5.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:14470ace8bd3b5d51318782cd94e6f94431974f16cb3b8dc15d52f3b69df8e00"}, + {file = "xxhash-3.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:59aa1203de1cb96dbeab595ded0ad0c0056bb2245ae11fac11c0ceea861382b9"}, + {file = "xxhash-3.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:08424f6648526076e28fae6ea2806c0a7d504b9ef05ae61d196d571e5c879c84"}, + {file = "xxhash-3.5.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:61a1ff00674879725b194695e17f23d3248998b843eb5e933007ca743310f793"}, + {file = "xxhash-3.5.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2f2c61bee5844d41c3eb015ac652a0229e901074951ae48581d58bfb2ba01be"}, + {file = "xxhash-3.5.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d32a592cac88d18cc09a89172e1c32d7f2a6e516c3dfde1b9adb90ab5df54a6"}, + {file = "xxhash-3.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:70dabf941dede727cca579e8c205e61121afc9b28516752fd65724be1355cc90"}, + {file = "xxhash-3.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e5d0ddaca65ecca9c10dcf01730165fd858533d0be84c75c327487c37a906a27"}, + {file = "xxhash-3.5.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3e5b5e16c5a480fe5f59f56c30abdeba09ffd75da8d13f6b9b6fd224d0b4d0a2"}, + {file = "xxhash-3.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:149b7914451eb154b3dfaa721315117ea1dac2cc55a01bfbd4df7c68c5dd683d"}, + {file = "xxhash-3.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:eade977f5c96c677035ff39c56ac74d851b1cca7d607ab3d8f23c6b859379cab"}, + {file = "xxhash-3.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fa9f547bd98f5553d03160967866a71056a60960be00356a15ecc44efb40ba8e"}, + {file = "xxhash-3.5.0-cp312-cp312-win32.whl", hash = "sha256:f7b58d1fd3551b8c80a971199543379be1cee3d0d409e1f6d8b01c1a2eebf1f8"}, + {file = "xxhash-3.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:fa0cafd3a2af231b4e113fba24a65d7922af91aeb23774a8b78228e6cd785e3e"}, + {file = "xxhash-3.5.0-cp312-cp312-win_arm64.whl", hash = "sha256:586886c7e89cb9828bcd8a5686b12e161368e0064d040e225e72607b43858ba2"}, + {file = "xxhash-3.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:37889a0d13b0b7d739cfc128b1c902f04e32de17b33d74b637ad42f1c55101f6"}, + {file = "xxhash-3.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:97a662338797c660178e682f3bc180277b9569a59abfb5925e8620fba00b9fc5"}, + {file = "xxhash-3.5.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f85e0108d51092bdda90672476c7d909c04ada6923c14ff9d913c4f7dc8a3bc"}, + {file = "xxhash-3.5.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cd2fd827b0ba763ac919440042302315c564fdb797294d86e8cdd4578e3bc7f3"}, + {file = "xxhash-3.5.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:82085c2abec437abebf457c1d12fccb30cc8b3774a0814872511f0f0562c768c"}, + {file = "xxhash-3.5.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07fda5de378626e502b42b311b049848c2ef38784d0d67b6f30bb5008642f8eb"}, + {file = "xxhash-3.5.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c279f0d2b34ef15f922b77966640ade58b4ccdfef1c4d94b20f2a364617a493f"}, + {file = "xxhash-3.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:89e66ceed67b213dec5a773e2f7a9e8c58f64daeb38c7859d8815d2c89f39ad7"}, + {file = "xxhash-3.5.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:bcd51708a633410737111e998ceb3b45d3dbc98c0931f743d9bb0a209033a326"}, + {file = "xxhash-3.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3ff2c0a34eae7df88c868be53a8dd56fbdf592109e21d4bfa092a27b0bf4a7bf"}, + {file = "xxhash-3.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:4e28503dccc7d32e0b9817aa0cbfc1f45f563b2c995b7a66c4c8a0d232e840c7"}, + {file = "xxhash-3.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a6c50017518329ed65a9e4829154626f008916d36295b6a3ba336e2458824c8c"}, + {file = "xxhash-3.5.0-cp313-cp313-win32.whl", hash = "sha256:53a068fe70301ec30d868ece566ac90d873e3bb059cf83c32e76012c889b8637"}, + {file = "xxhash-3.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:80babcc30e7a1a484eab952d76a4f4673ff601f54d5142c26826502740e70b43"}, + {file = "xxhash-3.5.0-cp313-cp313-win_arm64.whl", hash = "sha256:4811336f1ce11cac89dcbd18f3a25c527c16311709a89313c3acaf771def2d4b"}, + {file = "xxhash-3.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bfc8cdd7f33d57f0468b0614ae634cc38ab9202c6957a60e31d285a71ebe0301"}, + {file = "xxhash-3.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e0c48b6300cd0b0106bf49169c3e0536408dfbeb1ccb53180068a18b03c662ab"}, + {file = "xxhash-3.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe1a92cfbaa0a1253e339ccec42dbe6db262615e52df591b68726ab10338003f"}, + {file = "xxhash-3.5.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:33513d6cc3ed3b559134fb307aae9bdd94d7e7c02907b37896a6c45ff9ce51bd"}, + {file = "xxhash-3.5.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eefc37f6138f522e771ac6db71a6d4838ec7933939676f3753eafd7d3f4c40bc"}, + {file = "xxhash-3.5.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a606c8070ada8aa2a88e181773fa1ef17ba65ce5dd168b9d08038e2a61b33754"}, + {file = "xxhash-3.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:42eca420c8fa072cc1dd62597635d140e78e384a79bb4944f825fbef8bfeeef6"}, + {file = "xxhash-3.5.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:604253b2143e13218ff1ef0b59ce67f18b8bd1c4205d2ffda22b09b426386898"}, + {file = "xxhash-3.5.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:6e93a5ad22f434d7876665444a97e713a8f60b5b1a3521e8df11b98309bff833"}, + {file = "xxhash-3.5.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:7a46e1d6d2817ba8024de44c4fd79913a90e5f7265434cef97026215b7d30df6"}, + {file = "xxhash-3.5.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:30eb2efe6503c379b7ab99c81ba4a779748e3830241f032ab46bd182bf5873af"}, + {file = "xxhash-3.5.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c8aa771ff2c13dd9cda8166d685d7333d389fae30a4d2bb39d63ab5775de8606"}, + {file = "xxhash-3.5.0-cp39-cp39-win32.whl", hash = "sha256:5ed9ebc46f24cf91034544b26b131241b699edbfc99ec5e7f8f3d02d6eb7fba4"}, + {file = "xxhash-3.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:220f3f896c6b8d0316f63f16c077d52c412619e475f9372333474ee15133a558"}, + {file = "xxhash-3.5.0-cp39-cp39-win_arm64.whl", hash = "sha256:a7b1d8315d9b5e9f89eb2933b73afae6ec9597a258d52190944437158b49d38e"}, + {file = "xxhash-3.5.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:2014c5b3ff15e64feecb6b713af12093f75b7926049e26a580e94dcad3c73d8c"}, + {file = "xxhash-3.5.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fab81ef75003eda96239a23eda4e4543cedc22e34c373edcaf744e721a163986"}, + {file = "xxhash-3.5.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e2febf914ace002132aa09169cc572e0d8959d0f305f93d5828c4836f9bc5a6"}, + {file = "xxhash-3.5.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5d3a10609c51da2a1c0ea0293fc3968ca0a18bd73838455b5bca3069d7f8e32b"}, + {file = "xxhash-3.5.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5a74f23335b9689b66eb6dbe2a931a88fcd7a4c2cc4b1cb0edba8ce381c7a1da"}, + {file = "xxhash-3.5.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:531af8845aaadcadf951b7e0c1345c6b9c68a990eeb74ff9acd8501a0ad6a1c9"}, + {file = "xxhash-3.5.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ce379bcaa9fcc00f19affa7773084dd09f5b59947b3fb47a1ceb0179f91aaa1"}, + {file = "xxhash-3.5.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd1b2281d01723f076df3c8188f43f2472248a6b63118b036e641243656b1b0f"}, + {file = "xxhash-3.5.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9c770750cc80e8694492244bca7251385188bc5597b6a39d98a9f30e8da984e0"}, + {file = "xxhash-3.5.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:b150b8467852e1bd844387459aa6fbe11d7f38b56e901f9f3b3e6aba0d660240"}, + {file = "xxhash-3.5.0.tar.gz", hash = "sha256:84f2caddf951c9cbf8dc2e22a89d4ccf5d86391ac6418fe81e3c67d0cf60b45f"}, +] + +[[package]] +name = "yarl" +version = "1.11.1" +requires_python = ">=3.8" +summary = "Yet another URL library" +groups = ["default"] +dependencies = [ + "idna>=2.0", + "multidict>=4.0", +] +files = [ + {file = "yarl-1.11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:400cd42185f92de559d29eeb529e71d80dfbd2f45c36844914a4a34297ca6f00"}, + {file = "yarl-1.11.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8258c86f47e080a258993eed877d579c71da7bda26af86ce6c2d2d072c11320d"}, + {file = "yarl-1.11.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2164cd9725092761fed26f299e3f276bb4b537ca58e6ff6b252eae9631b5c96e"}, + {file = "yarl-1.11.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08ea567c16f140af8ddc7cb58e27e9138a1386e3e6e53982abaa6f2377b38cc"}, + {file = "yarl-1.11.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:768ecc550096b028754ea28bf90fde071c379c62c43afa574edc6f33ee5daaec"}, + {file = "yarl-1.11.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2909fa3a7d249ef64eeb2faa04b7957e34fefb6ec9966506312349ed8a7e77bf"}, + {file = "yarl-1.11.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01a8697ec24f17c349c4f655763c4db70eebc56a5f82995e5e26e837c6eb0e49"}, + {file = "yarl-1.11.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e286580b6511aac7c3268a78cdb861ec739d3e5a2a53b4809faef6b49778eaff"}, + {file = "yarl-1.11.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4179522dc0305c3fc9782549175c8e8849252fefeb077c92a73889ccbcd508ad"}, + {file = "yarl-1.11.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:27fcb271a41b746bd0e2a92182df507e1c204759f460ff784ca614e12dd85145"}, + {file = "yarl-1.11.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f61db3b7e870914dbd9434b560075e0366771eecbe6d2b5561f5bc7485f39efd"}, + {file = "yarl-1.11.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:c92261eb2ad367629dc437536463dc934030c9e7caca861cc51990fe6c565f26"}, + {file = "yarl-1.11.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d95b52fbef190ca87d8c42f49e314eace4fc52070f3dfa5f87a6594b0c1c6e46"}, + {file = "yarl-1.11.1-cp310-cp310-win32.whl", hash = "sha256:489fa8bde4f1244ad6c5f6d11bb33e09cf0d1d0367edb197619c3e3fc06f3d91"}, + {file = "yarl-1.11.1-cp310-cp310-win_amd64.whl", hash = "sha256:476e20c433b356e16e9a141449f25161e6b69984fb4cdbd7cd4bd54c17844998"}, + {file = "yarl-1.11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:946eedc12895873891aaceb39bceb484b4977f70373e0122da483f6c38faaa68"}, + {file = "yarl-1.11.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:21a7c12321436b066c11ec19c7e3cb9aec18884fe0d5b25d03d756a9e654edfe"}, + {file = "yarl-1.11.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c35f493b867912f6fda721a59cc7c4766d382040bdf1ddaeeaa7fa4d072f4675"}, + {file = "yarl-1.11.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25861303e0be76b60fddc1250ec5986c42f0a5c0c50ff57cc30b1be199c00e63"}, + {file = "yarl-1.11.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e4b53f73077e839b3f89c992223f15b1d2ab314bdbdf502afdc7bb18e95eae27"}, + {file = "yarl-1.11.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:327c724b01b8641a1bf1ab3b232fb638706e50f76c0b5bf16051ab65c868fac5"}, + {file = "yarl-1.11.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4307d9a3417eea87715c9736d050c83e8c1904e9b7aada6ce61b46361b733d92"}, + {file = "yarl-1.11.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48a28bed68ab8fb7e380775f0029a079f08a17799cb3387a65d14ace16c12e2b"}, + {file = "yarl-1.11.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:067b961853c8e62725ff2893226fef3d0da060656a9827f3f520fb1d19b2b68a"}, + {file = "yarl-1.11.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8215f6f21394d1f46e222abeb06316e77ef328d628f593502d8fc2a9117bde83"}, + {file = "yarl-1.11.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:498442e3af2a860a663baa14fbf23fb04b0dd758039c0e7c8f91cb9279799bff"}, + {file = "yarl-1.11.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:69721b8effdb588cb055cc22f7c5105ca6fdaa5aeb3ea09021d517882c4a904c"}, + {file = "yarl-1.11.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1e969fa4c1e0b1a391f3fcbcb9ec31e84440253325b534519be0d28f4b6b533e"}, + {file = "yarl-1.11.1-cp311-cp311-win32.whl", hash = "sha256:7d51324a04fc4b0e097ff8a153e9276c2593106a811704025bbc1d6916f45ca6"}, + {file = "yarl-1.11.1-cp311-cp311-win_amd64.whl", hash = "sha256:15061ce6584ece023457fb8b7a7a69ec40bf7114d781a8c4f5dcd68e28b5c53b"}, + {file = "yarl-1.11.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:a4264515f9117be204935cd230fb2a052dd3792789cc94c101c535d349b3dab0"}, + {file = "yarl-1.11.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f41fa79114a1d2eddb5eea7b912d6160508f57440bd302ce96eaa384914cd265"}, + {file = "yarl-1.11.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:02da8759b47d964f9173c8675710720b468aa1c1693be0c9c64abb9d8d9a4867"}, + {file = "yarl-1.11.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9361628f28f48dcf8b2f528420d4d68102f593f9c2e592bfc842f5fb337e44fd"}, + {file = "yarl-1.11.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b91044952da03b6f95fdba398d7993dd983b64d3c31c358a4c89e3c19b6f7aef"}, + {file = "yarl-1.11.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:74db2ef03b442276d25951749a803ddb6e270d02dda1d1c556f6ae595a0d76a8"}, + {file = "yarl-1.11.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e975a2211952a8a083d1b9d9ba26472981ae338e720b419eb50535de3c02870"}, + {file = "yarl-1.11.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8aef97ba1dd2138112890ef848e17d8526fe80b21f743b4ee65947ea184f07a2"}, + {file = "yarl-1.11.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a7915ea49b0c113641dc4d9338efa9bd66b6a9a485ffe75b9907e8573ca94b84"}, + {file = "yarl-1.11.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:504cf0d4c5e4579a51261d6091267f9fd997ef58558c4ffa7a3e1460bd2336fa"}, + {file = "yarl-1.11.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:3de5292f9f0ee285e6bd168b2a77b2a00d74cbcfa420ed078456d3023d2f6dff"}, + {file = "yarl-1.11.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:a34e1e30f1774fa35d37202bbeae62423e9a79d78d0874e5556a593479fdf239"}, + {file = "yarl-1.11.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:66b63c504d2ca43bf7221a1f72fbe981ff56ecb39004c70a94485d13e37ebf45"}, + {file = "yarl-1.11.1-cp312-cp312-win32.whl", hash = "sha256:a28b70c9e2213de425d9cba5ab2e7f7a1c8ca23a99c4b5159bf77b9c31251447"}, + {file = "yarl-1.11.1-cp312-cp312-win_amd64.whl", hash = "sha256:17b5a386d0d36fb828e2fb3ef08c8829c1ebf977eef88e5367d1c8c94b454639"}, + {file = "yarl-1.11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:1fa2e7a406fbd45b61b4433e3aa254a2c3e14c4b3186f6e952d08a730807fa0c"}, + {file = "yarl-1.11.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:750f656832d7d3cb0c76be137ee79405cc17e792f31e0a01eee390e383b2936e"}, + {file = "yarl-1.11.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0b8486f322d8f6a38539136a22c55f94d269addb24db5cb6f61adc61eabc9d93"}, + {file = "yarl-1.11.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3fce4da3703ee6048ad4138fe74619c50874afe98b1ad87b2698ef95bf92c96d"}, + {file = "yarl-1.11.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8ed653638ef669e0efc6fe2acb792275cb419bf9cb5c5049399f3556995f23c7"}, + {file = "yarl-1.11.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18ac56c9dd70941ecad42b5a906820824ca72ff84ad6fa18db33c2537ae2e089"}, + {file = "yarl-1.11.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:688654f8507464745ab563b041d1fb7dab5d9912ca6b06e61d1c4708366832f5"}, + {file = "yarl-1.11.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4973eac1e2ff63cf187073cd4e1f1148dcd119314ab79b88e1b3fad74a18c9d5"}, + {file = "yarl-1.11.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:964a428132227edff96d6f3cf261573cb0f1a60c9a764ce28cda9525f18f7786"}, + {file = "yarl-1.11.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:6d23754b9939cbab02c63434776df1170e43b09c6a517585c7ce2b3d449b7318"}, + {file = "yarl-1.11.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c2dc4250fe94d8cd864d66018f8344d4af50e3758e9d725e94fecfa27588ff82"}, + {file = "yarl-1.11.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09696438cb43ea6f9492ef237761b043f9179f455f405279e609f2bc9100212a"}, + {file = "yarl-1.11.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:999bfee0a5b7385a0af5ffb606393509cfde70ecca4f01c36985be6d33e336da"}, + {file = "yarl-1.11.1-cp313-cp313-win32.whl", hash = "sha256:ce928c9c6409c79e10f39604a7e214b3cb69552952fbda8d836c052832e6a979"}, + {file = "yarl-1.11.1-cp313-cp313-win_amd64.whl", hash = "sha256:501c503eed2bb306638ccb60c174f856cc3246c861829ff40eaa80e2f0330367"}, + {file = "yarl-1.11.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:884eab2ce97cbaf89f264372eae58388862c33c4f551c15680dd80f53c89a269"}, + {file = "yarl-1.11.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8a336eaa7ee7e87cdece3cedb395c9657d227bfceb6781295cf56abcd3386a26"}, + {file = "yarl-1.11.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:87f020d010ba80a247c4abc335fc13421037800ca20b42af5ae40e5fd75e7909"}, + {file = "yarl-1.11.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:637c7ddb585a62d4469f843dac221f23eec3cbad31693b23abbc2c366ad41ff4"}, + {file = "yarl-1.11.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:48dfd117ab93f0129084577a07287376cc69c08138694396f305636e229caa1a"}, + {file = "yarl-1.11.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75e0ae31fb5ccab6eda09ba1494e87eb226dcbd2372dae96b87800e1dcc98804"}, + {file = "yarl-1.11.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f46f81501160c28d0c0b7333b4f7be8983dbbc161983b6fb814024d1b4952f79"}, + {file = "yarl-1.11.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:04293941646647b3bfb1719d1d11ff1028e9c30199509a844da3c0f5919dc520"}, + {file = "yarl-1.11.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:250e888fa62d73e721f3041e3a9abf427788a1934b426b45e1b92f62c1f68366"}, + {file = "yarl-1.11.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e8f63904df26d1a66aabc141bfd258bf738b9bc7bc6bdef22713b4f5ef789a4c"}, + {file = "yarl-1.11.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:aac44097d838dda26526cffb63bdd8737a2dbdf5f2c68efb72ad83aec6673c7e"}, + {file = "yarl-1.11.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:267b24f891e74eccbdff42241c5fb4f974de2d6271dcc7d7e0c9ae1079a560d9"}, + {file = "yarl-1.11.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6907daa4b9d7a688063ed098c472f96e8181733c525e03e866fb5db480a424df"}, + {file = "yarl-1.11.1-cp39-cp39-win32.whl", hash = "sha256:14438dfc5015661f75f85bc5adad0743678eefee266ff0c9a8e32969d5d69f74"}, + {file = "yarl-1.11.1-cp39-cp39-win_amd64.whl", hash = "sha256:94d0caaa912bfcdc702a4204cd5e2bb01eb917fc4f5ea2315aa23962549561b0"}, + {file = "yarl-1.11.1-py3-none-any.whl", hash = "sha256:72bf26f66456baa0584eff63e44545c9f0eaed9b73cb6601b647c91f14c11f38"}, + {file = "yarl-1.11.1.tar.gz", hash = "sha256:1bb2d9e212fb7449b8fb73bc461b51eaa17cc8430b4a87d87be7b25052d92f53"}, +] + +[[package]] +name = "zipp" +version = "3.20.2" +requires_python = ">=3.8" +summary = "Backport of pathlib-compatible object wrapper for zip files" +groups = ["docs"] +files = [ + {file = "zipp-3.20.2-py3-none-any.whl", hash = "sha256:a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350"}, + {file = "zipp-3.20.2.tar.gz", hash = "sha256:bc9eb26f4506fda01b81bcde0ca78103b6e62f991b381fec825435c836edbc29"}, +] diff --git a/pyproject.toml b/pyproject.toml index 01f5e903b5..7ee7e1c83d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -61,6 +61,7 @@ docs = [ "CairoSVG >= 2.7.1", "mknotebooks >= 0.8.0", "pandas >= 2.0", + "tabulate>=0.9.0", ] tests = [ "pytest >= 7.4.0", @@ -122,3 +123,5 @@ ignore = ["E501", "B905", "B008"] [tool.pytest.ini_options] testpaths = ["tests"] + +[tool.pdm] diff --git a/src/distilabel/utils/mkdocs/components_gallery.py b/src/distilabel/utils/mkdocs/components_gallery.py index 3798b6f90a..c2e9fa5cb1 100644 --- a/src/distilabel/utils/mkdocs/components_gallery.py +++ b/src/distilabel/utils/mkdocs/components_gallery.py @@ -16,6 +16,7 @@ from pathlib import Path from typing import TYPE_CHECKING, List, Union +import pandas as pd from jinja2 import Template from mkdocs.config.base import Config from mkdocs.config.config_options import Type @@ -91,6 +92,21 @@ "clustering": ":material-scatter-plot:", } +_STEP_CATEGORY_TO_DESCRIPTION = { + "text-generation": "Text generation steps are used to generate text based on a given prompt.", + "evol": "Evol steps are used to rewrite input text and evolve it to a higher quality.", + "text-manipulation": "Text manipulation steps are used to manipulate or rewrite an input text.", + "critique": "Critique steps are used to provide feedback on the quality of the data with a written explanation.", + "scorer": "Scorer steps are used to evaluate and score the data with a numerical value.", + "preference": "Preference steps are used to collect preferences on the data with numerical values or ranks.", + "embedding": "Embedding steps are used to generate embeddings for the data.", + "columns": "Columns steps are used to manipulate columns in the data.", + "filtering": "Filtering steps are used to filter the data based on some criteria.", + "format": "Format steps are used to format the data.", + "load": "Load steps are used to load the data.", + "save": "Save steps are used to save the data.", +} + class ComponentsGalleryConfig(Config): enabled = Type(bool, default=True) @@ -291,10 +307,30 @@ def _generate_tasks_pages(self, src_dir: Path, tasks: list) -> List[str]: paths.append(task_path) + global _STEP_CATEGORY_TO_DESCRIPTION + categories = list(_STEP_CATEGORY_TO_DESCRIPTION.keys()) + table = pd.DataFrame( + { + "Category": categories, + "Icon": [_STEPS_CATEGORY_TO_ICON[category] for category in categories], + "Description": [ + _STEP_CATEGORY_TO_DESCRIPTION[category] for category in categories + ], + } + ).to_markdown(index=False) + + description = [ + '??? info "Task Category Overview"', + " The tasks gallery page showcases the different types of tasks that can be performed with `distilabel`.", + "", + ] + for row in table.split("\n"): + description.append(f" {row}") + # Create the `components-gallery/steps/index.md` file content = _COMPONENTS_LIST_TEMPLATE.render( title="Tasks Gallery", - description="", + description="\n".join(description), components=tasks, default_icon=":material-check-outline:", ) From e67864e1b65a2633205bb3966cde41e16373862d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Mart=C3=ADn=20Bl=C3=A1zquez?= Date: Tue, 17 Sep 2024 10:03:11 +0200 Subject: [PATCH 57/82] Fix missing `system_prompt_key` column in `Magpie` tasks (#983) * Fix missing `system_prompt_key` column * Fix wrong `system_prompt_key` associated to conversation * Update unit tests --- src/distilabel/steps/tasks/magpie/base.py | 31 ++++++----- tests/unit/steps/tasks/magpie/test_base.py | 62 ++++++++++++++++++---- 2 files changed, 70 insertions(+), 23 deletions(-) diff --git a/src/distilabel/steps/tasks/magpie/base.py b/src/distilabel/steps/tasks/magpie/base.py index 13c31f00ef..a137d931dd 100644 --- a/src/distilabel/steps/tasks/magpie/base.py +++ b/src/distilabel/steps/tasks/magpie/base.py @@ -13,6 +13,7 @@ # limitations under the License. import random +from itertools import zip_longest from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union from pydantic import Field, PositiveInt, field_validator @@ -115,7 +116,7 @@ def system_prompts_weights_validator( def _prepare_inputs_for_instruction_generation( self, inputs: List[Dict[str, Any]] - ) -> Tuple[List["ChatType"], Union[str, None]]: + ) -> Tuple[List["ChatType"], List[str]]: """Prepares the inputs adding the system (if required) prompt provided in each row, or if the conversations to generate have more than one turn, then adding the system prompt for multi-turn conversation from the paper. @@ -124,10 +125,10 @@ def _prepare_inputs_for_instruction_generation( inputs: the inputs to prepare. Returns: - The prepared inputs. + The prepared inputs and the system prompt keys used for each input. """ prepared_inputs = [] - system_prompt_key = None + system_prompt_keys = [] for input in inputs: conversation = [] if "system_prompt" in input: @@ -146,6 +147,7 @@ def _prepare_inputs_for_instruction_generation( system_prompt_key = random.choices( system_prompts_keys, weights, k=1 )[0] + system_prompt_keys.append(system_prompt_key) system_prompt = self.system_prompt[system_prompt_key] if isinstance(system_prompt, tuple): system_prompt = system_prompt[0] @@ -159,7 +161,7 @@ def _prepare_inputs_for_instruction_generation( prepared_inputs.append(conversation) - return prepared_inputs, system_prompt_key + return prepared_inputs, system_prompt_keys def _append_messages_to_conversations( self, role: str, messages: List[str], conversations: List["ChatType"] @@ -182,7 +184,7 @@ def _append_messages_to_conversations( def _generate_instruction( self, inputs: List[Dict[str, Any]] ) -> List[Dict[str, Any]]: - prepared_inputs, system_prompt_key = ( + prepared_inputs, system_prompt_keys = ( self._prepare_inputs_for_instruction_generation(inputs) ) outputs = self.llm.generate( @@ -191,15 +193,17 @@ def _generate_instruction( **self.llm.generation_kwargs, # type: ignore ) rows = [] - for output in outputs: - row = {"instruction": output[0]} + for output, system_prompt_key in zip_longest( + outputs, system_prompt_keys, fillvalue=None + ): + row = {"instruction": output[0]} # type: ignore if system_prompt_key is not None: row["system_prompt_key"] = system_prompt_key rows.append(row) return rows def _prepare_conversation_outputs( - self, conversations: List["ChatType"], system_prompt_key: Optional[str] = None + self, conversations: List["ChatType"], system_prompt_keys: List[str] ) -> List[Dict[str, Any]]: """Prepare the output conversation removing the system prompt if necessary. If `n_turns==1`, then it will return a dictionary with "instruction" and "response" @@ -207,14 +211,17 @@ def _prepare_conversation_outputs( Args: conversations: the list of generated conversations. - system_prompt_key: the key of the system prompt used to generate the conversation. + system_prompt_keys: the list of system prompt keys used to generate the conversations. Returns: A list of dictionaries containing a "conversation" key or "instruction" and "responses" key. """ outputs = [] - for conversation in conversations: + for conversation, system_prompt_key in zip_longest( + conversations, system_prompt_keys, fillvalue=None + ): + assert conversation is not None # Something went wrong with the `LLM` and it didn't generate any message if len(conversation) == 0: if self.n_turns == 1: @@ -265,7 +272,7 @@ def _generate_conversation_turn( def _generate_multi_turn_conversation( self, inputs: List[Dict[str, Any]] ) -> List[Dict[str, Any]]: - conversations, system_prompt_key = ( + conversations, system_prompt_keys = ( self._prepare_inputs_for_instruction_generation(inputs) ) # Keep track of the active conversations, as it could happen that for some conversation @@ -294,7 +301,7 @@ def _generate_multi_turn_conversation( active_indices=active_indices, ) - return self._prepare_conversation_outputs(conversations) + return self._prepare_conversation_outputs(conversations, system_prompt_keys) def _generate_with_pre_query_template( self, inputs: List[Dict[str, Any]] diff --git a/tests/unit/steps/tasks/magpie/test_base.py b/tests/unit/steps/tasks/magpie/test_base.py index 2e00404e09..8b830a0db8 100644 --- a/tests/unit/steps/tasks/magpie/test_base.py +++ b/tests/unit/steps/tasks/magpie/test_base.py @@ -395,6 +395,46 @@ def test_process_with_system_prompt_per_row(self) -> None: }, ] + def test_process_with_system_prompt_and_probabilities(self) -> None: + with mock.patch( + "random.choices", + side_effect=[ + ["system_prompt_1"], + ["system_prompt_2"], + ["system_prompt_1"], + ], + ): + task = Magpie( + llm=DummyMagpieLLM(magpie_pre_query_template="llama3"), + system_prompt={ + "system_prompt_1": ("system_prompt", 0.6), + "system_prompt_2": ("system_prompt", 0.4), + }, + ) + + task.load() + + assert next(task.process(inputs=[{}, {}, {}])) == [ + { + "instruction": "Hello Magpie", + "response": "Hello Magpie", + "system_prompt_key": "system_prompt_1", + "model_name": "test", + }, + { + "instruction": "Hello Magpie", + "response": "Hello Magpie", + "system_prompt_key": "system_prompt_2", + "model_name": "test", + }, + { + "instruction": "Hello Magpie", + "response": "Hello Magpie", + "system_prompt_key": "system_prompt_1", + "model_name": "test", + }, + ] + def test_process_only_instruction(self) -> None: task = Magpie( llm=DummyMagpieLLM(magpie_pre_query_template="llama3"), @@ -504,10 +544,10 @@ def test_prepare_conversation_outputs( n_turns=n_turns, include_system_prompt=include_system_prompt, ) - assert task._prepare_conversation_outputs([conversation]) == [expected] + assert task._prepare_conversation_outputs([conversation], []) == [expected] @pytest.mark.parametrize( - "system_prompt, n_turns, inputs, random_choices_return, expected_prepared_inputs, expected_system_prompt_key", + "system_prompt, n_turns, inputs, random_choices_return, expected_prepared_inputs, expected_system_prompt_keys", [ ( None, @@ -515,7 +555,7 @@ def test_prepare_conversation_outputs( [{"system_prompt": "Custom system prompt."}], None, [[{"role": "system", "content": "Custom system prompt."}]], - None, + [], ), ( ["Prompt A", "Prompt B"], @@ -523,7 +563,7 @@ def test_prepare_conversation_outputs( [{}], ["Prompt A"], [[{"role": "system", "content": "Prompt A"}]], - None, + [], ), ( {"Key1": "Prompt 1", "Key2": "Prompt 2"}, @@ -531,7 +571,7 @@ def test_prepare_conversation_outputs( [{}], ["Key1"], [[{"role": "system", "content": "Prompt 1"}]], - "Key1", + ["Key1"], ), ( {"Key1": ("Prompt 1", 0.7), "Key2": ("Prompt 2", 0.3)}, @@ -539,7 +579,7 @@ def test_prepare_conversation_outputs( [{}], ["Key1"], [[{"role": "system", "content": "Prompt 1"}]], - "Key1", + ["Key1"], ), ( None, @@ -547,7 +587,7 @@ def test_prepare_conversation_outputs( [{}], None, [[{"role": "system", "content": MAGPIE_MULTI_TURN_SYSTEM_PROMPT}]], - None, + [], ), ( None, @@ -555,7 +595,7 @@ def test_prepare_conversation_outputs( [{}], None, [[]], - None, + [], ), ], ) @@ -566,7 +606,7 @@ def test_prepare_inputs_for_instruction_generation( inputs, random_choices_return, expected_prepared_inputs, - expected_system_prompt_key, + expected_system_prompt_keys, ): task = Magpie( llm=DummyMagpieLLM(magpie_pre_query_template="llama3"), @@ -578,12 +618,12 @@ def test_prepare_inputs_for_instruction_generation( if random_choices_return is not None: mock_choices.return_value = random_choices_return - prepared_inputs, system_prompt_key = ( + prepared_inputs, system_prompt_keys = ( task._prepare_inputs_for_instruction_generation(inputs) ) assert prepared_inputs == expected_prepared_inputs - assert system_prompt_key == expected_system_prompt_key + assert system_prompt_keys == expected_system_prompt_keys def test_serialization(self) -> None: task = Magpie( From 370e5b56bc5bf5fba7a6783dae92426c80a13fa0 Mon Sep 17 00:00:00 2001 From: David Berenstein Date: Wed, 18 Sep 2024 12:27:46 +0200 Subject: [PATCH 58/82] docs: update component gallery (#987) --- .../utils/mkdocs/components_gallery.py | 110 ++++++++++++------ 1 file changed, 77 insertions(+), 33 deletions(-) diff --git a/src/distilabel/utils/mkdocs/components_gallery.py b/src/distilabel/utils/mkdocs/components_gallery.py index c2e9fa5cb1..9d5d9b59ee 100644 --- a/src/distilabel/utils/mkdocs/components_gallery.py +++ b/src/distilabel/utils/mkdocs/components_gallery.py @@ -76,30 +76,34 @@ ) _STEPS_CATEGORY_TO_ICON = { + "text-generation": ":material-text-box-edit:", + "chat-generation": ":material-chat:", + "text-classification": ":material-label:", + "text-manipulation": ":material-receipt-text-edit:", + "evol": ":material-dna:", "critique": ":material-comment-edit:", + "scorer": ":octicons-number-16:", + "preference": ":material-poll:", "embedding": ":material-vector-line:", - "evol": ":material-dna:", + "clustering": ":material-scatter-plot:", + "columns": ":material-table-column:", "filtering": ":material-filter:", "format": ":material-format-list-bulleted:", "load": ":material-file-download:", - "preference": ":material-poll:", "save": ":material-content-save:", - "scorer": ":octicons-number-16:", - "text-generation": ":material-text-box-edit:", - "text-manipulation": ":material-receipt-text-edit:", - "columns": ":material-table-column:", - "text-classification": ":material-label:", - "clustering": ":material-scatter-plot:", } _STEP_CATEGORY_TO_DESCRIPTION = { "text-generation": "Text generation steps are used to generate text based on a given prompt.", - "evol": "Evol steps are used to rewrite input text and evolve it to a higher quality.", + "chat-generation": "Chat generation steps are used to generate text based on a conversation.", + "text-classification": "Text classification steps are used to classify text into a category.", "text-manipulation": "Text manipulation steps are used to manipulate or rewrite an input text.", + "evol": "Evol steps are used to rewrite input text and evolve it to a higher quality.", "critique": "Critique steps are used to provide feedback on the quality of the data with a written explanation.", "scorer": "Scorer steps are used to evaluate and score the data with a numerical value.", "preference": "Preference steps are used to collect preferences on the data with numerical values or ranks.", "embedding": "Embedding steps are used to generate embeddings for the data.", + "clustering": "Clustering steps are used to group similar data points together.", "columns": "Columns steps are used to manipulate columns in the data.", "filtering": "Filtering steps are used to filter the data based on some criteria.", "format": "Format steps are used to format the data.", @@ -107,6 +111,34 @@ "save": "Save steps are used to save the data.", } +assert list(_STEP_CATEGORY_TO_DESCRIPTION.keys()) == list( + _STEPS_CATEGORY_TO_ICON.keys() +) + +_STEP_CATEGORIES = list(_STEP_CATEGORY_TO_DESCRIPTION.keys()) +_STEP_CATEGORY_TABLE = pd.DataFrame( + { + "Icon": [_STEPS_CATEGORY_TO_ICON[category] for category in _STEP_CATEGORIES], + "Category": _STEP_CATEGORIES, + "Description": [ + _STEP_CATEGORY_TO_DESCRIPTION[category] for category in _STEP_CATEGORIES + ], + } +).to_markdown(index=False) +_STEP_CATEGORY_TABLE_DESCRIPTION = [ + '??? info "Category Overview"', + " The gallery page showcases the different types of components within `distilabel`.", + "", +] +for row in _STEP_CATEGORY_TABLE.split("\n"): + _STEP_CATEGORY_TABLE_DESCRIPTION.append(f" {row}") +_STEP_CATEGORY_TABLE_DESCRIPTION = "\n".join(_STEP_CATEGORY_TABLE_DESCRIPTION) + +_CATEGORY_ORDER_INDEX = { + category: idx + for idx, category in enumerate(list(_STEP_CATEGORY_TO_DESCRIPTION.keys())) +} + class ComponentsGalleryConfig(Config): enabled = Type(bool, default=True) @@ -229,6 +261,18 @@ def _generate_steps_pages(self, src_dir: Path, steps: list) -> List[str]: steps_gallery_page_path = src_dir / paths[0] steps_gallery_page_path.parent.mkdir(parents=True, exist_ok=True) + # Sort steps based on the index of their first category in the 'category_order' + steps = sorted( + steps, + key=lambda step: _CATEGORY_ORDER_INDEX.get( + step["docstring"]["categories"][0] + if step["docstring"]["categories"] + else float("inf"), + float("inf"), + ), + reverse=True, + ) + # Create detail page for each `Step` for step in steps: docstring = step["docstring"] @@ -236,6 +280,11 @@ def _generate_steps_pages(self, src_dir: Path, steps: list) -> List[str]: first_category = docstring["categories"][0] docstring["icon"] = _STEPS_CATEGORY_TO_ICON.get(first_category, "") + if docstring["icon"]: + assert ( + docstring["icon"] in _STEPS_CATEGORY_TO_ICON.values() + ), f"Icon {docstring['icon']} not found in _STEPS_CATEGORY_TO_ICON" + name = step["name"] content = _STEP_DETAIL_TEMPLATE.render( @@ -254,10 +303,10 @@ def _generate_steps_pages(self, src_dir: Path, steps: list) -> List[str]: paths.append(step_path) - # Create the `components-gallery/steps.md` file + # Create the `components-gallery/steps/index.md` file content = _COMPONENTS_LIST_TEMPLATE.render( title="Steps Gallery", - description="", + description=_STEP_CATEGORY_TABLE_DESCRIPTION, components=steps, default_icon=":material-step-forward:", ) @@ -282,12 +331,27 @@ def _generate_tasks_pages(self, src_dir: Path, tasks: list) -> List[str]: tasks_gallery_page_path = src_dir / paths[0] tasks_gallery_page_path.parent.mkdir(parents=True, exist_ok=True) + # Sort tasks based on the index of their first category in the 'category_order' + tasks = sorted( + tasks, + key=lambda task: _CATEGORY_ORDER_INDEX.get( + task["docstring"]["categories"][0] + if task["docstring"]["categories"] + else float("inf"), + float("inf"), + ), + ) + # Create detail page for each `Task` for task in tasks: docstring = task["docstring"] if docstring["icon"] == "" and docstring["categories"]: first_category = docstring["categories"][0] docstring["icon"] = _STEPS_CATEGORY_TO_ICON.get(first_category, "") + if docstring["icon"]: + assert ( + docstring["icon"] in _STEPS_CATEGORY_TO_ICON.values() + ), f"Icon {docstring['icon']} not found in _STEPS_CATEGORY_TO_ICON" name = task["name"] @@ -307,30 +371,10 @@ def _generate_tasks_pages(self, src_dir: Path, tasks: list) -> List[str]: paths.append(task_path) - global _STEP_CATEGORY_TO_DESCRIPTION - categories = list(_STEP_CATEGORY_TO_DESCRIPTION.keys()) - table = pd.DataFrame( - { - "Category": categories, - "Icon": [_STEPS_CATEGORY_TO_ICON[category] for category in categories], - "Description": [ - _STEP_CATEGORY_TO_DESCRIPTION[category] for category in categories - ], - } - ).to_markdown(index=False) - - description = [ - '??? info "Task Category Overview"', - " The tasks gallery page showcases the different types of tasks that can be performed with `distilabel`.", - "", - ] - for row in table.split("\n"): - description.append(f" {row}") - - # Create the `components-gallery/steps/index.md` file + # Create the `components-gallery/tasks/index.md` file content = _COMPONENTS_LIST_TEMPLATE.render( title="Tasks Gallery", - description="\n".join(description), + description=_STEP_CATEGORY_TABLE_DESCRIPTION, components=tasks, default_icon=":material-check-outline:", ) From 33b58bfc49c3f79454c15d1ec9ad9948b2c56499 Mon Sep 17 00:00:00 2001 From: davidberenstein1957 Date: Fri, 20 Sep 2024 10:03:10 +0200 Subject: [PATCH 59/82] docs: update install overview in readme --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 013203067b..fb85104778 100644 --- a/README.md +++ b/README.md @@ -91,6 +91,9 @@ In addition, the following extras are available: - `openai`: for using [OpenAI API](https://openai.com/blog/openai-api) models via the `OpenAILLM` integration, or the rest of the integrations based on OpenAI and relying on its client as `AnyscaleLLM`, `AzureOpenAILLM`, and `TogetherLLM`. - `vertexai`: for using [Google Vertex AI](https://cloud.google.com/vertex-ai) proprietary models via the `VertexAILLM` integration. - `vllm`: for using [vllm](https://github.com/vllm-project/vllm) serving engine via the `vLLM` integration. +- `faiss-cpu` and `faiss-gpu`: for generating sentence embeddings using [faiss](https://github.com/facebookresearch/faiss). +- `outlines`: for using structured generation of LLMs with [outlines](https://github.com/outlines-dev/outlines). +- `instructor`: for using structured generation of LLMs with [Instructor](https://github.com/jxnl/instructor/). ### Example From a2ab68dcca04fe21a762d9be32a2c75587371b34 Mon Sep 17 00:00:00 2001 From: davidberenstein1957 Date: Fri, 20 Sep 2024 10:09:46 +0200 Subject: [PATCH 60/82] docs: update installation overview --- README.md | 14 +++++++++++- docs/sections/getting_started/installation.md | 22 ++++++++++++++----- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index fb85104778..728d69c0b4 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,8 @@ Requires Python 3.9+ In addition, the following extras are available: +### LLMs + - `anthropic`: for using models available in [Anthropic API](https://www.anthropic.com/api) via the `AnthropicLLM` integration. - `cohere`: for using models available in [Cohere](https://cohere.ai/) via the `CohereLLM` integration. - `argilla`: for exporting the generated datasets to [Argilla](https://argilla.io/). @@ -91,10 +93,20 @@ In addition, the following extras are available: - `openai`: for using [OpenAI API](https://openai.com/blog/openai-api) models via the `OpenAILLM` integration, or the rest of the integrations based on OpenAI and relying on its client as `AnyscaleLLM`, `AzureOpenAILLM`, and `TogetherLLM`. - `vertexai`: for using [Google Vertex AI](https://cloud.google.com/vertex-ai) proprietary models via the `VertexAILLM` integration. - `vllm`: for using [vllm](https://github.com/vllm-project/vllm) serving engine via the `vLLM` integration. -- `faiss-cpu` and `faiss-gpu`: for generating sentence embeddings using [faiss](https://github.com/facebookresearch/faiss). +- `sentence-transformers`: for generating sentence embeddings using [sentence-transformers](https://github.com/UKPLab/sentence-transformers). + +### Structured generation + - `outlines`: for using structured generation of LLMs with [outlines](https://github.com/outlines-dev/outlines). - `instructor`: for using structured generation of LLMs with [Instructor](https://github.com/jxnl/instructor/). +### Data processing + +- `ray`: for scaling and distributing a pipeline with [Ray](https://github.com/ray-project/ray). +- `faiss-cpu` and `faiss-gpu`: for generating sentence embeddings using [faiss](https://github.com/facebookresearch/faiss). +- `text-clustering`: for using text clustering with [UMAP](https://github.com/lmcinnes/umap) and [Scikit-learn](https://github.com/scikit-learn/scikit-learn). +- `minhash`: for using minhash for duplicate detection with [datasketch](https://github.com/datasketch/datasketch) and [nltk](https://github.com/nltk/nltk). + ### Example To run the following example you must install `distilabel` with the `hf-inference-endpoints` extra: diff --git a/docs/sections/getting_started/installation.md b/docs/sections/getting_started/installation.md index a169241726..54e130b7fa 100644 --- a/docs/sections/getting_started/installation.md +++ b/docs/sections/getting_started/installation.md @@ -27,6 +27,8 @@ pip install "distilabel @ git+https://github.com/argilla-io/distilabel.git@devel Additionally, as part of `distilabel` some extra dependencies are available, mainly to add support for some of the LLM integrations we support. Here's a list of the available extras: +### LLMs + - `anthropic`: for using models available in [Anthropic API](https://www.anthropic.com/api) via the `AnthropicLLM` integration. - `argilla`: for exporting the generated datasets to [Argilla](https://argilla.io/). @@ -39,8 +41,6 @@ Additionally, as part of `distilabel` some extra dependencies are available, mai - `hf-transformers`: for using models available in [transformers](https://github.com/huggingface/transformers) package via the `TransformersLLM` integration. -- `instructor`: for using structured generation of LLMs with [Instructor](https://github.com/jxnl/instructor/). - - `litellm`: for using [`LiteLLM`](https://github.com/BerriAI/litellm) to call any LLM using OpenAI format via the `LiteLLM` integration. - `llama-cpp`: for using [llama-cpp-python](https://github.com/abetlen/llama-cpp-python) Python bindings for `llama.cpp` via the `LlamaCppLLM` integration. @@ -51,18 +51,28 @@ Additionally, as part of `distilabel` some extra dependencies are available, mai - `openai`: for using [OpenAI API](https://openai.com/blog/openai-api) models via the `OpenAILLM` integration, or the rest of the integrations based on OpenAI and relying on its client as `AnyscaleLLM`, `AzureOpenAILLM`, and `TogetherLLM`. -- `outlines`: for using structured generation of LLMs with [outlines](https://github.com/outlines-dev/outlines). - -- `ray`: for scaling and distributing a pipeline with [Ray](https://github.com/ray-project/ray). - - `vertexai`: for using [Google Vertex AI](https://cloud.google.com/vertex-ai) proprietary models via the `VertexAILLM` integration. - `vllm`: for using [vllm](https://github.com/vllm-project/vllm) serving engine via the `vLLM` integration. - `sentence-transformers`: for generating sentence embeddings using [sentence-transformers](https://github.com/UKPLab/sentence-transformers). +### Data processing + +- `ray`: for scaling and distributing a pipeline with [Ray](https://github.com/ray-project/ray). + - `faiss-cpu` and `faiss-gpu`: for generating sentence embeddings using [faiss](https://github.com/facebookresearch/faiss). +- `minhash`: for using minhash for duplicate detection with [datasketch](https://github.com/datasketch/datasketch) and [nltk](https://github.com/nltk/nltk). + +- `text-clustering`: for using text clustering with [UMAP](https://github.com/lmcinnes/umap) and [Scikit-learn](https://github.com/scikit-learn/scikit-learn). + +### Structured generation + +- `outlines`: for using structured generation of LLMs with [outlines](https://github.com/outlines-dev/outlines). + +- `instructor`: for using structured generation of LLMs with [Instructor](https://github.com/jxnl/instructor/). + ## Recommendations / Notes The [`mistralai`](https://github.com/mistralai/client-python) dependency requires Python 3.9 or higher, so if you're willing to use the `distilabel.llms.MistralLLM` implementation, you will need to have Python 3.9 or higher. From f997cfd491d50a0e5e9d3ff766359f1306f52d46 Mon Sep 17 00:00:00 2001 From: zye1996 Date: Fri, 20 Sep 2024 01:44:32 -0700 Subject: [PATCH 61/82] Fix missing batch when last batch arrive early (#989) --- src/distilabel/pipeline/batch_manager.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/distilabel/pipeline/batch_manager.py b/src/distilabel/pipeline/batch_manager.py index 286d5ef9da..8ddfa30e09 100644 --- a/src/distilabel/pipeline/batch_manager.py +++ b/src/distilabel/pipeline/batch_manager.py @@ -508,9 +508,11 @@ def _ready_to_create_batch_normal(self) -> bool: # `batches` are sorted by `seq_no` num_rows = 0 + is_batch_in_order = True for batch in batches: # Need to create batches using the data from batches with sequential `seq_no` if batch.seq_no != next_expected_seq_no: + is_batch_in_order = False break # There are enough rows to create a batch num_rows += len(batch.data[0]) @@ -524,11 +526,12 @@ def _ready_to_create_batch_normal(self) -> bool: return False # If there are not enough rows and the last batch was not received yet, then - # there is not enough data yet to creata a batch + # there is not enough data yet to create a batch + # If the last batch was received, the batch preceding it must be in order if ( self.input_batch_size and num_rows < self.input_batch_size - and step_name not in self.last_batch_received + and not (step_name in self.last_batch_received and is_batch_in_order) ): return False From ad231abb893e165202164a5e19604e18d14a31b0 Mon Sep 17 00:00:00 2001 From: Agus Date: Fri, 20 Sep 2024 15:59:42 +0200 Subject: [PATCH 62/82] Fine personas socialai tutorial (#992) * Remove pdm things * Draft of socialai example * Add example/post for socialai/fine personas * Simplify title per code review --- .../examples/fine_personas_social_network.md | 232 ++ examples/finepersonas_social_ai.py | 124 + mkdocs.yml | 1 + pdm.lock | 2874 ----------------- pyproject.toml | 2 - 5 files changed, 357 insertions(+), 2876 deletions(-) create mode 100644 docs/sections/pipeline_samples/examples/fine_personas_social_network.md create mode 100644 examples/finepersonas_social_ai.py delete mode 100644 pdm.lock diff --git a/docs/sections/pipeline_samples/examples/fine_personas_social_network.md b/docs/sections/pipeline_samples/examples/fine_personas_social_network.md new file mode 100644 index 0000000000..52df495fc4 --- /dev/null +++ b/docs/sections/pipeline_samples/examples/fine_personas_social_network.md @@ -0,0 +1,232 @@ +--- +hide: toc +--- + +# Create a social network with FinePersonas + +In this example, we'll explore the creation of specialized user personas for social network interactions using the [FinePersonas-v0.1](https://huggingface.co/datasets/argilla/FinePersonas-v0.1) dataset from Hugging Face. The final dataset will be ready to fine-tune a chat model with specific traits and characteristics. + +## Introduction + +We'll delve into the process of fine-tuning different LoRA (Low-Rank Adaptation) models to imbue these personas with specific traits and characteristics. + +This approach draws inspiration from Michael Sayman's work on [SocialAI](https://apps.apple.com/us/app/socialai-ai-social-network/id6670229993) (visit the [profile](https://x.com/michaelsayman) to see some examples), to leverage [FinePersonas-v0.1](https://huggingface.co/datasets/argilla/FinePersonas-v0.1) for building models that can emulate bots with specific behaviour. + +By fine-tuning these adapters, we can potentially create AI personas with distinct characteristics, communication styles, and areas of expertise. The result? AI interactions that feel more natural and tailored to specific contexts or user needs. For those interested in the technical aspects of this approach, we recommend the insightful blog post on [Multi-LoRA serving](https://huggingface.co/blog/multi-lora-serving). It provides a clear and comprehensive explanation of the technology behind this innovative method. + +Let's jump to the demo. + +## Creating our SocialAI Task + +Building on the new [`TextGeneration`](https://distilabel.argilla.io/dev/components-gallery/tasks/textgeneration/), creating custom tasks is easier than ever before. This powerful tool opens up a world of possibilities for creating tailored text-based content with ease and precision. We will create a `SocialAI` task that will be in charge of generating responses to user interactions, taking into account a given `follower_type`, and use the perspective from a given `persona`: + +```python +from distilabel.steps.tasks import TextGeneration + +class SocialAI(TextGeneration): + follower_type: Literal["supporter", "troll", "alarmist"] = "supporter" + system_prompt: str = ( + "You are an AI assistant expert at simulating user interactions. " + "You must answer as if you were a '{follower_type}', be concise answer with no more than 200 characters, nothing else." + "Here are some traits to use for your personality:\n\n" + "{traits}" + ) # (1) + template: str = "You are the folowing persona:\n\n{{ persona }}\n\nWhat would you say to the following?\n\n {{ post }}" # (2) + columns: str | list[str] = ["persona", "post"] # (3) + + _follower_traits: dict[str, str] = { + "supporter": ( + "- Encouraging and positive\n" + "- Tends to prioritize enjoyment and relaxation\n" + "- Focuses on the present moment and short-term pleasure\n" + "- Often uses humor and playful language\n" + "- Wants to help others feel good and have fun\n" + ), + "troll": ( + "- Provocative and confrontational\n" + "- Enjoys stirring up controversy and conflict\n" + "- Often uses sarcasm, irony, and mocking language\n" + "- Tends to belittle or dismiss others' opinions and feelings\n" + "- Seeks to get a rise out of others and create drama\n" + ), + "alarmist": ( + "- Anxious and warning-oriented\n" + "- Focuses on potential risks and negative consequences\n" + "- Often uses dramatic or sensational language\n" + "- Tends to be serious and stern in tone\n" + "- Seeks to alert others to potential dangers and protect them from harm (even if it's excessive or unwarranted)\n" + ), + } + + def load(self) -> None: + super().load() + self.system_prompt = self.system_prompt.format( + follower_type=self.follower_type, + traits=self._follower_traits[self.follower_type] + ) # (4) +``` + +1. We have a custom system prompt that will depend on the `follower_type` we decide for our model. + +2. The base template or prompt will answert to the `post` we have, from the point of view of a `persona`. + +3. We will need our dataset to have both `persona` and `post` columns to populate the prompt. + +4. In the load method we place the specific traits for our follower type in the system prompt. + +## Data preparation + +This is an example, so let's keep it short. We will use 3 posts, and 3 different types of personas. While there's potential to enhance this process (perhaps by implementing random persona selection or leveraging semantic similarity) we'll opt for a straightforward method in this demonstration. + +Our goal is to create a set of nine examples, each pairing a post with a persona. To achieve this, we'll employ an LLM to respond to each post from the perspective of a specific `persona`, effectively simulating how different characters might engage with the content. + +```python +posts = [ + { + "post": "Hmm, ok now I'm torn: should I go for healthy chicken tacos or unhealthy beef tacos for late night cravings?" + }, + { + "post": "I need to develop a training course for my company on communication skills. Need to decide how deliver it remotely." + }, + { + "post": "I'm always 10 minutes late to meetups but no one's complained. Could this be annoying to them?" + }, +] + +personas = ( + load_dataset("argilla/FinePersonas-v0.1-clustering-100k", split="train") + .shuffle() + .select(range(3)) + .select_columns("persona") + .to_list() +) + +data = [] +for post in posts: + for persona in personas: + data.append({"post": post["post"], "persona": persona["persona"]}) +``` + +Each row in will have the following format: + +```python +import json +print(json.dumps(data[0], indent=4)) +{ + "post": "Hmm, ok now I'm torn: should I go for healthy chicken tacos or unhealthy beef tacos for late night cravings?", + "persona": "A high school or college environmental science teacher or an ecology student specializing in biogeography and ecosystem dynamics." +} +``` + +This will be our dataset, that we can ingest using the [`LoadDataFromDicts`](https://distilabel.argilla.io/dev/components-gallery/steps/loaddatafromdicts/): + +```python +loader = LoadDataFromDicts(data=data) +``` + +## Simulating from different types of followers + +With our data in hand, we're ready to explore the capabilities of our SocialAI task. For this demonstration, we'll make use of of `meta-llama/Meta-Llama-3.1-70B-Instruct` +While this model has become something of a go-to choice recently, it's worth noting that experimenting with a variety of models could yield even more interesting results: + +```python +from distilabel.llms import InferenceEndpointsLLM + +llm = InferenceEndpointsLLM( + model_id="meta-llama/Meta-Llama-3.1-70B-Instruct", + generation_kwargs={ + "temperature": 0.7, + "max_new_tokens": 256, + }, +) +follower_type = "supporter" + +follower = SocialAI( + llm=llm, + follower_type=follower_type, + name=f"{follower_type}_user", +) +``` + +This setup simplifies the process, we only need to input the follower type, and the system handles the rest. We could update this too to have a random type of follower by default, and simulate from a bunch of different personalities. + +## Building our Pipeline + +The foundation of our pipeline is now in place. At its core is a single, powerful LLM. This versatile model will be repurposed to drive three distinct `SocialAI` Tasks, each tailored to a specific `TextGeneration` task, and each one of them will be prepared for Supervised Fine Tuning using [`FormatTextGenerationSFT`](https://distilabel.argilla.io/dev/components-gallery/steps/formattextgenerationsft/): + +```python +with Pipeline(name="Social AI Personas") as pipeline: + loader = LoadDataFromDicts(data=data, batch_size=1) + + llm = InferenceEndpointsLLM( + model_id="meta-llama/Meta-Llama-3.1-70B-Instruct", + generation_kwargs={ + "temperature": 0.7, + "max_new_tokens": 256, + }, + ) + + for follower_type in ["supporter", "troll", "alarmist"]: + follower = SocialAI( + llm=llm, + follower_type=follower_type, + name=f"{follower_type}_user", # (1) + output_mappings={ + "generation": f"interaction_{follower_type}" # (2) + } + ) + format_sft = FormatTextGenerationSFT( + name=f"format_sft_{follower_type}", + input_mappings={ + "instruction": "post", + "generation": f"interaction_{follower_type}" # (3) + }, + ) + loader >> follower >> format_sft # (4) +``` + +1. We update the name of the step to keep track in the pipeline. + +2. The `generation` column from each LLM will be mapped to avoid them being overriden, as we are reusing the same task. + +3. As we have modified the output column from `SocialAI`, we redirect each one of the "follower_type" responses. + +4. Connect the loader to each one of the follower tasks and `format_sft` to obtain 3 different subsets. + +The outcome of this pipeline will be three specialized models, each fine-tuned to a unique `follower type` crafted by the `SocialAI` task. These models will generate SFT-formatted datasets, where each post is paired with its corresponding interaction data for a specific follower type. This setup enables seamless fine-tuning using your preferred framework, such as [TRL](https://huggingface.co/docs/trl/index), or any other training framework of your choice. + +## Script and final dataset + +All the pieces are in place for our script, the full pipeline can be seen here: + +??? Run + + ```python + python examples/finepersonas_social_ai.py + ``` + +```python title="finepersonas_social_ai.py" +--8<-- "examples/finepersonas_social_ai.py" +``` + +This is the final toy dataset we obtain: [FinePersonas-SocialAI-test](https://huggingface.co/datasets/plaguss/FinePersonas-SocialAI-test) + +You can see examples of how to load each subset of them to fine-tune a model: + +```python +from datasets import load_dataset + +ds = load_dataset("plaguss/FinePersonas-SocialAI-test", "format_sft_troll") +``` + +And a sample of the generated field with the corresponding `post` and `persona`: + +```json +{ + "post": "Hmm, ok now I\u0027m torn: should I go for healthy chicken tacos or unhealthy beef tacos for late night cravings?", + "persona": "A high school or undergraduate physics or chemistry teacher, likely with a focus on experimental instruction.", + "interaction_troll": "\"Late night cravings? More like late night brain drain. Either way, it\u0027s just a collision of molecules in your stomach. Choose the one with more calories, at least that\u0027s some decent kinetic energy.\"", +} +``` + +There's a lot of room for improvement, but quite a promising start. diff --git a/examples/finepersonas_social_ai.py b/examples/finepersonas_social_ai.py new file mode 100644 index 0000000000..8c4f9afc73 --- /dev/null +++ b/examples/finepersonas_social_ai.py @@ -0,0 +1,124 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import Literal + +from datasets import load_dataset + +from distilabel.llms import InferenceEndpointsLLM +from distilabel.pipeline import Pipeline +from distilabel.steps import FormatTextGenerationSFT, LoadDataFromDicts +from distilabel.steps.tasks import TextGeneration + + +class SocialAI(TextGeneration): + follower_type: Literal["supporter", "troll", "alarmist"] = "supporter" + system_prompt: str = ( + "You are an AI assistant expert at simulating user interactions. " + "You must answer as if you were a '{follower_type}', be concise answer with no more than 200 characters, nothing else." + "Here are some traits to use for your personality:\n\n" + "{traits}" + ) + template: str = "You are the folowing persona:\n\n{{ persona }}\n\nWhat would you say to the following?\n\n {{ post }}" + columns: str | list[str] = ["persona", "post"] + + _follower_traits: dict[str, str] = { + "supporter": ( + "- Encouraging and positive\n" + "- Tends to prioritize enjoyment and relaxation\n" + "- Focuses on the present moment and short-term pleasure\n" + "- Often uses humor and playful language\n" + "- Wants to help others feel good and have fun\n" + ), + "troll": ( + "- Provocative and confrontational\n" + "- Enjoys stirring up controversy and conflict\n" + "- Often uses sarcasm, irony, and mocking language\n" + "- Tends to belittle or dismiss others' opinions and feelings\n" + "- Seeks to get a rise out of others and create drama\n" + ), + "alarmist": ( + "- Anxious and warning-oriented\n" + "- Focuses on potential risks and negative consequences\n" + "- Often uses dramatic or sensational language\n" + "- Tends to be serious and stern in tone\n" + "- Seeks to alert others to potential dangers and protect them from harm (even if it's excessive or unwarranted)\n" + ), + } + + def load(self) -> None: + super().load() + self.system_prompt = self.system_prompt.format( + follower_type=self.follower_type, + traits=self._follower_traits[self.follower_type], + ) + + +posts = [ + { + "post": "Hmm, ok now I'm torn: should I go for healthy chicken tacos or unhealthy beef tacos for late night cravings?" + }, + { + "post": "I need to develop a training course for my company on communication skills. Need to decide how deliver it remotely." + }, + { + "post": "I'm always 10 minutes late to meetups but no one's complained. Could this be annoying to them?" + }, +] + +personas = ( + load_dataset("argilla/FinePersonas-v0.1-clustering-100k", split="train") + .shuffle() + .select(range(3)) + .select_columns("persona") + .to_list() +) + +data = [] +for post in posts: + for persona in personas: + data.append({"post": post["post"], "persona": persona["persona"]}) + + +with Pipeline(name="Social AI Personas") as pipeline: + loader = LoadDataFromDicts(data=data, batch_size=1) + + llm = InferenceEndpointsLLM( + model_id="meta-llama/Meta-Llama-3.1-70B-Instruct", + generation_kwargs={ + "temperature": 0.7, + "max_new_tokens": 256, + }, + ) + + for follower_type in ["supporter", "troll", "alarmist"]: + follower = SocialAI( + llm=llm, + follower_type=follower_type, + name=f"{follower_type}_user", + output_mappings={"generation": f"interaction_{follower_type}"}, + ) + format_sft = FormatTextGenerationSFT( + name=f"format_sft_{follower_type}", + input_mappings={ + "instruction": "post", + "generation": f"interaction_{follower_type}", + }, + ) + loader >> follower >> format_sft + + +if __name__ == "__main__": + distiset = pipeline.run(use_cache=False) + distiset.push_to_hub("plaguss/FinePersonas-SocialAI-test", include_script=True) diff --git a/mkdocs.yml b/mkdocs.yml index 3f53b4d4a8..b91cd814fa 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -212,6 +212,7 @@ nav: - Benchmarking with distilabel: "sections/pipeline_samples/examples/benchmarking_with_distilabel.md" - Structured generation with outlines: "sections/pipeline_samples/examples/llama_cpp_with_outlines.md" - Structured generation with instructor: "sections/pipeline_samples/examples/mistralai_with_instructor.md" + - Create a social network with FinePersonas: "sections/pipeline_samples/examples/fine_personas_social_network.md" - API Reference: - Step: - "api/step/index.md" diff --git a/pdm.lock b/pdm.lock deleted file mode 100644 index 453da56d45..0000000000 --- a/pdm.lock +++ /dev/null @@ -1,2874 +0,0 @@ -# This file is @generated by PDM. -# It is not intended for manual editing. - -[metadata] -groups = ["default", "docs"] -strategy = ["inherit_metadata"] -lock_version = "4.5.0" -content_hash = "sha256:40f8a1dc7b5bcf50262cca3a8692ff149c88af6bfab71c1469310a98f38b0643" - -[[metadata.targets]] -requires_python = ">=3.9" - -[[package]] -name = "aiohappyeyeballs" -version = "2.4.0" -requires_python = ">=3.8" -summary = "Happy Eyeballs for asyncio" -groups = ["default"] -files = [ - {file = "aiohappyeyeballs-2.4.0-py3-none-any.whl", hash = "sha256:7ce92076e249169a13c2f49320d1967425eaf1f407522d707d59cac7628d62bd"}, - {file = "aiohappyeyeballs-2.4.0.tar.gz", hash = "sha256:55a1714f084e63d49639800f95716da97a1f173d46a16dfcfda0016abb93b6b2"}, -] - -[[package]] -name = "aiohttp" -version = "3.10.5" -requires_python = ">=3.8" -summary = "Async http client/server framework (asyncio)" -groups = ["default"] -dependencies = [ - "aiohappyeyeballs>=2.3.0", - "aiosignal>=1.1.2", - "async-timeout<5.0,>=4.0; python_version < \"3.11\"", - "attrs>=17.3.0", - "frozenlist>=1.1.1", - "multidict<7.0,>=4.5", - "yarl<2.0,>=1.0", -] -files = [ - {file = "aiohttp-3.10.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:18a01eba2574fb9edd5f6e5fb25f66e6ce061da5dab5db75e13fe1558142e0a3"}, - {file = "aiohttp-3.10.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:94fac7c6e77ccb1ca91e9eb4cb0ac0270b9fb9b289738654120ba8cebb1189c6"}, - {file = "aiohttp-3.10.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2f1f1c75c395991ce9c94d3e4aa96e5c59c8356a15b1c9231e783865e2772699"}, - {file = "aiohttp-3.10.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f7acae3cf1a2a2361ec4c8e787eaaa86a94171d2417aae53c0cca6ca3118ff6"}, - {file = "aiohttp-3.10.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:94c4381ffba9cc508b37d2e536b418d5ea9cfdc2848b9a7fea6aebad4ec6aac1"}, - {file = "aiohttp-3.10.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c31ad0c0c507894e3eaa843415841995bf8de4d6b2d24c6e33099f4bc9fc0d4f"}, - {file = "aiohttp-3.10.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0912b8a8fadeb32ff67a3ed44249448c20148397c1ed905d5dac185b4ca547bb"}, - {file = "aiohttp-3.10.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d93400c18596b7dc4794d48a63fb361b01a0d8eb39f28800dc900c8fbdaca91"}, - {file = "aiohttp-3.10.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d00f3c5e0d764a5c9aa5a62d99728c56d455310bcc288a79cab10157b3af426f"}, - {file = "aiohttp-3.10.5-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:d742c36ed44f2798c8d3f4bc511f479b9ceef2b93f348671184139e7d708042c"}, - {file = "aiohttp-3.10.5-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:814375093edae5f1cb31e3407997cf3eacefb9010f96df10d64829362ae2df69"}, - {file = "aiohttp-3.10.5-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8224f98be68a84b19f48e0bdc14224b5a71339aff3a27df69989fa47d01296f3"}, - {file = "aiohttp-3.10.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d9a487ef090aea982d748b1b0d74fe7c3950b109df967630a20584f9a99c0683"}, - {file = "aiohttp-3.10.5-cp310-cp310-win32.whl", hash = "sha256:d9ef084e3dc690ad50137cc05831c52b6ca428096e6deb3c43e95827f531d5ef"}, - {file = "aiohttp-3.10.5-cp310-cp310-win_amd64.whl", hash = "sha256:66bf9234e08fe561dccd62083bf67400bdbf1c67ba9efdc3dac03650e97c6088"}, - {file = "aiohttp-3.10.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8c6a4e5e40156d72a40241a25cc226051c0a8d816610097a8e8f517aeacd59a2"}, - {file = "aiohttp-3.10.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2c634a3207a5445be65536d38c13791904fda0748b9eabf908d3fe86a52941cf"}, - {file = "aiohttp-3.10.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4aff049b5e629ef9b3e9e617fa6e2dfeda1bf87e01bcfecaf3949af9e210105e"}, - {file = "aiohttp-3.10.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1942244f00baaacaa8155eca94dbd9e8cc7017deb69b75ef67c78e89fdad3c77"}, - {file = "aiohttp-3.10.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e04a1f2a65ad2f93aa20f9ff9f1b672bf912413e5547f60749fa2ef8a644e061"}, - {file = "aiohttp-3.10.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7f2bfc0032a00405d4af2ba27f3c429e851d04fad1e5ceee4080a1c570476697"}, - {file = "aiohttp-3.10.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:424ae21498790e12eb759040bbb504e5e280cab64693d14775c54269fd1d2bb7"}, - {file = "aiohttp-3.10.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:975218eee0e6d24eb336d0328c768ebc5d617609affaca5dbbd6dd1984f16ed0"}, - {file = "aiohttp-3.10.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4120d7fefa1e2d8fb6f650b11489710091788de554e2b6f8347c7a20ceb003f5"}, - {file = "aiohttp-3.10.5-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:b90078989ef3fc45cf9221d3859acd1108af7560c52397ff4ace8ad7052a132e"}, - {file = "aiohttp-3.10.5-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:ba5a8b74c2a8af7d862399cdedce1533642fa727def0b8c3e3e02fcb52dca1b1"}, - {file = "aiohttp-3.10.5-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:02594361128f780eecc2a29939d9dfc870e17b45178a867bf61a11b2a4367277"}, - {file = "aiohttp-3.10.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:8fb4fc029e135859f533025bc82047334e24b0d489e75513144f25408ecaf058"}, - {file = "aiohttp-3.10.5-cp311-cp311-win32.whl", hash = "sha256:e1ca1ef5ba129718a8fc827b0867f6aa4e893c56eb00003b7367f8a733a9b072"}, - {file = "aiohttp-3.10.5-cp311-cp311-win_amd64.whl", hash = "sha256:349ef8a73a7c5665cca65c88ab24abe75447e28aa3bc4c93ea5093474dfdf0ff"}, - {file = "aiohttp-3.10.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:305be5ff2081fa1d283a76113b8df7a14c10d75602a38d9f012935df20731487"}, - {file = "aiohttp-3.10.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3a1c32a19ee6bbde02f1cb189e13a71b321256cc1d431196a9f824050b160d5a"}, - {file = "aiohttp-3.10.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:61645818edd40cc6f455b851277a21bf420ce347baa0b86eaa41d51ef58ba23d"}, - {file = "aiohttp-3.10.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c225286f2b13bab5987425558baa5cbdb2bc925b2998038fa028245ef421e75"}, - {file = "aiohttp-3.10.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8ba01ebc6175e1e6b7275c907a3a36be48a2d487549b656aa90c8a910d9f3178"}, - {file = "aiohttp-3.10.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8eaf44ccbc4e35762683078b72bf293f476561d8b68ec8a64f98cf32811c323e"}, - {file = "aiohttp-3.10.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1c43eb1ab7cbf411b8e387dc169acb31f0ca0d8c09ba63f9eac67829585b44f"}, - {file = "aiohttp-3.10.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:de7a5299827253023c55ea549444e058c0eb496931fa05d693b95140a947cb73"}, - {file = "aiohttp-3.10.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4790f0e15f00058f7599dab2b206d3049d7ac464dc2e5eae0e93fa18aee9e7bf"}, - {file = "aiohttp-3.10.5-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:44b324a6b8376a23e6ba25d368726ee3bc281e6ab306db80b5819999c737d820"}, - {file = "aiohttp-3.10.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:0d277cfb304118079e7044aad0b76685d30ecb86f83a0711fc5fb257ffe832ca"}, - {file = "aiohttp-3.10.5-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:54d9ddea424cd19d3ff6128601a4a4d23d54a421f9b4c0fff740505813739a91"}, - {file = "aiohttp-3.10.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4f1c9866ccf48a6df2b06823e6ae80573529f2af3a0992ec4fe75b1a510df8a6"}, - {file = "aiohttp-3.10.5-cp312-cp312-win32.whl", hash = "sha256:dc4826823121783dccc0871e3f405417ac116055bf184ac04c36f98b75aacd12"}, - {file = "aiohttp-3.10.5-cp312-cp312-win_amd64.whl", hash = "sha256:22c0a23a3b3138a6bf76fc553789cb1a703836da86b0f306b6f0dc1617398abc"}, - {file = "aiohttp-3.10.5-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7f6b639c36734eaa80a6c152a238242bedcee9b953f23bb887e9102976343092"}, - {file = "aiohttp-3.10.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f29930bc2921cef955ba39a3ff87d2c4398a0394ae217f41cb02d5c26c8b1b77"}, - {file = "aiohttp-3.10.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f489a2c9e6455d87eabf907ac0b7d230a9786be43fbe884ad184ddf9e9c1e385"}, - {file = "aiohttp-3.10.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:123dd5b16b75b2962d0fff566effb7a065e33cd4538c1692fb31c3bda2bfb972"}, - {file = "aiohttp-3.10.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b98e698dc34966e5976e10bbca6d26d6724e6bdea853c7c10162a3235aba6e16"}, - {file = "aiohttp-3.10.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3b9162bab7e42f21243effc822652dc5bb5e8ff42a4eb62fe7782bcbcdfacf6"}, - {file = "aiohttp-3.10.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1923a5c44061bffd5eebeef58cecf68096e35003907d8201a4d0d6f6e387ccaa"}, - {file = "aiohttp-3.10.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d55f011da0a843c3d3df2c2cf4e537b8070a419f891c930245f05d329c4b0689"}, - {file = "aiohttp-3.10.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:afe16a84498441d05e9189a15900640a2d2b5e76cf4efe8cbb088ab4f112ee57"}, - {file = "aiohttp-3.10.5-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f8112fb501b1e0567a1251a2fd0747baae60a4ab325a871e975b7bb67e59221f"}, - {file = "aiohttp-3.10.5-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:1e72589da4c90337837fdfe2026ae1952c0f4a6e793adbbfbdd40efed7c63599"}, - {file = "aiohttp-3.10.5-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:4d46c7b4173415d8e583045fbc4daa48b40e31b19ce595b8d92cf639396c15d5"}, - {file = "aiohttp-3.10.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:33e6bc4bab477c772a541f76cd91e11ccb6d2efa2b8d7d7883591dfb523e5987"}, - {file = "aiohttp-3.10.5-cp313-cp313-win32.whl", hash = "sha256:c58c6837a2c2a7cf3133983e64173aec11f9c2cd8e87ec2fdc16ce727bcf1a04"}, - {file = "aiohttp-3.10.5-cp313-cp313-win_amd64.whl", hash = "sha256:38172a70005252b6893088c0f5e8a47d173df7cc2b2bd88650957eb84fcf5022"}, - {file = "aiohttp-3.10.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7e2fe37ac654032db1f3499fe56e77190282534810e2a8e833141a021faaab0e"}, - {file = "aiohttp-3.10.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f5bf3ead3cb66ab990ee2561373b009db5bc0e857549b6c9ba84b20bc462e172"}, - {file = "aiohttp-3.10.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1b2c16a919d936ca87a3c5f0e43af12a89a3ce7ccbce59a2d6784caba945b68b"}, - {file = "aiohttp-3.10.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad146dae5977c4dd435eb31373b3fe9b0b1bf26858c6fc452bf6af394067e10b"}, - {file = "aiohttp-3.10.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8c5c6fa16412b35999320f5c9690c0f554392dc222c04e559217e0f9ae244b92"}, - {file = "aiohttp-3.10.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:95c4dc6f61d610bc0ee1edc6f29d993f10febfe5b76bb470b486d90bbece6b22"}, - {file = "aiohttp-3.10.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da452c2c322e9ce0cfef392e469a26d63d42860f829026a63374fde6b5c5876f"}, - {file = "aiohttp-3.10.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:898715cf566ec2869d5cb4d5fb4be408964704c46c96b4be267442d265390f32"}, - {file = "aiohttp-3.10.5-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:391cc3a9c1527e424c6865e087897e766a917f15dddb360174a70467572ac6ce"}, - {file = "aiohttp-3.10.5-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:380f926b51b92d02a34119d072f178d80bbda334d1a7e10fa22d467a66e494db"}, - {file = "aiohttp-3.10.5-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ce91db90dbf37bb6fa0997f26574107e1b9d5ff939315247b7e615baa8ec313b"}, - {file = "aiohttp-3.10.5-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:9093a81e18c45227eebe4c16124ebf3e0d893830c6aca7cc310bfca8fe59d857"}, - {file = "aiohttp-3.10.5-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:ee40b40aa753d844162dcc80d0fe256b87cba48ca0054f64e68000453caead11"}, - {file = "aiohttp-3.10.5-cp39-cp39-win32.whl", hash = "sha256:03f2645adbe17f274444953bdea69f8327e9d278d961d85657cb0d06864814c1"}, - {file = "aiohttp-3.10.5-cp39-cp39-win_amd64.whl", hash = "sha256:d17920f18e6ee090bdd3d0bfffd769d9f2cb4c8ffde3eb203777a3895c128862"}, - {file = "aiohttp-3.10.5.tar.gz", hash = "sha256:f071854b47d39591ce9a17981c46790acb30518e2f83dfca8db2dfa091178691"}, -] - -[[package]] -name = "aiosignal" -version = "1.3.1" -requires_python = ">=3.7" -summary = "aiosignal: a list of registered asynchronous callbacks" -groups = ["default"] -dependencies = [ - "frozenlist>=1.1.0", -] -files = [ - {file = "aiosignal-1.3.1-py3-none-any.whl", hash = "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17"}, - {file = "aiosignal-1.3.1.tar.gz", hash = "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc"}, -] - -[[package]] -name = "annotated-types" -version = "0.7.0" -requires_python = ">=3.8" -summary = "Reusable constraint types to use with typing.Annotated" -groups = ["default"] -dependencies = [ - "typing-extensions>=4.0.0; python_version < \"3.9\"", -] -files = [ - {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, - {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, -] - -[[package]] -name = "anyio" -version = "4.4.0" -requires_python = ">=3.8" -summary = "High level compatibility layer for multiple asynchronous event loop implementations" -groups = ["default"] -dependencies = [ - "exceptiongroup>=1.0.2; python_version < \"3.11\"", - "idna>=2.8", - "sniffio>=1.1", - "typing-extensions>=4.1; python_version < \"3.11\"", -] -files = [ - {file = "anyio-4.4.0-py3-none-any.whl", hash = "sha256:c1b2d8f46a8a812513012e1107cb0e68c17159a7a594208005a57dc776e1bdc7"}, - {file = "anyio-4.4.0.tar.gz", hash = "sha256:5aadc6a1bbb7cdb0bede386cac5e2940f5e2ff3aa20277e991cf028e0585ce94"}, -] - -[[package]] -name = "async-timeout" -version = "4.0.3" -requires_python = ">=3.7" -summary = "Timeout context manager for asyncio programs" -groups = ["default"] -marker = "python_version < \"3.11\"" -dependencies = [ - "typing-extensions>=3.6.5; python_version < \"3.8\"", -] -files = [ - {file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"}, - {file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"}, -] - -[[package]] -name = "attrs" -version = "24.2.0" -requires_python = ">=3.7" -summary = "Classes Without Boilerplate" -groups = ["default", "docs"] -dependencies = [ - "importlib-metadata; python_version < \"3.8\"", -] -files = [ - {file = "attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2"}, - {file = "attrs-24.2.0.tar.gz", hash = "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346"}, -] - -[[package]] -name = "babel" -version = "2.16.0" -requires_python = ">=3.8" -summary = "Internationalization utilities" -groups = ["docs"] -dependencies = [ - "pytz>=2015.7; python_version < \"3.9\"", -] -files = [ - {file = "babel-2.16.0-py3-none-any.whl", hash = "sha256:368b5b98b37c06b7daf6696391c3240c938b37767d4584413e8438c5c435fa8b"}, - {file = "babel-2.16.0.tar.gz", hash = "sha256:d1f3554ca26605fe173f3de0c65f750f5a42f924499bf134de6423582298e316"}, -] - -[[package]] -name = "beautifulsoup4" -version = "4.12.3" -requires_python = ">=3.6.0" -summary = "Screen-scraping library" -groups = ["docs"] -dependencies = [ - "soupsieve>1.2", -] -files = [ - {file = "beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed"}, - {file = "beautifulsoup4-4.12.3.tar.gz", hash = "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051"}, -] - -[[package]] -name = "bleach" -version = "6.1.0" -requires_python = ">=3.8" -summary = "An easy safelist-based HTML-sanitizing tool." -groups = ["docs"] -dependencies = [ - "six>=1.9.0", - "webencodings", -] -files = [ - {file = "bleach-6.1.0-py3-none-any.whl", hash = "sha256:3225f354cfc436b9789c66c4ee030194bee0568fbf9cbdad3bc8b5c26c5f12b6"}, - {file = "bleach-6.1.0.tar.gz", hash = "sha256:0a31f1837963c41d46bbf1331b8778e1308ea0791db03cc4e7357b97cf42a8fe"}, -] - -[[package]] -name = "cairocffi" -version = "1.7.1" -requires_python = ">=3.8" -summary = "cffi-based cairo bindings for Python" -groups = ["docs"] -dependencies = [ - "cffi>=1.1.0", -] -files = [ - {file = "cairocffi-1.7.1-py3-none-any.whl", hash = "sha256:9803a0e11f6c962f3b0ae2ec8ba6ae45e957a146a004697a1ac1bbf16b073b3f"}, - {file = "cairocffi-1.7.1.tar.gz", hash = "sha256:2e48ee864884ec4a3a34bfa8c9ab9999f688286eb714a15a43ec9d068c36557b"}, -] - -[[package]] -name = "cairosvg" -version = "2.7.1" -requires_python = ">=3.5" -summary = "A Simple SVG Converter based on Cairo" -groups = ["docs"] -dependencies = [ - "cairocffi", - "cssselect2", - "defusedxml", - "pillow", - "tinycss2", -] -files = [ - {file = "CairoSVG-2.7.1-py3-none-any.whl", hash = "sha256:8a5222d4e6c3f86f1f7046b63246877a63b49923a1cd202184c3a634ef546b3b"}, - {file = "CairoSVG-2.7.1.tar.gz", hash = "sha256:432531d72347291b9a9ebfb6777026b607563fd8719c46ee742db0aef7271ba0"}, -] - -[[package]] -name = "certifi" -version = "2024.8.30" -requires_python = ">=3.6" -summary = "Python package for providing Mozilla's CA Bundle." -groups = ["default", "docs"] -files = [ - {file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"}, - {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, -] - -[[package]] -name = "cffi" -version = "1.17.1" -requires_python = ">=3.8" -summary = "Foreign Function Interface for Python calling C code." -groups = ["docs"] -dependencies = [ - "pycparser", -] -files = [ - {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"}, - {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"}, - {file = "cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382"}, - {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702"}, - {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3"}, - {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6"}, - {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17"}, - {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8"}, - {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e"}, - {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be"}, - {file = "cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c"}, - {file = "cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15"}, - {file = "cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401"}, - {file = "cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf"}, - {file = "cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4"}, - {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41"}, - {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1"}, - {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6"}, - {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d"}, - {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6"}, - {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f"}, - {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b"}, - {file = "cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655"}, - {file = "cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0"}, - {file = "cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4"}, - {file = "cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c"}, - {file = "cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36"}, - {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5"}, - {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff"}, - {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99"}, - {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93"}, - {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3"}, - {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8"}, - {file = "cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65"}, - {file = "cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903"}, - {file = "cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e"}, - {file = "cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2"}, - {file = "cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3"}, - {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683"}, - {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5"}, - {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4"}, - {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd"}, - {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed"}, - {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9"}, - {file = "cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d"}, - {file = "cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a"}, - {file = "cffi-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16"}, - {file = "cffi-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36"}, - {file = "cffi-1.17.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8"}, - {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576"}, - {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87"}, - {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0"}, - {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3"}, - {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595"}, - {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a"}, - {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e"}, - {file = "cffi-1.17.1-cp39-cp39-win32.whl", hash = "sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7"}, - {file = "cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662"}, - {file = "cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824"}, -] - -[[package]] -name = "charset-normalizer" -version = "3.3.2" -requires_python = ">=3.7.0" -summary = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -groups = ["default", "docs"] -files = [ - {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, - {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, -] - -[[package]] -name = "click" -version = "8.1.7" -requires_python = ">=3.7" -summary = "Composable command line interface toolkit" -groups = ["default", "docs"] -dependencies = [ - "colorama; platform_system == \"Windows\"", - "importlib-metadata; python_version < \"3.8\"", -] -files = [ - {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, - {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, -] - -[[package]] -name = "colorama" -version = "0.4.6" -requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -summary = "Cross-platform colored terminal text." -groups = ["default", "docs"] -files = [ - {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, - {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, -] - -[[package]] -name = "cssselect2" -version = "0.7.0" -requires_python = ">=3.7" -summary = "CSS selectors for Python ElementTree" -groups = ["docs"] -dependencies = [ - "tinycss2", - "webencodings", -] -files = [ - {file = "cssselect2-0.7.0-py3-none-any.whl", hash = "sha256:fd23a65bfd444595913f02fc71f6b286c29261e354c41d722ca7a261a49b5969"}, - {file = "cssselect2-0.7.0.tar.gz", hash = "sha256:1ccd984dab89fc68955043aca4e1b03e0cf29cad9880f6e28e3ba7a74b14aa5a"}, -] - -[[package]] -name = "datasets" -version = "3.0.0" -requires_python = ">=3.8.0" -summary = "HuggingFace community-driven open-source library of datasets" -groups = ["default"] -dependencies = [ - "aiohttp", - "dill<0.3.9,>=0.3.0", - "filelock", - "fsspec[http]<=2024.6.1,>=2023.1.0", - "huggingface-hub>=0.22.0", - "multiprocess", - "numpy>=1.17", - "packaging", - "pandas", - "pyarrow>=15.0.0", - "pyyaml>=5.1", - "requests>=2.32.2", - "tqdm>=4.66.3", - "xxhash", -] -files = [ - {file = "datasets-3.0.0-py3-none-any.whl", hash = "sha256:c23fefb6c953dcb1cd5f6deb6c502729c733ef98791e0c3f2d80c7ca2d9a01dd"}, - {file = "datasets-3.0.0.tar.gz", hash = "sha256:592317eb137f0fc5aac068ff283ba13c3c66d10c9c034d44bc8aa584126cf3e2"}, -] - -[[package]] -name = "defusedxml" -version = "0.7.1" -requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -summary = "XML bomb protection for Python stdlib modules" -groups = ["docs"] -files = [ - {file = "defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61"}, - {file = "defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69"}, -] - -[[package]] -name = "dill" -version = "0.3.8" -requires_python = ">=3.8" -summary = "serialize all of Python" -groups = ["default"] -files = [ - {file = "dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"}, - {file = "dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca"}, -] - -[[package]] -name = "exceptiongroup" -version = "1.2.2" -requires_python = ">=3.7" -summary = "Backport of PEP 654 (exception groups)" -groups = ["default"] -marker = "python_version < \"3.11\"" -files = [ - {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, - {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, -] - -[[package]] -name = "fastjsonschema" -version = "2.20.0" -summary = "Fastest Python implementation of JSON schema" -groups = ["docs"] -files = [ - {file = "fastjsonschema-2.20.0-py3-none-any.whl", hash = "sha256:5875f0b0fa7a0043a91e93a9b8f793bcbbba9691e7fd83dca95c28ba26d21f0a"}, - {file = "fastjsonschema-2.20.0.tar.gz", hash = "sha256:3d48fc5300ee96f5d116f10fe6f28d938e6008f59a6a025c2649475b87f76a23"}, -] - -[[package]] -name = "filelock" -version = "3.16.0" -requires_python = ">=3.8" -summary = "A platform independent file lock." -groups = ["default"] -files = [ - {file = "filelock-3.16.0-py3-none-any.whl", hash = "sha256:f6ed4c963184f4c84dd5557ce8fece759a3724b37b80c6c4f20a2f63a4dc6609"}, - {file = "filelock-3.16.0.tar.gz", hash = "sha256:81de9eb8453c769b63369f87f11131a7ab04e367f8d97ad39dc230daa07e3bec"}, -] - -[[package]] -name = "frozenlist" -version = "1.4.1" -requires_python = ">=3.8" -summary = "A list-like structure which implements collections.abc.MutableSequence" -groups = ["default"] -files = [ - {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f9aa1878d1083b276b0196f2dfbe00c9b7e752475ed3b682025ff20c1c1f51ac"}, - {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:29acab3f66f0f24674b7dc4736477bcd4bc3ad4b896f5f45379a67bce8b96868"}, - {file = "frozenlist-1.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:74fb4bee6880b529a0c6560885fce4dc95936920f9f20f53d99a213f7bf66776"}, - {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:590344787a90ae57d62511dd7c736ed56b428f04cd8c161fcc5e7232c130c69a"}, - {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:068b63f23b17df8569b7fdca5517edef76171cf3897eb68beb01341131fbd2ad"}, - {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c849d495bf5154cd8da18a9eb15db127d4dba2968d88831aff6f0331ea9bd4c"}, - {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9750cc7fe1ae3b1611bb8cfc3f9ec11d532244235d75901fb6b8e42ce9229dfe"}, - {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9b2de4cf0cdd5bd2dee4c4f63a653c61d2408055ab77b151c1957f221cabf2a"}, - {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0633c8d5337cb5c77acbccc6357ac49a1770b8c487e5b3505c57b949b4b82e98"}, - {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:27657df69e8801be6c3638054e202a135c7f299267f1a55ed3a598934f6c0d75"}, - {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:f9a3ea26252bd92f570600098783d1371354d89d5f6b7dfd87359d669f2109b5"}, - {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:4f57dab5fe3407b6c0c1cc907ac98e8a189f9e418f3b6e54d65a718aaafe3950"}, - {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e02a0e11cf6597299b9f3bbd3f93d79217cb90cfd1411aec33848b13f5c656cc"}, - {file = "frozenlist-1.4.1-cp310-cp310-win32.whl", hash = "sha256:a828c57f00f729620a442881cc60e57cfcec6842ba38e1b19fd3e47ac0ff8dc1"}, - {file = "frozenlist-1.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:f56e2333dda1fe0f909e7cc59f021eba0d2307bc6f012a1ccf2beca6ba362439"}, - {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a0cb6f11204443f27a1628b0e460f37fb30f624be6051d490fa7d7e26d4af3d0"}, - {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b46c8ae3a8f1f41a0d2ef350c0b6e65822d80772fe46b653ab6b6274f61d4a49"}, - {file = "frozenlist-1.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fde5bd59ab5357e3853313127f4d3565fc7dad314a74d7b5d43c22c6a5ed2ced"}, - {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:722e1124aec435320ae01ee3ac7bec11a5d47f25d0ed6328f2273d287bc3abb0"}, - {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2471c201b70d58a0f0c1f91261542a03d9a5e088ed3dc6c160d614c01649c106"}, - {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c757a9dd70d72b076d6f68efdbb9bc943665ae954dad2801b874c8c69e185068"}, - {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f146e0911cb2f1da549fc58fc7bcd2b836a44b79ef871980d605ec392ff6b0d2"}, - {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f9c515e7914626b2a2e1e311794b4c35720a0be87af52b79ff8e1429fc25f19"}, - {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c302220494f5c1ebeb0912ea782bcd5e2f8308037b3c7553fad0e48ebad6ad82"}, - {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:442acde1e068288a4ba7acfe05f5f343e19fac87bfc96d89eb886b0363e977ec"}, - {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:1b280e6507ea8a4fa0c0a7150b4e526a8d113989e28eaaef946cc77ffd7efc0a"}, - {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:fe1a06da377e3a1062ae5fe0926e12b84eceb8a50b350ddca72dc85015873f74"}, - {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:db9e724bebd621d9beca794f2a4ff1d26eed5965b004a97f1f1685a173b869c2"}, - {file = "frozenlist-1.4.1-cp311-cp311-win32.whl", hash = "sha256:e774d53b1a477a67838a904131c4b0eef6b3d8a651f8b138b04f748fccfefe17"}, - {file = "frozenlist-1.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:fb3c2db03683b5767dedb5769b8a40ebb47d6f7f45b1b3e3b4b51ec8ad9d9825"}, - {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1979bc0aeb89b33b588c51c54ab0161791149f2461ea7c7c946d95d5f93b56ae"}, - {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cc7b01b3754ea68a62bd77ce6020afaffb44a590c2289089289363472d13aedb"}, - {file = "frozenlist-1.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c9c92be9fd329ac801cc420e08452b70e7aeab94ea4233a4804f0915c14eba9b"}, - {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c3894db91f5a489fc8fa6a9991820f368f0b3cbdb9cd8849547ccfab3392d86"}, - {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba60bb19387e13597fb059f32cd4d59445d7b18b69a745b8f8e5db0346f33480"}, - {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8aefbba5f69d42246543407ed2461db31006b0f76c4e32dfd6f42215a2c41d09"}, - {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780d3a35680ced9ce682fbcf4cb9c2bad3136eeff760ab33707b71db84664e3a"}, - {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9acbb16f06fe7f52f441bb6f413ebae6c37baa6ef9edd49cdd567216da8600cd"}, - {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:23b701e65c7b36e4bf15546a89279bd4d8675faabc287d06bbcfac7d3c33e1e6"}, - {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3e0153a805a98f5ada7e09826255ba99fb4f7524bb81bf6b47fb702666484ae1"}, - {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:dd9b1baec094d91bf36ec729445f7769d0d0cf6b64d04d86e45baf89e2b9059b"}, - {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:1a4471094e146b6790f61b98616ab8e44f72661879cc63fa1049d13ef711e71e"}, - {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5667ed53d68d91920defdf4035d1cdaa3c3121dc0b113255124bcfada1cfa1b8"}, - {file = "frozenlist-1.4.1-cp312-cp312-win32.whl", hash = "sha256:beee944ae828747fd7cb216a70f120767fc9f4f00bacae8543c14a6831673f89"}, - {file = "frozenlist-1.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:64536573d0a2cb6e625cf309984e2d873979709f2cf22839bf2d61790b448ad5"}, - {file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bfa4a17e17ce9abf47a74ae02f32d014c5e9404b6d9ac7f729e01562bbee601e"}, - {file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b7e3ed87d4138356775346e6845cccbe66cd9e207f3cd11d2f0b9fd13681359d"}, - {file = "frozenlist-1.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c99169d4ff810155ca50b4da3b075cbde79752443117d89429595c2e8e37fed8"}, - {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edb678da49d9f72c9f6c609fbe41a5dfb9a9282f9e6a2253d5a91e0fc382d7c0"}, - {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6db4667b187a6742b33afbbaf05a7bc551ffcf1ced0000a571aedbb4aa42fc7b"}, - {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55fdc093b5a3cb41d420884cdaf37a1e74c3c37a31f46e66286d9145d2063bd0"}, - {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82e8211d69a4f4bc360ea22cd6555f8e61a1bd211d1d5d39d3d228b48c83a897"}, - {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89aa2c2eeb20957be2d950b85974b30a01a762f3308cd02bb15e1ad632e22dc7"}, - {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9d3e0c25a2350080e9319724dede4f31f43a6c9779be48021a7f4ebde8b2d742"}, - {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7268252af60904bf52c26173cbadc3a071cece75f873705419c8681f24d3edea"}, - {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:0c250a29735d4f15321007fb02865f0e6b6a41a6b88f1f523ca1596ab5f50bd5"}, - {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:96ec70beabbd3b10e8bfe52616a13561e58fe84c0101dd031dc78f250d5128b9"}, - {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:23b2d7679b73fe0e5a4560b672a39f98dfc6f60df63823b0a9970525325b95f6"}, - {file = "frozenlist-1.4.1-cp39-cp39-win32.whl", hash = "sha256:a7496bfe1da7fb1a4e1cc23bb67c58fab69311cc7d32b5a99c2007b4b2a0e932"}, - {file = "frozenlist-1.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:e6a20a581f9ce92d389a8c7d7c3dd47c81fd5d6e655c8dddf341e14aa48659d0"}, - {file = "frozenlist-1.4.1-py3-none-any.whl", hash = "sha256:04ced3e6a46b4cfffe20f9ae482818e34eba9b5fb0ce4056e4cc9b6e212d09b7"}, - {file = "frozenlist-1.4.1.tar.gz", hash = "sha256:c037a86e8513059a2613aaba4d817bb90b9d9b6b69aace3ce9c877e8c8ed402b"}, -] - -[[package]] -name = "fsspec" -version = "2024.6.1" -requires_python = ">=3.8" -summary = "File-system specification" -groups = ["default"] -files = [ - {file = "fsspec-2024.6.1-py3-none-any.whl", hash = "sha256:3cb443f8bcd2efb31295a5b9fdb02aee81d8452c80d28f97a6d0959e6cee101e"}, - {file = "fsspec-2024.6.1.tar.gz", hash = "sha256:fad7d7e209dd4c1208e3bbfda706620e0da5142bebbd9c384afb95b07e798e49"}, -] - -[[package]] -name = "fsspec" -version = "2024.6.1" -extras = ["http"] -requires_python = ">=3.8" -summary = "File-system specification" -groups = ["default"] -dependencies = [ - "aiohttp!=4.0.0a0,!=4.0.0a1", - "fsspec==2024.6.1", -] -files = [ - {file = "fsspec-2024.6.1-py3-none-any.whl", hash = "sha256:3cb443f8bcd2efb31295a5b9fdb02aee81d8452c80d28f97a6d0959e6cee101e"}, - {file = "fsspec-2024.6.1.tar.gz", hash = "sha256:fad7d7e209dd4c1208e3bbfda706620e0da5142bebbd9c384afb95b07e798e49"}, -] - -[[package]] -name = "ghp-import" -version = "2.1.0" -summary = "Copy your docs directly to the gh-pages branch." -groups = ["docs"] -dependencies = [ - "python-dateutil>=2.8.1", -] -files = [ - {file = "ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343"}, - {file = "ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619"}, -] - -[[package]] -name = "gitdb" -version = "4.0.11" -requires_python = ">=3.7" -summary = "Git Object Database" -groups = ["docs"] -dependencies = [ - "smmap<6,>=3.0.1", -] -files = [ - {file = "gitdb-4.0.11-py3-none-any.whl", hash = "sha256:81a3407ddd2ee8df444cbacea00e2d038e40150acfa3001696fe0dcf1d3adfa4"}, - {file = "gitdb-4.0.11.tar.gz", hash = "sha256:bf5421126136d6d0af55bc1e7c1af1c397a34f5b7bd79e776cd3e89785c2b04b"}, -] - -[[package]] -name = "gitpython" -version = "3.1.43" -requires_python = ">=3.7" -summary = "GitPython is a Python library used to interact with Git repositories" -groups = ["docs"] -dependencies = [ - "gitdb<5,>=4.0.1", - "typing-extensions>=3.7.4.3; python_version < \"3.8\"", -] -files = [ - {file = "GitPython-3.1.43-py3-none-any.whl", hash = "sha256:eec7ec56b92aad751f9912a73404bc02ba212a23adb2c7098ee668417051a1ff"}, - {file = "GitPython-3.1.43.tar.gz", hash = "sha256:35f314a9f878467f5453cc1fee295c3e18e52f1b99f10f6cf5b1682e968a9e7c"}, -] - -[[package]] -name = "griffe" -version = "1.3.1" -requires_python = ">=3.8" -summary = "Signatures for entire Python programs. Extract the structure, the frame, the skeleton of your project, to generate API documentation or find breaking changes in your API." -groups = ["docs"] -dependencies = [ - "astunparse>=1.6; python_version < \"3.9\"", - "colorama>=0.4", -] -files = [ - {file = "griffe-1.3.1-py3-none-any.whl", hash = "sha256:940aeb630bc3054b4369567f150b6365be6f11eef46b0ed8623aea96e6d17b19"}, - {file = "griffe-1.3.1.tar.gz", hash = "sha256:3f86a716b631a4c0f96a43cb75d05d3c85975003c20540426c0eba3b0581c56a"}, -] - -[[package]] -name = "h11" -version = "0.14.0" -requires_python = ">=3.7" -summary = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" -groups = ["default"] -dependencies = [ - "typing-extensions; python_version < \"3.8\"", -] -files = [ - {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, - {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, -] - -[[package]] -name = "httpcore" -version = "1.0.5" -requires_python = ">=3.8" -summary = "A minimal low-level HTTP client." -groups = ["default"] -dependencies = [ - "certifi", - "h11<0.15,>=0.13", -] -files = [ - {file = "httpcore-1.0.5-py3-none-any.whl", hash = "sha256:421f18bac248b25d310f3cacd198d55b8e6125c107797b609ff9b7a6ba7991b5"}, - {file = "httpcore-1.0.5.tar.gz", hash = "sha256:34a38e2f9291467ee3b44e89dd52615370e152954ba21721378a87b2960f7a61"}, -] - -[[package]] -name = "httpx" -version = "0.27.2" -requires_python = ">=3.8" -summary = "The next generation HTTP client." -groups = ["default"] -dependencies = [ - "anyio", - "certifi", - "httpcore==1.*", - "idna", - "sniffio", -] -files = [ - {file = "httpx-0.27.2-py3-none-any.whl", hash = "sha256:7bb2708e112d8fdd7829cd4243970f0c223274051cb35ee80c03301ee29a3df0"}, - {file = "httpx-0.27.2.tar.gz", hash = "sha256:f7c2be1d2f3c3c3160d441802406b206c2b76f5947b11115e6df10c6c65e66c2"}, -] - -[[package]] -name = "huggingface-hub" -version = "0.24.7" -requires_python = ">=3.8.0" -summary = "Client library to download and publish models, datasets and other repos on the huggingface.co hub" -groups = ["default"] -dependencies = [ - "filelock", - "fsspec>=2023.5.0", - "packaging>=20.9", - "pyyaml>=5.1", - "requests", - "tqdm>=4.42.1", - "typing-extensions>=3.7.4.3", -] -files = [ - {file = "huggingface_hub-0.24.7-py3-none-any.whl", hash = "sha256:a212c555324c8a7b1ffdd07266bb7e7d69ca71aa238d27b7842d65e9a26ac3e5"}, - {file = "huggingface_hub-0.24.7.tar.gz", hash = "sha256:0ad8fb756e2831da0ac0491175b960f341fe06ebcf80ed6f8728313f95fc0207"}, -] - -[[package]] -name = "idna" -version = "3.10" -requires_python = ">=3.6" -summary = "Internationalized Domain Names in Applications (IDNA)" -groups = ["default", "docs"] -files = [ - {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, - {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, -] - -[[package]] -name = "importlib-metadata" -version = "8.5.0" -requires_python = ">=3.8" -summary = "Read metadata from Python packages" -groups = ["docs"] -dependencies = [ - "typing-extensions>=3.6.4; python_version < \"3.8\"", - "zipp>=3.20", -] -files = [ - {file = "importlib_metadata-8.5.0-py3-none-any.whl", hash = "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b"}, - {file = "importlib_metadata-8.5.0.tar.gz", hash = "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7"}, -] - -[[package]] -name = "importlib-resources" -version = "6.4.5" -requires_python = ">=3.8" -summary = "Read resources from Python packages" -groups = ["docs"] -dependencies = [ - "zipp>=3.1.0; python_version < \"3.10\"", -] -files = [ - {file = "importlib_resources-6.4.5-py3-none-any.whl", hash = "sha256:ac29d5f956f01d5e4bb63102a5a19957f1b9175e45649977264a1416783bb717"}, - {file = "importlib_resources-6.4.5.tar.gz", hash = "sha256:980862a1d16c9e147a59603677fa2aa5fd82b87f223b6cb870695bcfce830065"}, -] - -[[package]] -name = "jinja2" -version = "3.1.4" -requires_python = ">=3.7" -summary = "A very fast and expressive template engine." -groups = ["default", "docs"] -dependencies = [ - "MarkupSafe>=2.0", -] -files = [ - {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"}, - {file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"}, -] - -[[package]] -name = "jsonschema" -version = "4.23.0" -requires_python = ">=3.8" -summary = "An implementation of JSON Schema validation for Python" -groups = ["docs"] -dependencies = [ - "attrs>=22.2.0", - "importlib-resources>=1.4.0; python_version < \"3.9\"", - "jsonschema-specifications>=2023.03.6", - "pkgutil-resolve-name>=1.3.10; python_version < \"3.9\"", - "referencing>=0.28.4", - "rpds-py>=0.7.1", -] -files = [ - {file = "jsonschema-4.23.0-py3-none-any.whl", hash = "sha256:fbadb6f8b144a8f8cf9f0b89ba94501d143e50411a1278633f56a7acf7fd5566"}, - {file = "jsonschema-4.23.0.tar.gz", hash = "sha256:d71497fef26351a33265337fa77ffeb82423f3ea21283cd9467bb03999266bc4"}, -] - -[[package]] -name = "jsonschema-specifications" -version = "2023.12.1" -requires_python = ">=3.8" -summary = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" -groups = ["docs"] -dependencies = [ - "importlib-resources>=1.4.0; python_version < \"3.9\"", - "referencing>=0.31.0", -] -files = [ - {file = "jsonschema_specifications-2023.12.1-py3-none-any.whl", hash = "sha256:87e4fdf3a94858b8a2ba2778d9ba57d8a9cafca7c7489c46ba0d30a8bc6a9c3c"}, - {file = "jsonschema_specifications-2023.12.1.tar.gz", hash = "sha256:48a76787b3e70f5ed53f1160d2b81f586e4ca6d1548c5de7085d1682674764cc"}, -] - -[[package]] -name = "jupyter-client" -version = "8.6.2" -requires_python = ">=3.8" -summary = "Jupyter protocol implementation and client libraries" -groups = ["docs"] -dependencies = [ - "importlib-metadata>=4.8.3; python_version < \"3.10\"", - "jupyter-core!=5.0.*,>=4.12", - "python-dateutil>=2.8.2", - "pyzmq>=23.0", - "tornado>=6.2", - "traitlets>=5.3", -] -files = [ - {file = "jupyter_client-8.6.2-py3-none-any.whl", hash = "sha256:50cbc5c66fd1b8f65ecb66bc490ab73217993632809b6e505687de18e9dea39f"}, - {file = "jupyter_client-8.6.2.tar.gz", hash = "sha256:2bda14d55ee5ba58552a8c53ae43d215ad9868853489213f37da060ced54d8df"}, -] - -[[package]] -name = "jupyter-core" -version = "5.7.2" -requires_python = ">=3.8" -summary = "Jupyter core package. A base package on which Jupyter projects rely." -groups = ["docs"] -dependencies = [ - "platformdirs>=2.5", - "pywin32>=300; sys_platform == \"win32\" and platform_python_implementation != \"PyPy\"", - "traitlets>=5.3", -] -files = [ - {file = "jupyter_core-5.7.2-py3-none-any.whl", hash = "sha256:4f7315d2f6b4bcf2e3e7cb6e46772eba760ae459cd1f59d29eb57b0a01bd7409"}, - {file = "jupyter_core-5.7.2.tar.gz", hash = "sha256:aa5f8d32bbf6b431ac830496da7392035d6f61b4f54872f15c4bd2a9c3f536d9"}, -] - -[[package]] -name = "jupyterlab-pygments" -version = "0.3.0" -requires_python = ">=3.8" -summary = "Pygments theme using JupyterLab CSS variables" -groups = ["docs"] -files = [ - {file = "jupyterlab_pygments-0.3.0-py3-none-any.whl", hash = "sha256:841a89020971da1d8693f1a99997aefc5dc424bb1b251fd6322462a1b8842780"}, - {file = "jupyterlab_pygments-0.3.0.tar.gz", hash = "sha256:721aca4d9029252b11cfa9d185e5b5af4d54772bb8072f9b7036f4170054d35d"}, -] - -[[package]] -name = "markdown" -version = "3.7" -requires_python = ">=3.8" -summary = "Python implementation of John Gruber's Markdown." -groups = ["docs"] -dependencies = [ - "importlib-metadata>=4.4; python_version < \"3.10\"", -] -files = [ - {file = "Markdown-3.7-py3-none-any.whl", hash = "sha256:7eb6df5690b81a1d7942992c97fad2938e956e79df20cbc6186e9c3a77b1c803"}, - {file = "markdown-3.7.tar.gz", hash = "sha256:2ae2471477cfd02dbbf038d5d9bc226d40def84b4fe2986e49b59b6b472bbed2"}, -] - -[[package]] -name = "markdown-it-py" -version = "3.0.0" -requires_python = ">=3.8" -summary = "Python port of markdown-it. Markdown parsing, done right!" -groups = ["default"] -dependencies = [ - "mdurl~=0.1", -] -files = [ - {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, - {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, -] - -[[package]] -name = "markupsafe" -version = "2.1.5" -requires_python = ">=3.7" -summary = "Safely add untrusted strings to HTML/XML markup." -groups = ["default", "docs"] -files = [ - {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, - {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, -] - -[[package]] -name = "material-plausible-plugin" -version = "0.2.0" -requires_python = ">=3.7" -summary = "Plausible Analytics implementation for Material for MkDocs" -groups = ["docs"] -dependencies = [ - "mkdocs", - "mkdocs-material", -] -files = [ - {file = "material_plausible_plugin-0.2.0-py3-none-any.whl", hash = "sha256:b7dc866b358475d940c5c61f56f86c400b9c1e73ffa2b06819207df38f34fcf4"}, - {file = "material_plausible_plugin-0.2.0.tar.gz", hash = "sha256:9416d6313ff9ddeec25e14e9c4baac1341511263706a5997bd714d552992d752"}, -] - -[[package]] -name = "mdurl" -version = "0.1.2" -requires_python = ">=3.7" -summary = "Markdown URL utilities" -groups = ["default"] -files = [ - {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, - {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, -] - -[[package]] -name = "mergedeep" -version = "1.3.4" -requires_python = ">=3.6" -summary = "A deep merge function for 🐍." -groups = ["docs"] -files = [ - {file = "mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307"}, - {file = "mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8"}, -] - -[[package]] -name = "mike" -version = "2.1.3" -summary = "Manage multiple versions of your MkDocs-powered documentation" -groups = ["docs"] -dependencies = [ - "importlib-metadata", - "importlib-resources", - "jinja2>=2.7", - "mkdocs>=1.0", - "pyparsing>=3.0", - "pyyaml-env-tag", - "pyyaml>=5.1", - "verspec", -] -files = [ - {file = "mike-2.1.3-py3-none-any.whl", hash = "sha256:d90c64077e84f06272437b464735130d380703a76a5738b152932884c60c062a"}, - {file = "mike-2.1.3.tar.gz", hash = "sha256:abd79b8ea483fb0275b7972825d3082e5ae67a41820f8d8a0dc7a3f49944e810"}, -] - -[[package]] -name = "mistune" -version = "3.0.2" -requires_python = ">=3.7" -summary = "A sane and fast Markdown parser with useful plugins and renderers" -groups = ["docs"] -files = [ - {file = "mistune-3.0.2-py3-none-any.whl", hash = "sha256:71481854c30fdbc938963d3605b72501f5c10a9320ecd412c121c163a1c7d205"}, - {file = "mistune-3.0.2.tar.gz", hash = "sha256:fc7f93ded930c92394ef2cb6f04a8aabab4117a91449e72dcc8dfa646a508be8"}, -] - -[[package]] -name = "mkdocs" -version = "1.6.1" -requires_python = ">=3.8" -summary = "Project documentation with Markdown." -groups = ["docs"] -dependencies = [ - "click>=7.0", - "colorama>=0.4; platform_system == \"Windows\"", - "ghp-import>=1.0", - "importlib-metadata>=4.4; python_version < \"3.10\"", - "jinja2>=2.11.1", - "markdown>=3.3.6", - "markupsafe>=2.0.1", - "mergedeep>=1.3.4", - "mkdocs-get-deps>=0.2.0", - "packaging>=20.5", - "pathspec>=0.11.1", - "pyyaml-env-tag>=0.1", - "pyyaml>=5.1", - "watchdog>=2.0", -] -files = [ - {file = "mkdocs-1.6.1-py3-none-any.whl", hash = "sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e"}, - {file = "mkdocs-1.6.1.tar.gz", hash = "sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2"}, -] - -[[package]] -name = "mkdocs-autorefs" -version = "1.2.0" -requires_python = ">=3.8" -summary = "Automatically link across pages in MkDocs." -groups = ["docs"] -dependencies = [ - "Markdown>=3.3", - "markupsafe>=2.0.1", - "mkdocs>=1.1", -] -files = [ - {file = "mkdocs_autorefs-1.2.0-py3-none-any.whl", hash = "sha256:d588754ae89bd0ced0c70c06f58566a4ee43471eeeee5202427da7de9ef85a2f"}, - {file = "mkdocs_autorefs-1.2.0.tar.gz", hash = "sha256:a86b93abff653521bda71cf3fc5596342b7a23982093915cb74273f67522190f"}, -] - -[[package]] -name = "mkdocs-gen-files" -version = "0.5.0" -requires_python = ">=3.7" -summary = "MkDocs plugin to programmatically generate documentation pages during the build" -groups = ["docs"] -dependencies = [ - "mkdocs>=1.0.3", -] -files = [ - {file = "mkdocs_gen_files-0.5.0-py3-none-any.whl", hash = "sha256:7ac060096f3f40bd19039e7277dd3050be9a453c8ac578645844d4d91d7978ea"}, - {file = "mkdocs_gen_files-0.5.0.tar.gz", hash = "sha256:4c7cf256b5d67062a788f6b1d035e157fc1a9498c2399be9af5257d4ff4d19bc"}, -] - -[[package]] -name = "mkdocs-get-deps" -version = "0.2.0" -requires_python = ">=3.8" -summary = "MkDocs extension that lists all dependencies according to a mkdocs.yml file" -groups = ["docs"] -dependencies = [ - "importlib-metadata>=4.3; python_version < \"3.10\"", - "mergedeep>=1.3.4", - "platformdirs>=2.2.0", - "pyyaml>=5.1", -] -files = [ - {file = "mkdocs_get_deps-0.2.0-py3-none-any.whl", hash = "sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134"}, - {file = "mkdocs_get_deps-0.2.0.tar.gz", hash = "sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c"}, -] - -[[package]] -name = "mkdocs-literate-nav" -version = "0.6.1" -requires_python = ">=3.7" -summary = "MkDocs plugin to specify the navigation in Markdown instead of YAML" -groups = ["docs"] -dependencies = [ - "mkdocs>=1.0.3", -] -files = [ - {file = "mkdocs_literate_nav-0.6.1-py3-none-any.whl", hash = "sha256:e70bdc4a07050d32da79c0b697bd88e9a104cf3294282e9cb20eec94c6b0f401"}, - {file = "mkdocs_literate_nav-0.6.1.tar.gz", hash = "sha256:78a7ab6d878371728acb0cdc6235c9b0ffc6e83c997b037f4a5c6ff7cef7d759"}, -] - -[[package]] -name = "mkdocs-material" -version = "9.5.34" -requires_python = ">=3.8" -summary = "Documentation that simply works" -groups = ["docs"] -dependencies = [ - "babel~=2.10", - "colorama~=0.4", - "jinja2~=3.0", - "markdown~=3.2", - "mkdocs-material-extensions~=1.3", - "mkdocs~=1.6", - "paginate~=0.5", - "pygments~=2.16", - "pymdown-extensions~=10.2", - "regex>=2022.4", - "requests~=2.26", -] -files = [ - {file = "mkdocs_material-9.5.34-py3-none-any.whl", hash = "sha256:54caa8be708de2b75167fd4d3b9f3d949579294f49cb242515d4653dbee9227e"}, - {file = "mkdocs_material-9.5.34.tar.gz", hash = "sha256:1e60ddf716cfb5679dfd65900b8a25d277064ed82d9a53cd5190e3f894df7840"}, -] - -[[package]] -name = "mkdocs-material-extensions" -version = "1.3.1" -requires_python = ">=3.8" -summary = "Extension pack for Python Markdown and MkDocs Material." -groups = ["docs"] -files = [ - {file = "mkdocs_material_extensions-1.3.1-py3-none-any.whl", hash = "sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31"}, - {file = "mkdocs_material_extensions-1.3.1.tar.gz", hash = "sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443"}, -] - -[[package]] -name = "mkdocs-section-index" -version = "0.3.9" -requires_python = ">=3.8" -summary = "MkDocs plugin to allow clickable sections that lead to an index page" -groups = ["docs"] -dependencies = [ - "mkdocs>=1.2", -] -files = [ - {file = "mkdocs_section_index-0.3.9-py3-none-any.whl", hash = "sha256:5e5eb288e8d7984d36c11ead5533f376fdf23498f44e903929d72845b24dfe34"}, - {file = "mkdocs_section_index-0.3.9.tar.gz", hash = "sha256:b66128d19108beceb08b226ee1ba0981840d14baf8a652b6c59e650f3f92e4f8"}, -] - -[[package]] -name = "mkdocstrings" -version = "0.26.1" -requires_python = ">=3.8" -summary = "Automatic documentation from sources, for MkDocs." -groups = ["docs"] -dependencies = [ - "Jinja2>=2.11.1", - "Markdown>=3.6", - "MarkupSafe>=1.1", - "click>=7.0", - "importlib-metadata>=4.6; python_version < \"3.10\"", - "mkdocs-autorefs>=1.2", - "mkdocs>=1.4", - "platformdirs>=2.2", - "pymdown-extensions>=6.3", - "typing-extensions>=4.1; python_version < \"3.10\"", -] -files = [ - {file = "mkdocstrings-0.26.1-py3-none-any.whl", hash = "sha256:29738bfb72b4608e8e55cc50fb8a54f325dc7ebd2014e4e3881a49892d5983cf"}, - {file = "mkdocstrings-0.26.1.tar.gz", hash = "sha256:bb8b8854d6713d5348ad05b069a09f3b79edbc6a0f33a34c6821141adb03fe33"}, -] - -[[package]] -name = "mkdocstrings-python" -version = "1.11.1" -requires_python = ">=3.8" -summary = "A Python handler for mkdocstrings." -groups = ["docs"] -dependencies = [ - "griffe>=0.49", - "mkdocs-autorefs>=1.2", - "mkdocstrings>=0.26", -] -files = [ - {file = "mkdocstrings_python-1.11.1-py3-none-any.whl", hash = "sha256:a21a1c05acef129a618517bb5aae3e33114f569b11588b1e7af3e9d4061a71af"}, - {file = "mkdocstrings_python-1.11.1.tar.gz", hash = "sha256:8824b115c5359304ab0b5378a91f6202324a849e1da907a3485b59208b797322"}, -] - -[[package]] -name = "mkdocstrings" -version = "0.26.1" -extras = ["python"] -requires_python = ">=3.8" -summary = "Automatic documentation from sources, for MkDocs." -groups = ["docs"] -dependencies = [ - "mkdocstrings-python>=0.5.2", - "mkdocstrings==0.26.1", -] -files = [ - {file = "mkdocstrings-0.26.1-py3-none-any.whl", hash = "sha256:29738bfb72b4608e8e55cc50fb8a54f325dc7ebd2014e4e3881a49892d5983cf"}, - {file = "mkdocstrings-0.26.1.tar.gz", hash = "sha256:bb8b8854d6713d5348ad05b069a09f3b79edbc6a0f33a34c6821141adb03fe33"}, -] - -[[package]] -name = "mknotebooks" -version = "0.8.0" -summary = "Plugin for mkdocs to generate markdown documents from jupyter notebooks." -groups = ["docs"] -dependencies = [ - "gitpython", - "jupyter-client", - "markdown>=3.3.3", - "mkdocs>=1.5.0", - "nbconvert>=6.0.0", -] -files = [ - {file = "mknotebooks-0.8.0-py3-none-any.whl", hash = "sha256:4a9b998260c09bcc311455a19a44cc395a30ee82dc1e86e3316dd09f2445ebd3"}, -] - -[[package]] -name = "multidict" -version = "6.1.0" -requires_python = ">=3.8" -summary = "multidict implementation" -groups = ["default"] -dependencies = [ - "typing-extensions>=4.1.0; python_version < \"3.11\"", -] -files = [ - {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3380252550e372e8511d49481bd836264c009adb826b23fefcc5dd3c69692f60"}, - {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:99f826cbf970077383d7de805c0681799491cb939c25450b9b5b3ced03ca99f1"}, - {file = "multidict-6.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a114d03b938376557927ab23f1e950827c3b893ccb94b62fd95d430fd0e5cf53"}, - {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1c416351ee6271b2f49b56ad7f308072f6f44b37118d69c2cad94f3fa8a40d5"}, - {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6b5d83030255983181005e6cfbac1617ce9746b219bc2aad52201ad121226581"}, - {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3e97b5e938051226dc025ec80980c285b053ffb1e25a3db2a3aa3bc046bf7f56"}, - {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d618649d4e70ac6efcbba75be98b26ef5078faad23592f9b51ca492953012429"}, - {file = "multidict-6.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10524ebd769727ac77ef2278390fb0068d83f3acb7773792a5080f2b0abf7748"}, - {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ff3827aef427c89a25cc96ded1759271a93603aba9fb977a6d264648ebf989db"}, - {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:06809f4f0f7ab7ea2cabf9caca7d79c22c0758b58a71f9d32943ae13c7ace056"}, - {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f179dee3b863ab1c59580ff60f9d99f632f34ccb38bf67a33ec6b3ecadd0fd76"}, - {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:aaed8b0562be4a0876ee3b6946f6869b7bcdb571a5d1496683505944e268b160"}, - {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3c8b88a2ccf5493b6c8da9076fb151ba106960a2df90c2633f342f120751a9e7"}, - {file = "multidict-6.1.0-cp310-cp310-win32.whl", hash = "sha256:4a9cb68166a34117d6646c0023c7b759bf197bee5ad4272f420a0141d7eb03a0"}, - {file = "multidict-6.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:20b9b5fbe0b88d0bdef2012ef7dee867f874b72528cf1d08f1d59b0e3850129d"}, - {file = "multidict-6.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3efe2c2cb5763f2f1b275ad2bf7a287d3f7ebbef35648a9726e3b69284a4f3d6"}, - {file = "multidict-6.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c7053d3b0353a8b9de430a4f4b4268ac9a4fb3481af37dfe49825bf45ca24156"}, - {file = "multidict-6.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:27e5fc84ccef8dfaabb09d82b7d179c7cf1a3fbc8a966f8274fcb4ab2eb4cadb"}, - {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e2b90b43e696f25c62656389d32236e049568b39320e2735d51f08fd362761b"}, - {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d83a047959d38a7ff552ff94be767b7fd79b831ad1cd9920662db05fec24fe72"}, - {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d1a9dd711d0877a1ece3d2e4fea11a8e75741ca21954c919406b44e7cf971304"}, - {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec2abea24d98246b94913b76a125e855eb5c434f7c46546046372fe60f666351"}, - {file = "multidict-6.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4867cafcbc6585e4b678876c489b9273b13e9fff9f6d6d66add5e15d11d926cb"}, - {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5b48204e8d955c47c55b72779802b219a39acc3ee3d0116d5080c388970b76e3"}, - {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:d8fff389528cad1618fb4b26b95550327495462cd745d879a8c7c2115248e399"}, - {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a7a9541cd308eed5e30318430a9c74d2132e9a8cb46b901326272d780bf2d423"}, - {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:da1758c76f50c39a2efd5e9859ce7d776317eb1dd34317c8152ac9251fc574a3"}, - {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c943a53e9186688b45b323602298ab727d8865d8c9ee0b17f8d62d14b56f0753"}, - {file = "multidict-6.1.0-cp311-cp311-win32.whl", hash = "sha256:90f8717cb649eea3504091e640a1b8568faad18bd4b9fcd692853a04475a4b80"}, - {file = "multidict-6.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:82176036e65644a6cc5bd619f65f6f19781e8ec2e5330f51aa9ada7504cc1926"}, - {file = "multidict-6.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b04772ed465fa3cc947db808fa306d79b43e896beb677a56fb2347ca1a49c1fa"}, - {file = "multidict-6.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6180c0ae073bddeb5a97a38c03f30c233e0a4d39cd86166251617d1bbd0af436"}, - {file = "multidict-6.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:071120490b47aa997cca00666923a83f02c7fbb44f71cf7f136df753f7fa8761"}, - {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50b3a2710631848991d0bf7de077502e8994c804bb805aeb2925a981de58ec2e"}, - {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b58c621844d55e71c1b7f7c498ce5aa6985d743a1a59034c57a905b3f153c1ef"}, - {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55b6d90641869892caa9ca42ff913f7ff1c5ece06474fbd32fb2cf6834726c95"}, - {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b820514bfc0b98a30e3d85462084779900347e4d49267f747ff54060cc33925"}, - {file = "multidict-6.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10a9b09aba0c5b48c53761b7c720aaaf7cf236d5fe394cd399c7ba662d5f9966"}, - {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e16bf3e5fc9f44632affb159d30a437bfe286ce9e02754759be5536b169b305"}, - {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:76f364861c3bfc98cbbcbd402d83454ed9e01a5224bb3a28bf70002a230f73e2"}, - {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:820c661588bd01a0aa62a1283f20d2be4281b086f80dad9e955e690c75fb54a2"}, - {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:0e5f362e895bc5b9e67fe6e4ded2492d8124bdf817827f33c5b46c2fe3ffaca6"}, - {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3ec660d19bbc671e3a6443325f07263be452c453ac9e512f5eb935e7d4ac28b3"}, - {file = "multidict-6.1.0-cp312-cp312-win32.whl", hash = "sha256:58130ecf8f7b8112cdb841486404f1282b9c86ccb30d3519faf301b2e5659133"}, - {file = "multidict-6.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:188215fc0aafb8e03341995e7c4797860181562380f81ed0a87ff455b70bf1f1"}, - {file = "multidict-6.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d569388c381b24671589335a3be6e1d45546c2988c2ebe30fdcada8457a31008"}, - {file = "multidict-6.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:052e10d2d37810b99cc170b785945421141bf7bb7d2f8799d431e7db229c385f"}, - {file = "multidict-6.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f90c822a402cb865e396a504f9fc8173ef34212a342d92e362ca498cad308e28"}, - {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b225d95519a5bf73860323e633a664b0d85ad3d5bede6d30d95b35d4dfe8805b"}, - {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:23bfd518810af7de1116313ebd9092cb9aa629beb12f6ed631ad53356ed6b86c"}, - {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c09fcfdccdd0b57867577b719c69e347a436b86cd83747f179dbf0cc0d4c1f3"}, - {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf6bea52ec97e95560af5ae576bdac3aa3aae0b6758c6efa115236d9e07dae44"}, - {file = "multidict-6.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57feec87371dbb3520da6192213c7d6fc892d5589a93db548331954de8248fd2"}, - {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0c3f390dc53279cbc8ba976e5f8035eab997829066756d811616b652b00a23a3"}, - {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:59bfeae4b25ec05b34f1956eaa1cb38032282cd4dfabc5056d0a1ec4d696d3aa"}, - {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b2f59caeaf7632cc633b5cf6fc449372b83bbdf0da4ae04d5be36118e46cc0aa"}, - {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:37bb93b2178e02b7b618893990941900fd25b6b9ac0fa49931a40aecdf083fe4"}, - {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4e9f48f58c2c523d5a06faea47866cd35b32655c46b443f163d08c6d0ddb17d6"}, - {file = "multidict-6.1.0-cp313-cp313-win32.whl", hash = "sha256:3a37ffb35399029b45c6cc33640a92bef403c9fd388acce75cdc88f58bd19a81"}, - {file = "multidict-6.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:e9aa71e15d9d9beaad2c6b9319edcdc0a49a43ef5c0a4c8265ca9ee7d6c67774"}, - {file = "multidict-6.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:4e18b656c5e844539d506a0a06432274d7bd52a7487e6828c63a63d69185626c"}, - {file = "multidict-6.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a185f876e69897a6f3325c3f19f26a297fa058c5e456bfcff8015e9a27e83ae1"}, - {file = "multidict-6.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ab7c4ceb38d91570a650dba194e1ca87c2b543488fe9309b4212694174fd539c"}, - {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e617fb6b0b6953fffd762669610c1c4ffd05632c138d61ac7e14ad187870669c"}, - {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:16e5f4bf4e603eb1fdd5d8180f1a25f30056f22e55ce51fb3d6ad4ab29f7d96f"}, - {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4c035da3f544b1882bac24115f3e2e8760f10a0107614fc9839fd232200b875"}, - {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:957cf8e4b6e123a9eea554fa7ebc85674674b713551de587eb318a2df3e00255"}, - {file = "multidict-6.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:483a6aea59cb89904e1ceabd2b47368b5600fb7de78a6e4a2c2987b2d256cf30"}, - {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:87701f25a2352e5bf7454caa64757642734da9f6b11384c1f9d1a8e699758057"}, - {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:682b987361e5fd7a139ed565e30d81fd81e9629acc7d925a205366877d8c8657"}, - {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ce2186a7df133a9c895dea3331ddc5ddad42cdd0d1ea2f0a51e5d161e4762f28"}, - {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:9f636b730f7e8cb19feb87094949ba54ee5357440b9658b2a32a5ce4bce53972"}, - {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:73eae06aa53af2ea5270cc066dcaf02cc60d2994bbb2c4ef5764949257d10f43"}, - {file = "multidict-6.1.0-cp39-cp39-win32.whl", hash = "sha256:1ca0083e80e791cffc6efce7660ad24af66c8d4079d2a750b29001b53ff59ada"}, - {file = "multidict-6.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:aa466da5b15ccea564bdab9c89175c762bc12825f4659c11227f515cee76fa4a"}, - {file = "multidict-6.1.0-py3-none-any.whl", hash = "sha256:48e171e52d1c4d33888e529b999e5900356b9ae588c2f09a52dcefb158b27506"}, - {file = "multidict-6.1.0.tar.gz", hash = "sha256:22ae2ebf9b0c69d206c003e2f6a914ea33f0a932d4aa16f236afc049d9958f4a"}, -] - -[[package]] -name = "multiprocess" -version = "0.70.16" -requires_python = ">=3.8" -summary = "better multiprocessing and multithreading in Python" -groups = ["default"] -dependencies = [ - "dill>=0.3.8", -] -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"}, - {file = "multiprocess-0.70.16-pp39-pypy39_pp73-macosx_10_13_x86_64.whl", hash = "sha256:0dfd078c306e08d46d7a8d06fb120313d87aa43af60d66da43ffff40b44d2f41"}, - {file = "multiprocess-0.70.16-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e7b9d0f307cd9bd50851afaac0dba2cb6c44449efff697df7c7645f7d3f2be3a"}, - {file = "multiprocess-0.70.16-py310-none-any.whl", hash = "sha256:c4a9944c67bd49f823687463660a2d6daae94c289adff97e0f9d696ba6371d02"}, - {file = "multiprocess-0.70.16-py311-none-any.whl", hash = "sha256:af4cabb0dac72abfb1e794fa7855c325fd2b55a10a44628a3c1ad3311c04127a"}, - {file = "multiprocess-0.70.16-py312-none-any.whl", hash = "sha256:fc0544c531920dde3b00c29863377f87e1632601092ea2daca74e4beb40faa2e"}, - {file = "multiprocess-0.70.16-py39-none-any.whl", hash = "sha256:a0bafd3ae1b732eac64be2e72038231c1ba97724b60b09400d68f229fcc2fbf3"}, - {file = "multiprocess-0.70.16.tar.gz", hash = "sha256:161af703d4652a0e1410be6abccecde4a7ddffd19341be0a7011b94aeb171ac1"}, -] - -[[package]] -name = "nbclient" -version = "0.10.0" -requires_python = ">=3.8.0" -summary = "A client library for executing notebooks. Formerly nbconvert's ExecutePreprocessor." -groups = ["docs"] -dependencies = [ - "jupyter-client>=6.1.12", - "jupyter-core!=5.0.*,>=4.12", - "nbformat>=5.1", - "traitlets>=5.4", -] -files = [ - {file = "nbclient-0.10.0-py3-none-any.whl", hash = "sha256:f13e3529332a1f1f81d82a53210322476a168bb7090a0289c795fe9cc11c9d3f"}, - {file = "nbclient-0.10.0.tar.gz", hash = "sha256:4b3f1b7dba531e498449c4db4f53da339c91d449dc11e9af3a43b4eb5c5abb09"}, -] - -[[package]] -name = "nbconvert" -version = "7.16.4" -requires_python = ">=3.8" -summary = "Converting Jupyter Notebooks (.ipynb files) to other formats. Output formats include asciidoc, html, latex, markdown, pdf, py, rst, script. nbconvert can be used both as a Python library (`import nbconvert`) or as a command line tool (invoked as `jupyter nbconvert ...`)." -groups = ["docs"] -dependencies = [ - "beautifulsoup4", - "bleach!=5.0.0", - "defusedxml", - "importlib-metadata>=3.6; python_version < \"3.10\"", - "jinja2>=3.0", - "jupyter-core>=4.7", - "jupyterlab-pygments", - "markupsafe>=2.0", - "mistune<4,>=2.0.3", - "nbclient>=0.5.0", - "nbformat>=5.7", - "packaging", - "pandocfilters>=1.4.1", - "pygments>=2.4.1", - "tinycss2", - "traitlets>=5.1", -] -files = [ - {file = "nbconvert-7.16.4-py3-none-any.whl", hash = "sha256:05873c620fe520b6322bf8a5ad562692343fe3452abda5765c7a34b7d1aa3eb3"}, - {file = "nbconvert-7.16.4.tar.gz", hash = "sha256:86ca91ba266b0a448dc96fa6c5b9d98affabde2867b363258703536807f9f7f4"}, -] - -[[package]] -name = "nbformat" -version = "5.10.4" -requires_python = ">=3.8" -summary = "The Jupyter Notebook format" -groups = ["docs"] -dependencies = [ - "fastjsonschema>=2.15", - "jsonschema>=2.6", - "jupyter-core!=5.0.*,>=4.12", - "traitlets>=5.1", -] -files = [ - {file = "nbformat-5.10.4-py3-none-any.whl", hash = "sha256:3b48d6c8fbca4b299bf3982ea7db1af21580e4fec269ad087b9e81588891200b"}, - {file = "nbformat-5.10.4.tar.gz", hash = "sha256:322168b14f937a5d11362988ecac2a4952d3d8e3a2cbeb2319584631226d5b3a"}, -] - -[[package]] -name = "nest-asyncio" -version = "1.6.0" -requires_python = ">=3.5" -summary = "Patch asyncio to allow nested event loops" -groups = ["default"] -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"}, -] - -[[package]] -name = "networkx" -version = "3.2.1" -requires_python = ">=3.9" -summary = "Python package for creating and manipulating graphs and networks" -groups = ["default"] -files = [ - {file = "networkx-3.2.1-py3-none-any.whl", hash = "sha256:f18c69adc97877c42332c170849c96cefa91881c99a7cb3e95b7c659ebdc1ec2"}, - {file = "networkx-3.2.1.tar.gz", hash = "sha256:9f1bb5cf3409bf324e0a722c20bdb4c20ee39bf1c30ce8ae499c8502b0b5e0c6"}, -] - -[[package]] -name = "numpy" -version = "2.0.2" -requires_python = ">=3.9" -summary = "Fundamental package for array computing in Python" -groups = ["default", "docs"] -files = [ - {file = "numpy-2.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:51129a29dbe56f9ca83438b706e2e69a39892b5eda6cedcb6b0c9fdc9b0d3ece"}, - {file = "numpy-2.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f15975dfec0cf2239224d80e32c3170b1d168335eaedee69da84fbe9f1f9cd04"}, - {file = "numpy-2.0.2-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:8c5713284ce4e282544c68d1c3b2c7161d38c256d2eefc93c1d683cf47683e66"}, - {file = "numpy-2.0.2-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:becfae3ddd30736fe1889a37f1f580e245ba79a5855bff5f2a29cb3ccc22dd7b"}, - {file = "numpy-2.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2da5960c3cf0df7eafefd806d4e612c5e19358de82cb3c343631188991566ccd"}, - {file = "numpy-2.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:496f71341824ed9f3d2fd36cf3ac57ae2e0165c143b55c3a035ee219413f3318"}, - {file = "numpy-2.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a61ec659f68ae254e4d237816e33171497e978140353c0c2038d46e63282d0c8"}, - {file = "numpy-2.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d731a1c6116ba289c1e9ee714b08a8ff882944d4ad631fd411106a30f083c326"}, - {file = "numpy-2.0.2-cp310-cp310-win32.whl", hash = "sha256:984d96121c9f9616cd33fbd0618b7f08e0cfc9600a7ee1d6fd9b239186d19d97"}, - {file = "numpy-2.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:c7b0be4ef08607dd04da4092faee0b86607f111d5ae68036f16cc787e250a131"}, - {file = "numpy-2.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:49ca4decb342d66018b01932139c0961a8f9ddc7589611158cb3c27cbcf76448"}, - {file = "numpy-2.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:11a76c372d1d37437857280aa142086476136a8c0f373b2e648ab2c8f18fb195"}, - {file = "numpy-2.0.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:807ec44583fd708a21d4a11d94aedf2f4f3c3719035c76a2bbe1fe8e217bdc57"}, - {file = "numpy-2.0.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:8cafab480740e22f8d833acefed5cc87ce276f4ece12fdaa2e8903db2f82897a"}, - {file = "numpy-2.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a15f476a45e6e5a3a79d8a14e62161d27ad897381fecfa4a09ed5322f2085669"}, - {file = "numpy-2.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13e689d772146140a252c3a28501da66dfecd77490b498b168b501835041f951"}, - {file = "numpy-2.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9ea91dfb7c3d1c56a0e55657c0afb38cf1eeae4544c208dc465c3c9f3a7c09f9"}, - {file = "numpy-2.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c1c9307701fec8f3f7a1e6711f9089c06e6284b3afbbcd259f7791282d660a15"}, - {file = "numpy-2.0.2-cp311-cp311-win32.whl", hash = "sha256:a392a68bd329eafac5817e5aefeb39038c48b671afd242710b451e76090e81f4"}, - {file = "numpy-2.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:286cd40ce2b7d652a6f22efdfc6d1edf879440e53e76a75955bc0c826c7e64dc"}, - {file = "numpy-2.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:df55d490dea7934f330006d0f81e8551ba6010a5bf035a249ef61a94f21c500b"}, - {file = "numpy-2.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8df823f570d9adf0978347d1f926b2a867d5608f434a7cff7f7908c6570dcf5e"}, - {file = "numpy-2.0.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:9a92ae5c14811e390f3767053ff54eaee3bf84576d99a2456391401323f4ec2c"}, - {file = "numpy-2.0.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:a842d573724391493a97a62ebbb8e731f8a5dcc5d285dfc99141ca15a3302d0c"}, - {file = "numpy-2.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c05e238064fc0610c840d1cf6a13bf63d7e391717d247f1bf0318172e759e692"}, - {file = "numpy-2.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0123ffdaa88fa4ab64835dcbde75dcdf89c453c922f18dced6e27c90d1d0ec5a"}, - {file = "numpy-2.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:96a55f64139912d61de9137f11bf39a55ec8faec288c75a54f93dfd39f7eb40c"}, - {file = "numpy-2.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ec9852fb39354b5a45a80bdab5ac02dd02b15f44b3804e9f00c556bf24b4bded"}, - {file = "numpy-2.0.2-cp312-cp312-win32.whl", hash = "sha256:671bec6496f83202ed2d3c8fdc486a8fc86942f2e69ff0e986140339a63bcbe5"}, - {file = "numpy-2.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:cfd41e13fdc257aa5778496b8caa5e856dc4896d4ccf01841daee1d96465467a"}, - {file = "numpy-2.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9059e10581ce4093f735ed23f3b9d283b9d517ff46009ddd485f1747eb22653c"}, - {file = "numpy-2.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:423e89b23490805d2a5a96fe40ec507407b8ee786d66f7328be214f9679df6dd"}, - {file = "numpy-2.0.2-cp39-cp39-macosx_14_0_arm64.whl", hash = "sha256:2b2955fa6f11907cf7a70dab0d0755159bca87755e831e47932367fc8f2f2d0b"}, - {file = "numpy-2.0.2-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:97032a27bd9d8988b9a97a8c4d2c9f2c15a81f61e2f21404d7e8ef00cb5be729"}, - {file = "numpy-2.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e795a8be3ddbac43274f18588329c72939870a16cae810c2b73461c40718ab1"}, - {file = "numpy-2.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f26b258c385842546006213344c50655ff1555a9338e2e5e02a0756dc3e803dd"}, - {file = "numpy-2.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5fec9451a7789926bcf7c2b8d187292c9f93ea30284802a0ab3f5be8ab36865d"}, - {file = "numpy-2.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9189427407d88ff25ecf8f12469d4d39d35bee1db5d39fc5c168c6f088a6956d"}, - {file = "numpy-2.0.2-cp39-cp39-win32.whl", hash = "sha256:905d16e0c60200656500c95b6b8dca5d109e23cb24abc701d41c02d74c6b3afa"}, - {file = "numpy-2.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:a3f4ab0caa7f053f6797fcd4e1e25caee367db3112ef2b6ef82d749530768c73"}, - {file = "numpy-2.0.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7f0a0c6f12e07fa94133c8a67404322845220c06a9e80e85999afe727f7438b8"}, - {file = "numpy-2.0.2-pp39-pypy39_pp73-macosx_14_0_x86_64.whl", hash = "sha256:312950fdd060354350ed123c0e25a71327d3711584beaef30cdaa93320c392d4"}, - {file = "numpy-2.0.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26df23238872200f63518dd2aa984cfca675d82469535dc7162dc2ee52d9dd5c"}, - {file = "numpy-2.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a46288ec55ebbd58947d31d72be2c63cbf839f0a63b49cb755022310792a3385"}, - {file = "numpy-2.0.2.tar.gz", hash = "sha256:883c987dee1880e2a864ab0dc9892292582510604156762362d9326444636e78"}, -] - -[[package]] -name = "orjson" -version = "3.10.7" -requires_python = ">=3.8" -summary = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" -groups = ["default"] -files = [ - {file = "orjson-3.10.7-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:74f4544f5a6405b90da8ea724d15ac9c36da4d72a738c64685003337401f5c12"}, - {file = "orjson-3.10.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34a566f22c28222b08875b18b0dfbf8a947e69df21a9ed5c51a6bf91cfb944ac"}, - {file = "orjson-3.10.7-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bf6ba8ebc8ef5792e2337fb0419f8009729335bb400ece005606336b7fd7bab7"}, - {file = "orjson-3.10.7-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac7cf6222b29fbda9e3a472b41e6a5538b48f2c8f99261eecd60aafbdb60690c"}, - {file = "orjson-3.10.7-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:de817e2f5fc75a9e7dd350c4b0f54617b280e26d1631811a43e7e968fa71e3e9"}, - {file = "orjson-3.10.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:348bdd16b32556cf8d7257b17cf2bdb7ab7976af4af41ebe79f9796c218f7e91"}, - {file = "orjson-3.10.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:479fd0844ddc3ca77e0fd99644c7fe2de8e8be1efcd57705b5c92e5186e8a250"}, - {file = "orjson-3.10.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:fdf5197a21dd660cf19dfd2a3ce79574588f8f5e2dbf21bda9ee2d2b46924d84"}, - {file = "orjson-3.10.7-cp310-none-win32.whl", hash = "sha256:d374d36726746c81a49f3ff8daa2898dccab6596864ebe43d50733275c629175"}, - {file = "orjson-3.10.7-cp310-none-win_amd64.whl", hash = "sha256:cb61938aec8b0ffb6eef484d480188a1777e67b05d58e41b435c74b9d84e0b9c"}, - {file = "orjson-3.10.7-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:7db8539039698ddfb9a524b4dd19508256107568cdad24f3682d5773e60504a2"}, - {file = "orjson-3.10.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:480f455222cb7a1dea35c57a67578848537d2602b46c464472c995297117fa09"}, - {file = "orjson-3.10.7-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8a9c9b168b3a19e37fe2778c0003359f07822c90fdff8f98d9d2a91b3144d8e0"}, - {file = "orjson-3.10.7-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8de062de550f63185e4c1c54151bdddfc5625e37daf0aa1e75d2a1293e3b7d9a"}, - {file = "orjson-3.10.7-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6b0dd04483499d1de9c8f6203f8975caf17a6000b9c0c54630cef02e44ee624e"}, - {file = "orjson-3.10.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b58d3795dafa334fc8fd46f7c5dc013e6ad06fd5b9a4cc98cb1456e7d3558bd6"}, - {file = "orjson-3.10.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:33cfb96c24034a878d83d1a9415799a73dc77480e6c40417e5dda0710d559ee6"}, - {file = "orjson-3.10.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e724cebe1fadc2b23c6f7415bad5ee6239e00a69f30ee423f319c6af70e2a5c0"}, - {file = "orjson-3.10.7-cp311-none-win32.whl", hash = "sha256:82763b46053727a7168d29c772ed5c870fdae2f61aa8a25994c7984a19b1021f"}, - {file = "orjson-3.10.7-cp311-none-win_amd64.whl", hash = "sha256:eb8d384a24778abf29afb8e41d68fdd9a156cf6e5390c04cc07bbc24b89e98b5"}, - {file = "orjson-3.10.7-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:44a96f2d4c3af51bfac6bc4ef7b182aa33f2f054fd7f34cc0ee9a320d051d41f"}, - {file = "orjson-3.10.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76ac14cd57df0572453543f8f2575e2d01ae9e790c21f57627803f5e79b0d3c3"}, - {file = "orjson-3.10.7-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bdbb61dcc365dd9be94e8f7df91975edc9364d6a78c8f7adb69c1cdff318ec93"}, - {file = "orjson-3.10.7-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b48b3db6bb6e0a08fa8c83b47bc169623f801e5cc4f24442ab2b6617da3b5313"}, - {file = "orjson-3.10.7-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:23820a1563a1d386414fef15c249040042b8e5d07b40ab3fe3efbfbbcbcb8864"}, - {file = "orjson-3.10.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0c6a008e91d10a2564edbb6ee5069a9e66df3fbe11c9a005cb411f441fd2c09"}, - {file = "orjson-3.10.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d352ee8ac1926d6193f602cbe36b1643bbd1bbcb25e3c1a657a4390f3000c9a5"}, - {file = "orjson-3.10.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d2d9f990623f15c0ae7ac608103c33dfe1486d2ed974ac3f40b693bad1a22a7b"}, - {file = "orjson-3.10.7-cp312-none-win32.whl", hash = "sha256:7c4c17f8157bd520cdb7195f75ddbd31671997cbe10aee559c2d613592e7d7eb"}, - {file = "orjson-3.10.7-cp312-none-win_amd64.whl", hash = "sha256:1d9c0e733e02ada3ed6098a10a8ee0052dd55774de3d9110d29868d24b17faa1"}, - {file = "orjson-3.10.7-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:77d325ed866876c0fa6492598ec01fe30e803272a6e8b10e992288b009cbe149"}, - {file = "orjson-3.10.7-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ea2c232deedcb605e853ae1db2cc94f7390ac776743b699b50b071b02bea6fe"}, - {file = "orjson-3.10.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3dcfbede6737fdbef3ce9c37af3fb6142e8e1ebc10336daa05872bfb1d87839c"}, - {file = "orjson-3.10.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:11748c135f281203f4ee695b7f80bb1358a82a63905f9f0b794769483ea854ad"}, - {file = "orjson-3.10.7-cp313-none-win32.whl", hash = "sha256:a7e19150d215c7a13f39eb787d84db274298d3f83d85463e61d277bbd7f401d2"}, - {file = "orjson-3.10.7-cp313-none-win_amd64.whl", hash = "sha256:eef44224729e9525d5261cc8d28d6b11cafc90e6bd0be2157bde69a52ec83024"}, - {file = "orjson-3.10.7-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:e76be12658a6fa376fcd331b1ea4e58f5a06fd0220653450f0d415b8fd0fbe20"}, - {file = "orjson-3.10.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed350d6978d28b92939bfeb1a0570c523f6170efc3f0a0ef1f1df287cd4f4960"}, - {file = "orjson-3.10.7-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:144888c76f8520e39bfa121b31fd637e18d4cc2f115727865fdf9fa325b10412"}, - {file = "orjson-3.10.7-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09b2d92fd95ad2402188cf51573acde57eb269eddabaa60f69ea0d733e789fe9"}, - {file = "orjson-3.10.7-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5b24a579123fa884f3a3caadaed7b75eb5715ee2b17ab5c66ac97d29b18fe57f"}, - {file = "orjson-3.10.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e72591bcfe7512353bd609875ab38050efe3d55e18934e2f18950c108334b4ff"}, - {file = "orjson-3.10.7-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f4db56635b58cd1a200b0a23744ff44206ee6aa428185e2b6c4a65b3197abdcd"}, - {file = "orjson-3.10.7-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0fa5886854673222618638c6df7718ea7fe2f3f2384c452c9ccedc70b4a510a5"}, - {file = "orjson-3.10.7-cp39-none-win32.whl", hash = "sha256:8272527d08450ab16eb405f47e0f4ef0e5ff5981c3d82afe0efd25dcbef2bcd2"}, - {file = "orjson-3.10.7-cp39-none-win_amd64.whl", hash = "sha256:974683d4618c0c7dbf4f69c95a979734bf183d0658611760017f6e70a145af58"}, - {file = "orjson-3.10.7.tar.gz", hash = "sha256:75ef0640403f945f3a1f9f6400686560dbfb0fb5b16589ad62cd477043c4eee3"}, -] - -[[package]] -name = "packaging" -version = "24.1" -requires_python = ">=3.8" -summary = "Core utilities for Python packages" -groups = ["default", "docs"] -files = [ - {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, - {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, -] - -[[package]] -name = "paginate" -version = "0.5.7" -summary = "Divides large result sets into pages for easier browsing" -groups = ["docs"] -files = [ - {file = "paginate-0.5.7-py2.py3-none-any.whl", hash = "sha256:b885e2af73abcf01d9559fd5216b57ef722f8c42affbb63942377668e35c7591"}, - {file = "paginate-0.5.7.tar.gz", hash = "sha256:22bd083ab41e1a8b4f3690544afb2c60c25e5c9a63a30fa2f483f6c60c8e5945"}, -] - -[[package]] -name = "pandas" -version = "2.2.2" -requires_python = ">=3.9" -summary = "Powerful data structures for data analysis, time series, and statistics" -groups = ["default", "docs"] -dependencies = [ - "numpy>=1.22.4; python_version < \"3.11\"", - "numpy>=1.23.2; python_version == \"3.11\"", - "numpy>=1.26.0; python_version >= \"3.12\"", - "python-dateutil>=2.8.2", - "pytz>=2020.1", - "tzdata>=2022.7", -] -files = [ - {file = "pandas-2.2.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:90c6fca2acf139569e74e8781709dccb6fe25940488755716d1d354d6bc58bce"}, - {file = "pandas-2.2.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c7adfc142dac335d8c1e0dcbd37eb8617eac386596eb9e1a1b77791cf2498238"}, - {file = "pandas-2.2.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4abfe0be0d7221be4f12552995e58723c7422c80a659da13ca382697de830c08"}, - {file = "pandas-2.2.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8635c16bf3d99040fdf3ca3db669a7250ddf49c55dc4aa8fe0ae0fa8d6dcc1f0"}, - {file = "pandas-2.2.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:40ae1dffb3967a52203105a077415a86044a2bea011b5f321c6aa64b379a3f51"}, - {file = "pandas-2.2.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8e5a0b00e1e56a842f922e7fae8ae4077aee4af0acb5ae3622bd4b4c30aedf99"}, - {file = "pandas-2.2.2-cp310-cp310-win_amd64.whl", hash = "sha256:ddf818e4e6c7c6f4f7c8a12709696d193976b591cc7dc50588d3d1a6b5dc8772"}, - {file = "pandas-2.2.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:696039430f7a562b74fa45f540aca068ea85fa34c244d0deee539cb6d70aa288"}, - {file = "pandas-2.2.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8e90497254aacacbc4ea6ae5e7a8cd75629d6ad2b30025a4a8b09aa4faf55151"}, - {file = "pandas-2.2.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58b84b91b0b9f4bafac2a0ac55002280c094dfc6402402332c0913a59654ab2b"}, - {file = "pandas-2.2.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2123dc9ad6a814bcdea0f099885276b31b24f7edf40f6cdbc0912672e22eee"}, - {file = "pandas-2.2.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:2925720037f06e89af896c70bca73459d7e6a4be96f9de79e2d440bd499fe0db"}, - {file = "pandas-2.2.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0cace394b6ea70c01ca1595f839cf193df35d1575986e484ad35c4aeae7266c1"}, - {file = "pandas-2.2.2-cp311-cp311-win_amd64.whl", hash = "sha256:873d13d177501a28b2756375d59816c365e42ed8417b41665f346289adc68d24"}, - {file = "pandas-2.2.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9dfde2a0ddef507a631dc9dc4af6a9489d5e2e740e226ad426a05cabfbd7c8ef"}, - {file = "pandas-2.2.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e9b79011ff7a0f4b1d6da6a61aa1aa604fb312d6647de5bad20013682d1429ce"}, - {file = "pandas-2.2.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cb51fe389360f3b5a4d57dbd2848a5f033350336ca3b340d1c53a1fad33bcad"}, - {file = "pandas-2.2.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eee3a87076c0756de40b05c5e9a6069c035ba43e8dd71c379e68cab2c20f16ad"}, - {file = "pandas-2.2.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3e374f59e440d4ab45ca2fffde54b81ac3834cf5ae2cdfa69c90bc03bde04d76"}, - {file = "pandas-2.2.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:43498c0bdb43d55cb162cdc8c06fac328ccb5d2eabe3cadeb3529ae6f0517c32"}, - {file = "pandas-2.2.2-cp312-cp312-win_amd64.whl", hash = "sha256:d187d355ecec3629624fccb01d104da7d7f391db0311145817525281e2804d23"}, - {file = "pandas-2.2.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0ca6377b8fca51815f382bd0b697a0814c8bda55115678cbc94c30aacbb6eff2"}, - {file = "pandas-2.2.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9057e6aa78a584bc93a13f0a9bf7e753a5e9770a30b4d758b8d5f2a62a9433cd"}, - {file = "pandas-2.2.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:001910ad31abc7bf06f49dcc903755d2f7f3a9186c0c040b827e522e9cef0863"}, - {file = "pandas-2.2.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66b479b0bd07204e37583c191535505410daa8df638fd8e75ae1b383851fe921"}, - {file = "pandas-2.2.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a77e9d1c386196879aa5eb712e77461aaee433e54c68cf253053a73b7e49c33a"}, - {file = "pandas-2.2.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:92fd6b027924a7e178ac202cfbe25e53368db90d56872d20ffae94b96c7acc57"}, - {file = "pandas-2.2.2-cp39-cp39-win_amd64.whl", hash = "sha256:640cef9aa381b60e296db324337a554aeeb883ead99dc8f6c18e81a93942f5f4"}, - {file = "pandas-2.2.2.tar.gz", hash = "sha256:9e79019aba43cb4fda9e4d983f8e88ca0373adbb697ae9c6c43093218de28b54"}, -] - -[[package]] -name = "pandocfilters" -version = "1.5.1" -requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -summary = "Utilities for writing pandoc filters in python" -groups = ["docs"] -files = [ - {file = "pandocfilters-1.5.1-py2.py3-none-any.whl", hash = "sha256:93be382804a9cdb0a7267585f157e5d1731bbe5545a85b268d6f5fe6232de2bc"}, - {file = "pandocfilters-1.5.1.tar.gz", hash = "sha256:002b4a555ee4ebc03f8b66307e287fa492e4a77b4ea14d3f934328297bb4939e"}, -] - -[[package]] -name = "pathspec" -version = "0.12.1" -requires_python = ">=3.8" -summary = "Utility library for gitignore style pattern matching of file paths." -groups = ["docs"] -files = [ - {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, - {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, -] - -[[package]] -name = "pillow" -version = "10.4.0" -requires_python = ">=3.8" -summary = "Python Imaging Library (Fork)" -groups = ["docs"] -files = [ - {file = "pillow-10.4.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:4d9667937cfa347525b319ae34375c37b9ee6b525440f3ef48542fcf66f2731e"}, - {file = "pillow-10.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:543f3dc61c18dafb755773efc89aae60d06b6596a63914107f75459cf984164d"}, - {file = "pillow-10.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7928ecbf1ece13956b95d9cbcfc77137652b02763ba384d9ab508099a2eca856"}, - {file = "pillow-10.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4d49b85c4348ea0b31ea63bc75a9f3857869174e2bf17e7aba02945cd218e6f"}, - {file = "pillow-10.4.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:6c762a5b0997f5659a5ef2266abc1d8851ad7749ad9a6a5506eb23d314e4f46b"}, - {file = "pillow-10.4.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a985e028fc183bf12a77a8bbf36318db4238a3ded7fa9df1b9a133f1cb79f8fc"}, - {file = "pillow-10.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:812f7342b0eee081eaec84d91423d1b4650bb9828eb53d8511bcef8ce5aecf1e"}, - {file = "pillow-10.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ac1452d2fbe4978c2eec89fb5a23b8387aba707ac72810d9490118817d9c0b46"}, - {file = "pillow-10.4.0-cp310-cp310-win32.whl", hash = "sha256:bcd5e41a859bf2e84fdc42f4edb7d9aba0a13d29a2abadccafad99de3feff984"}, - {file = "pillow-10.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:ecd85a8d3e79cd7158dec1c9e5808e821feea088e2f69a974db5edf84dc53141"}, - {file = "pillow-10.4.0-cp310-cp310-win_arm64.whl", hash = "sha256:ff337c552345e95702c5fde3158acb0625111017d0e5f24bf3acdb9cc16b90d1"}, - {file = "pillow-10.4.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:0a9ec697746f268507404647e531e92889890a087e03681a3606d9b920fbee3c"}, - {file = "pillow-10.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfe91cb65544a1321e631e696759491ae04a2ea11d36715eca01ce07284738be"}, - {file = "pillow-10.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5dc6761a6efc781e6a1544206f22c80c3af4c8cf461206d46a1e6006e4429ff3"}, - {file = "pillow-10.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e84b6cc6a4a3d76c153a6b19270b3526a5a8ed6b09501d3af891daa2a9de7d6"}, - {file = "pillow-10.4.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:bbc527b519bd3aa9d7f429d152fea69f9ad37c95f0b02aebddff592688998abe"}, - {file = "pillow-10.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:76a911dfe51a36041f2e756b00f96ed84677cdeb75d25c767f296c1c1eda1319"}, - {file = "pillow-10.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:59291fb29317122398786c2d44427bbd1a6d7ff54017075b22be9d21aa59bd8d"}, - {file = "pillow-10.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:416d3a5d0e8cfe4f27f574362435bc9bae57f679a7158e0096ad2beb427b8696"}, - {file = "pillow-10.4.0-cp311-cp311-win32.whl", hash = "sha256:7086cc1d5eebb91ad24ded9f58bec6c688e9f0ed7eb3dbbf1e4800280a896496"}, - {file = "pillow-10.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cbed61494057c0f83b83eb3a310f0bf774b09513307c434d4366ed64f4128a91"}, - {file = "pillow-10.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:f5f0c3e969c8f12dd2bb7e0b15d5c468b51e5017e01e2e867335c81903046a22"}, - {file = "pillow-10.4.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:673655af3eadf4df6b5457033f086e90299fdd7a47983a13827acf7459c15d94"}, - {file = "pillow-10.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:866b6942a92f56300012f5fbac71f2d610312ee65e22f1aa2609e491284e5597"}, - {file = "pillow-10.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29dbdc4207642ea6aad70fbde1a9338753d33fb23ed6956e706936706f52dd80"}, - {file = "pillow-10.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf2342ac639c4cf38799a44950bbc2dfcb685f052b9e262f446482afaf4bffca"}, - {file = "pillow-10.4.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:f5b92f4d70791b4a67157321c4e8225d60b119c5cc9aee8ecf153aace4aad4ef"}, - {file = "pillow-10.4.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:86dcb5a1eb778d8b25659d5e4341269e8590ad6b4e8b44d9f4b07f8d136c414a"}, - {file = "pillow-10.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:780c072c2e11c9b2c7ca37f9a2ee8ba66f44367ac3e5c7832afcfe5104fd6d1b"}, - {file = "pillow-10.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:37fb69d905be665f68f28a8bba3c6d3223c8efe1edf14cc4cfa06c241f8c81d9"}, - {file = "pillow-10.4.0-cp312-cp312-win32.whl", hash = "sha256:7dfecdbad5c301d7b5bde160150b4db4c659cee2b69589705b6f8a0c509d9f42"}, - {file = "pillow-10.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:1d846aea995ad352d4bdcc847535bd56e0fd88d36829d2c90be880ef1ee4668a"}, - {file = "pillow-10.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:e553cad5179a66ba15bb18b353a19020e73a7921296a7979c4a2b7f6a5cd57f9"}, - {file = "pillow-10.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8bc1a764ed8c957a2e9cacf97c8b2b053b70307cf2996aafd70e91a082e70df3"}, - {file = "pillow-10.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6209bb41dc692ddfee4942517c19ee81b86c864b626dbfca272ec0f7cff5d9fb"}, - {file = "pillow-10.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bee197b30783295d2eb680b311af15a20a8b24024a19c3a26431ff83eb8d1f70"}, - {file = "pillow-10.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ef61f5dd14c300786318482456481463b9d6b91ebe5ef12f405afbba77ed0be"}, - {file = "pillow-10.4.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:297e388da6e248c98bc4a02e018966af0c5f92dfacf5a5ca22fa01cb3179bca0"}, - {file = "pillow-10.4.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:e4db64794ccdf6cb83a59d73405f63adbe2a1887012e308828596100a0b2f6cc"}, - {file = "pillow-10.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bd2880a07482090a3bcb01f4265f1936a903d70bc740bfcb1fd4e8a2ffe5cf5a"}, - {file = "pillow-10.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b35b21b819ac1dbd1233317adeecd63495f6babf21b7b2512d244ff6c6ce309"}, - {file = "pillow-10.4.0-cp313-cp313-win32.whl", hash = "sha256:551d3fd6e9dc15e4c1eb6fc4ba2b39c0c7933fa113b220057a34f4bb3268a060"}, - {file = "pillow-10.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:030abdbe43ee02e0de642aee345efa443740aa4d828bfe8e2eb11922ea6a21ea"}, - {file = "pillow-10.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:5b001114dd152cfd6b23befeb28d7aee43553e2402c9f159807bf55f33af8a8d"}, - {file = "pillow-10.4.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:0ae24a547e8b711ccaaf99c9ae3cd975470e1a30caa80a6aaee9a2f19c05701d"}, - {file = "pillow-10.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:298478fe4f77a4408895605f3482b6cc6222c018b2ce565c2b6b9c354ac3229b"}, - {file = "pillow-10.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:134ace6dc392116566980ee7436477d844520a26a4b1bd4053f6f47d096997fd"}, - {file = "pillow-10.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:930044bb7679ab003b14023138b50181899da3f25de50e9dbee23b61b4de2126"}, - {file = "pillow-10.4.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:c76e5786951e72ed3686e122d14c5d7012f16c8303a674d18cdcd6d89557fc5b"}, - {file = "pillow-10.4.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:b2724fdb354a868ddf9a880cb84d102da914e99119211ef7ecbdc613b8c96b3c"}, - {file = "pillow-10.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dbc6ae66518ab3c5847659e9988c3b60dc94ffb48ef9168656e0019a93dbf8a1"}, - {file = "pillow-10.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:06b2f7898047ae93fad74467ec3d28fe84f7831370e3c258afa533f81ef7f3df"}, - {file = "pillow-10.4.0-cp39-cp39-win32.whl", hash = "sha256:7970285ab628a3779aecc35823296a7869f889b8329c16ad5a71e4901a3dc4ef"}, - {file = "pillow-10.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:961a7293b2457b405967af9c77dcaa43cc1a8cd50d23c532e62d48ab6cdd56f5"}, - {file = "pillow-10.4.0-cp39-cp39-win_arm64.whl", hash = "sha256:32cda9e3d601a52baccb2856b8ea1fc213c90b340c542dcef77140dfa3278a9e"}, - {file = "pillow-10.4.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5b4815f2e65b30f5fbae9dfffa8636d992d49705723fe86a3661806e069352d4"}, - {file = "pillow-10.4.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8f0aef4ef59694b12cadee839e2ba6afeab89c0f39a3adc02ed51d109117b8da"}, - {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f4727572e2918acaa9077c919cbbeb73bd2b3ebcfe033b72f858fc9fbef0026"}, - {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff25afb18123cea58a591ea0244b92eb1e61a1fd497bf6d6384f09bc3262ec3e"}, - {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:dc3e2db6ba09ffd7d02ae9141cfa0ae23393ee7687248d46a7507b75d610f4f5"}, - {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:02a2be69f9c9b8c1e97cf2713e789d4e398c751ecfd9967c18d0ce304efbf885"}, - {file = "pillow-10.4.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:0755ffd4a0c6f267cccbae2e9903d95477ca2f77c4fcf3a3a09570001856c8a5"}, - {file = "pillow-10.4.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:a02364621fe369e06200d4a16558e056fe2805d3468350df3aef21e00d26214b"}, - {file = "pillow-10.4.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:1b5dea9831a90e9d0721ec417a80d4cbd7022093ac38a568db2dd78363b00908"}, - {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b885f89040bb8c4a1573566bbb2f44f5c505ef6e74cec7ab9068c900047f04b"}, - {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87dd88ded2e6d74d31e1e0a99a726a6765cda32d00ba72dc37f0651f306daaa8"}, - {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:2db98790afc70118bd0255c2eeb465e9767ecf1f3c25f9a1abb8ffc8cfd1fe0a"}, - {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f7baece4ce06bade126fb84b8af1c33439a76d8a6fd818970215e0560ca28c27"}, - {file = "pillow-10.4.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:cfdd747216947628af7b259d274771d84db2268ca062dd5faf373639d00113a3"}, - {file = "pillow-10.4.0.tar.gz", hash = "sha256:166c1cd4d24309b30d61f79f4a9114b7b2313d7450912277855ff5dfd7cd4a06"}, -] - -[[package]] -name = "platformdirs" -version = "4.3.3" -requires_python = ">=3.8" -summary = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." -groups = ["docs"] -files = [ - {file = "platformdirs-4.3.3-py3-none-any.whl", hash = "sha256:50a5450e2e84f44539718293cbb1da0a0885c9d14adf21b77bae4e66fc99d9b5"}, - {file = "platformdirs-4.3.3.tar.gz", hash = "sha256:d4e0b7d8ec176b341fb03cb11ca12d0276faa8c485f9cd218f613840463fc2c0"}, -] - -[[package]] -name = "portalocker" -version = "2.10.1" -requires_python = ">=3.8" -summary = "Wraps the portalocker recipe for easy usage" -groups = ["default"] -dependencies = [ - "pywin32>=226; platform_system == \"Windows\"", -] -files = [ - {file = "portalocker-2.10.1-py3-none-any.whl", hash = "sha256:53a5984ebc86a025552264b459b46a2086e269b21823cb572f8f28ee759e45bf"}, - {file = "portalocker-2.10.1.tar.gz", hash = "sha256:ef1bf844e878ab08aee7e40184156e1151f228f103aa5c6bd0724cc330960f8f"}, -] - -[[package]] -name = "pyarrow" -version = "17.0.0" -requires_python = ">=3.8" -summary = "Python library for Apache Arrow" -groups = ["default"] -dependencies = [ - "numpy>=1.16.6", -] -files = [ - {file = "pyarrow-17.0.0-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:a5c8b238d47e48812ee577ee20c9a2779e6a5904f1708ae240f53ecbee7c9f07"}, - {file = "pyarrow-17.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:db023dc4c6cae1015de9e198d41250688383c3f9af8f565370ab2b4cb5f62655"}, - {file = "pyarrow-17.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da1e060b3876faa11cee287839f9cc7cdc00649f475714b8680a05fd9071d545"}, - {file = "pyarrow-17.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75c06d4624c0ad6674364bb46ef38c3132768139ddec1c56582dbac54f2663e2"}, - {file = "pyarrow-17.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:fa3c246cc58cb5a4a5cb407a18f193354ea47dd0648194e6265bd24177982fe8"}, - {file = "pyarrow-17.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:f7ae2de664e0b158d1607699a16a488de3d008ba99b3a7aa5de1cbc13574d047"}, - {file = "pyarrow-17.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:5984f416552eea15fd9cee03da53542bf4cddaef5afecefb9aa8d1010c335087"}, - {file = "pyarrow-17.0.0-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:1c8856e2ef09eb87ecf937104aacfa0708f22dfeb039c363ec99735190ffb977"}, - {file = "pyarrow-17.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2e19f569567efcbbd42084e87f948778eb371d308e137a0f97afe19bb860ccb3"}, - {file = "pyarrow-17.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b244dc8e08a23b3e352899a006a26ae7b4d0da7bb636872fa8f5884e70acf15"}, - {file = "pyarrow-17.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b72e87fe3e1db343995562f7fff8aee354b55ee83d13afba65400c178ab2597"}, - {file = "pyarrow-17.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:dc5c31c37409dfbc5d014047817cb4ccd8c1ea25d19576acf1a001fe07f5b420"}, - {file = "pyarrow-17.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:e3343cb1e88bc2ea605986d4b94948716edc7a8d14afd4e2c097232f729758b4"}, - {file = "pyarrow-17.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:a27532c38f3de9eb3e90ecab63dfda948a8ca859a66e3a47f5f42d1e403c4d03"}, - {file = "pyarrow-17.0.0-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:9b8a823cea605221e61f34859dcc03207e52e409ccf6354634143e23af7c8d22"}, - {file = "pyarrow-17.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f1e70de6cb5790a50b01d2b686d54aaf73da01266850b05e3af2a1bc89e16053"}, - {file = "pyarrow-17.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0071ce35788c6f9077ff9ecba4858108eebe2ea5a3f7cf2cf55ebc1dbc6ee24a"}, - {file = "pyarrow-17.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:757074882f844411fcca735e39aae74248a1531367a7c80799b4266390ae51cc"}, - {file = "pyarrow-17.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:9ba11c4f16976e89146781a83833df7f82077cdab7dc6232c897789343f7891a"}, - {file = "pyarrow-17.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:b0c6ac301093b42d34410b187bba560b17c0330f64907bfa4f7f7f2444b0cf9b"}, - {file = "pyarrow-17.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:392bc9feabc647338e6c89267635e111d71edad5fcffba204425a7c8d13610d7"}, - {file = "pyarrow-17.0.0-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:13d7a460b412f31e4c0efa1148e1d29bdf18ad1411eb6757d38f8fbdcc8645fb"}, - {file = "pyarrow-17.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9b564a51fbccfab5a04a80453e5ac6c9954a9c5ef2890d1bcf63741909c3f8df"}, - {file = "pyarrow-17.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32503827abbc5aadedfa235f5ece8c4f8f8b0a3cf01066bc8d29de7539532687"}, - {file = "pyarrow-17.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a155acc7f154b9ffcc85497509bcd0d43efb80d6f733b0dc3bb14e281f131c8b"}, - {file = "pyarrow-17.0.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:dec8d129254d0188a49f8a1fc99e0560dc1b85f60af729f47de4046015f9b0a5"}, - {file = "pyarrow-17.0.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:a48ddf5c3c6a6c505904545c25a4ae13646ae1f8ba703c4df4a1bfe4f4006bda"}, - {file = "pyarrow-17.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:42bf93249a083aca230ba7e2786c5f673507fa97bbd9725a1e2754715151a204"}, - {file = "pyarrow-17.0.0.tar.gz", hash = "sha256:4beca9521ed2c0921c1023e68d097d0299b62c362639ea315572a58f3f50fd28"}, -] - -[[package]] -name = "pycparser" -version = "2.22" -requires_python = ">=3.8" -summary = "C parser in Python" -groups = ["docs"] -files = [ - {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, - {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, -] - -[[package]] -name = "pydantic" -version = "2.9.1" -requires_python = ">=3.8" -summary = "Data validation using Python type hints" -groups = ["default"] -dependencies = [ - "annotated-types>=0.6.0", - "pydantic-core==2.23.3", - "typing-extensions>=4.12.2; python_version >= \"3.13\"", - "typing-extensions>=4.6.1; python_version < \"3.13\"", -] -files = [ - {file = "pydantic-2.9.1-py3-none-any.whl", hash = "sha256:7aff4db5fdf3cf573d4b3c30926a510a10e19a0774d38fc4967f78beb6deb612"}, - {file = "pydantic-2.9.1.tar.gz", hash = "sha256:1363c7d975c7036df0db2b4a61f2e062fbc0aa5ab5f2772e0ffc7191a4f4bce2"}, -] - -[[package]] -name = "pydantic-core" -version = "2.23.3" -requires_python = ">=3.8" -summary = "Core functionality for Pydantic validation and serialization" -groups = ["default"] -dependencies = [ - "typing-extensions!=4.7.0,>=4.6.0", -] -files = [ - {file = "pydantic_core-2.23.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:7f10a5d1b9281392f1bf507d16ac720e78285dfd635b05737c3911637601bae6"}, - {file = "pydantic_core-2.23.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3c09a7885dd33ee8c65266e5aa7fb7e2f23d49d8043f089989726391dd7350c5"}, - {file = "pydantic_core-2.23.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6470b5a1ec4d1c2e9afe928c6cb37eb33381cab99292a708b8cb9aa89e62429b"}, - {file = "pydantic_core-2.23.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9172d2088e27d9a185ea0a6c8cebe227a9139fd90295221d7d495944d2367700"}, - {file = "pydantic_core-2.23.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86fc6c762ca7ac8fbbdff80d61b2c59fb6b7d144aa46e2d54d9e1b7b0e780e01"}, - {file = "pydantic_core-2.23.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0cb80fd5c2df4898693aa841425ea1727b1b6d2167448253077d2a49003e0ed"}, - {file = "pydantic_core-2.23.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03667cec5daf43ac4995cefa8aaf58f99de036204a37b889c24a80927b629cec"}, - {file = "pydantic_core-2.23.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:047531242f8e9c2db733599f1c612925de095e93c9cc0e599e96cf536aaf56ba"}, - {file = "pydantic_core-2.23.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5499798317fff7f25dbef9347f4451b91ac2a4330c6669821c8202fd354c7bee"}, - {file = "pydantic_core-2.23.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bbb5e45eab7624440516ee3722a3044b83fff4c0372efe183fd6ba678ff681fe"}, - {file = "pydantic_core-2.23.3-cp310-none-win32.whl", hash = "sha256:8b5b3ed73abb147704a6e9f556d8c5cb078f8c095be4588e669d315e0d11893b"}, - {file = "pydantic_core-2.23.3-cp310-none-win_amd64.whl", hash = "sha256:2b603cde285322758a0279995b5796d64b63060bfbe214b50a3ca23b5cee3e83"}, - {file = "pydantic_core-2.23.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:c889fd87e1f1bbeb877c2ee56b63bb297de4636661cc9bbfcf4b34e5e925bc27"}, - {file = "pydantic_core-2.23.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ea85bda3189fb27503af4c45273735bcde3dd31c1ab17d11f37b04877859ef45"}, - {file = "pydantic_core-2.23.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a7f7f72f721223f33d3dc98a791666ebc6a91fa023ce63733709f4894a7dc611"}, - {file = "pydantic_core-2.23.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b2b55b0448e9da68f56b696f313949cda1039e8ec7b5d294285335b53104b61"}, - {file = "pydantic_core-2.23.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c24574c7e92e2c56379706b9a3f07c1e0c7f2f87a41b6ee86653100c4ce343e5"}, - {file = "pydantic_core-2.23.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2b05e6ccbee333a8f4b8f4d7c244fdb7a979e90977ad9c51ea31261e2085ce0"}, - {file = "pydantic_core-2.23.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2c409ce1c219c091e47cb03feb3c4ed8c2b8e004efc940da0166aaee8f9d6c8"}, - {file = "pydantic_core-2.23.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d965e8b325f443ed3196db890d85dfebbb09f7384486a77461347f4adb1fa7f8"}, - {file = "pydantic_core-2.23.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f56af3a420fb1ffaf43ece3ea09c2d27c444e7c40dcb7c6e7cf57aae764f2b48"}, - {file = "pydantic_core-2.23.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5b01a078dd4f9a52494370af21aa52964e0a96d4862ac64ff7cea06e0f12d2c5"}, - {file = "pydantic_core-2.23.3-cp311-none-win32.whl", hash = "sha256:560e32f0df04ac69b3dd818f71339983f6d1f70eb99d4d1f8e9705fb6c34a5c1"}, - {file = "pydantic_core-2.23.3-cp311-none-win_amd64.whl", hash = "sha256:c744fa100fdea0d000d8bcddee95213d2de2e95b9c12be083370b2072333a0fa"}, - {file = "pydantic_core-2.23.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:e0ec50663feedf64d21bad0809f5857bac1ce91deded203efc4a84b31b2e4305"}, - {file = "pydantic_core-2.23.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:db6e6afcb95edbe6b357786684b71008499836e91f2a4a1e55b840955b341dbb"}, - {file = "pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98ccd69edcf49f0875d86942f4418a4e83eb3047f20eb897bffa62a5d419c8fa"}, - {file = "pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a678c1ac5c5ec5685af0133262103defb427114e62eafeda12f1357a12140162"}, - {file = "pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:01491d8b4d8db9f3391d93b0df60701e644ff0894352947f31fff3e52bd5c801"}, - {file = "pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fcf31facf2796a2d3b7fe338fe8640aa0166e4e55b4cb108dbfd1058049bf4cb"}, - {file = "pydantic_core-2.23.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7200fd561fb3be06827340da066df4311d0b6b8eb0c2116a110be5245dceb326"}, - {file = "pydantic_core-2.23.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dc1636770a809dee2bd44dd74b89cc80eb41172bcad8af75dd0bc182c2666d4c"}, - {file = "pydantic_core-2.23.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:67a5def279309f2e23014b608c4150b0c2d323bd7bccd27ff07b001c12c2415c"}, - {file = "pydantic_core-2.23.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:748bdf985014c6dd3e1e4cc3db90f1c3ecc7246ff5a3cd4ddab20c768b2f1dab"}, - {file = "pydantic_core-2.23.3-cp312-none-win32.whl", hash = "sha256:255ec6dcb899c115f1e2a64bc9ebc24cc0e3ab097775755244f77360d1f3c06c"}, - {file = "pydantic_core-2.23.3-cp312-none-win_amd64.whl", hash = "sha256:40b8441be16c1e940abebed83cd006ddb9e3737a279e339dbd6d31578b802f7b"}, - {file = "pydantic_core-2.23.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:6daaf5b1ba1369a22c8b050b643250e3e5efc6a78366d323294aee54953a4d5f"}, - {file = "pydantic_core-2.23.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d015e63b985a78a3d4ccffd3bdf22b7c20b3bbd4b8227809b3e8e75bc37f9cb2"}, - {file = "pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3fc572d9b5b5cfe13f8e8a6e26271d5d13f80173724b738557a8c7f3a8a3791"}, - {file = "pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f6bd91345b5163ee7448bee201ed7dd601ca24f43f439109b0212e296eb5b423"}, - {file = "pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc379c73fd66606628b866f661e8785088afe2adaba78e6bbe80796baf708a63"}, - {file = "pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fbdce4b47592f9e296e19ac31667daed8753c8367ebb34b9a9bd89dacaa299c9"}, - {file = "pydantic_core-2.23.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc3cf31edf405a161a0adad83246568647c54404739b614b1ff43dad2b02e6d5"}, - {file = "pydantic_core-2.23.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8e22b477bf90db71c156f89a55bfe4d25177b81fce4aa09294d9e805eec13855"}, - {file = "pydantic_core-2.23.3-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:0a0137ddf462575d9bce863c4c95bac3493ba8e22f8c28ca94634b4a1d3e2bb4"}, - {file = "pydantic_core-2.23.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:203171e48946c3164fe7691fc349c79241ff8f28306abd4cad5f4f75ed80bc8d"}, - {file = "pydantic_core-2.23.3-cp313-none-win32.whl", hash = "sha256:76bdab0de4acb3f119c2a4bff740e0c7dc2e6de7692774620f7452ce11ca76c8"}, - {file = "pydantic_core-2.23.3-cp313-none-win_amd64.whl", hash = "sha256:37ba321ac2a46100c578a92e9a6aa33afe9ec99ffa084424291d84e456f490c1"}, - {file = "pydantic_core-2.23.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:82da2f4703894134a9f000e24965df73cc103e31e8c31906cc1ee89fde72cbd8"}, - {file = "pydantic_core-2.23.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:dd9be0a42de08f4b58a3cc73a123f124f65c24698b95a54c1543065baca8cf0e"}, - {file = "pydantic_core-2.23.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89b731f25c80830c76fdb13705c68fef6a2b6dc494402987c7ea9584fe189f5d"}, - {file = "pydantic_core-2.23.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c6de1ec30c4bb94f3a69c9f5f2182baeda5b809f806676675e9ef6b8dc936f28"}, - {file = "pydantic_core-2.23.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb68b41c3fa64587412b104294b9cbb027509dc2f6958446c502638d481525ef"}, - {file = "pydantic_core-2.23.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c3980f2843de5184656aab58698011b42763ccba11c4a8c35936c8dd6c7068c"}, - {file = "pydantic_core-2.23.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94f85614f2cba13f62c3c6481716e4adeae48e1eaa7e8bac379b9d177d93947a"}, - {file = "pydantic_core-2.23.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:510b7fb0a86dc8f10a8bb43bd2f97beb63cffad1203071dc434dac26453955cd"}, - {file = "pydantic_core-2.23.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:1eba2f7ce3e30ee2170410e2171867ea73dbd692433b81a93758ab2de6c64835"}, - {file = "pydantic_core-2.23.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4b259fd8409ab84b4041b7b3f24dcc41e4696f180b775961ca8142b5b21d0e70"}, - {file = "pydantic_core-2.23.3-cp39-none-win32.whl", hash = "sha256:40d9bd259538dba2f40963286009bf7caf18b5112b19d2b55b09c14dde6db6a7"}, - {file = "pydantic_core-2.23.3-cp39-none-win_amd64.whl", hash = "sha256:5a8cd3074a98ee70173a8633ad3c10e00dcb991ecec57263aacb4095c5efb958"}, - {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f399e8657c67313476a121a6944311fab377085ca7f490648c9af97fc732732d"}, - {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:6b5547d098c76e1694ba85f05b595720d7c60d342f24d5aad32c3049131fa5c4"}, - {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0dda0290a6f608504882d9f7650975b4651ff91c85673341789a476b1159f211"}, - {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65b6e5da855e9c55a0c67f4db8a492bf13d8d3316a59999cfbaf98cc6e401961"}, - {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:09e926397f392059ce0afdcac920df29d9c833256354d0c55f1584b0b70cf07e"}, - {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:87cfa0ed6b8c5bd6ae8b66de941cece179281239d482f363814d2b986b79cedc"}, - {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e61328920154b6a44d98cabcb709f10e8b74276bc709c9a513a8c37a18786cc4"}, - {file = "pydantic_core-2.23.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ce3317d155628301d649fe5e16a99528d5680af4ec7aa70b90b8dacd2d725c9b"}, - {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:e89513f014c6be0d17b00a9a7c81b1c426f4eb9224b15433f3d98c1a071f8433"}, - {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:4f62c1c953d7ee375df5eb2e44ad50ce2f5aff931723b398b8bc6f0ac159791a"}, - {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2718443bc671c7ac331de4eef9b673063b10af32a0bb385019ad61dcf2cc8f6c"}, - {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0d90e08b2727c5d01af1b5ef4121d2f0c99fbee692c762f4d9d0409c9da6541"}, - {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2b676583fc459c64146debea14ba3af54e540b61762dfc0613dc4e98c3f66eeb"}, - {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:50e4661f3337977740fdbfbae084ae5693e505ca2b3130a6d4eb0f2281dc43b8"}, - {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:68f4cf373f0de6abfe599a38307f4417c1c867ca381c03df27c873a9069cda25"}, - {file = "pydantic_core-2.23.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:59d52cf01854cb26c46958552a21acb10dd78a52aa34c86f284e66b209db8cab"}, - {file = "pydantic_core-2.23.3.tar.gz", hash = "sha256:3cb0f65d8b4121c1b015c60104a685feb929a29d7cf204387c7f2688c7974690"}, -] - -[[package]] -name = "pygments" -version = "2.18.0" -requires_python = ">=3.8" -summary = "Pygments is a syntax highlighting package written in Python." -groups = ["default", "docs"] -files = [ - {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"}, - {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"}, -] - -[[package]] -name = "pymdown-extensions" -version = "10.9" -requires_python = ">=3.8" -summary = "Extension pack for Python Markdown." -groups = ["docs"] -dependencies = [ - "markdown>=3.6", - "pyyaml", -] -files = [ - {file = "pymdown_extensions-10.9-py3-none-any.whl", hash = "sha256:d323f7e90d83c86113ee78f3fe62fc9dee5f56b54d912660703ea1816fed5626"}, - {file = "pymdown_extensions-10.9.tar.gz", hash = "sha256:6ff740bcd99ec4172a938970d42b96128bdc9d4b9bcad72494f29921dc69b753"}, -] - -[[package]] -name = "pyparsing" -version = "3.1.4" -requires_python = ">=3.6.8" -summary = "pyparsing module - Classes and methods to define and execute parsing grammars" -groups = ["docs"] -files = [ - {file = "pyparsing-3.1.4-py3-none-any.whl", hash = "sha256:a6a7ee4235a3f944aa1fa2249307708f893fe5717dc603503c6c7969c070fb7c"}, - {file = "pyparsing-3.1.4.tar.gz", hash = "sha256:f86ec8d1a83f11977c9a6ea7598e8c27fc5cddfa5b07ea2241edbbde1d7bc032"}, -] - -[[package]] -name = "python-dateutil" -version = "2.9.0.post0" -requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" -summary = "Extensions to the standard Python datetime module" -groups = ["default", "docs"] -dependencies = [ - "six>=1.5", -] -files = [ - {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, - {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, -] - -[[package]] -name = "pytz" -version = "2024.2" -summary = "World timezone definitions, modern and historical" -groups = ["default", "docs"] -files = [ - {file = "pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725"}, - {file = "pytz-2024.2.tar.gz", hash = "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a"}, -] - -[[package]] -name = "pywin32" -version = "306" -summary = "Python for Window Extensions" -groups = ["default", "docs"] -marker = "sys_platform == \"win32\" and platform_python_implementation != \"PyPy\" or platform_system == \"Windows\"" -files = [ - {file = "pywin32-306-cp310-cp310-win32.whl", hash = "sha256:06d3420a5155ba65f0b72f2699b5bacf3109f36acbe8923765c22938a69dfc8d"}, - {file = "pywin32-306-cp310-cp310-win_amd64.whl", hash = "sha256:84f4471dbca1887ea3803d8848a1616429ac94a4a8d05f4bc9c5dcfd42ca99c8"}, - {file = "pywin32-306-cp311-cp311-win32.whl", hash = "sha256:e65028133d15b64d2ed8f06dd9fbc268352478d4f9289e69c190ecd6818b6407"}, - {file = "pywin32-306-cp311-cp311-win_amd64.whl", hash = "sha256:a7639f51c184c0272e93f244eb24dafca9b1855707d94c192d4a0b4c01e1100e"}, - {file = "pywin32-306-cp311-cp311-win_arm64.whl", hash = "sha256:70dba0c913d19f942a2db25217d9a1b726c278f483a919f1abfed79c9cf64d3a"}, - {file = "pywin32-306-cp312-cp312-win32.whl", hash = "sha256:383229d515657f4e3ed1343da8be101000562bf514591ff383ae940cad65458b"}, - {file = "pywin32-306-cp312-cp312-win_amd64.whl", hash = "sha256:37257794c1ad39ee9be652da0462dc2e394c8159dfd913a8a4e8eb6fd346da0e"}, - {file = "pywin32-306-cp312-cp312-win_arm64.whl", hash = "sha256:5821ec52f6d321aa59e2db7e0a35b997de60c201943557d108af9d4ae1ec7040"}, - {file = "pywin32-306-cp39-cp39-win32.whl", hash = "sha256:e25fd5b485b55ac9c057f67d94bc203f3f6595078d1fb3b458c9c28b7153a802"}, - {file = "pywin32-306-cp39-cp39-win_amd64.whl", hash = "sha256:39b61c15272833b5c329a2989999dcae836b1eed650252ab1b7bfbe1d59f30f4"}, -] - -[[package]] -name = "pyyaml" -version = "6.0.2" -requires_python = ">=3.8" -summary = "YAML parser and emitter for Python" -groups = ["default", "docs"] -files = [ - {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, - {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, - {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"}, - {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"}, - {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"}, - {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"}, - {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"}, - {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"}, - {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"}, - {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"}, - {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"}, - {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"}, - {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"}, - {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"}, - {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"}, - {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"}, - {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"}, - {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"}, - {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"}, - {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"}, - {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"}, - {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"}, - {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"}, - {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"}, - {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"}, - {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"}, - {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"}, - {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"}, - {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"}, - {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"}, - {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"}, - {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"}, - {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"}, - {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"}, - {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"}, - {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"}, - {file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"}, - {file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"}, - {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"}, - {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"}, - {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"}, - {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"}, - {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"}, - {file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"}, - {file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"}, - {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, -] - -[[package]] -name = "pyyaml-env-tag" -version = "0.1" -requires_python = ">=3.6" -summary = "A custom YAML tag for referencing environment variables in YAML files. " -groups = ["docs"] -dependencies = [ - "pyyaml", -] -files = [ - {file = "pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069"}, - {file = "pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb"}, -] - -[[package]] -name = "pyzmq" -version = "26.2.0" -requires_python = ">=3.7" -summary = "Python bindings for 0MQ" -groups = ["docs"] -dependencies = [ - "cffi; implementation_name == \"pypy\"", -] -files = [ - {file = "pyzmq-26.2.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:ddf33d97d2f52d89f6e6e7ae66ee35a4d9ca6f36eda89c24591b0c40205a3629"}, - {file = "pyzmq-26.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dacd995031a01d16eec825bf30802fceb2c3791ef24bcce48fa98ce40918c27b"}, - {file = "pyzmq-26.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89289a5ee32ef6c439086184529ae060c741334b8970a6855ec0b6ad3ff28764"}, - {file = "pyzmq-26.2.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5506f06d7dc6ecf1efacb4a013b1f05071bb24b76350832c96449f4a2d95091c"}, - {file = "pyzmq-26.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ea039387c10202ce304af74def5021e9adc6297067f3441d348d2b633e8166a"}, - {file = "pyzmq-26.2.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a2224fa4a4c2ee872886ed00a571f5e967c85e078e8e8c2530a2fb01b3309b88"}, - {file = "pyzmq-26.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:28ad5233e9c3b52d76196c696e362508959741e1a005fb8fa03b51aea156088f"}, - {file = "pyzmq-26.2.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:1c17211bc037c7d88e85ed8b7d8f7e52db6dc8eca5590d162717c654550f7282"}, - {file = "pyzmq-26.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b8f86dd868d41bea9a5f873ee13bf5551c94cf6bc51baebc6f85075971fe6eea"}, - {file = "pyzmq-26.2.0-cp310-cp310-win32.whl", hash = "sha256:46a446c212e58456b23af260f3d9fb785054f3e3653dbf7279d8f2b5546b21c2"}, - {file = "pyzmq-26.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:49d34ab71db5a9c292a7644ce74190b1dd5a3475612eefb1f8be1d6961441971"}, - {file = "pyzmq-26.2.0-cp310-cp310-win_arm64.whl", hash = "sha256:bfa832bfa540e5b5c27dcf5de5d82ebc431b82c453a43d141afb1e5d2de025fa"}, - {file = "pyzmq-26.2.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:8f7e66c7113c684c2b3f1c83cdd3376103ee0ce4c49ff80a648643e57fb22218"}, - {file = "pyzmq-26.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3a495b30fc91db2db25120df5847d9833af237546fd59170701acd816ccc01c4"}, - {file = "pyzmq-26.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77eb0968da535cba0470a5165468b2cac7772cfb569977cff92e240f57e31bef"}, - {file = "pyzmq-26.2.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ace4f71f1900a548f48407fc9be59c6ba9d9aaf658c2eea6cf2779e72f9f317"}, - {file = "pyzmq-26.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:92a78853d7280bffb93df0a4a6a2498cba10ee793cc8076ef797ef2f74d107cf"}, - {file = "pyzmq-26.2.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:689c5d781014956a4a6de61d74ba97b23547e431e9e7d64f27d4922ba96e9d6e"}, - {file = "pyzmq-26.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0aca98bc423eb7d153214b2df397c6421ba6373d3397b26c057af3c904452e37"}, - {file = "pyzmq-26.2.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1f3496d76b89d9429a656293744ceca4d2ac2a10ae59b84c1da9b5165f429ad3"}, - {file = "pyzmq-26.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5c2b3bfd4b9689919db068ac6c9911f3fcb231c39f7dd30e3138be94896d18e6"}, - {file = "pyzmq-26.2.0-cp311-cp311-win32.whl", hash = "sha256:eac5174677da084abf378739dbf4ad245661635f1600edd1221f150b165343f4"}, - {file = "pyzmq-26.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:5a509df7d0a83a4b178d0f937ef14286659225ef4e8812e05580776c70e155d5"}, - {file = "pyzmq-26.2.0-cp311-cp311-win_arm64.whl", hash = "sha256:c0e6091b157d48cbe37bd67233318dbb53e1e6327d6fc3bb284afd585d141003"}, - {file = "pyzmq-26.2.0-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:ded0fc7d90fe93ae0b18059930086c51e640cdd3baebdc783a695c77f123dcd9"}, - {file = "pyzmq-26.2.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:17bf5a931c7f6618023cdacc7081f3f266aecb68ca692adac015c383a134ca52"}, - {file = "pyzmq-26.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55cf66647e49d4621a7e20c8d13511ef1fe1efbbccf670811864452487007e08"}, - {file = "pyzmq-26.2.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4661c88db4a9e0f958c8abc2b97472e23061f0bc737f6f6179d7a27024e1faa5"}, - {file = "pyzmq-26.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea7f69de383cb47522c9c208aec6dd17697db7875a4674c4af3f8cfdac0bdeae"}, - {file = "pyzmq-26.2.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:7f98f6dfa8b8ccaf39163ce872bddacca38f6a67289116c8937a02e30bbe9711"}, - {file = "pyzmq-26.2.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e3e0210287329272539eea617830a6a28161fbbd8a3271bf4150ae3e58c5d0e6"}, - {file = "pyzmq-26.2.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:6b274e0762c33c7471f1a7471d1a2085b1a35eba5cdc48d2ae319f28b6fc4de3"}, - {file = "pyzmq-26.2.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:29c6a4635eef69d68a00321e12a7d2559fe2dfccfa8efae3ffb8e91cd0b36a8b"}, - {file = "pyzmq-26.2.0-cp312-cp312-win32.whl", hash = "sha256:989d842dc06dc59feea09e58c74ca3e1678c812a4a8a2a419046d711031f69c7"}, - {file = "pyzmq-26.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:2a50625acdc7801bc6f74698c5c583a491c61d73c6b7ea4dee3901bb99adb27a"}, - {file = "pyzmq-26.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:4d29ab8592b6ad12ebbf92ac2ed2bedcfd1cec192d8e559e2e099f648570e19b"}, - {file = "pyzmq-26.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9dd8cd1aeb00775f527ec60022004d030ddc51d783d056e3e23e74e623e33726"}, - {file = "pyzmq-26.2.0-cp313-cp313-macosx_10_15_universal2.whl", hash = "sha256:28c812d9757fe8acecc910c9ac9dafd2ce968c00f9e619db09e9f8f54c3a68a3"}, - {file = "pyzmq-26.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d80b1dd99c1942f74ed608ddb38b181b87476c6a966a88a950c7dee118fdf50"}, - {file = "pyzmq-26.2.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8c997098cc65e3208eca09303630e84d42718620e83b733d0fd69543a9cab9cb"}, - {file = "pyzmq-26.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ad1bc8d1b7a18497dda9600b12dc193c577beb391beae5cd2349184db40f187"}, - {file = "pyzmq-26.2.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:bea2acdd8ea4275e1278350ced63da0b166421928276c7c8e3f9729d7402a57b"}, - {file = "pyzmq-26.2.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:23f4aad749d13698f3f7b64aad34f5fc02d6f20f05999eebc96b89b01262fb18"}, - {file = "pyzmq-26.2.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:a4f96f0d88accc3dbe4a9025f785ba830f968e21e3e2c6321ccdfc9aef755115"}, - {file = "pyzmq-26.2.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ced65e5a985398827cc9276b93ef6dfabe0273c23de8c7931339d7e141c2818e"}, - {file = "pyzmq-26.2.0-cp313-cp313-win32.whl", hash = "sha256:31507f7b47cc1ead1f6e86927f8ebb196a0bab043f6345ce070f412a59bf87b5"}, - {file = "pyzmq-26.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:70fc7fcf0410d16ebdda9b26cbd8bf8d803d220a7f3522e060a69a9c87bf7bad"}, - {file = "pyzmq-26.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:c3789bd5768ab5618ebf09cef6ec2b35fed88709b104351748a63045f0ff9797"}, - {file = "pyzmq-26.2.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:034da5fc55d9f8da09015d368f519478a52675e558c989bfcb5cf6d4e16a7d2a"}, - {file = "pyzmq-26.2.0-cp313-cp313t-macosx_10_15_universal2.whl", hash = "sha256:c92d73464b886931308ccc45b2744e5968cbaade0b1d6aeb40d8ab537765f5bc"}, - {file = "pyzmq-26.2.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:794a4562dcb374f7dbbfb3f51d28fb40123b5a2abadee7b4091f93054909add5"}, - {file = "pyzmq-26.2.0-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aee22939bb6075e7afededabad1a56a905da0b3c4e3e0c45e75810ebe3a52672"}, - {file = "pyzmq-26.2.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ae90ff9dad33a1cfe947d2c40cb9cb5e600d759ac4f0fd22616ce6540f72797"}, - {file = "pyzmq-26.2.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:43a47408ac52647dfabbc66a25b05b6a61700b5165807e3fbd40063fcaf46386"}, - {file = "pyzmq-26.2.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:25bf2374a2a8433633c65ccb9553350d5e17e60c8eb4de4d92cc6bd60f01d306"}, - {file = "pyzmq-26.2.0-cp313-cp313t-musllinux_1_1_i686.whl", hash = "sha256:007137c9ac9ad5ea21e6ad97d3489af654381324d5d3ba614c323f60dab8fae6"}, - {file = "pyzmq-26.2.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:470d4a4f6d48fb34e92d768b4e8a5cc3780db0d69107abf1cd7ff734b9766eb0"}, - {file = "pyzmq-26.2.0-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:b1d464cb8d72bfc1a3adc53305a63a8e0cac6bc8c5a07e8ca190ab8d3faa43c2"}, - {file = "pyzmq-26.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4da04c48873a6abdd71811c5e163bd656ee1b957971db7f35140a2d573f6949c"}, - {file = "pyzmq-26.2.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:d049df610ac811dcffdc147153b414147428567fbbc8be43bb8885f04db39d98"}, - {file = "pyzmq-26.2.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:05590cdbc6b902101d0e65d6a4780af14dc22914cc6ab995d99b85af45362cc9"}, - {file = "pyzmq-26.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c811cfcd6a9bf680236c40c6f617187515269ab2912f3d7e8c0174898e2519db"}, - {file = "pyzmq-26.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6835dd60355593de10350394242b5757fbbd88b25287314316f266e24c61d073"}, - {file = "pyzmq-26.2.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc6bee759a6bddea5db78d7dcd609397449cb2d2d6587f48f3ca613b19410cfc"}, - {file = "pyzmq-26.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c530e1eecd036ecc83c3407f77bb86feb79916d4a33d11394b8234f3bd35b940"}, - {file = "pyzmq-26.2.0-cp39-cp39-win32.whl", hash = "sha256:367b4f689786fca726ef7a6c5ba606958b145b9340a5e4808132cc65759abd44"}, - {file = "pyzmq-26.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:e6fa2e3e683f34aea77de8112f6483803c96a44fd726d7358b9888ae5bb394ec"}, - {file = "pyzmq-26.2.0-cp39-cp39-win_arm64.whl", hash = "sha256:7445be39143a8aa4faec43b076e06944b8f9d0701b669df4af200531b21e40bb"}, - {file = "pyzmq-26.2.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:706e794564bec25819d21a41c31d4df2d48e1cc4b061e8d345d7fb4dd3e94072"}, - {file = "pyzmq-26.2.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b435f2753621cd36e7c1762156815e21c985c72b19135dac43a7f4f31d28dd1"}, - {file = "pyzmq-26.2.0-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:160c7e0a5eb178011e72892f99f918c04a131f36056d10d9c1afb223fc952c2d"}, - {file = "pyzmq-26.2.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c4a71d5d6e7b28a47a394c0471b7e77a0661e2d651e7ae91e0cab0a587859ca"}, - {file = "pyzmq-26.2.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:90412f2db8c02a3864cbfc67db0e3dcdbda336acf1c469526d3e869394fe001c"}, - {file = "pyzmq-26.2.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:cdeabcff45d1c219636ee2e54d852262e5c2e085d6cb476d938aee8d921356b3"}, - {file = "pyzmq-26.2.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35cffef589bcdc587d06f9149f8d5e9e8859920a071df5a2671de2213bef592a"}, - {file = "pyzmq-26.2.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18c8dc3b7468d8b4bdf60ce9d7141897da103c7a4690157b32b60acb45e333e6"}, - {file = "pyzmq-26.2.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7133d0a1677aec369d67dd78520d3fa96dd7f3dcec99d66c1762870e5ea1a50a"}, - {file = "pyzmq-26.2.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:6a96179a24b14fa6428cbfc08641c779a53f8fcec43644030328f44034c7f1f4"}, - {file = "pyzmq-26.2.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:4f78c88905461a9203eac9faac157a2a0dbba84a0fd09fd29315db27be40af9f"}, - {file = "pyzmq-26.2.0.tar.gz", hash = "sha256:070672c258581c8e4f640b5159297580a9974b026043bd4ab0470be9ed324f1f"}, -] - -[[package]] -name = "referencing" -version = "0.35.1" -requires_python = ">=3.8" -summary = "JSON Referencing + Python" -groups = ["docs"] -dependencies = [ - "attrs>=22.2.0", - "rpds-py>=0.7.0", -] -files = [ - {file = "referencing-0.35.1-py3-none-any.whl", hash = "sha256:eda6d3234d62814d1c64e305c1331c9a3a6132da475ab6382eaa997b21ee75de"}, - {file = "referencing-0.35.1.tar.gz", hash = "sha256:25b42124a6c8b632a425174f24087783efb348a6f1e0008e63cd4466fedf703c"}, -] - -[[package]] -name = "regex" -version = "2024.9.11" -requires_python = ">=3.8" -summary = "Alternative regular expression module, to replace re." -groups = ["docs"] -files = [ - {file = "regex-2024.9.11-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1494fa8725c285a81d01dc8c06b55287a1ee5e0e382d8413adc0a9197aac6408"}, - {file = "regex-2024.9.11-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0e12c481ad92d129c78f13a2a3662317e46ee7ef96c94fd332e1c29131875b7d"}, - {file = "regex-2024.9.11-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:16e13a7929791ac1216afde26f712802e3df7bf0360b32e4914dca3ab8baeea5"}, - {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46989629904bad940bbec2106528140a218b4a36bb3042d8406980be1941429c"}, - {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a906ed5e47a0ce5f04b2c981af1c9acf9e8696066900bf03b9d7879a6f679fc8"}, - {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e9a091b0550b3b0207784a7d6d0f1a00d1d1c8a11699c1a4d93db3fbefc3ad35"}, - {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ddcd9a179c0a6fa8add279a4444015acddcd7f232a49071ae57fa6e278f1f71"}, - {file = "regex-2024.9.11-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6b41e1adc61fa347662b09398e31ad446afadff932a24807d3ceb955ed865cc8"}, - {file = "regex-2024.9.11-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ced479f601cd2f8ca1fd7b23925a7e0ad512a56d6e9476f79b8f381d9d37090a"}, - {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:635a1d96665f84b292e401c3d62775851aedc31d4f8784117b3c68c4fcd4118d"}, - {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c0256beda696edcf7d97ef16b2a33a8e5a875affd6fa6567b54f7c577b30a137"}, - {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:3ce4f1185db3fbde8ed8aa223fc9620f276c58de8b0d4f8cc86fd1360829edb6"}, - {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:09d77559e80dcc9d24570da3745ab859a9cf91953062e4ab126ba9d5993688ca"}, - {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7a22ccefd4db3f12b526eccb129390942fe874a3a9fdbdd24cf55773a1faab1a"}, - {file = "regex-2024.9.11-cp310-cp310-win32.whl", hash = "sha256:f745ec09bc1b0bd15cfc73df6fa4f726dcc26bb16c23a03f9e3367d357eeedd0"}, - {file = "regex-2024.9.11-cp310-cp310-win_amd64.whl", hash = "sha256:01c2acb51f8a7d6494c8c5eafe3d8e06d76563d8a8a4643b37e9b2dd8a2ff623"}, - {file = "regex-2024.9.11-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2cce2449e5927a0bf084d346da6cd5eb016b2beca10d0013ab50e3c226ffc0df"}, - {file = "regex-2024.9.11-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3b37fa423beefa44919e009745ccbf353d8c981516e807995b2bd11c2c77d268"}, - {file = "regex-2024.9.11-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:64ce2799bd75039b480cc0360907c4fb2f50022f030bf9e7a8705b636e408fad"}, - {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a4cc92bb6db56ab0c1cbd17294e14f5e9224f0cc6521167ef388332604e92679"}, - {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d05ac6fa06959c4172eccd99a222e1fbf17b5670c4d596cb1e5cde99600674c4"}, - {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:040562757795eeea356394a7fb13076ad4f99d3c62ab0f8bdfb21f99a1f85664"}, - {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6113c008a7780792efc80f9dfe10ba0cd043cbf8dc9a76ef757850f51b4edc50"}, - {file = "regex-2024.9.11-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8e5fb5f77c8745a60105403a774fe2c1759b71d3e7b4ca237a5e67ad066c7199"}, - {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:54d9ff35d4515debf14bc27f1e3b38bfc453eff3220f5bce159642fa762fe5d4"}, - {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:df5cbb1fbc74a8305b6065d4ade43b993be03dbe0f8b30032cced0d7740994bd"}, - {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:7fb89ee5d106e4a7a51bce305ac4efb981536301895f7bdcf93ec92ae0d91c7f"}, - {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:a738b937d512b30bf75995c0159c0ddf9eec0775c9d72ac0202076c72f24aa96"}, - {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e28f9faeb14b6f23ac55bfbbfd3643f5c7c18ede093977f1df249f73fd22c7b1"}, - {file = "regex-2024.9.11-cp311-cp311-win32.whl", hash = "sha256:18e707ce6c92d7282dfce370cd205098384b8ee21544e7cb29b8aab955b66fa9"}, - {file = "regex-2024.9.11-cp311-cp311-win_amd64.whl", hash = "sha256:313ea15e5ff2a8cbbad96ccef6be638393041b0a7863183c2d31e0c6116688cf"}, - {file = "regex-2024.9.11-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b0d0a6c64fcc4ef9c69bd5b3b3626cc3776520a1637d8abaa62b9edc147a58f7"}, - {file = "regex-2024.9.11-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:49b0e06786ea663f933f3710a51e9385ce0cba0ea56b67107fd841a55d56a231"}, - {file = "regex-2024.9.11-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5b513b6997a0b2f10e4fd3a1313568e373926e8c252bd76c960f96fd039cd28d"}, - {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee439691d8c23e76f9802c42a95cfeebf9d47cf4ffd06f18489122dbb0a7ad64"}, - {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a8f877c89719d759e52783f7fe6e1c67121076b87b40542966c02de5503ace42"}, - {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:23b30c62d0f16827f2ae9f2bb87619bc4fba2044911e2e6c2eb1af0161cdb766"}, - {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85ab7824093d8f10d44330fe1e6493f756f252d145323dd17ab6b48733ff6c0a"}, - {file = "regex-2024.9.11-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8dee5b4810a89447151999428fe096977346cf2f29f4d5e29609d2e19e0199c9"}, - {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:98eeee2f2e63edae2181c886d7911ce502e1292794f4c5ee71e60e23e8d26b5d"}, - {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:57fdd2e0b2694ce6fc2e5ccf189789c3e2962916fb38779d3e3521ff8fe7a822"}, - {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:d552c78411f60b1fdaafd117a1fca2f02e562e309223b9d44b7de8be451ec5e0"}, - {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:a0b2b80321c2ed3fcf0385ec9e51a12253c50f146fddb2abbb10f033fe3d049a"}, - {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:18406efb2f5a0e57e3a5881cd9354c1512d3bb4f5c45d96d110a66114d84d23a"}, - {file = "regex-2024.9.11-cp312-cp312-win32.whl", hash = "sha256:e464b467f1588e2c42d26814231edecbcfe77f5ac414d92cbf4e7b55b2c2a776"}, - {file = "regex-2024.9.11-cp312-cp312-win_amd64.whl", hash = "sha256:9e8719792ca63c6b8340380352c24dcb8cd7ec49dae36e963742a275dfae6009"}, - {file = "regex-2024.9.11-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c157bb447303070f256e084668b702073db99bbb61d44f85d811025fcf38f784"}, - {file = "regex-2024.9.11-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4db21ece84dfeefc5d8a3863f101995de646c6cb0536952c321a2650aa202c36"}, - {file = "regex-2024.9.11-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:220e92a30b426daf23bb67a7962900ed4613589bab80382be09b48896d211e92"}, - {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb1ae19e64c14c7ec1995f40bd932448713d3c73509e82d8cd7744dc00e29e86"}, - {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f47cd43a5bfa48f86925fe26fbdd0a488ff15b62468abb5d2a1e092a4fb10e85"}, - {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9d4a76b96f398697fe01117093613166e6aa8195d63f1b4ec3f21ab637632963"}, - {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ea51dcc0835eea2ea31d66456210a4e01a076d820e9039b04ae8d17ac11dee6"}, - {file = "regex-2024.9.11-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7aaa315101c6567a9a45d2839322c51c8d6e81f67683d529512f5bcfb99c802"}, - {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c57d08ad67aba97af57a7263c2d9006d5c404d721c5f7542f077f109ec2a4a29"}, - {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f8404bf61298bb6f8224bb9176c1424548ee1181130818fcd2cbffddc768bed8"}, - {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:dd4490a33eb909ef5078ab20f5f000087afa2a4daa27b4c072ccb3cb3050ad84"}, - {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:eee9130eaad130649fd73e5cd92f60e55708952260ede70da64de420cdcad554"}, - {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6a2644a93da36c784e546de579ec1806bfd2763ef47babc1b03d765fe560c9f8"}, - {file = "regex-2024.9.11-cp313-cp313-win32.whl", hash = "sha256:e997fd30430c57138adc06bba4c7c2968fb13d101e57dd5bb9355bf8ce3fa7e8"}, - {file = "regex-2024.9.11-cp313-cp313-win_amd64.whl", hash = "sha256:042c55879cfeb21a8adacc84ea347721d3d83a159da6acdf1116859e2427c43f"}, - {file = "regex-2024.9.11-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:07f45f287469039ffc2c53caf6803cd506eb5f5f637f1d4acb37a738f71dd066"}, - {file = "regex-2024.9.11-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4838e24ee015101d9f901988001038f7f0d90dc0c3b115541a1365fb439add62"}, - {file = "regex-2024.9.11-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6edd623bae6a737f10ce853ea076f56f507fd7726bee96a41ee3d68d347e4d16"}, - {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c69ada171c2d0e97a4b5aa78fbb835e0ffbb6b13fc5da968c09811346564f0d3"}, - {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:02087ea0a03b4af1ed6ebab2c54d7118127fee8d71b26398e8e4b05b78963199"}, - {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:69dee6a020693d12a3cf892aba4808fe168d2a4cef368eb9bf74f5398bfd4ee8"}, - {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:297f54910247508e6e5cae669f2bc308985c60540a4edd1c77203ef19bfa63ca"}, - {file = "regex-2024.9.11-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ecea58b43a67b1b79805f1a0255730edaf5191ecef84dbc4cc85eb30bc8b63b9"}, - {file = "regex-2024.9.11-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:eab4bb380f15e189d1313195b062a6aa908f5bd687a0ceccd47c8211e9cf0d4a"}, - {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0cbff728659ce4bbf4c30b2a1be040faafaa9eca6ecde40aaff86f7889f4ab39"}, - {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:54c4a097b8bc5bb0dfc83ae498061d53ad7b5762e00f4adaa23bee22b012e6ba"}, - {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:73d6d2f64f4d894c96626a75578b0bf7d9e56dcda8c3d037a2118fdfe9b1c664"}, - {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:e53b5fbab5d675aec9f0c501274c467c0f9a5d23696cfc94247e1fb56501ed89"}, - {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0ffbcf9221e04502fc35e54d1ce9567541979c3fdfb93d2c554f0ca583a19b35"}, - {file = "regex-2024.9.11-cp39-cp39-win32.whl", hash = "sha256:e4c22e1ac1f1ec1e09f72e6c44d8f2244173db7eb9629cc3a346a8d7ccc31142"}, - {file = "regex-2024.9.11-cp39-cp39-win_amd64.whl", hash = "sha256:faa3c142464efec496967359ca99696c896c591c56c53506bac1ad465f66e919"}, - {file = "regex-2024.9.11.tar.gz", hash = "sha256:6c188c307e8433bcb63dc1915022deb553b4203a70722fc542c363bf120a01fd"}, -] - -[[package]] -name = "requests" -version = "2.32.3" -requires_python = ">=3.8" -summary = "Python HTTP for Humans." -groups = ["default", "docs"] -dependencies = [ - "certifi>=2017.4.17", - "charset-normalizer<4,>=2", - "idna<4,>=2.5", - "urllib3<3,>=1.21.1", -] -files = [ - {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, - {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, -] - -[[package]] -name = "rich" -version = "13.8.1" -requires_python = ">=3.7.0" -summary = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" -groups = ["default"] -dependencies = [ - "markdown-it-py>=2.2.0", - "pygments<3.0.0,>=2.13.0", - "typing-extensions<5.0,>=4.0.0; python_version < \"3.9\"", -] -files = [ - {file = "rich-13.8.1-py3-none-any.whl", hash = "sha256:1760a3c0848469b97b558fc61c85233e3dafb69c7a071b4d60c38099d3cd4c06"}, - {file = "rich-13.8.1.tar.gz", hash = "sha256:8260cda28e3db6bf04d2d1ef4dbc03ba80a824c88b0e7668a0f23126a424844a"}, -] - -[[package]] -name = "rpds-py" -version = "0.20.0" -requires_python = ">=3.8" -summary = "Python bindings to Rust's persistent data structures (rpds)" -groups = ["docs"] -files = [ - {file = "rpds_py-0.20.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3ad0fda1635f8439cde85c700f964b23ed5fc2d28016b32b9ee5fe30da5c84e2"}, - {file = "rpds_py-0.20.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9bb4a0d90fdb03437c109a17eade42dfbf6190408f29b2744114d11586611d6f"}, - {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6377e647bbfd0a0b159fe557f2c6c602c159fc752fa316572f012fc0bf67150"}, - {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb851b7df9dda52dc1415ebee12362047ce771fc36914586b2e9fcbd7d293b3e"}, - {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1e0f80b739e5a8f54837be5d5c924483996b603d5502bfff79bf33da06164ee2"}, - {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a8c94dad2e45324fc74dce25e1645d4d14df9a4e54a30fa0ae8bad9a63928e3"}, - {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8e604fe73ba048c06085beaf51147eaec7df856824bfe7b98657cf436623daf"}, - {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:df3de6b7726b52966edf29663e57306b23ef775faf0ac01a3e9f4012a24a4140"}, - {file = "rpds_py-0.20.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf258ede5bc22a45c8e726b29835b9303c285ab46fc7c3a4cc770736b5304c9f"}, - {file = "rpds_py-0.20.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:55fea87029cded5df854ca7e192ec7bdb7ecd1d9a3f63d5c4eb09148acf4a7ce"}, - {file = "rpds_py-0.20.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ae94bd0b2f02c28e199e9bc51485d0c5601f58780636185660f86bf80c89af94"}, - {file = "rpds_py-0.20.0-cp310-none-win32.whl", hash = "sha256:28527c685f237c05445efec62426d285e47a58fb05ba0090a4340b73ecda6dee"}, - {file = "rpds_py-0.20.0-cp310-none-win_amd64.whl", hash = "sha256:238a2d5b1cad28cdc6ed15faf93a998336eb041c4e440dd7f902528b8891b399"}, - {file = "rpds_py-0.20.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:ac2f4f7a98934c2ed6505aead07b979e6f999389f16b714448fb39bbaa86a489"}, - {file = "rpds_py-0.20.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:220002c1b846db9afd83371d08d239fdc865e8f8c5795bbaec20916a76db3318"}, - {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d7919548df3f25374a1f5d01fbcd38dacab338ef5f33e044744b5c36729c8db"}, - {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:758406267907b3781beee0f0edfe4a179fbd97c0be2e9b1154d7f0a1279cf8e5"}, - {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3d61339e9f84a3f0767b1995adfb171a0d00a1185192718a17af6e124728e0f5"}, - {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1259c7b3705ac0a0bd38197565a5d603218591d3f6cee6e614e380b6ba61c6f6"}, - {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c1dc0f53856b9cc9a0ccca0a7cc61d3d20a7088201c0937f3f4048c1718a209"}, - {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7e60cb630f674a31f0368ed32b2a6b4331b8350d67de53c0359992444b116dd3"}, - {file = "rpds_py-0.20.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dbe982f38565bb50cb7fb061ebf762c2f254ca3d8c20d4006878766e84266272"}, - {file = "rpds_py-0.20.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:514b3293b64187172bc77c8fb0cdae26981618021053b30d8371c3a902d4d5ad"}, - {file = "rpds_py-0.20.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d0a26ffe9d4dd35e4dfdd1e71f46401cff0181c75ac174711ccff0459135fa58"}, - {file = "rpds_py-0.20.0-cp311-none-win32.whl", hash = "sha256:89c19a494bf3ad08c1da49445cc5d13d8fefc265f48ee7e7556839acdacf69d0"}, - {file = "rpds_py-0.20.0-cp311-none-win_amd64.whl", hash = "sha256:c638144ce971df84650d3ed0096e2ae7af8e62ecbbb7b201c8935c370df00a2c"}, - {file = "rpds_py-0.20.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a84ab91cbe7aab97f7446652d0ed37d35b68a465aeef8fc41932a9d7eee2c1a6"}, - {file = "rpds_py-0.20.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:56e27147a5a4c2c21633ff8475d185734c0e4befd1c989b5b95a5d0db699b21b"}, - {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2580b0c34583b85efec8c5c5ec9edf2dfe817330cc882ee972ae650e7b5ef739"}, - {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b80d4a7900cf6b66bb9cee5c352b2d708e29e5a37fe9bf784fa97fc11504bf6c"}, - {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50eccbf054e62a7b2209b28dc7a22d6254860209d6753e6b78cfaeb0075d7bee"}, - {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:49a8063ea4296b3a7e81a5dfb8f7b2d73f0b1c20c2af401fb0cdf22e14711a96"}, - {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea438162a9fcbee3ecf36c23e6c68237479f89f962f82dae83dc15feeceb37e4"}, - {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:18d7585c463087bddcfa74c2ba267339f14f2515158ac4db30b1f9cbdb62c8ef"}, - {file = "rpds_py-0.20.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d4c7d1a051eeb39f5c9547e82ea27cbcc28338482242e3e0b7768033cb083821"}, - {file = "rpds_py-0.20.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e4df1e3b3bec320790f699890d41c59d250f6beda159ea3c44c3f5bac1976940"}, - {file = "rpds_py-0.20.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2cf126d33a91ee6eedc7f3197b53e87a2acdac63602c0f03a02dd69e4b138174"}, - {file = "rpds_py-0.20.0-cp312-none-win32.whl", hash = "sha256:8bc7690f7caee50b04a79bf017a8d020c1f48c2a1077ffe172abec59870f1139"}, - {file = "rpds_py-0.20.0-cp312-none-win_amd64.whl", hash = "sha256:0e13e6952ef264c40587d510ad676a988df19adea20444c2b295e536457bc585"}, - {file = "rpds_py-0.20.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:aa9a0521aeca7d4941499a73ad7d4f8ffa3d1affc50b9ea11d992cd7eff18a29"}, - {file = "rpds_py-0.20.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4a1f1d51eccb7e6c32ae89243cb352389228ea62f89cd80823ea7dd1b98e0b91"}, - {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a86a9b96070674fc88b6f9f71a97d2c1d3e5165574615d1f9168ecba4cecb24"}, - {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6c8ef2ebf76df43f5750b46851ed1cdf8f109d7787ca40035fe19fbdc1acc5a7"}, - {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b74b25f024b421d5859d156750ea9a65651793d51b76a2e9238c05c9d5f203a9"}, - {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57eb94a8c16ab08fef6404301c38318e2c5a32216bf5de453e2714c964c125c8"}, - {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1940dae14e715e2e02dfd5b0f64a52e8374a517a1e531ad9412319dc3ac7879"}, - {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d20277fd62e1b992a50c43f13fbe13277a31f8c9f70d59759c88f644d66c619f"}, - {file = "rpds_py-0.20.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:06db23d43f26478303e954c34c75182356ca9aa7797d22c5345b16871ab9c45c"}, - {file = "rpds_py-0.20.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b2a5db5397d82fa847e4c624b0c98fe59d2d9b7cf0ce6de09e4d2e80f8f5b3f2"}, - {file = "rpds_py-0.20.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5a35df9f5548fd79cb2f52d27182108c3e6641a4feb0f39067911bf2adaa3e57"}, - {file = "rpds_py-0.20.0-cp313-none-win32.whl", hash = "sha256:fd2d84f40633bc475ef2d5490b9c19543fbf18596dcb1b291e3a12ea5d722f7a"}, - {file = "rpds_py-0.20.0-cp313-none-win_amd64.whl", hash = "sha256:9bc2d153989e3216b0559251b0c260cfd168ec78b1fac33dd485750a228db5a2"}, - {file = "rpds_py-0.20.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:3fde368e9140312b6e8b6c09fb9f8c8c2f00999d1823403ae90cc00480221b22"}, - {file = "rpds_py-0.20.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9824fb430c9cf9af743cf7aaf6707bf14323fb51ee74425c380f4c846ea70789"}, - {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11ef6ce74616342888b69878d45e9f779b95d4bd48b382a229fe624a409b72c5"}, - {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c52d3f2f82b763a24ef52f5d24358553e8403ce05f893b5347098014f2d9eff2"}, - {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d35cef91e59ebbeaa45214861874bc6f19eb35de96db73e467a8358d701a96c"}, - {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d72278a30111e5b5525c1dd96120d9e958464316f55adb030433ea905866f4de"}, - {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4c29cbbba378759ac5786730d1c3cb4ec6f8ababf5c42a9ce303dc4b3d08cda"}, - {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6632f2d04f15d1bd6fe0eedd3b86d9061b836ddca4c03d5cf5c7e9e6b7c14580"}, - {file = "rpds_py-0.20.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:d0b67d87bb45ed1cd020e8fbf2307d449b68abc45402fe1a4ac9e46c3c8b192b"}, - {file = "rpds_py-0.20.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ec31a99ca63bf3cd7f1a5ac9fe95c5e2d060d3c768a09bc1d16e235840861420"}, - {file = "rpds_py-0.20.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:22e6c9976e38f4d8c4a63bd8a8edac5307dffd3ee7e6026d97f3cc3a2dc02a0b"}, - {file = "rpds_py-0.20.0-cp39-none-win32.whl", hash = "sha256:569b3ea770c2717b730b61998b6c54996adee3cef69fc28d444f3e7920313cf7"}, - {file = "rpds_py-0.20.0-cp39-none-win_amd64.whl", hash = "sha256:e6900ecdd50ce0facf703f7a00df12374b74bbc8ad9fe0f6559947fb20f82364"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:617c7357272c67696fd052811e352ac54ed1d9b49ab370261a80d3b6ce385045"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9426133526f69fcaba6e42146b4e12d6bc6c839b8b555097020e2b78ce908dcc"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:deb62214c42a261cb3eb04d474f7155279c1a8a8c30ac89b7dcb1721d92c3c02"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fcaeb7b57f1a1e071ebd748984359fef83ecb026325b9d4ca847c95bc7311c92"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d454b8749b4bd70dd0a79f428731ee263fa6995f83ccb8bada706e8d1d3ff89d"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d807dc2051abe041b6649681dce568f8e10668e3c1c6543ebae58f2d7e617855"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3c20f0ddeb6e29126d45f89206b8291352b8c5b44384e78a6499d68b52ae511"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b7f19250ceef892adf27f0399b9e5afad019288e9be756d6919cb58892129f51"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:4f1ed4749a08379555cebf4650453f14452eaa9c43d0a95c49db50c18b7da075"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:dcedf0b42bcb4cfff4101d7771a10532415a6106062f005ab97d1d0ab5681c60"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:39ed0d010457a78f54090fafb5d108501b5aa5604cc22408fc1c0c77eac14344"}, - {file = "rpds_py-0.20.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:bb273176be34a746bdac0b0d7e4e2c467323d13640b736c4c477881a3220a989"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f918a1a130a6dfe1d7fe0f105064141342e7dd1611f2e6a21cd2f5c8cb1cfb3e"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:f60012a73aa396be721558caa3a6fd49b3dd0033d1675c6d59c4502e870fcf0c"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d2b1ad682a3dfda2a4e8ad8572f3100f95fad98cb99faf37ff0ddfe9cbf9d03"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:614fdafe9f5f19c63ea02817fa4861c606a59a604a77c8cdef5aa01d28b97921"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fa518bcd7600c584bf42e6617ee8132869e877db2f76bcdc281ec6a4113a53ab"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0475242f447cc6cb8a9dd486d68b2ef7fbee84427124c232bff5f63b1fe11e5"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f90a4cd061914a60bd51c68bcb4357086991bd0bb93d8aa66a6da7701370708f"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:def7400461c3a3f26e49078302e1c1b38f6752342c77e3cf72ce91ca69fb1bc1"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:65794e4048ee837494aea3c21a28ad5fc080994dfba5b036cf84de37f7ad5074"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:faefcc78f53a88f3076b7f8be0a8f8d35133a3ecf7f3770895c25f8813460f08"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:5b4f105deeffa28bbcdff6c49b34e74903139afa690e35d2d9e3c2c2fba18cec"}, - {file = "rpds_py-0.20.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:fdfc3a892927458d98f3d55428ae46b921d1f7543b89382fdb483f5640daaec8"}, - {file = "rpds_py-0.20.0.tar.gz", hash = "sha256:d72a210824facfdaf8768cf2d7ca25a042c30320b3020de2fa04640920d4e121"}, -] - -[[package]] -name = "scipy" -version = "1.13.1" -requires_python = ">=3.9" -summary = "Fundamental algorithms for scientific computing in Python" -groups = ["default"] -dependencies = [ - "numpy<2.3,>=1.22.4", -] -files = [ - {file = "scipy-1.13.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:20335853b85e9a49ff7572ab453794298bcf0354d8068c5f6775a0eabf350aca"}, - {file = "scipy-1.13.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:d605e9c23906d1994f55ace80e0125c587f96c020037ea6aa98d01b4bd2e222f"}, - {file = "scipy-1.13.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cfa31f1def5c819b19ecc3a8b52d28ffdcc7ed52bb20c9a7589669dd3c250989"}, - {file = "scipy-1.13.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f26264b282b9da0952a024ae34710c2aff7d27480ee91a2e82b7b7073c24722f"}, - {file = "scipy-1.13.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:eccfa1906eacc02de42d70ef4aecea45415f5be17e72b61bafcfd329bdc52e94"}, - {file = "scipy-1.13.1-cp310-cp310-win_amd64.whl", hash = "sha256:2831f0dc9c5ea9edd6e51e6e769b655f08ec6db6e2e10f86ef39bd32eb11da54"}, - {file = "scipy-1.13.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:27e52b09c0d3a1d5b63e1105f24177e544a222b43611aaf5bc44d4a0979e32f9"}, - {file = "scipy-1.13.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:54f430b00f0133e2224c3ba42b805bfd0086fe488835effa33fa291561932326"}, - {file = "scipy-1.13.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e89369d27f9e7b0884ae559a3a956e77c02114cc60a6058b4e5011572eea9299"}, - {file = "scipy-1.13.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a78b4b3345f1b6f68a763c6e25c0c9a23a9fd0f39f5f3d200efe8feda560a5fa"}, - {file = "scipy-1.13.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:45484bee6d65633752c490404513b9ef02475b4284c4cfab0ef946def50b3f59"}, - {file = "scipy-1.13.1-cp311-cp311-win_amd64.whl", hash = "sha256:5713f62f781eebd8d597eb3f88b8bf9274e79eeabf63afb4a737abc6c84ad37b"}, - {file = "scipy-1.13.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5d72782f39716b2b3509cd7c33cdc08c96f2f4d2b06d51e52fb45a19ca0c86a1"}, - {file = "scipy-1.13.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:017367484ce5498445aade74b1d5ab377acdc65e27095155e448c88497755a5d"}, - {file = "scipy-1.13.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:949ae67db5fa78a86e8fa644b9a6b07252f449dcf74247108c50e1d20d2b4627"}, - {file = "scipy-1.13.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de3ade0e53bc1f21358aa74ff4830235d716211d7d077e340c7349bc3542e884"}, - {file = "scipy-1.13.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2ac65fb503dad64218c228e2dc2d0a0193f7904747db43014645ae139c8fad16"}, - {file = "scipy-1.13.1-cp312-cp312-win_amd64.whl", hash = "sha256:cdd7dacfb95fea358916410ec61bbc20440f7860333aee6d882bb8046264e949"}, - {file = "scipy-1.13.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:436bbb42a94a8aeef855d755ce5a465479c721e9d684de76bf61a62e7c2b81d5"}, - {file = "scipy-1.13.1-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:8335549ebbca860c52bf3d02f80784e91a004b71b059e3eea9678ba994796a24"}, - {file = "scipy-1.13.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d533654b7d221a6a97304ab63c41c96473ff04459e404b83275b60aa8f4b7004"}, - {file = "scipy-1.13.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:637e98dcf185ba7f8e663e122ebf908c4702420477ae52a04f9908707456ba4d"}, - {file = "scipy-1.13.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a014c2b3697bde71724244f63de2476925596c24285c7a637364761f8710891c"}, - {file = "scipy-1.13.1-cp39-cp39-win_amd64.whl", hash = "sha256:392e4ec766654852c25ebad4f64e4e584cf19820b980bc04960bca0b0cd6eaa2"}, - {file = "scipy-1.13.1.tar.gz", hash = "sha256:095a87a0312b08dfd6a6155cbbd310a8c51800fc931b8c0b84003014b874ed3c"}, -] - -[[package]] -name = "shellingham" -version = "1.5.4" -requires_python = ">=3.7" -summary = "Tool to Detect Surrounding Shell" -groups = ["default"] -files = [ - {file = "shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686"}, - {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"}, -] - -[[package]] -name = "six" -version = "1.16.0" -requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" -summary = "Python 2 and 3 compatibility utilities" -groups = ["default", "docs"] -files = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, -] - -[[package]] -name = "smmap" -version = "5.0.1" -requires_python = ">=3.7" -summary = "A pure Python implementation of a sliding window memory map manager" -groups = ["docs"] -files = [ - {file = "smmap-5.0.1-py3-none-any.whl", hash = "sha256:e6d8668fa5f93e706934a62d7b4db19c8d9eb8cf2adbb75ef1b675aa332b69da"}, - {file = "smmap-5.0.1.tar.gz", hash = "sha256:dceeb6c0028fdb6734471eb07c0cd2aae706ccaecab45965ee83f11c8d3b1f62"}, -] - -[[package]] -name = "sniffio" -version = "1.3.1" -requires_python = ">=3.7" -summary = "Sniff out which async library your code is running under" -groups = ["default"] -files = [ - {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, - {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, -] - -[[package]] -name = "soupsieve" -version = "2.6" -requires_python = ">=3.8" -summary = "A modern CSS selector implementation for Beautiful Soup." -groups = ["docs"] -files = [ - {file = "soupsieve-2.6-py3-none-any.whl", hash = "sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9"}, - {file = "soupsieve-2.6.tar.gz", hash = "sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb"}, -] - -[[package]] -name = "tabulate" -version = "0.9.0" -requires_python = ">=3.7" -summary = "Pretty-print tabular data" -groups = ["docs"] -files = [ - {file = "tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f"}, - {file = "tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c"}, -] - -[[package]] -name = "tblib" -version = "3.0.0" -requires_python = ">=3.8" -summary = "Traceback serialization library." -groups = ["default"] -files = [ - {file = "tblib-3.0.0-py3-none-any.whl", hash = "sha256:80a6c77e59b55e83911e1e607c649836a69c103963c5f28a46cbeef44acf8129"}, - {file = "tblib-3.0.0.tar.gz", hash = "sha256:93622790a0a29e04f0346458face1e144dc4d32f493714c6c3dff82a4adb77e6"}, -] - -[[package]] -name = "tinycss2" -version = "1.3.0" -requires_python = ">=3.8" -summary = "A tiny CSS parser" -groups = ["docs"] -dependencies = [ - "webencodings>=0.4", -] -files = [ - {file = "tinycss2-1.3.0-py3-none-any.whl", hash = "sha256:54a8dbdffb334d536851be0226030e9505965bb2f30f21a4a82c55fb2a80fae7"}, - {file = "tinycss2-1.3.0.tar.gz", hash = "sha256:152f9acabd296a8375fbca5b84c961ff95971fcfc32e79550c8df8e29118c54d"}, -] - -[[package]] -name = "tornado" -version = "6.4.1" -requires_python = ">=3.8" -summary = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." -groups = ["docs"] -files = [ - {file = "tornado-6.4.1-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:163b0aafc8e23d8cdc3c9dfb24c5368af84a81e3364745ccb4427669bf84aec8"}, - {file = "tornado-6.4.1-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:6d5ce3437e18a2b66fbadb183c1d3364fb03f2be71299e7d10dbeeb69f4b2a14"}, - {file = "tornado-6.4.1-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2e20b9113cd7293f164dc46fffb13535266e713cdb87bd2d15ddb336e96cfc4"}, - {file = "tornado-6.4.1-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ae50a504a740365267b2a8d1a90c9fbc86b780a39170feca9bcc1787ff80842"}, - {file = "tornado-6.4.1-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:613bf4ddf5c7a95509218b149b555621497a6cc0d46ac341b30bd9ec19eac7f3"}, - {file = "tornado-6.4.1-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:25486eb223babe3eed4b8aecbac33b37e3dd6d776bc730ca14e1bf93888b979f"}, - {file = "tornado-6.4.1-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:454db8a7ecfcf2ff6042dde58404164d969b6f5d58b926da15e6b23817950fc4"}, - {file = "tornado-6.4.1-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a02a08cc7a9314b006f653ce40483b9b3c12cda222d6a46d4ac63bb6c9057698"}, - {file = "tornado-6.4.1-cp38-abi3-win32.whl", hash = "sha256:d9a566c40b89757c9aa8e6f032bcdb8ca8795d7c1a9762910c722b1635c9de4d"}, - {file = "tornado-6.4.1-cp38-abi3-win_amd64.whl", hash = "sha256:b24b8982ed444378d7f21d563f4180a2de31ced9d8d84443907a0a64da2072e7"}, - {file = "tornado-6.4.1.tar.gz", hash = "sha256:92d3ab53183d8c50f8204a51e6f91d18a15d5ef261e84d452800d4ff6fc504e9"}, -] - -[[package]] -name = "tqdm" -version = "4.66.5" -requires_python = ">=3.7" -summary = "Fast, Extensible Progress Meter" -groups = ["default"] -dependencies = [ - "colorama; platform_system == \"Windows\"", -] -files = [ - {file = "tqdm-4.66.5-py3-none-any.whl", hash = "sha256:90279a3770753eafc9194a0364852159802111925aa30eb3f9d85b0e805ac7cd"}, - {file = "tqdm-4.66.5.tar.gz", hash = "sha256:e1020aef2e5096702d8a025ac7d16b1577279c9d63f8375b63083e9a5f0fcbad"}, -] - -[[package]] -name = "traitlets" -version = "5.14.3" -requires_python = ">=3.8" -summary = "Traitlets Python configuration system" -groups = ["docs"] -files = [ - {file = "traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f"}, - {file = "traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7"}, -] - -[[package]] -name = "typer" -version = "0.12.5" -requires_python = ">=3.7" -summary = "Typer, build great CLIs. Easy to code. Based on Python type hints." -groups = ["default"] -dependencies = [ - "click>=8.0.0", - "rich>=10.11.0", - "shellingham>=1.3.0", - "typing-extensions>=3.7.4.3", -] -files = [ - {file = "typer-0.12.5-py3-none-any.whl", hash = "sha256:62fe4e471711b147e3365034133904df3e235698399bc4de2b36c8579298d52b"}, - {file = "typer-0.12.5.tar.gz", hash = "sha256:f592f089bedcc8ec1b974125d64851029c3b1af145f04aca64d69410f0c9b722"}, -] - -[[package]] -name = "typing-extensions" -version = "4.12.2" -requires_python = ">=3.8" -summary = "Backported and Experimental Type Hints for Python 3.8+" -groups = ["default", "docs"] -files = [ - {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, - {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, -] - -[[package]] -name = "tzdata" -version = "2024.1" -requires_python = ">=2" -summary = "Provider of IANA time zone data" -groups = ["default", "docs"] -files = [ - {file = "tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252"}, - {file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"}, -] - -[[package]] -name = "universal-pathlib" -version = "0.2.5" -requires_python = ">=3.8" -summary = "pathlib api extended to use fsspec backends" -groups = ["default"] -dependencies = [ - "fsspec!=2024.3.1,>=2022.1.0", -] -files = [ - {file = "universal_pathlib-0.2.5-py3-none-any.whl", hash = "sha256:a634f700eca827b4ad03bfa0267e51161560dd1de83b051cf0fccf39b3e56b32"}, - {file = "universal_pathlib-0.2.5.tar.gz", hash = "sha256:ea5d4fb8178c2ab469cf4fa46d0ceb16ccb378da46dbbc28a8b9c1eebdccc655"}, -] - -[[package]] -name = "urllib3" -version = "2.2.3" -requires_python = ">=3.8" -summary = "HTTP library with thread-safe connection pooling, file post, and more." -groups = ["default", "docs"] -files = [ - {file = "urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac"}, - {file = "urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9"}, -] - -[[package]] -name = "verspec" -version = "0.1.0" -summary = "Flexible version handling" -groups = ["docs"] -files = [ - {file = "verspec-0.1.0-py3-none-any.whl", hash = "sha256:741877d5633cc9464c45a469ae2a31e801e6dbbaa85b9675d481cda100f11c31"}, - {file = "verspec-0.1.0.tar.gz", hash = "sha256:c4504ca697b2056cdb4bfa7121461f5a0e81809255b41c03dda4ba823637c01e"}, -] - -[[package]] -name = "watchdog" -version = "5.0.2" -requires_python = ">=3.9" -summary = "Filesystem events monitoring" -groups = ["docs"] -files = [ - {file = "watchdog-5.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d961f4123bb3c447d9fcdcb67e1530c366f10ab3a0c7d1c0c9943050936d4877"}, - {file = "watchdog-5.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72990192cb63872c47d5e5fefe230a401b87fd59d257ee577d61c9e5564c62e5"}, - {file = "watchdog-5.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6bec703ad90b35a848e05e1b40bf0050da7ca28ead7ac4be724ae5ac2653a1a0"}, - {file = "watchdog-5.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:dae7a1879918f6544201d33666909b040a46421054a50e0f773e0d870ed7438d"}, - {file = "watchdog-5.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c4a440f725f3b99133de610bfec93d570b13826f89616377715b9cd60424db6e"}, - {file = "watchdog-5.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f8b2918c19e0d48f5f20df458c84692e2a054f02d9df25e6c3c930063eca64c1"}, - {file = "watchdog-5.0.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:aa9cd6e24126d4afb3752a3e70fce39f92d0e1a58a236ddf6ee823ff7dba28ee"}, - {file = "watchdog-5.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f627c5bf5759fdd90195b0c0431f99cff4867d212a67b384442c51136a098ed7"}, - {file = "watchdog-5.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d7594a6d32cda2b49df3fd9abf9b37c8d2f3eab5df45c24056b4a671ac661619"}, - {file = "watchdog-5.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba32efcccfe2c58f4d01115440d1672b4eb26cdd6fc5b5818f1fb41f7c3e1889"}, - {file = "watchdog-5.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:963f7c4c91e3f51c998eeff1b3fb24a52a8a34da4f956e470f4b068bb47b78ee"}, - {file = "watchdog-5.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8c47150aa12f775e22efff1eee9f0f6beee542a7aa1a985c271b1997d340184f"}, - {file = "watchdog-5.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:14dd4ed023d79d1f670aa659f449bcd2733c33a35c8ffd88689d9d243885198b"}, - {file = "watchdog-5.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b84bff0391ad4abe25c2740c7aec0e3de316fdf7764007f41e248422a7760a7f"}, - {file = "watchdog-5.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3e8d5ff39f0a9968952cce548e8e08f849141a4fcc1290b1c17c032ba697b9d7"}, - {file = "watchdog-5.0.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:fb223456db6e5f7bd9bbd5cd969f05aae82ae21acc00643b60d81c770abd402b"}, - {file = "watchdog-5.0.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9814adb768c23727a27792c77812cf4e2fd9853cd280eafa2bcfa62a99e8bd6e"}, - {file = "watchdog-5.0.2-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:901ee48c23f70193d1a7bc2d9ee297df66081dd5f46f0ca011be4f70dec80dab"}, - {file = "watchdog-5.0.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:638bcca3d5b1885c6ec47be67bf712b00a9ab3d4b22ec0881f4889ad870bc7e8"}, - {file = "watchdog-5.0.2-py3-none-manylinux2014_aarch64.whl", hash = "sha256:5597c051587f8757798216f2485e85eac583c3b343e9aa09127a3a6f82c65ee8"}, - {file = "watchdog-5.0.2-py3-none-manylinux2014_armv7l.whl", hash = "sha256:53ed1bf71fcb8475dd0ef4912ab139c294c87b903724b6f4a8bd98e026862e6d"}, - {file = "watchdog-5.0.2-py3-none-manylinux2014_i686.whl", hash = "sha256:29e4a2607bd407d9552c502d38b45a05ec26a8e40cc7e94db9bb48f861fa5abc"}, - {file = "watchdog-5.0.2-py3-none-manylinux2014_ppc64.whl", hash = "sha256:b6dc8f1d770a8280997e4beae7b9a75a33b268c59e033e72c8a10990097e5fde"}, - {file = "watchdog-5.0.2-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:d2ab34adc9bf1489452965cdb16a924e97d4452fcf88a50b21859068b50b5c3b"}, - {file = "watchdog-5.0.2-py3-none-manylinux2014_s390x.whl", hash = "sha256:7d1aa7e4bb0f0c65a1a91ba37c10e19dabf7eaaa282c5787e51371f090748f4b"}, - {file = "watchdog-5.0.2-py3-none-manylinux2014_x86_64.whl", hash = "sha256:726eef8f8c634ac6584f86c9c53353a010d9f311f6c15a034f3800a7a891d941"}, - {file = "watchdog-5.0.2-py3-none-win32.whl", hash = "sha256:bda40c57115684d0216556671875e008279dea2dc00fcd3dde126ac8e0d7a2fb"}, - {file = "watchdog-5.0.2-py3-none-win_amd64.whl", hash = "sha256:d010be060c996db725fbce7e3ef14687cdcc76f4ca0e4339a68cc4532c382a73"}, - {file = "watchdog-5.0.2-py3-none-win_ia64.whl", hash = "sha256:3960136b2b619510569b90f0cd96408591d6c251a75c97690f4553ca88889769"}, - {file = "watchdog-5.0.2.tar.gz", hash = "sha256:dcebf7e475001d2cdeb020be630dc5b687e9acdd60d16fea6bb4508e7b94cf76"}, -] - -[[package]] -name = "webencodings" -version = "0.5.1" -summary = "Character encoding aliases for legacy web content" -groups = ["docs"] -files = [ - {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"}, - {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"}, -] - -[[package]] -name = "xxhash" -version = "3.5.0" -requires_python = ">=3.7" -summary = "Python binding for xxHash" -groups = ["default"] -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"}, - {file = "xxhash-3.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c5d3e570ef46adaf93fc81b44aca6002b5a4d8ca11bd0580c07eac537f36680"}, - {file = "xxhash-3.5.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7cb29a034301e2982df8b1fe6328a84f4b676106a13e9135a0d7e0c3e9f806da"}, - {file = "xxhash-3.5.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5d0d307d27099bb0cbeea7260eb39ed4fdb99c5542e21e94bb6fd29e49c57a23"}, - {file = "xxhash-3.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0342aafd421795d740e514bc9858ebddfc705a75a8c5046ac56d85fe97bf196"}, - {file = "xxhash-3.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3dbbd9892c5ebffeca1ed620cf0ade13eb55a0d8c84e0751a6653adc6ac40d0c"}, - {file = "xxhash-3.5.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4cc2d67fdb4d057730c75a64c5923abfa17775ae234a71b0200346bfb0a7f482"}, - {file = "xxhash-3.5.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:ec28adb204b759306a3d64358a5e5c07d7b1dd0ccbce04aa76cb9377b7b70296"}, - {file = "xxhash-3.5.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:1328f6d8cca2b86acb14104e381225a3d7b42c92c4b86ceae814e5c400dbb415"}, - {file = "xxhash-3.5.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8d47ebd9f5d9607fd039c1fbf4994e3b071ea23eff42f4ecef246ab2b7334198"}, - {file = "xxhash-3.5.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b96d559e0fcddd3343c510a0fe2b127fbff16bf346dd76280b82292567523442"}, - {file = "xxhash-3.5.0-cp310-cp310-win32.whl", hash = "sha256:61c722ed8d49ac9bc26c7071eeaa1f6ff24053d553146d5df031802deffd03da"}, - {file = "xxhash-3.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:9bed5144c6923cc902cd14bb8963f2d5e034def4486ab0bbe1f58f03f042f9a9"}, - {file = "xxhash-3.5.0-cp310-cp310-win_arm64.whl", hash = "sha256:893074d651cf25c1cc14e3bea4fceefd67f2921b1bb8e40fcfeba56820de80c6"}, - {file = "xxhash-3.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:02c2e816896dc6f85922ced60097bcf6f008dedfc5073dcba32f9c8dd786f3c1"}, - {file = "xxhash-3.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6027dcd885e21581e46d3c7f682cfb2b870942feeed58a21c29583512c3f09f8"}, - {file = "xxhash-3.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1308fa542bbdbf2fa85e9e66b1077eea3a88bef38ee8a06270b4298a7a62a166"}, - {file = "xxhash-3.5.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c28b2fdcee797e1c1961cd3bcd3d545cab22ad202c846235197935e1df2f8ef7"}, - {file = "xxhash-3.5.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:924361811732ddad75ff23e90efd9ccfda4f664132feecb90895bade6a1b4623"}, - {file = "xxhash-3.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89997aa1c4b6a5b1e5b588979d1da048a3c6f15e55c11d117a56b75c84531f5a"}, - {file = "xxhash-3.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:685c4f4e8c59837de103344eb1c8a3851f670309eb5c361f746805c5471b8c88"}, - {file = "xxhash-3.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dbd2ecfbfee70bc1a4acb7461fa6af7748ec2ab08ac0fa298f281c51518f982c"}, - {file = "xxhash-3.5.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:25b5a51dc3dfb20a10833c8eee25903fd2e14059e9afcd329c9da20609a307b2"}, - {file = "xxhash-3.5.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a8fb786fb754ef6ff8c120cb96629fb518f8eb5a61a16aac3a979a9dbd40a084"}, - {file = "xxhash-3.5.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:a905ad00ad1e1c34fe4e9d7c1d949ab09c6fa90c919860c1534ff479f40fd12d"}, - {file = "xxhash-3.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:963be41bcd49f53af6d795f65c0da9b4cc518c0dd9c47145c98f61cb464f4839"}, - {file = "xxhash-3.5.0-cp311-cp311-win32.whl", hash = "sha256:109b436096d0a2dd039c355fa3414160ec4d843dfecc64a14077332a00aeb7da"}, - {file = "xxhash-3.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:b702f806693201ad6c0a05ddbbe4c8f359626d0b3305f766077d51388a6bac58"}, - {file = "xxhash-3.5.0-cp311-cp311-win_arm64.whl", hash = "sha256:c4dcb4120d0cc3cc448624147dba64e9021b278c63e34a38789b688fd0da9bf3"}, - {file = "xxhash-3.5.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:14470ace8bd3b5d51318782cd94e6f94431974f16cb3b8dc15d52f3b69df8e00"}, - {file = "xxhash-3.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:59aa1203de1cb96dbeab595ded0ad0c0056bb2245ae11fac11c0ceea861382b9"}, - {file = "xxhash-3.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:08424f6648526076e28fae6ea2806c0a7d504b9ef05ae61d196d571e5c879c84"}, - {file = "xxhash-3.5.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:61a1ff00674879725b194695e17f23d3248998b843eb5e933007ca743310f793"}, - {file = "xxhash-3.5.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2f2c61bee5844d41c3eb015ac652a0229e901074951ae48581d58bfb2ba01be"}, - {file = "xxhash-3.5.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d32a592cac88d18cc09a89172e1c32d7f2a6e516c3dfde1b9adb90ab5df54a6"}, - {file = "xxhash-3.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:70dabf941dede727cca579e8c205e61121afc9b28516752fd65724be1355cc90"}, - {file = "xxhash-3.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e5d0ddaca65ecca9c10dcf01730165fd858533d0be84c75c327487c37a906a27"}, - {file = "xxhash-3.5.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3e5b5e16c5a480fe5f59f56c30abdeba09ffd75da8d13f6b9b6fd224d0b4d0a2"}, - {file = "xxhash-3.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:149b7914451eb154b3dfaa721315117ea1dac2cc55a01bfbd4df7c68c5dd683d"}, - {file = "xxhash-3.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:eade977f5c96c677035ff39c56ac74d851b1cca7d607ab3d8f23c6b859379cab"}, - {file = "xxhash-3.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fa9f547bd98f5553d03160967866a71056a60960be00356a15ecc44efb40ba8e"}, - {file = "xxhash-3.5.0-cp312-cp312-win32.whl", hash = "sha256:f7b58d1fd3551b8c80a971199543379be1cee3d0d409e1f6d8b01c1a2eebf1f8"}, - {file = "xxhash-3.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:fa0cafd3a2af231b4e113fba24a65d7922af91aeb23774a8b78228e6cd785e3e"}, - {file = "xxhash-3.5.0-cp312-cp312-win_arm64.whl", hash = "sha256:586886c7e89cb9828bcd8a5686b12e161368e0064d040e225e72607b43858ba2"}, - {file = "xxhash-3.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:37889a0d13b0b7d739cfc128b1c902f04e32de17b33d74b637ad42f1c55101f6"}, - {file = "xxhash-3.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:97a662338797c660178e682f3bc180277b9569a59abfb5925e8620fba00b9fc5"}, - {file = "xxhash-3.5.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f85e0108d51092bdda90672476c7d909c04ada6923c14ff9d913c4f7dc8a3bc"}, - {file = "xxhash-3.5.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cd2fd827b0ba763ac919440042302315c564fdb797294d86e8cdd4578e3bc7f3"}, - {file = "xxhash-3.5.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:82085c2abec437abebf457c1d12fccb30cc8b3774a0814872511f0f0562c768c"}, - {file = "xxhash-3.5.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07fda5de378626e502b42b311b049848c2ef38784d0d67b6f30bb5008642f8eb"}, - {file = "xxhash-3.5.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c279f0d2b34ef15f922b77966640ade58b4ccdfef1c4d94b20f2a364617a493f"}, - {file = "xxhash-3.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:89e66ceed67b213dec5a773e2f7a9e8c58f64daeb38c7859d8815d2c89f39ad7"}, - {file = "xxhash-3.5.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:bcd51708a633410737111e998ceb3b45d3dbc98c0931f743d9bb0a209033a326"}, - {file = "xxhash-3.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3ff2c0a34eae7df88c868be53a8dd56fbdf592109e21d4bfa092a27b0bf4a7bf"}, - {file = "xxhash-3.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:4e28503dccc7d32e0b9817aa0cbfc1f45f563b2c995b7a66c4c8a0d232e840c7"}, - {file = "xxhash-3.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a6c50017518329ed65a9e4829154626f008916d36295b6a3ba336e2458824c8c"}, - {file = "xxhash-3.5.0-cp313-cp313-win32.whl", hash = "sha256:53a068fe70301ec30d868ece566ac90d873e3bb059cf83c32e76012c889b8637"}, - {file = "xxhash-3.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:80babcc30e7a1a484eab952d76a4f4673ff601f54d5142c26826502740e70b43"}, - {file = "xxhash-3.5.0-cp313-cp313-win_arm64.whl", hash = "sha256:4811336f1ce11cac89dcbd18f3a25c527c16311709a89313c3acaf771def2d4b"}, - {file = "xxhash-3.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bfc8cdd7f33d57f0468b0614ae634cc38ab9202c6957a60e31d285a71ebe0301"}, - {file = "xxhash-3.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e0c48b6300cd0b0106bf49169c3e0536408dfbeb1ccb53180068a18b03c662ab"}, - {file = "xxhash-3.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe1a92cfbaa0a1253e339ccec42dbe6db262615e52df591b68726ab10338003f"}, - {file = "xxhash-3.5.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:33513d6cc3ed3b559134fb307aae9bdd94d7e7c02907b37896a6c45ff9ce51bd"}, - {file = "xxhash-3.5.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eefc37f6138f522e771ac6db71a6d4838ec7933939676f3753eafd7d3f4c40bc"}, - {file = "xxhash-3.5.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a606c8070ada8aa2a88e181773fa1ef17ba65ce5dd168b9d08038e2a61b33754"}, - {file = "xxhash-3.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:42eca420c8fa072cc1dd62597635d140e78e384a79bb4944f825fbef8bfeeef6"}, - {file = "xxhash-3.5.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:604253b2143e13218ff1ef0b59ce67f18b8bd1c4205d2ffda22b09b426386898"}, - {file = "xxhash-3.5.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:6e93a5ad22f434d7876665444a97e713a8f60b5b1a3521e8df11b98309bff833"}, - {file = "xxhash-3.5.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:7a46e1d6d2817ba8024de44c4fd79913a90e5f7265434cef97026215b7d30df6"}, - {file = "xxhash-3.5.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:30eb2efe6503c379b7ab99c81ba4a779748e3830241f032ab46bd182bf5873af"}, - {file = "xxhash-3.5.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c8aa771ff2c13dd9cda8166d685d7333d389fae30a4d2bb39d63ab5775de8606"}, - {file = "xxhash-3.5.0-cp39-cp39-win32.whl", hash = "sha256:5ed9ebc46f24cf91034544b26b131241b699edbfc99ec5e7f8f3d02d6eb7fba4"}, - {file = "xxhash-3.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:220f3f896c6b8d0316f63f16c077d52c412619e475f9372333474ee15133a558"}, - {file = "xxhash-3.5.0-cp39-cp39-win_arm64.whl", hash = "sha256:a7b1d8315d9b5e9f89eb2933b73afae6ec9597a258d52190944437158b49d38e"}, - {file = "xxhash-3.5.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:2014c5b3ff15e64feecb6b713af12093f75b7926049e26a580e94dcad3c73d8c"}, - {file = "xxhash-3.5.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fab81ef75003eda96239a23eda4e4543cedc22e34c373edcaf744e721a163986"}, - {file = "xxhash-3.5.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e2febf914ace002132aa09169cc572e0d8959d0f305f93d5828c4836f9bc5a6"}, - {file = "xxhash-3.5.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5d3a10609c51da2a1c0ea0293fc3968ca0a18bd73838455b5bca3069d7f8e32b"}, - {file = "xxhash-3.5.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5a74f23335b9689b66eb6dbe2a931a88fcd7a4c2cc4b1cb0edba8ce381c7a1da"}, - {file = "xxhash-3.5.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:531af8845aaadcadf951b7e0c1345c6b9c68a990eeb74ff9acd8501a0ad6a1c9"}, - {file = "xxhash-3.5.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ce379bcaa9fcc00f19affa7773084dd09f5b59947b3fb47a1ceb0179f91aaa1"}, - {file = "xxhash-3.5.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd1b2281d01723f076df3c8188f43f2472248a6b63118b036e641243656b1b0f"}, - {file = "xxhash-3.5.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9c770750cc80e8694492244bca7251385188bc5597b6a39d98a9f30e8da984e0"}, - {file = "xxhash-3.5.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:b150b8467852e1bd844387459aa6fbe11d7f38b56e901f9f3b3e6aba0d660240"}, - {file = "xxhash-3.5.0.tar.gz", hash = "sha256:84f2caddf951c9cbf8dc2e22a89d4ccf5d86391ac6418fe81e3c67d0cf60b45f"}, -] - -[[package]] -name = "yarl" -version = "1.11.1" -requires_python = ">=3.8" -summary = "Yet another URL library" -groups = ["default"] -dependencies = [ - "idna>=2.0", - "multidict>=4.0", -] -files = [ - {file = "yarl-1.11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:400cd42185f92de559d29eeb529e71d80dfbd2f45c36844914a4a34297ca6f00"}, - {file = "yarl-1.11.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8258c86f47e080a258993eed877d579c71da7bda26af86ce6c2d2d072c11320d"}, - {file = "yarl-1.11.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2164cd9725092761fed26f299e3f276bb4b537ca58e6ff6b252eae9631b5c96e"}, - {file = "yarl-1.11.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08ea567c16f140af8ddc7cb58e27e9138a1386e3e6e53982abaa6f2377b38cc"}, - {file = "yarl-1.11.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:768ecc550096b028754ea28bf90fde071c379c62c43afa574edc6f33ee5daaec"}, - {file = "yarl-1.11.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2909fa3a7d249ef64eeb2faa04b7957e34fefb6ec9966506312349ed8a7e77bf"}, - {file = "yarl-1.11.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01a8697ec24f17c349c4f655763c4db70eebc56a5f82995e5e26e837c6eb0e49"}, - {file = "yarl-1.11.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e286580b6511aac7c3268a78cdb861ec739d3e5a2a53b4809faef6b49778eaff"}, - {file = "yarl-1.11.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4179522dc0305c3fc9782549175c8e8849252fefeb077c92a73889ccbcd508ad"}, - {file = "yarl-1.11.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:27fcb271a41b746bd0e2a92182df507e1c204759f460ff784ca614e12dd85145"}, - {file = "yarl-1.11.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f61db3b7e870914dbd9434b560075e0366771eecbe6d2b5561f5bc7485f39efd"}, - {file = "yarl-1.11.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:c92261eb2ad367629dc437536463dc934030c9e7caca861cc51990fe6c565f26"}, - {file = "yarl-1.11.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d95b52fbef190ca87d8c42f49e314eace4fc52070f3dfa5f87a6594b0c1c6e46"}, - {file = "yarl-1.11.1-cp310-cp310-win32.whl", hash = "sha256:489fa8bde4f1244ad6c5f6d11bb33e09cf0d1d0367edb197619c3e3fc06f3d91"}, - {file = "yarl-1.11.1-cp310-cp310-win_amd64.whl", hash = "sha256:476e20c433b356e16e9a141449f25161e6b69984fb4cdbd7cd4bd54c17844998"}, - {file = "yarl-1.11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:946eedc12895873891aaceb39bceb484b4977f70373e0122da483f6c38faaa68"}, - {file = "yarl-1.11.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:21a7c12321436b066c11ec19c7e3cb9aec18884fe0d5b25d03d756a9e654edfe"}, - {file = "yarl-1.11.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c35f493b867912f6fda721a59cc7c4766d382040bdf1ddaeeaa7fa4d072f4675"}, - {file = "yarl-1.11.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25861303e0be76b60fddc1250ec5986c42f0a5c0c50ff57cc30b1be199c00e63"}, - {file = "yarl-1.11.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e4b53f73077e839b3f89c992223f15b1d2ab314bdbdf502afdc7bb18e95eae27"}, - {file = "yarl-1.11.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:327c724b01b8641a1bf1ab3b232fb638706e50f76c0b5bf16051ab65c868fac5"}, - {file = "yarl-1.11.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4307d9a3417eea87715c9736d050c83e8c1904e9b7aada6ce61b46361b733d92"}, - {file = "yarl-1.11.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48a28bed68ab8fb7e380775f0029a079f08a17799cb3387a65d14ace16c12e2b"}, - {file = "yarl-1.11.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:067b961853c8e62725ff2893226fef3d0da060656a9827f3f520fb1d19b2b68a"}, - {file = "yarl-1.11.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8215f6f21394d1f46e222abeb06316e77ef328d628f593502d8fc2a9117bde83"}, - {file = "yarl-1.11.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:498442e3af2a860a663baa14fbf23fb04b0dd758039c0e7c8f91cb9279799bff"}, - {file = "yarl-1.11.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:69721b8effdb588cb055cc22f7c5105ca6fdaa5aeb3ea09021d517882c4a904c"}, - {file = "yarl-1.11.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1e969fa4c1e0b1a391f3fcbcb9ec31e84440253325b534519be0d28f4b6b533e"}, - {file = "yarl-1.11.1-cp311-cp311-win32.whl", hash = "sha256:7d51324a04fc4b0e097ff8a153e9276c2593106a811704025bbc1d6916f45ca6"}, - {file = "yarl-1.11.1-cp311-cp311-win_amd64.whl", hash = "sha256:15061ce6584ece023457fb8b7a7a69ec40bf7114d781a8c4f5dcd68e28b5c53b"}, - {file = "yarl-1.11.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:a4264515f9117be204935cd230fb2a052dd3792789cc94c101c535d349b3dab0"}, - {file = "yarl-1.11.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f41fa79114a1d2eddb5eea7b912d6160508f57440bd302ce96eaa384914cd265"}, - {file = "yarl-1.11.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:02da8759b47d964f9173c8675710720b468aa1c1693be0c9c64abb9d8d9a4867"}, - {file = "yarl-1.11.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9361628f28f48dcf8b2f528420d4d68102f593f9c2e592bfc842f5fb337e44fd"}, - {file = "yarl-1.11.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b91044952da03b6f95fdba398d7993dd983b64d3c31c358a4c89e3c19b6f7aef"}, - {file = "yarl-1.11.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:74db2ef03b442276d25951749a803ddb6e270d02dda1d1c556f6ae595a0d76a8"}, - {file = "yarl-1.11.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e975a2211952a8a083d1b9d9ba26472981ae338e720b419eb50535de3c02870"}, - {file = "yarl-1.11.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8aef97ba1dd2138112890ef848e17d8526fe80b21f743b4ee65947ea184f07a2"}, - {file = "yarl-1.11.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a7915ea49b0c113641dc4d9338efa9bd66b6a9a485ffe75b9907e8573ca94b84"}, - {file = "yarl-1.11.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:504cf0d4c5e4579a51261d6091267f9fd997ef58558c4ffa7a3e1460bd2336fa"}, - {file = "yarl-1.11.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:3de5292f9f0ee285e6bd168b2a77b2a00d74cbcfa420ed078456d3023d2f6dff"}, - {file = "yarl-1.11.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:a34e1e30f1774fa35d37202bbeae62423e9a79d78d0874e5556a593479fdf239"}, - {file = "yarl-1.11.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:66b63c504d2ca43bf7221a1f72fbe981ff56ecb39004c70a94485d13e37ebf45"}, - {file = "yarl-1.11.1-cp312-cp312-win32.whl", hash = "sha256:a28b70c9e2213de425d9cba5ab2e7f7a1c8ca23a99c4b5159bf77b9c31251447"}, - {file = "yarl-1.11.1-cp312-cp312-win_amd64.whl", hash = "sha256:17b5a386d0d36fb828e2fb3ef08c8829c1ebf977eef88e5367d1c8c94b454639"}, - {file = "yarl-1.11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:1fa2e7a406fbd45b61b4433e3aa254a2c3e14c4b3186f6e952d08a730807fa0c"}, - {file = "yarl-1.11.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:750f656832d7d3cb0c76be137ee79405cc17e792f31e0a01eee390e383b2936e"}, - {file = "yarl-1.11.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0b8486f322d8f6a38539136a22c55f94d269addb24db5cb6f61adc61eabc9d93"}, - {file = "yarl-1.11.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3fce4da3703ee6048ad4138fe74619c50874afe98b1ad87b2698ef95bf92c96d"}, - {file = "yarl-1.11.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8ed653638ef669e0efc6fe2acb792275cb419bf9cb5c5049399f3556995f23c7"}, - {file = "yarl-1.11.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18ac56c9dd70941ecad42b5a906820824ca72ff84ad6fa18db33c2537ae2e089"}, - {file = "yarl-1.11.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:688654f8507464745ab563b041d1fb7dab5d9912ca6b06e61d1c4708366832f5"}, - {file = "yarl-1.11.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4973eac1e2ff63cf187073cd4e1f1148dcd119314ab79b88e1b3fad74a18c9d5"}, - {file = "yarl-1.11.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:964a428132227edff96d6f3cf261573cb0f1a60c9a764ce28cda9525f18f7786"}, - {file = "yarl-1.11.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:6d23754b9939cbab02c63434776df1170e43b09c6a517585c7ce2b3d449b7318"}, - {file = "yarl-1.11.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c2dc4250fe94d8cd864d66018f8344d4af50e3758e9d725e94fecfa27588ff82"}, - {file = "yarl-1.11.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09696438cb43ea6f9492ef237761b043f9179f455f405279e609f2bc9100212a"}, - {file = "yarl-1.11.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:999bfee0a5b7385a0af5ffb606393509cfde70ecca4f01c36985be6d33e336da"}, - {file = "yarl-1.11.1-cp313-cp313-win32.whl", hash = "sha256:ce928c9c6409c79e10f39604a7e214b3cb69552952fbda8d836c052832e6a979"}, - {file = "yarl-1.11.1-cp313-cp313-win_amd64.whl", hash = "sha256:501c503eed2bb306638ccb60c174f856cc3246c861829ff40eaa80e2f0330367"}, - {file = "yarl-1.11.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:884eab2ce97cbaf89f264372eae58388862c33c4f551c15680dd80f53c89a269"}, - {file = "yarl-1.11.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8a336eaa7ee7e87cdece3cedb395c9657d227bfceb6781295cf56abcd3386a26"}, - {file = "yarl-1.11.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:87f020d010ba80a247c4abc335fc13421037800ca20b42af5ae40e5fd75e7909"}, - {file = "yarl-1.11.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:637c7ddb585a62d4469f843dac221f23eec3cbad31693b23abbc2c366ad41ff4"}, - {file = "yarl-1.11.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:48dfd117ab93f0129084577a07287376cc69c08138694396f305636e229caa1a"}, - {file = "yarl-1.11.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75e0ae31fb5ccab6eda09ba1494e87eb226dcbd2372dae96b87800e1dcc98804"}, - {file = "yarl-1.11.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f46f81501160c28d0c0b7333b4f7be8983dbbc161983b6fb814024d1b4952f79"}, - {file = "yarl-1.11.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:04293941646647b3bfb1719d1d11ff1028e9c30199509a844da3c0f5919dc520"}, - {file = "yarl-1.11.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:250e888fa62d73e721f3041e3a9abf427788a1934b426b45e1b92f62c1f68366"}, - {file = "yarl-1.11.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e8f63904df26d1a66aabc141bfd258bf738b9bc7bc6bdef22713b4f5ef789a4c"}, - {file = "yarl-1.11.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:aac44097d838dda26526cffb63bdd8737a2dbdf5f2c68efb72ad83aec6673c7e"}, - {file = "yarl-1.11.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:267b24f891e74eccbdff42241c5fb4f974de2d6271dcc7d7e0c9ae1079a560d9"}, - {file = "yarl-1.11.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6907daa4b9d7a688063ed098c472f96e8181733c525e03e866fb5db480a424df"}, - {file = "yarl-1.11.1-cp39-cp39-win32.whl", hash = "sha256:14438dfc5015661f75f85bc5adad0743678eefee266ff0c9a8e32969d5d69f74"}, - {file = "yarl-1.11.1-cp39-cp39-win_amd64.whl", hash = "sha256:94d0caaa912bfcdc702a4204cd5e2bb01eb917fc4f5ea2315aa23962549561b0"}, - {file = "yarl-1.11.1-py3-none-any.whl", hash = "sha256:72bf26f66456baa0584eff63e44545c9f0eaed9b73cb6601b647c91f14c11f38"}, - {file = "yarl-1.11.1.tar.gz", hash = "sha256:1bb2d9e212fb7449b8fb73bc461b51eaa17cc8430b4a87d87be7b25052d92f53"}, -] - -[[package]] -name = "zipp" -version = "3.20.2" -requires_python = ">=3.8" -summary = "Backport of pathlib-compatible object wrapper for zip files" -groups = ["docs"] -files = [ - {file = "zipp-3.20.2-py3-none-any.whl", hash = "sha256:a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350"}, - {file = "zipp-3.20.2.tar.gz", hash = "sha256:bc9eb26f4506fda01b81bcde0ca78103b6e62f991b381fec825435c836edbc29"}, -] diff --git a/pyproject.toml b/pyproject.toml index 7ee7e1c83d..44404c683e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -123,5 +123,3 @@ ignore = ["E501", "B905", "B008"] [tool.pytest.ini_options] testpaths = ["tests"] - -[tool.pdm] From c7deafa1a9ea9a378576d2dcb9be0c578262dd70 Mon Sep 17 00:00:00 2001 From: David Berenstein Date: Fri, 20 Sep 2024 16:43:15 +0200 Subject: [PATCH 63/82] feat: add basic draw implementation to pipline (#966) * feat: add basic draw implementation to pipline * refactor: cleanup some code * feat: add functionality to draw TD or LR * refactor: remove step name from vis * refactor: default to LR generation * Add dag with mapping * feat: add edge labels * Remove images * feat: add support for leaf node to argilla and distilabel * refactor: order of functions * test: Add tests * fix: replace logger warning for `warning.warn` to avoid non-initialized logger * fix: avoid potentially getting raised errors during `get_outputs` call relying on dynamic calls * docs: Add visualizing pipelines section * feat: Add a try-except around pipeline visualization in Notebook to ensure it will never be a blocking action * feat: add a show method to the pipleines for visualizing in notebooks * docs: add more context on pipeline.show * Apply suggestions from code review Co-authored-by: Agus * Update src/distilabel/steps/generators/huggingface.py * feat: remove show to simplify flow * refactor: mermaid URL at top as constant * feat: improve flow for passing by info to a potential next step * docs: update docstring --------- Co-authored-by: Agus --- .../sections/how_to_guides/basic/pipeline.png | Bin 0 -> 77305 bytes .../how_to_guides/basic/pipeline/index.md | 30 ++- src/distilabel/pipeline/_dag.py | 190 +++++++++++++++++- src/distilabel/pipeline/base.py | 41 +++- .../steps/generators/huggingface.py | 9 +- tests/unit/pipeline/test_dag.py | 128 ++++++++++++ 6 files changed, 389 insertions(+), 9 deletions(-) create mode 100644 docs/assets/images/sections/how_to_guides/basic/pipeline.png diff --git a/docs/assets/images/sections/how_to_guides/basic/pipeline.png b/docs/assets/images/sections/how_to_guides/basic/pipeline.png new file mode 100644 index 0000000000000000000000000000000000000000..0718e1c28c701c469f09fcf791da164daa3837a2 GIT binary patch literal 77305 zcmb?@c{rA9^zLg2rJ@irmnmeP$0$lf=6Omeq|A}2P%@UW@}bD|37N?hGV_^9GRK!W zndh_Kz4z~&>pK6Of6l)4)xNZS@9;e9S?gZ+eXsB3ZH?=vPMkl1Ajm1z8`rcEgxDTI z2tvq6;BV-ONVwrY1g_fGm5@9f;~avpBC6LEbsu9EM?D|Y=pB;$+3NUd?$Y;^n2bzY ziL64zQt{?*?pwzcsbe`mlj-}V@{7AQk`PXv2;n#UZg%C>h4&%EG_-Lt630VgzlPqq zz)}5E9=}DZDy~aJ%=u~s@Aa2%7T4j0H&0%io#~(tpj0Ab3;O^62mi@FX@u=pTPqGX z)?pp+`2_oxBveu@eiS?sO4Y2w}gR={P_&6t!m=#|a7}`_$*Jr0+F6 zKQ+6tFf>wFROGIoqZem0EMwoFHPzB`LyVHla;`Uhv$$AP&Dpttl7)qZQQqgVGai>z zjeK^Qxw+tfxW=MmZ;!Rx`8%^YHFbv#{t>+MEhIJ9q|DfFfAxBdc|P9ki`!~AgGAnq zOqm^P+uz+EHWI}gX1Bbn>n9stxO-HZE&V9TvHR8Xy3zZ9KE{e?=GU(fa$5eABP9+A zTKPk33wf3Nf-b*WrUr9ONL^rMT4T7Hk*}YOiZ0l8z6;JXtMooxIXcX@?9b9F;L^ZQ zniSugOp$xy;ncVzf3)wq#+Bl8N5*?Si~quf3;jiQU4?Mpgpkw_BGS{pGj1iGJLnMU z`gPgti|FE?$%ftEzkg4_%3Abi>u`<7Z9D$At?ncA4$_oLd1g@xhug8~Y6&XnCqC)r z=pQNulEksIvlAB<7M>if^xUF5Nz(C7jCa3Xptg8(VJN@&;do6t`d|j146PpaWkl5a z9qfLPd$LJa>NtF7^|5SyAcef)tJv6Id-FN@d?uxi1*ZN78&6?xY2S9*x^K)6l>Yhi zs6;vJ906ija3{EPVWZHh;W^C~TwIr08CIexJ|#uq=i})J`TU`F{e1HWF*d_PSiKws z!ALqQy^(ge53jSF2qdN8S5;MAWs-f|{rOJgiM>-2KPPf6YW=d%2U@8;8WVN={(0)j zN_E*s-@Q%q7Y%jO)6;=3C|CwxriRG-?mk8=`!a6L?yTWoaB*?%JwMH4d`I~0Hr&V& zd4<6&cD1)ti93F`bgwYYfJLUII4_#DK2aCA>%K5J?7g*Ywz)a-u=d01^&9`LYDH5| z_<5!ISJusubLdkq{H!Rl`%!%V7|qB^&EB#$BI&dJd3$5XB9WAW*;E+f@(yz2)~$_5 z1_}Pn=_rNLGKZl&7u&WNP5HMh^z_+;WaXy(`Ry~p^80@pg2RQ1?FV>X#mC#Wc65lY zuZ-7zntGG?ux5B9*F-`9DSj{_e%sWP&u3>QVQ_D`GJo6A3-;E!a@5Jd4u^SbBD9cU z*Oi=1%KVtsW4b9Ko|MJs^Y7h-d8lLL3 z!*F5xQFdlB4-$YzXM0=Q_skSO`^q((j2dqwlZ2D0)zE;=U0cid+!qGG;Ak9H1j zZkj(7D5SsRefRF&tn%{hD)RXD!o`t7n=qTB!-L(~&O}m8|IsB6hPA(czf~@O$x$3w zsU$xX1_<4t~rJZ5Fam$kWo+=a=WIioQ>Sp)n)(o?VE0m`R&`RA*p0(J%(vf zU*!Gv%gGQSAtAfVk&fbt#_&s`_j`YZ4~x@cQ-dz?@JOdqW4ZGeJ!IsX690XpRH=7u znm3rH+@#wd{cXJ-j~yX;3KdL9sHcmrVQbv~A@*_S%2vaYN1`z#nEc1jbZ)_Zqles( z@7&l&)tL8i#E{z?9@!s1vX37g?45eDwJbMNaGN3iHS*@ zy3x>mZ*ytn7D?b!gHP^P|I0JRsq|t&{cOjNAAia$=N;<{!5I&kwQkOBH#9UfNT6oB zUf6ZEkPNCtn@)9DjT9;>D{f zX|Ev%jaJ!pF*k&qxft4ED{_6uTX118w@8ZdwENcp0-K8};oSPvjq5*MyOdAS3BG5S zl{M9)Zp^~+B>Nxk_+T)X(qPe_Lk_lsDpU3GW6-E`GgG=s+G_YJdRC{ z@ZR^&R&nGEaB`=2!p>cG@j)Y(fQTfvvAOw$Im9v5O7)iccQi+Z3yD7XA$r?g=x!5T zhDOpm1<&Q5e_&@7_@t$!`%4@Q9tM(}!j&gsVX>G~x|#>`%p68aA0%=>;ShzgrX&1c zRS${3=d2DVba8R_t>2^*HjN!+|ZLtqf6r7FiSpR)>(~)}-=l?f0 zd1d?z&iDVo1^CIy_rk~1J%0x?c`s9+nB%F@)uYkVqoKF97o-!_-5G9fuf?ZwlW6dJ zHRvX3&{~FP!%xauCxx3Ne-Co~cR*;agdL|+%Jas!o+BfMWZzABBG~Qem9N~PciyMo z>zQ-&)1tomj5UbP8Y9#sh37sJAo?na=c7LH665;R75A*}eI0(yh|b!co4PkFmF|_z zj+LICCoL_le_~wROyBO8|Io(}Gi)N8LHcd(k867eoVDW*r!ZmCNx00uxqX$i0(I)f zi4>+Tx}0qR(y#Q^l*AwYs%ojE_2{h1;Z4a%0(Wn76Dv|CyGb%8v8(<#$x8lp@Cxgn zB}kKh1;z)?D)E=4U5`4vsy85SjL+iT_w!E-3L@UEP=(dP92?!ec7w^KWPFzz-c3HG zYjJDuR=ALpAYBznV4X?QoeWOh>W$Vpw^HkHccSjsLW4wC#PL21c-d25#mh!~_ix_4 z5Y&H?$3Bk_eTSe|t|*YwUT7_pjdYcNDpYErH=wRassF_^FVGl|#hO)mJaCgXGeEL2 zYk5eiaMNEY{C&S#g$JA$Hu>Yy;?a)e>}TAE32X~ zD+{e7xCjWc8@a5iy-eW+_c5*l0S45{!ynx<+EwFcY_V7mn{apQ@KG0+&Z!uOpFv6= z@`Xi2qTpd(eff2hR!qbWcT&Oa_p3w5A-RLz`a3&2^)0V2F3A1)N)!yogr{=sN+!QeF-#*(kkr42No{ zO?W!NcP!Ta-N=%>X&?QpocCw0Y5UA~@&asP5w0G;KU8!obW8tJ5?kV~z>-{}(#S00 zwO*t_n%mmi4hub3#tw|_dbMqBXZ;EGX}_MuV4V95^#=O?(o1xyMaLe0|H?I)ldQg! z+(>fNLT)9`|GsoZ`)A*$yf73N@T^lP(^y)3(C`cmz}uT;i^e9~IGIP=#9Xlf0CO#Z$L zZw-m2=n?KUpg;!Ed$;HQ&a^*(@*pA!`FZ=#WMlP{txa9VGRWWufX`?>es{A4_T1C8 zG1|QKM(~3mA78k^;@W_L#oNP;eE(rhj3gTXF~KbCJpFV&;KM?XC1VqF^JF?IDyl^k zWdRQ7faIN;hyVJN=*#`S3}uD24iPW41pZSp0J`KMBR)Pl*rIix?{6hVnj@L0;`xlu zW^jl0Os|riIxk9gnpw8|qd~rT+>nIZ@9tfIaF_RI6CZX|daj5Y=jxZ}WNIWsAv`Ly zX6hPT+XUxcwxNi8uBT1zPKZ;kQnB~53OH1!|gmFRW-arcl zn?Vp?yEkr!5Lcn7Hz3j5*C*kSG-t0}^Zn2D8H*bl8j&>01*F!W7y`KN8VwSBuanXw zCkY&ZAl}krY^Ng%7?e(OD(#zdobsr&bMU$x)NkuB_^DuHX{2mDJ;A)~VDbe^_|f53 z?Gm6BQSbHN^{+2pKbLPUN<0+rN|KR~1(cxhXyR)xWUo;4HUl{Z0)|4%z3FNX&?-CXy^xy@RqZsh zl*_d;+s^YQ9v-DmDAJ{UBIj3|Qhm6)a0W0Y?v%4Hw|Xbz>Gasg$45{|sG!iz=(DBbc9vG!iIoa`@|33h&bQ5ByA-3c zhvQDkqW86*4m-}SRINo$Lx4mW7T@a;sy2^;0)5`xXSFel-|R~)bb$HASy?x>7mK<+ zX(}AJ_5ZAJ)eK2BBuY64*~H}m;4ey-VTg1={Jt1MMWej)V$dt^*xR>no00vZeRAk4 zHrBm;C7KMe=K21*(I0Oh_aSZSomNBs-1RbAd*v~AJiB^oYU)`GmnJ`90IV+w+Wy>! zmUjZW0s*3cmsDKmBAH|cV_(0vQonJ7dvB}u=m8Zmf&bpfxXS}^22un-BrZ`xKvqs} zwEK&@!x4HC2C;g)M}WdGIekg{wdPV%QePTgP%I!5+pE*{Q1ut1@pXb(#e25$WFu)m@>69+6MFyD&`wq z-qpzqZeQ9S6ciNjAk;K8$0fZ+VuUVTYBkX6ow&<3NfdCfx3vJ6_9h^!r$nUj`mv%2 zYv`SsL;i$zx)Z^I(DZ~zxz6e?H|(^UMY1UHtEi}$uP-Ta#$>yE^4^#aMpy*|G_$_P z$Hk@ZL69y&QznXB;ikS592~4P;Hv9uvF|0}0@WW*Ep(qNkh*r5KsF&x}Is15#Xo8;}H5)oDGm{>bQACDp?SKFFSxjSOboH1% z<6APOO3SY&1_YU=^VlZq#&V~pr}OdgEykWrpX#cps7QrAf&ds9lDeWK3NAIOGsxlY zQ0;8fw{IA&mT%3?q$rpyV)l6o&v)CrY2vQx-8VwkD!Pf{4}5eo)GvKD&YkDYuR5EJ z<*9tL=87}&^Q%D+&rx>+=S+>Hs>~HCKql~}B2MrNi$ zhI{gtys3m%7oGrFB>frdVrimv1HsQ!Gijwfzt^u{+dDh2UgEf^tYuX+tox$j1K||_ z>SPn2Y$8UT^7-uEiQad?DdAnVhiVUP@*%7K38pn~$>Da^NYQiqz|(dK&?wFwGR2Yh za}-nN-52|tFA_uv&C2v$qcI*brR&OC>T`2*48=IzA*m#sg(Q9$@gOq1KvsXRc6?k} zE0!}fb^ReV$HDc5@F3GA<5I`D@glpZo`r+)Z?E%f4}XNmI9%U>e8x;kOkh&xB$l$k zcCW~(_4Eqz^vc9O=|5eZEi4;9pgv9H@eRF!C!H8Oy z5pDtT-OVMFFV52{3q@V>3|@V=B)tAYw+l(sD8+Yk_~*IH#>Bb$yy)`Y8~x%g4%=`V zI-O6Pf4Mj~c6xhz`QQ-*M;yg#pjbMns=oXKWW}5$KCaB$jI4>SDV$zs{f9%ovh42f zv>xC`E}sfk;=6pg^Cxr;U9Z@#=5?$M0!n_BLro(V^eSwy^ufr&y7xhQc!7l}zroeq zHA2B99xr9o$x!%uZ<9;$f?+Rx_F6L)Y@bl49ZWX%U`%x)LqZxW~lXgH5 z;WJU2r>Uu_b#akzA(d;XtE;1&-C%fl_@9uFklRq8s(N~Q;&e=yA*bvENhDRXJI||j zi$*L>C4vXn)kRAyV7~Zy*s%&;JRR=%r1jY`@_HI&p_W1)tu1c%x!%T{zPZ1LJ6--i zj#i`mB|qus{3x_h^LKG6%;$7LH*csg%$PfpQVFAhw|>T|{&(--z!PXYg9X`N_0TxTt$PW;C_>oLcNp% z;RIs6NQ*M*5W_oMW%)V>{w;Pi7_ri5rGUAhdwd1<_`sbSH0$ItPVrM#cNSq}ZtL_Nh zLEB7I6Ld*134L>mZFGsb*m-iNvM0HNUhoW2%?6abs=e=cY&x_MBg`5fVuZvKwA=}| z0nB{f=LU95em~5V0JBE#?CiYmM@1MQBt&NE949>cNklKtHe5(A ze`pUt$c)Zw!lwZ}__eocYHAIzvQak~;L0DJfbphu(N|Vh*7B|%GcfO^R0^?GCwY>b z%DqsGb57U0?37iE=J@&3C;-hQBPMK$j*cd)98ETHH6$~+&qgDDE0`*3#;ZEBn9RPHE7Id9Q- zLi+V|BTH`537lM9L*MSSpb^(Y2YG|?5zYwSE(l^3ZkzAU3=i(V44oHqLc5rbR;)K> zZNt0T?=4?DFT1LJuDkd@8zcK=ov%pJyMMFzI^GD)$s7}9Uw-Y z4GjCETt9{UHmf5JBgHj8l<(gf6<6K8>YyY@_pM9e=+ncI(xpoxkpS(pp3G-wre>~)3b#XZ6 zX>|0RYs!u9P0DJ@@yX71qqX8PBkm-Dis3B&v`$~Ld7GmAul4!-`m3g?X+dBa45hS# z&g7D6FF)+xw`92|ego1B=sxhDgca$xvazw*1ipcsfM8yz+TT`{zsm>=a`p3XsCvt zBbNpvEJaRtx|$}if>kalpFjUmJ3k#JFlaUSiT1P4PJ$h^fWr`fGZX-l7Z(|sWHK)B z@i79Q*YtNrttV#)0it|6x~V1_=bjWU^xe@)@U8m0*^378BTI+GU%8^$PXS4m!=bvX zqD5^#kLI-;GI$l!bV;j>Ezj33omC;HD!1pB3 zP`scQnf+>8WKiR={pW+RPOt9P%2Gw14z+SeV%fSd`$YpHWvzH3L;m)^R}1-EaXZw? zTC)yGVJIZ?M`>r#QdHQ9=1{A5s@@pvWs>oD10iL!Hrq*L-4uSwegLb7Af-;D_V2_T z^uJMQ%Dz6D%SX;+-3m%G^+}Q*6rq1FunvQdoFfjvr})cX zke8SLy|(imN+ch~wfKJD^HYqHpC8S%#brQY9cDWHLfU8h&(v7;lPB23`>3!35VM}v z!pCZ7DolV6>fHwpwRlFNfX+mTL|T5+-*hq_j+41%WAg+7Ll*vWKG03uB8Ju8?vdvC z&1+o>EN6hqqEIg`%Fg`Z+GYV;z(H;tb$X=J2 z9{@pAd-A?`J(5ovr9+xo{6)opMf}sooBwMJD9}^?{Q8sSip-Bw2mZ5f?Jk4?Gy)ur zL6H2RFz7y7YJB%RT+!wmP~^>9w=Oe)iq;B31bvNl^JyvsRVD}!bita1g@sS9;kzhE z1MtaD_{*-oIPHgI;p|-V7Rros#r|%oMSrx$tVKM%VmSznfr!Jej|MkXnl!484t*G< zi&-WoU*Axj3(!hqzNV`y+S^<6wd1Yk^st>p^W`!(qJXvsBDNihR#x(3-U~Fy*X|E; z1PI7kjNV(!0sco%juR_>jnO<@ij5{g&YsPye9wqH!Y5_F5uUl^vo)#@l1kZUL&x7k z4+T}iAHV<0Xr5227GD(sVhT`)AFLXPPtpsY&CaeSK-TcJp$}_EpIv8u8>t*N_6?+^ z=LY6GGUvtBzeaQTH%BQrUB7?-IQrwLXOv2rlkd~N!=iSTxLrd-_Cp9TtA zv|~b%XIpFQnVjXd<;oT5e-x|zwJtmg6Z;o7rXbQIe}F9M3~Hq^6_*BI2MAs%UbAnr zpiH#+hguZbMx5eO3+e^-KZieo9G3eLs>+Qen0sF;1M=f3%969l`xt(5$^gz~#w1sN z?88;k6|{eq0V1UBtrW2MMRg$X0pW9=rxZOF#jbdqT}OsdtzY#L9HXiAxxTfgNG6kV zh@Q3UEWUS11JGU`I%?a8qhCrMPJMeB49drc!b0K6uBPVJ!d7NL(B5*bjKxQDW?I+EyJY3t#!pY@%H4(P5=VW}g+X>WwHf)BNpQkh$N^%+CZ1 zKj1rgcw(cYr&xsUe7j_=yHzALzA;3M^9pAr+nU?hEdh{zRBoJnV%p;^@F@uH)Izg0j={@s56lM0?zAyT4?5v2Le8jE9LEYrE;$mX8 z_aoj=JIGn9IwK+i&5&$MO`&dr<1yB zfd~V%Miwe=VuxH%22jlDrzBL$gtqa!kUi<|^=I|ahaEy!sIdJjM)RCt)2lac=(fB3 z53SKl5q@Ry-~T`qfGRMY9301ZZ1<6`J58hk(0y$;M=%(euHvsubft(NqO$Jh;;{7_ z=xXX7l#P0*WcNdJ^a|ux@)IY5u-51;`r*BFgdNAKDvVP)ck*gEk3V~bSjGa2oTOoA zEJ){*meHK0>VLGiP`*?uvpNx|sp}LolPKu)ld|0Os1DLzL+mBZsi&mN^`M2WwhI`2 z9%&G?s<%c0LPBnQYZN0!itXcwUrhhJ|I~-$ zigEPJUU#L9OLi?12Hz{nj`?~j&s@IkyL+w*+d-dcGF9-{tKm_k~j~W*Lz6ELP0$+#o z@f4n(cd6V)h?9$S5|xb+KlG-a>6w`iyQOJSB&C6N4kfptp}2~M1!{fBaq^T-%H&fi zc(Uab2m~RGGJ=TS>t^i-BE?c)U%%1uoUI_;wZOXDj-aqeR%8hb&AG*eje!h^m8@{; zl(HL6;_Vv-g4$e*@PIz8rzPi5m>x0>a9k+_h|`xj&XpG|estnbHWx!cM1TJW=wbQ2 zJB)U(F&NV&UtZAaW9lrg0QdV0gsK)PJ4vaFN)s57COXph*5SvQqnb~=7cL)Pp>=lA zj~t#}r2}sF*151_FJHbSiE4%&1XBbvEp4W)jm=v*W(rBXbYLCp<7#u0Mz7Sgv|s=d zFM-uu;ZoJE=2<;KpnDkq32Xn9m27fmob;Gt_vA0aLO)yu0sbqU!^sKxYV&b-e+}cT z%ga^WWpjlx-lC+nhr1Sc?T(&9)TXTNu1<3e)kH?*M)jnze3MzJB2c6hbQrwzrgL^J z*+dUek#**w-q8+TU3T@`wf9xOc7|$evOG>Bf^QEc^0-4oA|oNcwQgkiZ%>*K2iCQj zyygi~Vl&Jw<-rRkY4_?%K7OQtCa=wvaUCyHKfh(K{53~m5gO!5bL1#bnNSm}s)SX0 zypF#?zPH@QycmM~nq|&RojnhX87<(^NB12a7Z;-0M{~K`e2wkUW~Ai^l#CmfZHk8b zAp_|6C5k3?X3A=16&LH00gtSyFz8g41IiBIf6*+gJu&4c?)$imRPM?BeF{EllZ zhQZ}$@H;uZ_l`ef)vYqGNzz;N{NZfJccY!|s@JOS(n3Wv&rMLUo6>YLm+h*U>FLdO zeDlo@3LdYTiaHFQefhH0-`fcgyL8g&;o;h(!fMr+*~gQ?5(Re}wf7JA@@X;CZmC&U z!CN4!dH3%6B@un>vO%3=DQ4B%dpBx(!6Ng$S>@*A{Z%~Z(5q8nM+QccW+8=zF=`^p z*RCZO%JFp&L}i||u}Qa+)5w$=?$kG>s1RqAa{1;8n4mS9J>nI6_8V8f%8{k9RQG?t z2D$~gaR~`rO^uC7&N;XBw6wH7zyZVvJxP84{z`acmy0V40j+RnP97kO;s!p8hHt&{U^ z(hAlcU+K_hxoBi*a&K!z!YFreO>TMQJr;{|6i@kUk_#Ur`uy8XI%?Q)^SZL~OVZPf zf{C58(bzp2YHGu@w6s9mqKY7)47>1`dRtapnclzQGDpKun!5Ho4+()>)Hz41rMDp% zAd2LhS=27vLv#e(!{6DKB%7u-ryC6w3fg?mC)O7?7JBM&5ji~bIPUu4ic8w;+a!`@!)Qdruyk=LNGxXm;_ zU-Wn~wi8*~uMX^|Of@iCO_5NW@Lu=t;0(;!k1bBEtPm%-zDfG;vvbx2-4ljdIwkZ2K<0$iG_%AbECh~HBcz{m{foTW_oX>R{72y;cEpdyQVHQZ5hsTf0y_=alF$d;BE^^-Me?FO3^d5roO$FMA zkJl)PKY;{iRW`D-R5qUMUt{fXK_^E)cYmbpp@e`*X@_mu$iTO6*ZhlYJD;)m{CVmN zooPHO_%J_NELiK*kje+O<+sI?rIHY6yW-z<+9oF_JK2NEWYel8bO>;n+NQwp8+cFB z%&WbbF_=m1v&_tXZF>oTYe&KCkq)x|VM|L(H|iJx?&J$t1WAzhpf=kuNH`Uv`qQ29 z8sGFGGf%S#0L49apcsvVzBir9jS6;$sP}Oem$Yz0U44CI$zvnl#7W9p4%4T=phXQn z$@V2HE30`BUAy)w20xqX=?!ILrFlg}T(@W#w^D_g&>eguG<^gvp0w`TBTCpGfIr{` z`uQA+4k>dKDKH!tqNBCD%T`#oeS9YYYrhpqY5G~A_F-t@^_%afQ@QWdJ(K9J+`6)T zAiusKK?n53RO!g_Kk;GP!SwsP5Y4$Tn!U|~p!IbYURr#vM&$hOo@o$_Kc9e;+t`yf zd~d9Z<}}lfC_tN2qn-x!ccr(~<2{ys{KJv-Wr&fHyIKxlqqIy-gJALX#|Ay%V49hl zUIj9Pg~hL&^22RZCH~eFk@GqwCG38mc~y{RxG68lxG3ed(mm%Ulca%T+i7$%U7sAW+k5z1=`RqiWu*e7Q-ZeRWV8QO3iuKkGG1K(5cNw>s*ew8O%9!G3s6 zg57or+@5biiJ)0nri)PNz(}oqgQLN+36cA*cK@%M8gTVls16RM7cJ@@l;Q#1ZQa8E zphQ`V8jQ2{!^5HPATadwdQdr&h(h+ZE>+4c_lnVFSAmb(VWTc;5*=@ZP~#zjwnzi^ zSGA+59*o>X0>HLJj?!3R0=Q$=Cplw|_hnRvTxVel+!{-K@sf(=I_!n){vV5iyR9RS z@ktWU8|SnUPJeijth6xrpuq3oCcMRIjH{^Qn}ErNX3L4MRs%RKChuxy5xbg*t+6NM z_wT1zH$2x1iFz1*c<=(Vy1m#p>VIVMD#4QwxF%`GQo)}dBT00!kBMhHpMcFl=5=)M zWEEtK)|ix_0sDp@B}XdG&SyZ{dQNNpmGSn%P&V_{Go&DAx z;D7(#fnU-1ibQBCoiN===D@m%%l>Ek#9p|{U*zXsjvmUAN-1M$_pW|0)RK+*FK||5 z73|lK7V64!Ts$=142S>t$jisUF~hG#ZAX3X+}NAAIK7YmaIPQo-iz6jRkX#$$4g7% zm2Tx-*D-m7JC;A>aG9Gs_!bpUaOV z{o-sY%20M3UJmtm{o>I#G^BeV{Pt#q%5+NpO>dgx#DpYp#bbyTbs%H~FChAho*rlW zb%M8+hQ~De<1KK!q>n85sV%6ZXs#p@w2{;TP@7}(ENbQPN=`y2f|b5hRaKFQC+1GL z#ER0!WOF{2Wd;|ZV))QP$y@b|!q7|+Emd$A{?MjQ7qC{3WuLP!wnJNjJa-T>UE-kG z$pCQp2T~p*Jccp8LKlk-1tP?|B4%Gj<#7dUlo`$kd^uf@-uMp`ejgteydsn}Pc`*)@_g_<&LWQrz&vb#FKJQX-nD^gxPTi2 z#S+@x^~O@UReNtE{>NUOIT5^@nVHG*PoaGEE|a_MZf4)ko4z>=-lLMG_umVYfIID# zam$>W1{Lw7Z9@CN%>_Q%oAEP#n6(6g{SG>Y36}~W%AwT0YHwfi=+aPCo#RsN^$bmQ z>;Lx(V*gx0C4R;T+2>DK@PIWX%j_Et7TP?4mrW27ub*-0nKn7j_VKi;rsk1)^hZzoJ5(d1mui47%s=E{c!~6*j!GlKF_Bl6B;U-&xZlcHPTCq(3v>Bax`o{*w)6E7zC0&GK;otKQmT(dK0MbnaV9!m=TBm$U{xEn{Vl&;7 z=O0NY36MV%eCm2w-ad0a&7B#}b>FP(G+-1dB_Vv81iP3z(o4pcFaX-ZJ|f*( z=#vFiK2Sth_~(BQdJY~ml`XSR3L~_cmCAj_*e*K~%3Z|ZoEtfT?1D>0YBBCAu4~Cd z!c97f@d{DXn;2KWeDnOSIQHxiA&);pRH3<-z3rJ|C|ypuR+tU}MubLO6Lg`=sCEgb zjRd{J8Kd}=W8J+oE~D?=;}xdeq@SF4CYa)Ss0k>afU;kjDw|nxd%O=Ua)beNh!7Sl zetDCbcTkj(?W4w{3e!S%)x?;Xn9Jb00l^Bj4EKAWmnBe*mY>4dT?%!{A2h;oUAmP0 zy3X=V7o7-cVBJ$l&3h%dYuM}YGbC7S1{OO{MOB=GTP3kgxDTsQyn=1`gWJ?IUb#h>D0bC+`iQ271biM9rq$UW zvqymzr4>-^B{PbrRf~2DaIMjy3_3(kV3(Vv6qrv0$HPkG%&)F|bXq%h^TLnP2Tzg) zrCQRpsBNCHlF8W^9fqM&Cdj|xkm6i|TM8W~F-K=llI-=*D${VNoGOe*&)} zNY0DkUZA%tF|H)~-9)E0gULY-)4)i`N+x2)KU5d$o20=-OAIoh1=7*p((+hmWIf3A z&Fz+lqI4%MZbW}0aMadww}yU?6TuD+w*)&^m;$lBUdq?51D|L3cfaf}qDhSqTsIf+ z`=m{VF@Q0sjPX(oaDX%@gMu+@a_0FG*!`DGAL9Y(IXViz{gRL%Y(`K{5F4w|I+xzj z(Q%Z#VAw<_iGKohiKl zbug|usoZ@fI5@jO{hSbpF7&F=>#PAap{XakrQ3&ZM2jX1mA-P7SJaACHpXYSqXt%5 zGe|lGYx}S}YA0;VE)O z&bo-j*i^(|v1P}fb<CBQ5;S7?N_2d zY9c<6Kyyus_2Or0U2zXVLQX@S5^4*w?!oM;xl(uuk)~bbhE4d4Gp>x_`~22kpU29W zuP*A!$$^KmhVJ!2c!3|{#9JHpN2GSV7WI`YI3z{s;;uXZYUl31Mb{Y3?dLgFdtV^> zmqH7T7x?rbtx@3jeQ`S%p*4E&As;$ltKF&F`T#5fxM_a zM)=eTQcz_HPKS+yBmE_J=!z?@sM>sm&ih^8jMicQr##G>Zj<5AU*%hClKA_~QCtsJ z#;Plf?bw4bkl3?d_uO0Tl47}C!@cT_EbRLo8NZwJm;pMR7I^?iko~_`gq;8C{OuY_ zN0s~c@2CA(C$HdZ7a%N`pgs`+1xvHibQ)a6cP;W_Lskzdbe$Q)u#w%3Z~vMMp33#{|z2`h$`))FiTa5 z11JQ7zCr#_U&l8E=S#|`G!7pwfH?&=aWD7XEfw@^`Jrd)I6I_4%b-+lxe}bV1^|&u z0lR*+{@>!>mHJ;kCkBDMPTY+V5`XSmlj2(Q%(Vt=E}{wTdit&}kF%8w!@+erOFXHQ z(q(son2=LX?--yxaOS5CE%+Mab0YXlvJ`hZ&MQ(z&xvzGOuI*DEFeLqxp$5Hp1Y0} zm&S?$xkd7s?&|>*?2qorIT3vAJ(xB5@PaV}n2K}Z1p<)fcCWAs`hs_r1KsO;FN8AQ zsb>hswCa)|;4JyI;eENWvGFySJ*@vV?dQ1T9A1trnX|V4wP08EPCQ4)i4q4unYAdx z2!ffOUJ>}j=yHobe*7qUhS=&-KwxNU2w>EAfHxA4DbkU>7r^gJAi-FyAEO0M-W%N!4=$(#5v)})K#mfA2 z+Tyr1mvHPa|`MO9qAg% z%KzYyjcK3ReRA_=n0Lm`pCP&5qtdL(gPS0WxS{woew^;V7;=14R6C-eg4o3L;Rx^ASVcT zNGIt)UIjqJJoqojltSmg#Mr^d2^aHVrM%JE(eX3a=A2PHo}m1-tDlLtw|9KJQ66zh zmuFpAGB97T{{BRd;D~bJcKr49e1+xYeD`KlRmrl$pml%4N(S7tj6;Q%){pl-84+E0 z5ind`d!wFl7BWnJc5sLk-tv>T0uRyCuSTAW133wq;A=~M*Wy|Mr?m{i)iC;_1dXzn z?t(1Tf|eO2O=!I|WAh1$hVW~R2kU*BUdA)P(h68FTH|(HLsL73c$$aNOoC4F9D>41 zUb{2Pum%Eq1PsJ3^=HQbqH3*m92VX~wJis*BUtu+R1MBGE;*sgxZqKF>VI7GW0Q}9 zz=JW(BIoU<1%qD3LWvpkhN$8B_efd3-B6y{E{x2kY@*sO^4@E0w(iVD72@*}PLAt4 zK2JfZ#37%5%(yHKOv!=SNG1|^?oNun%6>q8{Q7 zJ?px^?c8~ot`+?;3i$}#KtBLEC~fF?9C)!AD%}^(-Bee<3;jtisIzmRb^H9B ze8iX}?LI#WMs}`ZzlSgfQ41rYpC&>0UGZP*l(^_rwW_j`-K-KZx?i#1sa{;3NjO@K794PLH$=*;lJa|WkBDxmDbIiH0F%;>_!i-9P2<_G~h z(Wj|U0&Vu&yxK!=ay;fT5Us^9APhipwB37i(P;LOWIsB?>$kTgVTM|w&GQp-&8vTS zz$07*o9N2n-imo@Jzo99lfq#_3cfYq z-|q!LiXDf0k5Es&4vhby$7HcLJ&cQ!QzarY^0b#M^Ckq>JUU%?R*RbVq0B7}jm=YV zM!($^UWLKyVT4smYM27y)&KMv6mQR2jU;I=l}JXTlL7h0C2iD7bI=Tpieh;a6H_$f60Qi<_1;UAn_&cNE2SbOGc*H3_up87_f&x3anp>;`8!F?m_?svIUFoWgL6b?;Xa`2teww2DrbLX(oR-jBQ;p7U_h)n-6?x((wW#N& zzmCr@SJ=5wIUPTH{P`<&gGv4{QyxP8gvt~1Cr*%?zYoYeQ5agz9C9JF`LODCPh+6o zpDT5NYIQNoIO4kmXA~n_UONb@&i-EBJ*Yc5{^$gIjB5R!SF)^-$f({!BKi7KLT7{LOUpFUX`Z~2dx2uH2)LRbnGP^9(&EF zXO+*uAkx(7U?t0<;R!J3L_tTVqodPlSa|F-$D<3d+6q9(p1H`~E@@xS8`t$&fiiz}2{j*I+g;*VQZ8_}z0c&!6R8MrV5L zSSQ^RU9H&r&PyjDyVzU4$x{3+$(>GhoXwX;c`h#GaBXePESkOg7TGFqXz!&nLYU); zogR;qNWvQH2@v+VqvvlsI@F1fuPa;RfPFv`Grtcn+Z#&5YkIxRed6oWi#&(*lb?%H zQ|ob3T)^W=OG#C@K}wDXig&yO2EL*iCIu(E-5TPDkiWrE@kT!< zj+Bsq;55f+N5~*kAdMOi$!iX|XUUDW*cPq+XpM#6qC;J8C!Se4{wx=|Lq{UJ_0zQ! zR%Q8*xF?3?yfFo%Bx_TK&;o}_>y@6&r>prN; z*ZPX*PW7PUs{NFLT7lhK-04LG0nnWZpE(dgrzKO^0c~=(HNOmD8t+qQ?Q0Li_yu6VbC6^t$r(6-+N-Efzi0)I9|HlBm6b(#DZNg}x=EMW zHxDKet)j0*vkTseL6C1}8IShy3TiOyZB_r_T9h2)X&ExMDmbhij}x;DUf5|n_*|6Z5TX3fpS!=nDIWRN8q;oFDX}zGy;r{FRh!VAXLew? zw*kI0q4RcWZ6P_>-4MR^E#aoabndqBYt4bc77x(Ke`lp`q}%Hk?G!ls`s0UKjRHeH z<>yxWYI*|<3}23}-Ged$6EjwypuQNHnOz?2?=No$4?zwV%ZN~FsHyP+=R^Zs2H}eg zCPwCbkWp?(GIB!pec`E^4I7Fe=l{l-26Yc#6c7;b8MaB#bH+`=+yj*Wh%#T{3mW_c zOPOz(o15PQ{&^4_q$c($20A*fZ3BEj>0Bdw3hENG5L0nR#`7D|)KCy;07&mmbi3U) z09xFb{cM3^uFz*N<-Hh6e#v2M9~A~R(q^9Xn(Ppw9pHh26f)rF3nL~TIRkB7pxU*( z?9w=StB;uwNz#@U=_x=*c-(_&#K@R#_EOV7UyTB(s*9n}!YE#bQi=Ikz#U~RW3jvA zFIC8%LTL7PycKEo1J~Q|3C^hrV^NIDQd#`gxBD?5J_9WTw>0-@)4$ z3lm#J2$(Oi+Is(d$4rVmAfqccZ-!=Lv7Mr~-blIH4rj0T)c8KJ?tZVR2O6Af=NqAm zx!0qZ#n2oC5~mp$AGzm#R;*3hGvy2z-!B{4uoV1_#bR$R1MDhkyz|t+!dL7*B)Gy6#v8F-qHsqM5ixzFq?zJZMsup-0g`hvxtO*#LsM;Uk~&0Q#p*0Mfc3J*enuQKfOW)k4Av5b{V#<)+}2W`@=Nt!|Y{lC(sY z?qM>z?&h5A{pcHyf1_tB|I>$+#h zJb<9>Y;8S0WX@g$=&1>w%(UjwWc^1lbaKJypU^*xj()MmB7E`3O|Pk!T-qj3{Gwo< zu@o?O=jZ$iAQZNYB8`-FT?4Tl7{XOsa5M zJ4UB+YjXX!$e6(?7cwy};GNil7B2)9>r&5HUfqE2TPbtR)nD+J!dqlvrAt2SMr6Vy zXmzcpx+G&MX`rHk;}B=%s5w*qV@doWbUSV@4fe=B6rsb9Pe6V#9NK7*84hl+dJG=c zX!(P6ogtX~G+<05jJr4a=)1X@+0q$hEl){C;K9!d-)a@@+yjPLM&XQa!-w(JD6(f0 z2Glb>*Ba69hf2EiDNE6go*t z(s^tbD0v$CIqpJuA2@5nrv@ITGMdT7ynDB6l4`9)_TdY3D1l>;ow|W-XNrr9Q-V$D zfo5`I{BX0(x^&vw0AX;--={!flht?e7=m0hL(0^2170;CYE=b z`&Z$xqn>__*qf|*ddzMRC04T_Tk~aP8ozoUbzt3lUdGcS55!I48YV*)j7451@9HvG zFCK?MBt5+pbvHhE>F(^WOBFpvfkK-Uij=z&y8nx?|A5A_kN?2&%czuaml+kxEEz>+ zDx)G>M)s&gGP1Wy$%qIQB0DltQ7I#eN|Y6$kYtmD?BDC^`F_9WfBxtE&hH%OIZscI z`@Y6!yg#q?j=idvs3|-=vP)%kUVyH%v7`u~1QuJ%y=A>n{XH?0!} z}>F358j{F#7F?&rQ*3oR`z(ZS@W%^m@mGMD$T(F@q)uEG%X5MM0y8ugN? z^xOt$0i0=q&1yULghj1EHJ~(8+&feP6caO|IQI@4LmzI{&B8!WJ zvaUQ5Sv~OYjphYK7Rzt3wXW^B8y3Z%f>rS0y?a(C()$mQ1CExR`A9>8NIg9ruXAo* znL^64cx@wimQczgE!{Z3?_Z3R-+%n*by6fjEqPj)_*G_~ILXe&R(Raq48Q(eJv~kl zzPK>ZRd}D8?uVxEfRSARQ;}(6PmCn*lKlZ@H(b|--J?GW!fjMwn`T0j`f~M<**e? z%APODwlCuQ_XYR&YqZ`w1LboU%o-<@=-wHy477XC+)PbPg?(tV(%Yv^U-=Rh)Q2qz zD_ru0#=+dY1ru`e@WvVcPZed|=!43tG~0fXUoLw=MK~ay^2kItZq0wsJN%UjgTsE{ zP@U?rPZ)@1R@K}nqk~PVbd%DW(_)F)0n9~#oY#cvf1f%`aT-hxDjHqr2kF@(ASZ+e zaQ(QG(}XNzX#e^~pLLD%=84$_YVH~{S0qZSQ0_=-`tAI0^A8T=Y&C;awMZzOTR6N~ z(IViKogG}L{ClW^8}jnBB8?6*T$K16<`%e)_s0?9cFyR+O~q7a?eu=KetrfL$ea>V zCp&o9ynPay{Lb4$w?}BQ{xA^6g?S^@Ds`7IFtOoD!TT`BPTMD;s(be3!o9etzT{~9 zQBhUb@Xdo%@0+LRHy@@<^d@`pg(zUxTs`itfv1+ZU48hF(%b%Q`@%KBi%B&mXCjNQ zWZR3jNsbr%_tCtqr+mu4)o-y)eN#+-QE!1a>xr6}u8v6o68i}TzC59_JkoBTpzxOueO60IN_TI0eE9uY~`}tkG)*DmebvB zCT;7u);9l=o?PXhd{gzNsPc+5|oIqitR<9*KD6 z#7)?%Pmkb~QV`cX9M`O;KD-;#!soxwl-pk0;azJimGx&rrS5`&P!_jD?OH)OMm#G! zCTyFU)LDO6TO@n-h=?p@rgQ}e$=wQkmM-*W=8LC-clkFFj3wIODMGwXvOyMpEnF$- zm|r-6osX(lu0_(hMRL4crOr)`!XYe0q65j5xp^ZyXWE=z;w$KB4?hc~d|wkR8=k%--y}gUkU6`hrJrKU@diy1OWCWd z8^?f0eL`0~b>GH*^VE+|cm11l4&QL<_HR;BtX{(aQJU1c$YP7+=|Q6R1!$U4C;_X$sVCt!R4ON=`-Rv~T&Xy|wPO z9RuWrlEYO`{h(Fk`Iyr_X8#pHoHVr^g!-^{WHH9yz?^vPXNY7|!lG#KIn$R|rS9v3 z0@|STme#;PEH`>qC`*i@q@t?pL`ytY?99isI82CNrr5YNa-(#~xH{qoLZr{ri4es2T<;i__Io-&b5>-w8k7rn~vur+fyeiu2X)q4Em>M+kcccbI-v8 z>lTmPqL@0l_;2pq_qqR6>JUXvKeBl05|5cVH(8v#*jF1)WrZEDpGzOLi!Y6>BNwre z2$pA29sYVljT7VXzHpP|_!a{L{=<~tTWq=g%D)9-(0C)rf={h^pGDE!rT_W(s&LLY zow()~R6IaQxJEmnhdJChU*#gB;11+@aYV4X%sTR<%%ImMrR)ohJy9w<9!ukBcR+W_ zN>%f#l`4Z$1G{0XWWIjx{oDd9laKk-KDss5XNBPqN|`WCjm|Mf$`MD~Y1rI&wL}Z975O@lej@N^j?`T94@&v}NawlmnW|)o>?MHg)u&gIdhXD`lm9r7c{L z85gDQu2CW!z5^?!U8KkH|E8KKrrO#%4pUlUR7!I($m48HHOC^{@DJ=w4yx+zZ*`8# zfzV|oviKwIVebaXhAe#GZV?fG;TQ=iDIDLc0oa|KBW$h5Xu@A&N{Gpn|@?o-WuW; z%Bko7DhHjG5Ul0*gtAhTvY#X1VxN|cj{53*StgFF5=RZ%Oh@rbcGmxHi)(JX#M6B~ z@uzKg`rE|p^7yy68%c}ICTBR=K9FccRN%*9_e9SZcLfFZ;^|KzwdO=t-iVCS+v|k* zEs5DPpn6(1Z)4x8&No0+m5+>wGyiQ?n$dAX#HTNp^%iIb{S&k8caMCkbtHx|^YRJ; zGvvo*1U4&WQ&k!Dx%@3<;M&jkBz@44Elh=Ouw=OI*55_$g z4Qo4Eu+r{sa zxnBcn@xBrUxIvs+t)RMpshz4Q1^jsKcypTd85lGQz^t$^uDx8{cvqeukR0izJM(9# zDW0uf;*EGH&N>ldFMlaD17>#w*^~-fZ%k9hQ6n;_D+V!)Q=d(p&r=bp~EbXh*`}95|=oS%twdEUo>ishq9BL@{p+^m#k{>{6f|; z6w0$C7zx(!u2bSqiRFJYf;{V+o0#k>J=>%NOSxAA5KWr>U3UWZyY8~>1bld<-NJ8= znt_2S&Rt{x({lLkLOL_JR6>iK$){TjwKmKgma-(Q1R2~JK(`LaV5x>=J$n!k+)a4H zxbKKtE2$#8a!2vBq~_z=1<*SC5xK+(eNp~Fs5(0$iq>IbFc8lA>UMhS*oh`1D-y)R z(|_owKh;xT&CeStRjEaZdw;kpS6gU;!o&982GxdJqXd0E6^H8MxTIfd(<*O;_K> zXmvPwa!b@Ly1Q#YRMAKiRY;sZu@?^fDumCDBMMMswV!*H!+$6(R2?>%9i(^oJ;tjl z5sV3R9BOgrvcl0Hm>he!fY|p!1^@IQ{NkZGz5XlngHf=6WM^k)-iA-JXL-$%DZy*>^LV#P?ZtlsFPxJ_8jv%PVYPui7nuMMji+<`0zp`H`6{HG#>N1lPCxTN z7-*G2wOC08V!^eM+rYp>Pq|K~2OdAMJtrT%#w43e1hSb>XHmH@@&I|wo)g`|uv_1X z+Fkf;hdB5QX#J7SyavQ{>T{PqCGEq9U-i9@0}CGyu=Nf>(PiPkFBE?M_m6k1A(SBm z&YePDbR(QcquA;4YzLQywSzWTxHqXDCW67ps_xpo`wE#N>w!nP&C4kN3O&280-h*R z>tc=DG?5m3&gog-)eo>I$s!(j=pZ1_9RvYFss&V6O|hF80LM?@70h7OAmEF(1`VhN zwz`kZK-fy$gV5>K$cV#lP{J=(5yNHprg0mhjou(_jxlQ*oS5K&xMj=a>0hr#;MoR@Vci-lx zT@6$OE`1)7jvT1>O`s-N`n5DaeY4Dccyr*7n4{`823JA4kxMH-5e&~A9U#@&Zm`&= zff>`++CWAoO=jTzFm-yKl`h)uo-R>9UOzBM4r~O~R7#8kS}UM|j7Ue(9vDK;`Qe1$ zcghPH-gyYdwqPJ`Bqolm0!tqwX_eDJ2bBrJ$jnu>7xb;#mls||9dP*f~Ev~%v7uFr7_=l$_>#|bYI#LfVeT+Hw!x?}C*XY4&W*qRf zLvAe6@s1O{x~jUIZJ$#;VEypTvE~r>{?(h;Uv7!FKtl|*J!tQX^-`kyF*q1Xrbite zw>>A;e6HB|8L;|}>x#r76O%;UlP7&Z^41!kCEpTha{`VwlhM;I*x4avJ?onj-Zsw~@+}rb)%l?0;!u6M`qANE3_6RdpS&T33@JPRQ>v_%~Isjx7*lI_YWUjosS+w z97!oP!EyC4aFi&HX@}M4h;wEnbQXPKH@@DHis*ZZF(mAhu4Ymb(cUz*RmP?FN$q6W zC&}?7W##gvIUtf3irVFcbzkh=72X`CSGHG7Y;pm4j}z)D8%dyJEvi=ELFoKKZt&w} zsXZ?}CcJ#xtpYVPuPfbT$V-b!7hRS~s^C4z(>biFC%@fTmJR`rw#nhE4tD|k7z6<^ z$JVJsy;I&Fti+GmOr~y7qPv~#)7+2Ma6O>|`l_4nn5ycPW5Nuzd43KM4(H5#+Du1F zOOVm4wuKDoE%{M%gPR}xkl47hFl&J>Et@ytMijp;Seavbdd-{q)yD=wol5LCEOGj} zaQl?IhP{0*9q|hc!sAS+!RjK1b|70g+-4bBb@AUy&b5ESkT#sS@caWr z36kS;wUf+!$u~Qm`2>g?>`bGJ+ZuvMH|~XHBs~yIF3Z=O*chdgSIS&ZJ~L2 z?E7jxe+~@HRG(r19KZ|=F3-rK=xM4@xEok@@3O(YwW8~SSE;M4RCN_2v_tCBj&hPeo+=rJS&ATxF(gQJrMp-F?JPQg`YQnmW2o8bs~+i z|JxHJytjakhfhJ!ze?7ii!R(A$-fKco-M~2Z{4~T zLAZ1mizJAzCGJO0O}M{Yp6yD(kR2hnb9<36{q|zTgk+d;eyFrFGloXbUFb%^ERRv3 z`JioKD@YO~xGVs4VAulGXq!;h3rOBCXWRR6J#r=wSTqU0GkleWmm`G2_pzv^*||*Y zPM#iRQ7x7{5sOokJfIk=L)Mq`k9=0QMuiWA=Fb*&L*a(<9jr;71m002x;+} z?eOb!;Xql1Xlq%s7az(CWd?s??U8{d{^R@i&9)I++5>yvVG5Bs+GUWS?PM#-FQ;VH z^vNXq%bg6un>usk$W_-rpC`!RQUsK2HyvFI@c1Z}XKz%NrB=UIHyo{iy=~oA&YEiM zftwCf-~}{@VlF|VhjIwT7|ZXbmJo+%k28>DvYZYYMJDefjIxRePT3}|;1HJrD}Ny_Ro1Q{(SRf%Gst5Dd9Jvfy&lWl=YwFJjC_2a zi=zGG%mbZi-OHr z#&)R9E$4og2RgYE25n~&1z;F_yfJ-Lm5dza4)_yO@AP!~5YM5N-?u7HY;%_HLd0y@ zZ(Nt=9aLzKMCy6537SG%74Z7nrubrP0z>ZqszJObw{8$(!`R^R6$G9-V=HCX9l*?5m-e#~xR){vdZ=ZMor-tCOHvgX_?qf{tzfHEM0$!)yy2Vaf zBd5uu+q17uE7-fr;P`(CeLkS-*PXUG;S}nBMPBrQS>qAX!7Fk0)4g3a;%^b0whc&3 z!E`cEr|^_ za@YdTon}>i{@uMXBTi9;ZkUO1tvoyhknj>uHJBhh}5~yG7vFc zM_kK%QQ1h|k*QC?6bA$-3~ucwrFCS!Z!?)1X@jB%z_3vdQiWEGV{H);!y3ysg;iJ% z*pLVH!Ge}~l%PG@D}5Xx@ma#%=8%3$LaCAJpHD>%Gd z!PT)YzK`F107cPbx5h9Yx~qkw2^syde8PJA`a55xsk4z(Vm!s>$c|0S>F3^6rJ3K` z1rg9|G8E{`xaah7vGOM)zw5=A6jqm}h4b=EKDv)h;<@U?W;Q?l=374vf49I3Gtqr$ z8V3l47KEE`UX-rC1xMAk2Z+GFnI8aJ}RBNYg}A4 zP6);|hf-Amz&FqF)q=8GUM)6(x@w1k0e6_4KY|2z(^Ve`xj`>8xbK9)vbMtFxiN(B zyojfKk2>0w?Dk4h6F(BOcilum?p|oTf@o3G#hFn%Gq(mQYTghUBAVaC<{`S5s#B#I|_)bwO#KkcBqbeIlAiloiKy3WD~yu_!u=eRIFNjXxwtnkQdhQlfT+fq229ddCC2BQ-JS(inO_&xEC@BRzuCat#qg8+}rNZPG?fj|WPEw-#UZj2*6T5mA7mMpJGR&ZZ6 zhhGawHYrWN6&CGH>0M6FU}KZL-+&UK=RL(qoVb%cADnIf^!0d5~PDC$mJA+AmQ^cFi34Fqr z-f@NKn7@wQ+w?k8wcTSkf#Pi^F2BI=9Ncq;pHGi%#W5IBSFZ%ka*j|!^b@~vJ&;46 zkfR!E;VL@}&D+Toj%{?b`FKnm@)M57x>cn24^?rzxG7->Xs`!2A>SAvps!97S*)R| zJ93(VjC0Xs)(TL{xvp0h=~mHu!rA!0s2| z{fL8hKsdjsP3p)I=Dm9E3p)8MiktU@I+w-^CcAC+yG>q9jkpM2B~fLY%6B}Lg$lI3 z&SS${x!QP}Hen0|&sFPVO_>@hNc5I0Zpv&|jWF!)fWAO=$EKu)GY{BQC2UGelz1q5RD?~j z@ujBCUSDmBX_MTPe!|Gx4cWsUw&NYpJG@%uch1L}ZDMYIH*>ViAU5-%NPP<(FBc?a zpRMfWlscY%?<+n0!Bl37mT$#gE#^6|4L!jl%+KjgS z*PV)c%Q}l~kGdc;s|63f>k^Mb9Y+`JP_FejOnl!PZ=}m2m?eo|m76#{vRqZ3)Ay4d zG)T<8%N~vH2y{}SII!hJN|dU$&oq%FLy!FD(HRRKx@tY*HayV}X0{wf+8`4C{OEY~ zsjMuZ+PVce_*SbOw+dZwXBh~%pF}YLag!G&8ZuAhG=nx8O9p#jJX!?CTddfnN#T+y0t79 zhrf8NDe?+wq`YMUxA$;tl%u(L$U)MT6=IK9KmWYQ26;~*MB`4+C*lyo8d=N)cg4e5 ziMZxenL&lkVSm0Mb)jP4_;J7_wj$Jor}}Vo$c@h-H)4S|%=A38R=zxT>`Gz+f;l2# zWqzeE!}2D^nj`3Zd*RoopnIeO$R`t`SJopQNgtrtqh0AG0xE>yuu43Wl5M|kWcSq} zC*3)s%A;w`rE%huqZ9Elq2G^P12s3s4pH0w@*YI1A|nK1D9sYH|k*0 zcoM8vmimu?T&+$EIykuD5|6P+0z1+ByX;SfQ1}(S_qJUx-5Oi=u+>wDDixe`q}-kz zVUWv3_qSVbY=qYZe-2OIs_>;4_3U|mHIw(UL7>(`M|ZESHI^oBuO-Se)+eqSS)P~X zN5Nl^Z#c@mp4Xg?mxhk_D^;&FVf;~c^&;g_MSA-8P*O)0!PS?Y4PNL0pIQLbJ?_Dm zVnqIqkT};yqtKeZHjnvJth_8nJ8Z``H~IuAz2#4yc3aPDIaIcCkxy-uRrO2q5RtUE zs6C0FuG)&=*hs7)dTD~AJh2y0Ne|HR9w2yTzfu!DSjzA4&?UhF zUe9}rn&?Xo`XIY%-13AqEUG{hll;mhp8V?ul~rk(?@JRC z6Sv##JbZmsdZU@wHY&uHbR{*fygk%a zPt$aUy8a;u?#V+RRC98d)hHnslcxg-9ZkBEnAoQ|Y8^ms3pPpYXf=a?ujI)W$IFD3 zmE_^am(f4KJ~8#Le?>`H)TAR>K|~mIQIz-yAt+XCIBErR@~g^4DMRI!y8X zr?~o~;C`^K@@H~Ue{vA3`9q47ZDg@lw*4%Bh#8CKr>PE36dCyEQ-9Y@z4bcge=J>JP9-G^7l!b{grd=kcgY5)B^ z-)28a_gVWPl?Gs`cL6E-|2|h#UMdD+BF=MT`EWMce>wM9R08x)8eh8+OV`XmW>}(K z_EIYou^?q8Bh+-sx^naujb*e<<@mjJEzj17mj~6N<=YfjX$NTt?+nZ9)vFbv*In-WZYQ*M>oNxz53!n?W^)tCm@Y@@=HE*; zY=IzaO|bsLm8*^0n^dKOkQUsl^QJ)-gAZlRhs-!OszYlA{Y#KM4@u$tiLQjxJ~*2g zahK0tJ@&VXZQE9_(zDUF3Jx#x-tT;9XWHgV$Oz~v3vZ(%Zf#P6&GLN6je%_Y3bb9I zQ`K#JfBtb2ed@=#ox~Cy5p8$+^cNiR*3lgDig>d(TCWV+IflWdeq&a^KIZFrKeDs1 zXw1y}AJ+(0+|$`L=^Q^0>6h;#K0C))+`1en%s`a22c~VGsMJvwcV{%M5PLu!{0th> zFE!sO*&ah9_Re16VT^0;rUl67S+5pJ$q~t}>1$0qT@G2KKJjH@OsjROpBsqL|FUQb z(*CSXw$~s1ZaN}gO4ALOF+bEGpp=6~5tbQD*+n(0dnN*6c<7d81|iG{qz&Fh7{8IV z1in(rT%t-hEE4{``qHIKe%P+RcTSd!425*l>vMgNrO54E49vZFpev7HG)Ni1{&zTe zx*mI!>|p(R-gEZn4IX`-XJ{{Z35L`)203BTBIhO1iEQs1u(-#obf zl6c+d%o&0>mD1n)bs@V?GAK?b&4IbpzejuxA6#LDqo4&!<>F-pan%o~DoX!vTq5Y3~vt&czs2qAQ@GBtSGzgxyKgL}E zdT#!Kr$`XT^sk9XF^2^F$icE8&6a%d(Z@QUyXEi$H5tOSdXIG!ujm&%lTCp|va}4# z!h4b(2+g+NpVRV=Bw|T|Weq_9^m>WzJ}FphWTEkV6Bozw4_`t|b-?Mdg)`_h39+*2 z0cfpB6hLiZ013FEd|GW zDlJJng6NB}M|tQMdUrT0C1KilZ{{fzu1v8Ls~R__mn%Of|GC|x*~$|V#REy7MaT_{ zOFXYP??WQRIVvlO!6in`mRSM0W35XnId#i|3u{ObWx9|Bbf9#^#F)<#lk9}TS{}w*nZI%!`L{ZO&O3zqTH-x0dz?xVtp%u{G-&HhBnWR#FflMo& z2QG4+1nH0tp~b!T)G-fuTk)!M;D@pX@fVoXP)jzxsK7{Rwgkts?DRBgvirGyvSNTQ z5*H=N*1DB^YX0Qd>0Cyj(zk`)%S_qiaPh^-%>W*lzkmCq%>iR*r%F42JB)UyAO9qh z{Rms}&JDqL?D#cs%d(RlG(1`ZNBtT5vX76B8i}l5%Dg7GJGSM;lg6WZ(t`t^9L&6@ z4Dzodf4}MgK(V~g?Y{SakW6X$bff%s&6Y9f{0lhX<{JQlW;ur*CP~PHV4i^H+m#%( zpqLtw7n`LBeH_4lGB!^Z4&X?^6z}hcUd44ce9(>qt zvbP+6WU=^GlecpI{nw?>h@wBoziIUIV`{1f-9_Bt*2q%&5XwIzfNem<-|rFcNL5AI zcxl&CaXb0g(eT#hC#nA!#f$r;Q8b9a_`-_W&T}*b0kHc2JbvWMQrr9?NqW+1e58yF zizOu`8J_7R9l1b8$%iVB9z9~1Z`?Nq)5;H^TSaRLG9!C8WDa(7^ZuoPXikWkJ^R!T zQU*9+JcuKL7y`-Hz#!dy<2elDgBTwctgxGLBgnrO>yGc=lU3qcS=+hOdP2iEL$re3 zloxtdqK#;(ZlO&_w*9w%CwItvhrCH0Bopy1D)6F6{hsue%&YB+2%cdhT(;TfA0U!l z%2<@Hhc%1{*YPprX(kMmBaw!n?A3f6OZmNTa49I;-XUGcsoptg-i5e*XGms~I_-pdG4yg>tRz7I)!GkNX#wfe=G?dz!R}wyVl4x& zR+Z+F#`x3-4Re93{r|CH_q5^#_9u=1nmHCrOMa@o{czuYuqanZav2VtJrp`Rfy>hjLrPBkvaIb$&j27In3OH+-ZC)| z|FzdSY&`|y#X~Jm_7eo;0q;5i$VO%KaCaTuns}p%;y_KuYn3PfFJNBYW&jmYCzWga zr*1>~N+X zxXEI4kkQ7*k-#9t9wHTy7-X`Eck)u+2yX9k+>iyztUtLu05IP$OgG)S)e`NOR zr<>O94AHsi{*sSDX{a?TCx;7LSc^m!4-N!2pmAcbhaCxG(`vnnc6(F=x(UB~|6cA) zOrN(&nR_ltfV`mOSR$?IfM5;B(;(?)u+_b|Nh$ zc@{IFUry@2Me?s8vQv&`l1RNgLXN1cb`md>z53A5$Vkw@&@g*`dBwf5C)Gk%HwLgwjIM>n zxAfUL>`NO7o^1P0fEwy}i6)4s=u3{swl{%sup2&;{fHrMX=wo%%Gdb3cW&`YhT?s> z^Prp3NQxT^zhmyQucl^0pX@1wTK0Zgkgml5eSqJ73lW$5hgPwWMp{GcpgQ}q>`fqZ zC%qOr{T^`RwUSL8Qjj2!LlOXRWX@wxSAb;XA2X!c!g3UDb+ldl_zceI*$>cp42Mvx zVV~YifYE+SQPq8hg=-!IHUz?leap?q#o8EZdMyLzvs$4Xc?&^NH2`D~nF~lO&Bu zjNFKxnH$pMr4T;HP-ObFaf)OeKZ4|0CRKo%YHF{}r|OjR2>MOm5kA;3_E~Uz<)xxM z12*F8uU|wv7wzA5MeEsZafiZ`7q(sfsENPc@w0YFS3W!xGP|rtbB>+8sM6jrhaPHd z2f37#mowiA%bQmWTyz)ICOtNVKh-H&Kl!oCLTV~(x_fF2kM%%5RI=i;`v#L$Zf;I5 z9Nj!~rQd1vYsgm7xsOixWI9VSV7Xu-J4pSc07u`gM%0#zZ<-LzTe zTEJ;XF>Mr|+9sv967-_n(f6RTUgJB)4bB)=njPY66E)a|$i+-VmF2w^f`I zr{KWsNb&#u?q;B#B>AYx=Ue~%bqJ;9KOp$;(A>tF8vv?-@PqiYGcbt1;wE}6og;=% zcB&%|AgF8FR`Tn8Vdrh~76``zSg z04QrM`jeS~YId}fSu3fO-`cgeliVs`;<~~^$DeJ#!V#vjAHe{wJ-L5W>FhP>o}*<7 zJP#72fy1?2rgg~b|1R$PedLvc{xIAIk@zWQrAJgecibv)Ra8~`A?hwGbpDy#qkrPW z6Zt0BjGH?{TyXcrZ=!NElV-gU8&J{E*r>IzI`-Aaf8*Thx?k;fXS^OKExN0GSLak| zG=JZ2u5O-n;<)hnui=KHU2=ge9+s9z?ad1w3yBL``vYnu--DZL%;?d@GnIT*BZXuy$Qe~WSYRqI{4NP1#c zdi(8t)a6`XxH5eG{EG6%WH_!`b6oY=8_OqjYVVga>h*V+LvBpE%Y7|DtLoWL;e(&R z#y%pFC4$bEXfV;!yAAj#9*fQ7Ng3qQRqrtnVwrF#DU;*FM+yudW4VFE*Z6h)BckO@csvoA))lY?M-|NZifH7BSXN*2oa)s%&$ z^wgcHuK`!x9xOdRZgzgxu@4Ge#?k-xJsekasfDs;i9(9f$|l-AyQu!CI<$*0u^TeW z?UT?}i`Q!1$i1?^yIVm#XhmAP_s;9EuAmgJrRojk zv%@*cR^l8FFAT6HefAjdWR2W@fY^6Nz$JFF8W-3p;sv8G#ufytaCGk^7O= z73GM#mAiI3$*(zF^{dZ0&1brlL($(XtF}&DY=^oqw23^7+q;4Mu3Tz58(idUP9|#K zl`FUS(s$rTN3n88$!_Yfuy)>CyLBFCGcEkta?NvG?#q*x1Dy{QqP}jN-VqTNmV5Gx zL>%9~eKgo0^Jct`9u=gerA_ymq&nOAL^aKaeNSQ(+Kkw~32!)E2ygv5HMOTx&Bqz& zHPP`~5HcboBnHu=qL_L;6_I}J_^rl+f96YeTTO?BpMtMu^Z7H z>+I}|%qsk;Afi^Oiz=$A%b8^^S(c1{ayNt`BPXYvd&1Dc9}g{-Z~gS3w(6-LV!vhz zBHArRsnmrV?=EiZ394ip@AA5+Vdx=R(_Um5R$$tm;beXgUJZE)Ei=3qm%6)?-!opf zzP(FG^2vIOz~}tX?YS2Od>~pYUj;7?@q>EcrFtHAzL_1Qs5!82CDzb-LKk zix%(vY>S+VE|C?zx~0&hB#}eh>VCs?QiRq4Pd|+|L)O`%cc*+a(Nt#a{%A*W#)SP- z%aowqXyh^$KGK@I6Htd@mKmoH6>urjYOa?v#FJkwMojL4>D@uplyGSWJRZ;4q} zN%x2-_1C8T{av5s;_m*aBH^}o#(O?{$K}dB_VPX{b-xF=cJu7LYW@Dvx%RnhkxOfw zUp6t-ga+A^=L&`+quwbViu}&Oxo6MZAk;hn*L*bkah%Ad-CQGXpnvu(^Qc|#+&8W( zYv{9WmKWmb7|vfpx*0(r!|qPQ=)OCVZ?u5K?dyChR#|MpZ@$k!_2ZuyV+t)Yt2P{s z8arxexYnV)F!UnLQ5;@82_mEE+GM3=#M`%)E~|liyT2Zo8hP+l+@<%LQn}TRolKDJ zOC3{t9?@AcdRZ^`Ln&0~d(dBt{^0Tx1~land0gN(7c1_5mYb*=`@$^YxBpYItrwuD z7FT@Qe#gI@b{~)MV!9|QdVDJCS?4{#+O;aY=c{IprfCRfdjI?Q2=B%5xnJM>GF;|= zugN!m!Gf21DE9;=`F;+?C1x2}SsEf+o14k9r%asVkIaQr2Zgfen1Z%I1kVUbvne0n z>sJj8G?&+2R|#b_=l0>oYO|EKag^OMQu)l8wF!H=tnPX){k6_FeY)}8JGEca=Yn~a z6_s3E{_ZT969GRR3LQFEGJ4C#`u+T{#lXo)bZTx+nqj+w%mW~R0tAi+w=Yb6f8&I( z4W9+1RBgp*C>+8Or4fgk$J`3uRR!iRKFcD5OPG$D6U6aWKx+w*><|R-6t4#c4-&Ax zzJ-2fTsVtv!-l;i>g5F)Rg_VonJ#j-k3lc4FFl`X1qjDcL(PAONhAy?T}Pbh(6i6)Q13AoujS-60DCX3l>sdw{48uIt#m}` zSW;|h&#PCZ@|4rM588y|nlB67w4VYV^1c3ambRK%w=CA#4XC?V3kxUc=+A}E^>8yV zF=?wD*^X8YH>cjP(>FaD*d07?tmrjy%71=9@6?&>en?lSf=ePM{!X=g%551%|3n&@ zsG8SdkLmgCiF;ai7ou6#5q~DWGTPQ@RS&n+{TfWov2vcg=JfU3>lc%Zj|-KH+7ap> zk70mbXEgr)BP^VH4{hG~C(C)Tpu66yM5*I0gN=JWB+W_TFXMazW&FJ@i@UaLPJiV$ zIYSUHryH%%Z7?*LVX(pc#SRyY#Xl1V2(JD4DkhyYe;1`+KXcXZ&{=!67!ZC3LcQ>4 zErrx*+^9=>{od`3wCZ>m?TT%AX~mCkX_?Dr>buA33d@X))v8RjjAHj;Ur7fRM{}G^ z3O;2VwM0h{wilkNRx=~3eb=QI`d)`w#v~h;=ia5qb{Rb3)SS|cH4m-(b&67g!s`7OhABevyFT1`TrGFLE~)|1?ptOju4Aj- z<~!#U3czqhDMy zM0$#yjK_%dSl8T6&ixy!t1f^yLQ0j)W%VZm1^ zagciGGaV>mBDoFA-8y1vX?n}sQ+vD1;;$7|Bt~GHt7>ZcvQY8#&$BO;LF?chxwMW+ zPp7<0+QKJmANC14d3kyAx8WW^hsjwJW;czay(1%r_CYV26;xx%Qn|VjuiiNBj&*92z>99~Optn5tpwv7Q4=IQn<|sWJ>hD;r_;>0S(3nk~$5ZM>~LgcU+%ZX!V3u|q~i{)N@I zm%`634A`X2(!L5=oV!_Ixn;{R(;H~){ZWkpn94u&)X8~=#MD{=4UY9U--T!&ynB_3 z<|^;55sotri8^PWk9SQ^mWdiaVof<13uhh?}7H_z9Zv_HnfjX|ZC6#%GV z3t*LQq|PZ!z*c8(vn7w;6u74P6<9I-=<}G9t9AfW%Ol=X_qf~bJG7TCp z<_4liZ!YqF9HL>RZ`KolP;$^7+yx!kW31uLyMw$d8w-o=Muv-TwfD;c-U>qt9rbN!x}LCS@eqX; z3<>*otmAHupY!hofWyJGJ-Ec$f4G*Z$fgh}3c=uMD`M+RU$C6!aR7 z1jFK#w%_rqbk_MGpL=n>u3M##iQbxIpr<$GMibi;7Ndp7BqW0HdL+;eMNx77)UI#- z)8@$sxv#HhKB83Ao?-q%%X@yn5aL^jKfVX_^tr9xg!{nE#Ew$wO17rycbxXu5acQ- zV)2quGpQ7D3D=@lXsoR@&vTBN8E$2ZVAH&uYDt4Vgbhy6H$NL5Ox1Dc5Yv=v_AQF9 z?(7ppyPS34bKd=$a}Iv)CfNTeEj54iZ9~JWs|=R5A8s#Y`WYLy?ET2Ze?I5I19^Ps zpHHEMj~bK2=VY+2?yJ3?bV;njm>j6ZE`3h~zvXP4PJiY(z85YL$h?h`LhFvI_(zPi zowHe4GC*#ff>r-wo8gg+O)c4bss~d)s+>6^hn`Ke6JL*C!o^3(hkp7b@auP;#dmn) zw=)YwsyL3Na_s%F)BjJ?sm$zebs_6_#vqI7?G#Ii;}=J?uBB9wXGmp^quc+ei2wx({n~zw}`MUQf=VsoulkQfiNVra4iRY(3xQSuL3Ae{uhL zi#O=!^!4Eb^^`B$rI5AyI%el0z#g5ATV<#T zxJuWNM+h8GYA>yMkpH!U-jf^E!2a~~K;=}$$!aTccnwtP7#ZujpYJXZY{|?teHOUv za7W_VvmbUnj~?&q>+3t|V7lQ7^~%b;?Sy~8DBCWp6OYgA$v0oj@c4qtKCB|Z^38Bd z3^=%Aj|zHro2iKxU-K*3Kj@gCmSNu}zhYWPe@I&?C$*4Yg$^3#t(bxU)LSo z$%j)7T1TXm_;nr^lX{(|><1OM?Vq8k{e;zzw^P@`<*xqqKA$Fk*fQ{>|E>M?rHbK) zFCRG>L5i|jl|yG=EzImLwtAI!)S~_IS;X9)oSmIz3C=xyd2Kf7mg#zI7hMCSHGh4u zsCn5Btmj`6$4FOd0KBV?NbJkyyk1L3yn6Mj7V(QlU5IO|LAS;WL{(RpDV%j0Y-9(= zD)&D_x_$=_9&F)qei^flK=0xYsN$~e6%}nPBr{S=Un9bJ=S>ZP3@0Ziy<^9YrLBZd zmd{xj!sMrkwWl~R_WY-N7vHtF&!weT;#f=(&`Q32nC`HZ)xBY)QW~L?-507Vyp~hM zFQBD^YuBu~BXS^+xc6k1iid}%N(K=Nqiq>`*1v|uO^A8(W)@WR)<@u5=T_gwUFD68jG`>lz*mkMJwY6-B+y8Coyqtl z`k=9*pkiPbeT|Nv0XV-?6EAJ6Yuf zzg+nC1V;At*dmGPE6u2=V1HR}XLn1m1P~y zg^1Xu&pq4&%MKCNHMOt4ecMh@D3ohVirzHn(22o+`Cj{IeZH6XpCPL5G7skI!Jx|z zA7&H5_wKp8oU>p;a&n0dQ*~Wau0E6dWJ=Gp|Invs*WW*M$(n7{!@4q%+p4eH{+Zug z>5SYH!-CG+;?@FmyqE?OcwP zMe!Js)a8n+7s{T@ojP=g2Za~zl0M)_;BeMerfo+jv9?tPDdRQK6Q6RlR~qtKTNgXZ zX5=-JWw;7Vy&g7B_VryEs2?QPr|P<#{mAT(NlAGhzTPPF@87e;p$N7jO@f|oG~adq?5)PX-g~O66WPL*Gv=9y z-jO!ib2y6&Ej`!4fm}o2>59|y`q(>nbkA1sD%Ylaj#oS1bFMUZYT19Qwx))Dk7>oF zo3yFKf~ju3^`bJz6}wdh=Ff?>dK&o?Sc+4lQfs?66P_&z?qXBpmZb-1y@(uI^))&-!~vMl0|k@x2lUtWHInudl|?yeRU0e}&!K z9@)*q?)T!}vus#{G`g*OB;F2OsGe3RLJW~`R`N~#mDQsl`t)*IT+@Yig}Zx{NtMukwq^xjhiE2ULI#kQ81mHS#b&!`sr<89@>K_R+JQv1s-oWrgoqx}M^ zvpx^GyBQ`00*s7|?l>fFhmrHMG|n?;?K93U5<-NHBTHsX%R*qbo`q!E|cyG z;fKo)9pN@!d;t{l(o^YuRK`Uk{A%xeWg6R-9#^?0e|TANL_L?9pC_bstvKq5r{%uV zdK-^S#-1sV1R^-~@nFzS;bAs_Wy_yN=OzVd2q0D$nG`v$J6D{fjp~2sJtcSlM4n#z z9<_O8(})TwG4t@c!PIEl4{9j|rc7O1*AYVTtEKhR3JM1v@JdNle*2Rel!A6NpVN-9 zr3;z77^+!1@#>NqC%tJSQ|ZH*mnYr*@{B$P{Z(-o2}rna&A}Dtp8Ta`)X(wDBR1kw z>G`7?DGKcioi**wrInW41}NTpM@`v*qdfeq6<0ryCCbcQbmj8p3@J65>Ec)IPYY){ z{_u&kH_sOfZ3s9TGpb|R6Ld@Xc4VPNxxnVl-w8sjvirTi?S)-Ot}kqCNG$RF&avgH zu(_e(k+iuBhX-a-#y+L(hW0!-emDEQpw%=ick4bV9|o?tG_MbUm1T4(!L}Qi*_%Mm z!uKg(RFHYPVrN(1SnT5Ic~R>YawN=#W_(G6bXWWV1Qx7ucII~M_$B4kqeTn9DeRRe zidKfSt*C=RHXahV`3mD7rqHZXgT#)G$A2qVxgt=*a2yQe92?&z?)KB}GxKs~*H4q5 z{SORwEh<$G4h$$rPBi}3PE0TyN|VZZluQ@F_Ww}zCg51EZTI+16DkxHGDPM=rVJTE z$UKv|Qbdz^C{u+ZA(=y=hY&?5ij+{1GNsHVWUfrf^jmlD_kREHzmI+Fs;qrYV?~n1qkjg4_I8!vdO^i{@d2aY=?|OUf#W`Yu@cNloM=xTq{B$?06z2 z&i!@l&A0YJMeOYCr*KncbX-@eMW&`%j!W z;fmuHH}F)T@Z0^?kv9Y5w=Z|u|C}=Wxw?Kq@q}!{*w9GAs!-t@cZO{g-jHBpZ5r;A zJHF?wP2eS~YR!D}39bu0Jw2^|J?f)+00&F_S&%+>^yuN_F>g1wP+fjC12KRMmODptvS4S<ZYDT0#;!3=#DLuB^RYXDpv)uIb+m!jIw9>6&Zr)r^ zF?ODI=y}I=vv-qq*{PY1*-10gjZo+~UyR5TNQ~nzcQfI5>2jFxotv7NIBT41DyQ&a z57qte?tbaolwx|W-#>r$ul^kqi`S+Iw~fI=uBNxO)x;2gE`(8zJ48$(I)af0QRA}3 z1%38<*7p-~aod=F&p%v4m^9DnymCw3O&p*YC+|*dFtRx_WOvxTp(Hq%XiQh!X;ok@ ze!f>>9%|U{@HKCn8!uj{OkdOg#w}HaVr+TUExEq*z!ud=^>3wXs&h^A*fljZEgc=R zi{JE71Yr$SoZ0n&96cC6DUqyCF=o~IB12D^*x4|Lm%2QRbWi!$(-+7>RBJIOroEPt z_UKC;>CQl+|Aj%PhTmiM+nd%0B(FQuQD9AkBhWWWu zQC%_={s$TL`exnns(5&w1MQAWsz%3iH1}BdJ~VK@G25>~hg4gY{}%VIU5svSPB#-0 zTsVFou~DdP->hq35SnQ5cD71*dL*u@%1iw8C->KN>9vuptF@Wxtyr~=`0Wfe3Lj8c zUV`lRS{hlc!T)jrHstH1<~*3cCt{$<9|9NFl^@;Z0tq6wuaH46aG8*mU+J>WesH=> zz>jXab*6DU)49+4XhIm57KVk!qyPvjugHd0S2O-ph<@;3)6l%NyYzpnPGFoErDq2Y7^Gm_0tM47ua}KlG4_BJC_&rT+osmT}FnU@bZ?ncy55H zusm;+L8|q~sXZA9^_!&Iw{=h&KL|*8X#0#`M_+$vCwK*;#e-XJrKQ;xRjMKLDNj4# zBY%%VQT!!dcdGHcepWL}yG!W`GLBDsx??-Vq7dc)7KpGgQJA41^C-#-iS&N3w%k#@ ztKK=X@zWEdTvKC5M}B+rsmnR#=ijLct)t9vX~pi>ift3&=O0=|Cs1H2-%iEf-zCec zli#&kB=-EHR0i-_b;=B?{bEAyQ!wDf8%?{@t+D#n<`0?^`ZfMjE z!QzK!Z)ENL`c+JgS$(|kMb^ie%VT@PMTW1&mOU1cjBNQ?0XUB2yL?9ZS9Y*Yz#t<}$ofP^ztUz8UAa^9I}Cyz2O;2|Zj-5P^DX_(86r zl%=ub)|W4qpHwpl*MT~F0CNem7g}P2g?+DltgU^2A*bt=aeii|Wg!=`yG+Z+pHQ9u zHgQfoW+#O`|7Y^qwnvX2s|`0X^zC~rIH8Cm$O#&9y@X3mZ!guA^@9wSFHp|oXD>5e zz7yDKerhGN!%CY%H!_kt_*?E-rr83gw2+X`ujTnMIz5xz@#befL7wBEIZ$z6EdRb` z>xK;slNuK;2t&w*HtEh(6`(-4UvDPQ-L~1CP3bo-^J-x%`5+R5pPi=_U4Qm-vfNlN z<=jH;?94zSwf#fpf1*^4TSUkoLxXK)X_|Q})A_qPiFT)2hsMsJXY++s4n61ltz0kJr zH3*ZBPByTGgzZ;3d~K+xV^L5lWwjrO7Fnb}E%!SL0Qjlb*pS+~t1@SfgwM>Ff)M)c z&CP%}O-;D(qI`wL&DW8#-?R)2G8nXaOV2Lb4^&YYv;6sORquhne4O}PRO1W{>vI6h z-hTBOm8zpJshAs$x%GbNkA32h=g5JSg zMn;B)kB^TVYRxDRt%_m9l1$u%(7hi-Nj#5K`ImPgLc4=Beje%v*$PTZy_4ji{fB7< z(uYR@{gGw(gWDhxzd=40z9$#km__sA(3X2pgf)o0R}24*9cPkWv}{q`=lb za$3Q>z$!#P?E;*o?pi;Q0T=lIS>$e@AAi;VvhpMT^>f#Oel(V@-B3I&(sWvfy^Q>L z@?d(6C2-5hy`brZ=+>5PUYkyLO!}TW?4VMX%B9`el=-|e;GM#GK+j5GP^#IW87*S{ z>Dzo?^lpXctb*~bbK}ks9*Fy5{W)PiA8lsSeDgKXY!bokpV2yqW>I`?AYobMPv17` zh?;S~_tXm2~9_wPHrX=tcT5ZOSY=Vy8< zI3GLe9YnkPr5igB&!?B|?VEd^y;zrYHO8I&>cgscV%@B4oom_N2QlK=;Vh_vc3`TU z2m4j}iQJeMekv$OUO%v8`j}PUWtcneHrefr9=5sfGr(>iU4pI((xL2o{r1)i9tg|~ z)EhkhRJtec!TbqBLs_)xLm=-0Xq@%ZCGPtcA67GF8&jmI=;@e#|Sr5RhLF{6Y{{p+zIrcDszZPE{)Zz`= zc=>wjvh_kQu0AU-&%&Jq=!FMKYLU;>ew@1u^oJXhie6j&cwm-4hvuvxM$w}xFFwga z8xZ0cYqpCnQKO`UA@F>@SMVN1k!;w8WMxT$ZgoSZ&wocl z;s+I*?sC=8F3$L9gR_n4mDEry-?lRUf)#5DPbZ5_JNHlK&YH5yxhq6!;K5SYZ`hzo zZsqk?sy=e;K%bHX)bakfB_%nK2qY;7ZKI}UQ;cWHr&eKn1Fm%H=Jji6H*Jyv8Ct6K z<;#q=DnIvfc2YCU7T!|RxUJIov{bV+ZbYK**B~r%%*alBGyd!z_z!iVVVU*#@#F8` zzQsZ(OJME#^=t;|3b_|r(s$E^UjCG4rbAj1;C=%iVn3;r@F^(uTPM!9SkZd`W+z1K zt5Qj>VS8_{9Xd@W=`WAM6G)Ez_r)JaVGzJTG8>0+_;Ul7XDu!QyDox%hrAIp4j$PZ zUxdHVcZgg)ijImh2X$Wb;nPEU>Qg(dYhS;PvpGzmJN@PNHWGCv8_xOWB4Oy)gJx1- z>+%A8Jq6#G%gK_7ml&yWyU{W^e!FF`exU1oq63gX)b20qxmLLVuZNT6XwYidr?qvO zTzXiEm!HwAiDLh7x6anCO^{!M*HsnwU+!lXQ*$s@v*zQPmNqZ8IO3OlDem1#hr;(rNZjnmoh z7y^#}hwMf2y>_Vh`u z-(PP^g<0|sza_a3)w$VlL=B|4vdg3oFP!xYiyV#L$vrLgi1YvBvVF*X{rZqhsH$60 z@qn+LEBm|-vIb?r4_+=(DJikM()=N90wK)S3U?SYbK>f9nVYBxmw2Z|-S+=KA&48fh~*I5?a)_a1K?aPEFu^vpPS z6Z6^((GSz_X}E6)QF`NyebR@;h^M1wwGY$>UTrqF?FzUEH7O@?41Wd zCZMzTg0MMWp&i6Zy1u%i1oeECJ5Q*wW-MJlO zAMC6A<=PWwjkQ~OwT#`_eU>0?as_zr9g|Sxwydz<{{LeDlUARnN4JkYkq#S}g z39r@?5->aS{Pq`HRoT{jG}ca|<=!Dg*}RLf(%{3?CUrQxuDYFYj#7wv5 z!!$Mbq2j$e85v!LBfT00})NTBY1)q=g#`w zV3WSatMz;r4ILm_o!Hk#+O(87fskwL^;$!RqD*IerloTK2%b?BQhzDH9$$AtV%q@+ zm=nIns##wJn^n$;*-tCh7yl&iYRh-gNHlo=QV{#CAcil9Z(4~WCf(DTCHwiKL;oAE z8a+hGbl!WyDiNU|`S$JG-^a#IlgYUM`xI#*;dnavv?%CYzZ4P{);=l47XIYPlaH7I zcn+vDg;(p_Jj__-1OAre!DlNQBO1c!z-r;C_MLdcI65)0S@qf>nwzMtA0Y=+5*NSe z;q9G`^kF+#ba&@xW|(QYQMDB{RaI4GK79BPRhUYA^C?~w1VKO1+nUB;iU(*35$wd$ z@^UEti*koVqcf83Nx%l(z+omBKxRT8Lxt3heU7X99sEZN{&`v2&~4tljmi}~!9>y| zkSx_xK`?1QB}Mk@a-LOG&=H{Yo$nxh&3eIZG7}dUZ$b~S1UX9imGzrpRF|f&NzR

z{7+!1+-VF&nSE_39TW>ulF+;?l=ymxY z-FMi+-CaV)Yt%HFEPwD6i8zE4@Yr<0aN6pbP*segh=YeP5AbXnZM-HoH@BN`5xsk) z_(b^imq*`V!Zkl?Kz0S3hj%C!JusT62Orz-(yCuqm>Dk<3=+sAU^PQYC9-PRGJdJtD?YV)b@csxEg z_N(Ynz5yL#umU4qH`}?5F7&lqDkCSS=*(1~J$Ma|EOJeg z;cNPLr_8xGU{ef^jE(JyWR>KXL|TdAtNd2p3{v6&%~jNBlx+939#F`84cq7$Zlo6);%Hqov`TMjn9shP&P52Q~* zpDqkbXzc*p-4yPi?a-FrDTk9L84?<*!hg#Q*|ZEmh}TwGb@lZdP(S~ME9FIED{%%h zfHwzZ0d5{>Z*IOx9Sm!z68{DFj?bTusH5KALXeu6GMLj~|32R@&Y^grjay#6vk-G+ zVVH96#YpVjScjOVXd(clNk4;Q7UG+#5z0*@v~$m1Imwz$Hz3>eR}$)5{p+fsdvoE- z-?(w@%~8i#hG?aMmG$DDSSbYGK08lGPjApncv~s&5gP9Mc9j*rt1AJVL{qwAKC}83 z)D`)5u5wzjhWnDK?|z(u79kTA*o+(e!FSQq){ccuYXdg_sG-z)9c!Y0c-Tp}0u#*o zsj?Z}WXBze-uHywJ&-=MY4(ce2S!gY4ktenrt!O6)fz!q(g zo*r9W9i0Ixa#o|_{(ZrAk|hL@)ASM4FwA{yHF3)54<&8YT4HG4&D7>Ht3~7pQgV(T zKOXpS{60(FcD5VY*}Pg4yjl{j>`oRxrL~C$=vDARW| zKki{PD|@@GxoBeIA6~wO)3LnVphd0;CXc~06haJ&JY7J)OO|Z*sT@b&)Uh72F>+p* ziJxDd@B5&SMcV#GEASo!$y#Df~{Np~phAE6&@o{otm&M;(s!fqOe-5pn#TO;G z6%LTv%XR>O-hfA@n++&a>$;?F`7w+l2vKoAXS6Ap4_bLzDM}pNqxy!Y)M}Yar)7Qh zTL_w)px!@Lxiv|92OFE!_2C5zh}H^H&I(cH*z47`W8P2K_pRSKRW>$)w<9Fi|lIq zN{VYvBnfBXz>yQbasZrLD>3d{0a;*SEFi9kn9W2~RFtu~`F0E77hm0&w%}j4LjPvH zF}Nz4qXEY;U4Y`fo;w3 zf=rJcO{gE^gM)nZY*KGN)v^v172!&Z<9U;#GbD3SxqHLWJ10{#vYau4v+ihQR@Q*| z4UK7rU=u>0T9(r8vYOj{KY0guBAcIcu(NlrdBp3)OT2(y*IT!48zhH466bw=Wd~*H z<63SL3H7zLBDvi^ScjUgWj5<)7_uYVVy@=K+%2XtIE&kOj@s$Q8DC=L*&V{{U-FRd zV8!)!i&DY*$kuC2InZirfp)aSZXTXgsCRQ&;;bM0_Bwnd;er&xLm0W^F&4+s3(o+X z26x=n%p94fTkY-bo3Wc%*9&sPb#Sz?>QPnu*RTV|hhbadZ0|EUOI zY*s>ujx(FosiO)$lc~lJJHW=LCdgAs4Nk+ZERik;DYgtl!Rl!R@>g}^qLR0R{^j=@ zN0Z!NBalpAE3ujo@;L6u`P%^G%=Gi??(PYeW4jTS6PKPD$GPF>MO|H8R6UIfi-7Y^ z?}>jmm%u4W94Edf-T5xHM@gybHQVM8Mni3}jhBD=`TH+b#SS?n<-M~T2{g!W-;ZlM zFs`LZ;X}~<=2LUV+R;YZ4AipBOiZ^7zqm;lXu7&S(hyC2LEoRj5RD;8J)tzqj(k56 z^5_Tg@$$a0z%?!p??l~l8-2MbHBfjO-@kv4Ocb(gXPh24eFN)-|EOD70_i$0IfqNmT)hj@F|M z&oN=@2Ojh=j;>}-b@jn4ijtnC_eAF)An71saEx-u=!*&2^2_18VY!D-^1H+`c~!qH z$zj@DW6B-7>+PlD(}|GTwq5JGrP^ibz*@>S?t60FCqgu`XbHx3{k#+@VWg(Kgm;XS z_BM8Q8+-m+rMp9pTxd*P7sIX9a-PTr-oo2iA*wy%b?a^0JhJRx>h{lkBpG67emJ~f z9ddrFwK(tUJSkMxvJ{G1l%Bobi|UbWR`XLr_K=VwD3I-K`KzkFGB|ZrXnwW%=EiZhgxnLCi(FD2;Oow9 zo^P&a1A~OqFEi9Yy_Xh&v$`3@xS1T`MFR1p37(1ONKV>ttv^Rj>9p;~C5t?; zBq1aj15*##1G2IoN$yw`6pH2cuZjzQls?NJ7xjR-{Dc8fEosktvGSLMCM6Gi9SUn)*~xXsY?JjeK{P5xxt zkGNX_vsWXM%(gfEC`JpmJ^e@U_`pCGOdecl?@Dvui9qi2?%DgnJ2pY_hPh^b4-=2Q+e!Ier`fP1 z6Tjc!)TvVqo=Dz%VFVxF`5rTuUOcq#@LE zOo}bq_PK9!N`QDrE;P(&GX>Ibvk)dFM)o_@MVj8wa4->LTHh}_nTR$>kfG&7(CjaJuFH3=PL_U% zV^AGA?~t%ARq)qr&QAL2y?FfdHE!K>1?D@A>-?c?_DqP<<8b^qeS+GvE3#3y4+&M$ z9X*E%I9b5f$!y#9?c3|7{WlUKLKG1iZtu93Rw7f7B7h%gGV_kqt)-)-bw;JynR2?Z zWbNIoI8K3(L!+J*eJFz_@dq|d@7=g@<7wx~v#rUD-}HnO>H2r5Fw)o8*XNje`|BQC zSy}N$v!e%ca`>C78y|q;efaa|&#inSIp+DTRNWHEx@(&5U_Nq0V_8h+)qO#&SmSjn zfBvBC=u;k*S5sB(^kDC(O?~9P!LVk@*v8Y-^E1&H0xN0r=?0DXwEM^Yl{m~gSwAl0aDwz`9fNwSLlm7^UaxskvvPeeUC#KOT* z;QzCq>O5;~8@yyv(G@G^UWg9nb4kj%gnw)e4GH=4=0tHr2%}7ax%DWOf`WoSZ@t^9 zfR^$6Z{c*zj9Mi#?JuQjL~D4!Qs)s#*!>u``FXTo2b3?E_&@rBfZ{-+z z&9U%K)%_*Krl+Ruyg~Kw0RmYTrc)}BWg(kj#g~2oZ>*#p@m2-&;sR$<7*+q`=G&?o z2z{_qXU>dU?lga?z^Pwg?jtSWByX&}nFtKz-0Fzh=&W4S^}P}c7a)vq>$c2!M}@k- zg7u0v0%)0?Fk z;7!^ia)|Rr%pbI3J@=E>d&FPeTJa8(QwTkpG|s0*T~aiJZnTYf-UKVFP@*)EB}_=S z!2C&0T-7@_n+@8|ySTqwz>)0ouGH(%(Ju_Y{8DUab18j6>DUw;7T0owP`-WfeRtmY zpY*tL&2eqjYX>MLRNVLYe=`X~6e*CIi_6~SET!ds`{*In!)F_&hNvI?!q>s)J$9qO=cohs8Z6^Fv~eMh*04$Nfd^#~X;ldCa}M z`_1%{n!%1^bWCeiicqs9;YN8%G;R#xb9uJ|Fs6Z8a`dnx3vn$fs<5#6lqk%J=Np87 z=eN7$goTDq49ZSkwkk-+QK+m^IDRcWT#I_&r~3L~4>7Ta;UO{XC=(#a=3(FVDrTrc zI%v(GW(g4~DXHM$g?-rMM%vHhWo1L0rP$IIL><{FQn>JUa&QC`xMu}r$KdMOj&G`Z zm_hZhwYD>Ly<5Gy(UN&iYh@5ryvN_s#~w=(aGH$&b8F$WIr7}o7!eBtI|3pY_36g% z_At|Na32DBPa|1(PPK2}!9eOuGHf@XNB^1ih6cq(R0cz&*)*5;E%`loxSf+zDQ4iQ z0kMQBwaSfE8Z_lvLh`=Cq*Z_w^lcV#I(k>1Q6d$jE$opQ*CWgWh4n_ z)}E?>XL28=pTz|Eb6?)|24^n~7@!gJpu&gSCnS=!_;0Zex|c(qu3K5L_Jo9xF8{5T zQ&OEIjr1d?cC+xkcPaZxmLhidnR}-W3MnW&r>%F}LuFHiU2`AWAkQOQ5W;CdilREG zx-#2#R`sxzrNQ?TB$&bg3`;?x)G8pDvAFirx2j{ujw$-3_h|`DY)A0aQGY$dquBZE zdD64_4O>+qiU85VgTy5wRfs|hRKkXc0S{UX3HFjnOGWzNAw6H;r-|jg(P4}&ZA%`h z3vOSG5X>-0uPm(O5_T*v?{av`H=4`4WP*eQDXFNr(}(reZI#V-QuNy$k=b}kN_d@$ zAI&)gtAUp->uqMS=Zu3EFtym2U&zH`)0p*=<>%#^r2G0$)6#PVte+OD~USK`ht0OZwl@PZ8gEv;i(@9>>s8U`<< z53kdCm{FV%WVaRF14o|Vx0!&Bv8gANHUcRJ@bbJ`f59d3HsNJ5$?-F6M%WpT6 zx{+u^rY^I(T)o?6IjZ9mel^q2j~FSs;@rX4WGfdV@M@<`txu>lP zii$$@Zn3)jlAS3@LZg-jZ6Q~$`f!Il$U;XbJUn?x3an?Ta+o-zpE)H$B6|_ElF=ga z@)rQ+C+9Yw+J3sU^HzJK|3qzB&sCjt$d9)9X^ltuBzt_lF)#TX%IeZAzt1zqY z-WvXymYbcBC0tc$cC3BL8{!JDG)qr87u1bqfK_w=@NVSiACf6pPJL&M%R9Rfp5E^MP{xnkDbuv4LBar4XQ~Gz^r6{O z)HTsP;eDGkI!6{s*OzlKxZ;#6q zWPnBR2Fi?=nVF`=a5?}nd#YX6jsMCzjGrc++8D(xRlG^Oeft>)u`)8f&|b`gq$gr)zS10yJz<_+ zwl%=x)t>80^=0<(C+O}BP=)Dj7Tf}PXsBP;)vY+Q=M}zVy;@haj{Q&HUUut~TT)${ zYTvz&jNX0}tLJHp4Q}YA@L=yaO-M{uD3(8WOddu6QIIgml|SuN9ZZEbd~j<(WOner zI=kEcS94i&-p|p-z;RrwEDAj@QYGP;CdkL(cQc|7l?GAhYubsFg zYM_J$+3roCNQdntDC)a0z-lJLID_3Nw80MxW_^=n`-x(2wt zFlp-t7S6>R;9|^-dyxk>J{@Q< z54yj)9)xTd*>2g6*)~CFVLhDb@#&L8mGbaIigZ5I8Kes_IeroTY&+hLc>ad%UO$8a ztx}SbPEiw)>~a#)%F6bUbGPuIdoeOL5B#<6eDCuw>j(_ZwJ2!C5=1}gof0t9tl0|{ ztuLt0A~6E7_z)hF?nVkNuc7E=MS@+m@Dz#HX_dO#H8F&j;Z_SpLBrmp1SP;{y}}XS zbW*3?V5VeqC%{B|qh70mS>!yWulc>B}!8MofSC+i%ba6u7~i9rCYE<0?n z)HBw2&y6J|CE*xtmx1L{rG26t1Bd)QaP`kNHZ)v(4qQ0xkWp$rpCoLr^id&+&06IzXY4P#7!yMq0}~hIidg z^qRhez%oU~cQD?P7~szo`RgGYnSrUiq6eDMoEbp=Jn)7f^;`=t zk{zeuS$qP)b`n2yn6zmjqRwpRT0yhNhI}|kE+m4WaUceoevpLSdFf#A5~TQAN;~YV zMI(3)Z5+hfN}+M;4guOksHueR=DH9balBCDMFrI~2!Gqh1 z7t0)fMNf(uX>TZVX?GFfKJg+iF93|z_{@B^q3e|&y*GQ9k$F*p$!^cI0(hp z$15Mz82AQir{q~n3*04F>;g@}XThVCZ4FU7gn0*=dG9adg-8^Tt_*aDZ* z-o2K^ypEU;S4Uvntg)!Ctu29mB-i}IpyhntzgAGlcqP;0X=2$Pi6)aU_xGS%6e!+O zbP_M9WiZOJ(~~{ebC19Hs;XXQ6)8J7_~#2EXO@7WThlSnM2pb0qVGc-ttAK$d1(JG z2Nt7yIt6DH%dg(g;9j2{;Ep#)uQ|X*kWt&URVQrEz$=^vRbqVsI6& z`FefNynR0O{I66L`t+51IH?58-#{S#TBEu-4;B_@v*V(UMlNRxzGKaCf0!xQKvZi9 zEhibvA2DnH<&`)S1ox($M6Y9{Eq}x}R2+Ax`UA|&EZQ|!^}=B+Chuh*%S&HBZgDFm z#f2c^5)$Yze~!C=stY;U83gHh_O4GrGgsZ~xVYE;vuy$gR{@%cx4*lW>&g~8g%~zO zhcbeBX1GgZV?99#2?=HQoI}tWq-I}O{9ttCh#*NoEEo;Ad3bFea$#Ba=Pu$<<*faK zIq(VB?^7yQVS0%9_z+S=wlZvbx7rwfW9<(Keh75uD4zAS1zR`4cmMg`adiI}Avr3e z#}sk>dMFH(Oc$AR5u>g{N}lwOzHDZncv7z9Sk3x*@{Hq6eEOQ?hB%~xFo8$;eAMZT ziT0F0NaWYlXcIH90(I8FH3Vy--R=-Z`7L|f?po#Kxt!C`s9QDCZZI{|BZ3c0pSvyr zhwwKn&tUHo!RUS`Q?P4v-=q-^(bnNi*? zjiiGmq?sAYozuE-^<|U?Jp?pdzd_?*99iQYw6?APpfl}Rc%FP$(5-|w97@u7@^r1%YvA+KHhg-BqL_%VA=(RpKGi#^cKJsI~{O8tR`nKSeR({JUr%tL`yV_;yu-&@W9JA3w8* z1IiAUlXtA{g;)ZSadF z#=D~8b#tMr8R@Z1? z`30Y6!8>vuj)9)aDQNJ0#JxNYr|Y=9UJEb(1Ib8)7`fL14Vb+;c(>hoxBPnV(YBEv zy^_tUI_+-FUtNFxh^%A6{@f1=!Da*@Gy6XHR=2|N?s1d6j`FXb&5%SCg0l?K|LZWX zI~*?^3ih@bYOii2D$8(N^yQH@nrsZIMps5t^YU^di?~=FWMGE)-no1AsJZzkcNNAm zSZiptmiUbEkNh$#8f zLuFB6>_?+4LBIC^yvkB=4n97H>h~hzgz#FGetZfUBS>{h99Ax+=epR6q>L^G_3!@v z^T*c&I=Rj%`#{}rNgHj261+oy^`&i44EY7JhV(BCZ*PRRZ3M_pv-l6Cd96# zLm%;K{jA%0{xvA;jPS_O?bJfR0X zfBekxhEi;2F%(qmAOU(nKVi^NUq8wLJEsZpIHBC8aJ^zy7z!m~lZ@W9s9vj5i9CbD znTN+%0zlRdDw5ww`tnh2ga)u+L`$;x96b9vN6z#nrll=&5G64u7ijA~OT%rGv|$i$`fP_;avWf6+0k{Gd2*l`V^DsKMJZUPuUxWVDW`(VbGdeq(hx4r%jUHmTrgLIo2bOP%^B9Q$0 z;Biw6dV}e!ke{uECfmDKBoVuaYCV}AQ!8j@lic-Q@Ma`YzRviDreM>phZ?hG(eY3p zGUbDGqyzLIDA4(TFQjM?kQqlW5Yjl9s$7ugQqk_$fB2?jbm7>Jp`oFbj?um3S%^02 zw4u*P$&4%fn)<@>mZdmyM76alM~c-VDf+)vbe_#ln+W>^=D`DF@Q!}0_l*X78I*k zOUH36Wk?NlyTAgazj+F4kBSoG5O?Qzfs3T&?~nNtEDl_-6{0m~UnMgKH!^eZi7fIV z`gPQe?DAA3VNc9A>>o5(;h`g52QTa?{*aP_y`QD>Z)H9*yu#@64ix>4X=v^~CapIk z6p;+BfeSz+z&Q{2`%$TN}3ebUdz!nio21Q zXGceLFsc5PtfQBxJNH#?)Nw!sr$=%&WjJKN6)<9 z;xNwLv=Fqp=FcIcxM{W{Hyv*mj#)U6D8$E+SFF-+B=A~%!ZSY`8^t|yY0(FV#Hzxk zFj{VSkC|DT`9)d-d+3O7eAc71oygv*>KIn@H3zqddBJ<$ox2h3ur8T8cOEn~C$+Ul zvekVyY+A5UeWRdml>NzjQCeN-Jyr0Qyu2Z+v~{HBG-{xjlQ3nzBd#}1P-bRRN*;si zawQ^jpX#-@EGCkYlGYt|?^+R`=G5}?lrr=3Zg|+8deY@%qHTX5&EXyL`-~w`Ny*0t zC^H@e3tRZ1rgPWS+3J!UQ-*H*yPHf+WV6;=Mj6Y;GD_eZ2ep+HAo%Y!Vz!{J-lT4{ zVtau0^ahG2GxrHP!k?PBzD<3hWmLCxofGu2QzTN2JVsqkYixGV{g(QIhHxf+mgMEx zx9Pv?tYtAF`W@AcO!FV_3So3va@E*ej6lC}Q_YcsLOnLEW%yQ)x2Zcf$bGmQSM`$R z^uBu6`+0xEG6ik%M{4EnsG!A1DXFU^^M(6DnQ{6aqa&X!ub|qIgt_9>*B>pehxQgj4I-c|4KTX zNocp!4-=pBa&#Q(d(cFBz&pPL(4vwN=e;i2D=)7sw_rc3!*#h~6`)OR=C@1VBEJS- zp0jD;pz52Q#Lq}Hi1Iy1ncvxwahN-p#NInV%RQmn%AVugsgy6dyiI##d((n#O7?R7 zzs;Mn9hu^K&4fYo%e$7CY1VGV^udMipXqo;9udnMy_$}NxuhZ z%TV>xs~e?1kvH%%)Ru8;%Guw>5C|z_>A)lRRW`FZj0ZcXEKBQk8Cjn-))oNX9d<0T z@$K7%s$eVcW ztJ}{K_Uja2FaH1RkqU$jHif#+-ULgQ;5(Vpc1PIxRtQGyksF@M&6BUhFW94WCt5q! zQ;bNpDCf;H@*l;6)0hnc>^mGgLruJPYpT!9`+KE5Iq$mo+%RB_JywK;%@(m+*;|eq4=pV^%AkXU>r?$Hq zp~in9DST1g$g$nKE3#-x)DG1!Pld@IT7M>Mb0T6(t6eo|MtiG4cH%9YC>smly^6X*9T(2*A%2oB)C^Or& zN3MIdSM6=M!s?JJ{^>`AG(kWxLSK7iIkJel?z5_8f@DDxx#65Mm!$ zqU!gWOZ5+$jCbO-IZD=vKK{@9*_^19p&_HV$QsyqVCzHoVDYB>mX14G!JIYe0rgtn{gfbJL`sOMC{<44h*U)C< zUs^vG{8?jp3rv{#3JU+8g5%5q5s|Y(!%IMLdaW#$+csDxbeW|yOW*WZVIN+ygqe>e zxkU|YG=$)vC~?cfPM4~S8QT96E z(Rxqy+TueEwa@d1D7=(gg>k!mo0;-#sQCVy9!ji)I}Bx#Z{3=v{O2p|#qp@*)p{&9 zETY@`97=>2ueT?=Z2Xsv=S%5b{7)i7g7wlWJPr?bvD3GbZHt0@eJDSt|8SK}*s5BL z>$~Z7#g0vR;n#is%LU*GS>hnlO_&GG{%yJ9guM*~G~BIJchP__aCCAKdm?Xg zHn022B=YONe=Ek#9EvT+D_-Y4R-v?4M#iF`A@GD{`azoHS8^{@TT2SOzE}jj#hoA; z@ogd`W=rbyFfF$Sv-;b|@;k^4>oNDmN#OIu+(_#5%aE82%<7h>ys|s%Sgenx46WB| zWt+W3Mea{dL~VX_8P*g2_wq*bD#mkux7IzOI%s4zJ6SQFujDoUxp~XZod>oM{8ZJ8 zsndgiSIYkVhKgJ1>62hArmE1CH%qJX?s!kb^y#-}~vX_@fw z-^S$j(RrY>$XYL3}$aqpBAa_0-NMWy@8MvYY>0To%V49q^QI zuo1OT513)4zxoW~C564wGud%XYl2bXRT=jMkY}u&?+6ExQ2`57y=>TyV^vam zU4Td!TBWWFQIw6e@hnaOTh+zCc|X&EnixIZWyak5_pgcU*`w3u)pN{iKlw5Basq)b zl(X;Ozt6}0TKDi_*(3JqZ29|0TV&zw&g7Z$#Fg~^a<=n4lj=$Xi?yl2vZ6W-D-Cg8 zqK-2^T50Td>&VQ-%HHESt(Kgd*7tzYm)2sW-DHYfatD*56H`tS7fI*pbnNfX_;-XFw08kzfD(`BMWKs`Flje~rt>{T^mJiyiqF0P;k!mmM`* zOJ-jl{V87e`Q_hH4=6-l1m?SSZAP4U*;V6rqxt9e9FAp<2oD#+B%O$qUaPsOX#N(+;L!j>SE zZaJp&e@!@cd)fK`DCCE!%Ms{aMT5vU?kYITf7-loDB`D3VN|82eb#pNQrU7qrcrl_vLRzI=6I5EUs?vT;8DsKam;RI{k%<2%UP-*N_04Xj zzpqXF1|v^Zg>h59hb8)0LSe_oKfHaLeEL5-(31?L7b7xDjz{Je?^V9*+?n!s)S|WI z3p#*PT@!OTEDoXQls|Y_g?Xh33>3xBS6^BK_ZtU^y2~Wqr=zPY(;%!UnB!4#7#rNLnJOdl&Xee?G%Jow{iLZyfhCt z%o0%TlhGKn{ZflIoo6E{!97D_6&Shcm~@rZtvm7qkbnsI7K&Etz2r1;<$Uk;w;w+2 z=qhn$LY-!?EQSI0%Hy?k=Z_uB0F`;`2znFHNj^46e|6xgAG+p>ESoOC0qH$lPZ9lA z9^HE)@6ZW$w|$hWX=&m4*Es?7=sB~1=GZ6q#w2k*INyby#@nLDd&b&G zLPu#!AbJ4D={Xfnkw!X!U1y)fqdTb{-J9c_qS$#J?W<;>OFyAQsSUVpb?Ikmh_QSc zqj8}#+kfJy)zcZ;jNP?t!ZfdZwMXv%9hU*(Dgzt~WCZcL-zP06?K=XX%e!j`raTb_ zIy%=QIS!QhLMn6cDm_Q>mkDorc!)m!Q1=iXc3o(ht=`kR6@i>?w}IlUsVYj9W|P!F z(9svkK6Hx2No^=UGhGJ(Tc&Gwv_kBOCtV8;(Te6!cCU^=ztRswQHxhAnV~~+bfjM6e@kdz9Rg+2{=PPFuaN*wOf~s2hQ`L-^jl?drX`c?KUyMbMG!&);asOU?Md2;r zzsi17y_IFKh#0`2o){e8hEh(ybA(xu>wD>rI4T0{ghF0LMcap+tE3{Z7~nyFtnciB z$){OANVKjBEg^A8;ugwW&;xErDj~3t4~6|Yjec_^xa0zeieG)h4J998oJx$S9*9=9 z!}wy%Z{REPuuDnsoGc$+4};rZmuF@?12AUy3%`PiioksP>5D&KZn(vBlZI6}(7zdX zZUfpR*2DO8rmVAtFcp%98%_v-MGvR|QE>J%fL;tTG)V6dwfPY+R`BFF`m#&aXP@+# zldg)2Pw@y-5Z?F}{T@RuJ@E~PE9tM+aAB7E&mjTXv@^Qb5AmEpPrn83lS?w)u4>Kz z@fW&1KGaUZypv(U4xj$Vqe)Ic7G3-$P|rh@SN=GBfM?Y=4fN!}k#D6h3!}Mf zZIva)NoO-9pq=;LQS1MLc&RW%E;cM;-slc$T$ht0V?`{evx4o7;si!B1IX;Wsy~t~ zL|7=oos}{pU3aA6?+TxRS$jG9^Fj3^o>iJcm3C(B&0ftPa3s!I!`JKy0^B7blWb;9 z`xlsQ*h3H)4iE^$EK6a2VMU%l7S%6lvaDDiSvVOYTwPy24?`M@z z3(XV^!|Cn8U04Y=zQ&m|dxOYsBJaqXi(vqTH=L_l7;1y*=Tpo(T3q;K=`=zRSebAb zH@lX=Us_I}1bt(JeJtqnquth(bb`h5npBE zpsK3s2$UrTo|8?QChTTLqT0;D)@$TZ9|^XHW9A8xrGjW-*WO`o!EzQ#>B``hpd^0!KOY@kS~A0E2m^ETE1*L-)A6#@ zZ)`mk*I&D!8G=cpR2D9f8->(hLk^YTjoCXeY{VX8q)RWV^s)4`b>Lza; zk0uFUnudJ&O+M&E3gjc9$vBBFUkI8X;@O8WnEd^vWlqR*r@Ap{VrVSUg(lrhUlJ4N zH)J&IYOAN@wiJ(w3l3$r8qq3A&cpTF=Id}O6x9b>s#TvCz)q#$yW z;O6EQj_Nn>#au8hn5Qa#Q4uw=v5^GJ4UpyVr}T3P?NiTpwInEqA8&hOash4D4Lc7V zj1oh(E+jAiYki;2OfV)lV+wi4YT^(MyX3s4moL@Xh_4sd@`)V+RKQ$w9mlr}h;|sm z_aj=SSqup%FLA6*cIDB@#7wbY$W&zSBV)<{ONpIqbVKi@tNiKr1;J#`4c2CISO$-Q ztu8wiK&4Swd@aV_B8ZhZL!szDcp(4xRvpK!>cdcT`?`<&>ppfwX0}cI8FR)Q4PSa< z`2`n`j`$K<0HrxjZXcdn$JTq!G#!{R?xm3PE`yAcp`V%=MM4Z?^5BK6vYgB`N={Z5 z?_9i3QHRSN!1ZXGun}TwL(U;xMMBIJrapy2#j0c0`Rq^2+14+VqJvRKp^^5B-lIMg zCoCe8Sl`SS&9S}0rfBm{7Tnb+!u|6Jy{C2sqf{lNW8gKY-xOLi<`ZBz!(G*5EBa&s zp||obkZBFR^WSg0Jk6GybMnGd1qiH^f&H_Wlch{0g_X zW^6QIx3>`5knoyovGyfXl&R^s5dF(F(T|SU10>ZBcr3Bl`})nn;E&(;Q0GsgHh3Az z`gvwN=nvkpA8}U|k@xO7O^7Amn6{041wC_~OEbm*@4{Syz|raXX2)nvFKf5kNlA<4oA$!%LcH-@ zu=;Bso#BlXO7s1?v%&8Rt(BG#P-lYC_n5%xwjVGd++TkT=cZB9{Jveigz(3MM3E&B z35j%$llrk^$G(HzHiZ(Gk)*Wb4||gFyAUo9HGqHt=9Hp^eH@H2zx$2--weupUy|4y zID-So1OAW|gt2M+JT!Eo%-phhq&1s~Y5Va+5#-?y4zbrBg0Ar}2B@bXvlBtVYIy9} z$QA|n^xj$j%gNJfy4~bq-#E~m!lQ;1NWtRH0l2KhkSP_i&SM-+A*|}T-x6I4#k+t# zk79SzIS=7-;QV+#Lc5ZCP8$%qRmbI(1QuL|NQHeB69;EgFw>=zTd_-Z;?u z^evITCfiH?q%KWJ@b<{Z(%qA?i(Cx73c8uQlLcALepG~YB4}2*xjxrWe!c; zN8n2iQ!&dWOz+usD!Sb-)gQc;M;?7zzBV?gJH^G{koFbBa=1U9OFEi9kd?vM2L8}iZ9M?eXXEd6@P zhL+#W)`V3=_y3LMUB;Di%YPXZ* zWN*LRR;OPh?Tx`CLr*J;x1`(0@+!C$Dw}zhLwurM-jq(TcUc<%4jQ5ze%yUSo(|s;Vd@@dfZ^rn8kld$!kZfvjtpV!j`&cVk(-_D5qd!#G`x;kNy}Fl1t2ELhy$CMtSx z6{)uLh<>z0xL?i`<9X1~dYw&OH@?x7hS-$snK??`#>=9#6ch}V#eTFY(Bfd%oC+0v zjM=!}!_lfof9Z%NuD-&lo0^fQ}cmmg|z)Q5rfx}GATpp%h7%g|GFN8A6V zpyF%Yo3S_8@N8bGIO-qqW{`_}KNWgipKJ|PokGv}`Yc+R27pCBTe61kVb>hC!xvlJ z*(DVEA8kl2a*o*NF=2_Vj$Gjw60v`d;gS>=v2d=riZ;YLd7Amgq3@*lAmx3|U99(h zP#y40aLyvfVrkxcmBT?un=&Qt*VO%nK@MH2RYbX*UO-vUEfTc(0MdYZarHmowBA6h z%?Dv4qM&(WEmuIarz$T|mq($3b|Po&J#6gY;NY9ow-!1v*A?)&pH9kiDX8A=norYn{ER=b?|;R5G^gwMks!*dqF}GN)m1SCTqpA zCs$RYOXPSgrQba6JtvWVRA|HIl%%A5d{-HOE5#_*pT4+q_vS^~kTllEnCaRg6|INE zv&tfcEh7gP2ngDWfr$5L-nAxV7xA@kAOFLV2+Z_&GZIK2BJHCzSfRrA0=y4;a>*(l z5Wf(~x*=bzp{k(yoP^H(^+c*@=$ILMzZoG_(A@S}s!s84&Zh|0_pz)$gAeM{t39s2 z20Q(8fC-Bgx2a#Vz?o#bpH)&gl<9l2C~dWKtE?J_rblt67`1)=-HH?m8j4aV{;};A zf1@f{w6zQ93_l>;!zh$^^OSx+Ky*9lUn&!&)`oHM1riK&!Bdm31{I&rAMG%isF%CW3Wo5^>JTf! z{K4o#Cv+s^v+*1$40Rt(T4hrbbGqIBHemBIs2c4Pw*OQp%^G%zfvaVy{P?8|~iglEhW(0ziiGTQ4>x1Zj^C2@dzKkJ9D z1qtC8;ES*aaz|@H^CjFMIU#b5^TF-a2N=p8E~Xr^bCceFJ|_2PTju%X>B`XSk0LXF z0}EXi1^J^`EOKeP}l~vw4rE;Eg7{a0UrRZ3T?;5ADtqATz4jXrl#@!9M_Bm(NwL z$$#@$`7-OdZAA1@Q}6rj=ik{jzJ7gLK-)-7{gAEtp|pl3WAGjp=`Xz#^iOe0jG+0d zqSq>N0?l~3h#;2gg*l&bn^LF$(0l)L!kf=TCi;=)?I(ooUlyG}n*nx=0HahLhkJ9d zsp;(g{3m}l)tPe-21*JDy^w91O3WF5sJM~BXK}}P_5L9|Wy?$a3NiCok!N_j2ba3> zXqm0svyYfL?w7;9d+_ser0;`&kNwo8b{baa7$o=W2mpYKW{&eaA~V0=r}*DK&J_!O z#YKt6SWsb=9yEvP>$g6w0k*8Fe}hh8&?EdfQ|HuZXHtQ71go)#e=xH%6V99KVZc+*aMhjGaY}eQc(_>c?Hgd>cQVCrsNFI; zrXjw?-dn`$#AAt-c+Vh$c($q<_On`HHN9Ubl$?Bu|M@PVg{}EAf}x4>bsu+yUhn;j zs*-?NoB78AmJkC^kP}nJr{|Bs#94I5j!_DFI6KW86*r^ba3WfzW^bCFcqJn-VPHDD zG2b&o?Y->I?hz&g?5olP1Ia}l-b;T-0U1^wxuZnL42h0^&z=%>!#G@}s(9m1Z?nPs zpu*^Y#=VL-x(a&9VMknV_g~sVK`qy4_1={wIJ$8D)2JzWle-c|Y}uRw8o)%KDjxPG z$tVb5?#)|;LzQc|7R~SU>;B~oQG%l%r6x}oBj}+x_*$a4p8RE8WVEHzGHnl_7g)Rb zWIh7Uc3HheE7SHBppb?9^o(35qhxmPRy0o`G5@9O_UEMr1H&m4M~lDsp7Vd?Nxv#f ztU;ni$_C6Q*E0dyh=*E+?4M+t=Ei&$*Mh+(mTip%f@)&|f>43b8DrDpl7Abvjs+W5 z&Hyl1eogO>+GuIDlVfMFU$=vpWMq09f|zrq?`9qR+q}A{kUOl*X{QjvxZK3 zSx+XTMtg$rj}#z>40CdVfY7Zvybq%K)q(TS@%-tw&?bjgv=yWWt>{U>H_o1wO@j)P;u&bZd)7ux=VIKLPER} zyLQE{4^x-KMREKe@D)&gP4B}eAp5;QLmTPjF%q{m}i@ej0=JsC0 ztMFsg)&^ptt?FIRhUG>a2b;&=Jzui;>aMUnQ#@Jq_TqZAKt`Xj*B5{O+beSlu z_?c#K%>R4+owOAt3`AH>+RG0QZ;7?p$iriMPgQlvOZdOUS$cG09D}!46qrxZRQ&$d z%F4;PelV?v^Tf%%515dyOyIU`QqRr&gpVNN6Dq!UrCHC6V=O`*H;XOF3q$PRP{B$hxeghz<`%g}YgHbwEP$Z@?& zUm&IRhZ&l^Wki{DdK^gx!G`Erw!KWhu3w~VLt@Q`xWy7I=QF4GX zt`I$NY5ba@u9Bj^$UE6b?8CcUMB)D#Z<|U-koWRW>aANR(;yL6CaNKeuyO~@9-_sV zrBA@5g_QxkzK5_&BU3GDqe@kM-~HPw&&9HBII6?%EAz@n7H^QP8+`s1QE_W zx}G3>zsyTn1y1q%tt_qKr>FfKFgc^f4iKcXSZO-$9P;e*_O<|$RN=jO?Z&x3xaU( z_P&Q9u3g0T_yd*m1C0+|ySIbA6E4$ydi^%7&kW=yCx|-|=hw=+Ewhu+0v{Y@V19Q! zbSD?JZ}AmO?Urp*Fp>1`1X)c8%+2C|83b>y!%KQORucK1C@&(dX?IEP+_{Mlfg}q; z0kt!ki=+_18cnuk47c8dZJH;@#q^SdYo9_4Q-6hRn-1*CERC$J&X9D8U+AVD?71ij zN&_SD<9A;yCuTTj1(MPdyk9saV4@7Z_`wzYK+wsj{s#!*s}Tnhf~2nirsM<>k2^z&Oi5r{oSKYg|E9whA^Yq+T-!V+Wt})3XsFq_{x@fSfc?>S_=kUV<8> z_~|YGhO5!js`Y^j?$0ohU zkJc#aX$BYs|5+cfddsAvF?5NlGw%*)L#TK#SNl)n(CjR0S(Pb@3I^P5r}a<=yW7@tIzj_)5>0l!!g1{VS`2(*kb6tEV#T{U!RH zPucVIGFfO>i75%v3o7>Q=I6hna7kv{MBdQ)HHP~JqB0d2F3T*0T?$!a$j&+&)tR;P z3V#%%`M#k$l1_I5-cCZr|9IQs$O_xHH3tGbJtqf+lrOw49}H~%?vYnms1?h1!F{&f zda7ZEjG?vlwam<#yGnC=XC~EF2md~B)XLkG6H@ORskukgR2XF(pU31Ej?n70em!?q z%j|>c>GwiY1*3VhFBy%YRSw@QV{_nb>Z*sQTk=0voZkqot+#H%?sY43!90*uZ}V7L zishG+V{Xk+xg_WECBIzD-hK_n0|!eTe|g~U?*28W?5)G>q^@jHdj6vNVvUr;e;OKE zTD@h1d^Is!dWh7E$DH&N2bIghhdGW{hX~wNp5H8cHZe3ebwTOb z8IO(>=pM|V%B9~Zsv0hu>PLf4{;TNC#?1>V*>!JSOJa9(F|FJ-^07N~KtqFFG1>4y z+o8`8CZ+!P;l7UlIz+}?a0$N!{{YL$vcXg;fuVcLw}D~iGb&-FY_>5sKkYYiKQ}U2 zKX)quM0W^NBFd96E`6~s$(1V35u&T3!=Qgx>cuJo>idge7KPMfN`4H@+!HzRg z;9z6RzyY#-b~54a-8`^bAH8rP`C+L)D&K%5@C9xWn{v5V%Cf|C4inqp4&LlqQna4# z@~-63faJp~(`qs>wJ<=Oz3g3|+^i2x9@ja~s(ta29p9cW^1Zw(y{8Xhwtl_Zn7W3B zP+iOgf?(aaF)Ax-fC!nHVZo@~_mcoAx!T*moOEdZy%ZF8d&?W{O_~pqc6us$6Z?9e zU-lS%u#E;%;j@n%n$CS62)d~DCu4{F*#$1k5-Nr`>%6KJCWykyXIm@2Y?rWI-a0e7 zr?fEq>c*`H{1=KWjy-XB<2PQs^1za|y=2nv^SQ&j0+;9gv$BiTdKGBWJqP)i@92+r zFRoJk*h6fWrd@orEOQQToIe$YGajC2P|_8@VPsXKxT)FC{pN}#Gbi! zJ3ls;cq~s(guv~<30@S86Q7=}b$C*y-Z85H+f06bY3^(9?WEe|D$l;py2?WE#SVr( z8Phjuozl?MeACG~Wb*Rm%Tr71Z^00z6S$);L1r)`uxr*tHWGy(8ZBN9wo^<`be{rD z)h|S5o`#c7L?Sjq+?@ANHxS_-% zsGGQ)oo5b<>nYF(ys{@IWVx?(S9Mj{j9$seUA$={g{fTiN7OFFzI<7U-13uj>{dlD zR^nZjwxCu_1?TiYC824E;gzEg&GnyKRu*n`>JieY2_ESQvJ6?fm3M7hsPf9vB(z|w zi2nv`C-W)Wzx5@h=M5dI8peFiP&+B}688Ida-1F>J3Cu=+!ZENot2A9631q?V=B63 z_q2{qq`qQvpSYMi4Wa1!{Mp1^R$7*Wq6L=mtzSPc^f`4t-Av%)ST85`2t?FKtLx0) zjQgoo(p4$YHp`0PSeLmowNPdbqv~W<)jK9}hfayX)XZ!I=AP@()^>48E{D}B+xyd2F&D;7u^Jy>Z zBDI0m&h|H>vypw3d%&rMemMDuT!Gs1!v5=VMr^Yic4`{Nb=JGYM0HNb_$r#%v_*Xyb`>Y6DXh6-7UW?#=Z= zu>7iLS$<7#ubFPjLM*jzD+`Va=s!ox_J8 zfPJt(s@BMG%_yUVbdk9J>jP>as>RG8f*?fP+?UkJr|F%AcQmd7S$B*Ar zv(*g^TZY<(V}JkdOQxm|=#D6CPTIM_-d_0h`>apWU7=|W-)f8QSv(o=TNt+RUUC?D z>$A5-Ay!nfa2-gE;`x@B!V>x4;6o*%yZmgogN?LW;MEhOhF++$FB^2=nAKo@lptle z?YmF89+uqR(LPhn^li46`LAJah>^L^S%E<7@U8idFJ+DmOWEZ#ZTH_ z6H6HrQ=R+n`K&W~AaGzgbY#R?H*MMPa(K1ENTq7GqJFJw6?bCJ@pg(Zb4*9oz*Rrx z>_gg4mYnZ#qWM*tGCQ;-pH4n__{bz7uo_i_akq3;Pb|tpfo6W|*>7*9jSWJ(YVGsDKk=o5{Cn|cB#fZ-=RsA}N;b`oM@ z^a1b?S`TUqh@FdS^WSg%0PpOe{i##GVemGClZdxfX;8N(X*%yv$Lwut;aj}tqd(2$ zV$wPRDc8?EKW182s$X5*1_rUz>BMlKsnv|me#?%}$vhAtfpmaN)mE*v z+xA4~(}Pk0zbvJ=uf2XyP?0Ma|NGaSqIo#%<=>Lm11k0mhdEpGNviX&^Hm|Wy(_0{T-Utis4R@PFQ{S8e z!QjMr=WX*R11fkB0!jhW4UH;rFzNPg?9`WSz9%d!cX0B{sz=DB-^nr~2dD?_0a_B! zMsibYm9#i%+QEpG$gZ%W_waRfE!@$z21iKCRp~qdXCWkc=B4h(v)&GF1gU8m#FluA z2On+~+T&;4RrGdlf!)D2xQ9GAWZlsM_Rk73LuFA02`0JANZry2jsXZPXen2As z0QH=WUTZpKI~ri)-DRG+9;nO^O&UMrk|=#^*EXfLg{wdU{2SQp*b+5x6r4F^*;R)}Yc2Mmn-?Up53Uv|4k?4&7-j?DN6| zS>2>xM{TQ zm&M~(+?2#2dhz~NWL=Kk_XbWa&sP_2-cnw_ta6WW@rqHO_m6;G^7fM}{$o$sbF>|+ zhrO0JSE~^PjS$FsRpucl7<#2}FMMc&!z1(USXlQ_h&Y`(_5HY}Vd(=#wieC*dfh(@ zhWZ`TnZKRmvHEv#W&22=%!xy|nC}J-z{Xqqt9*PioS`uZ+O!F@W~sK6Cu~6SyWry}sju zg3cPr`h(Yu8d72F>usI3iHXj*1PAzr5i0p>Kie4`&|t>nwT0g$4Nf0NBuS!OnT;v~CYNl3dfvB^dhA`^VQ8*64Cghf{MHd;NN&n;dp9 zds~zDF$jB_Ap?z#A{wDEYRs)S4GkkOXgW-!dX`xUEM42pW}TSTW=fPC7B_}KJP{kJ zb+>rGB;g$#%32QD4pckEjT=Y&gBiuMH_)%vMJpR;q@Ftcx_!Ho1q21DGq~lMq>tPp zQ*}E_#7S5=mA3Ss>)t$)cxCAQiFHxSwfHZS#S0L{WY(V0zHO94duWZ6iB1CdrcJj; zycAav&ve9t3%7nZIeK&jO>+6#dv7r_0DbKSWSP1gWy^t0mVGXA~D6&CJXwJo)tbm}$E_VU!(r zfru_Kmx#xSpF{j&5cs7nKBp~S1&>r+9gEfJI0FQ&JEGwFZRqMc(JS-du#PxEPrQ@W zYxlM$|B-Dnm@e6#Z4$<6WZE7=q0l_}WMwbKMGLz#t4dY!ZhM?0udAv5+>+9^Z70iW zPgoerlslZra}gAzxZwe55|KopcXC8n>+qQjr(<9-C$2gaXY`DC$F~nY1|Aejowj%% z5}*?GD=t}a%b@wWz^kOxRA(Jw1gcNi?#uxfdVwrf_3e=x;*1yo)@T|&O-$H*Y(f!}zN&TPs>$6?8i=g^n%k<}^VLgH55aSkck||Xw?B$5bb0F>oG&L$n@#PNhtJw- zn0Y`Rbk<_255=EZzA2!`mgq3-ggKpc-ZZ!)4NXlc+d_wOC?il0PKS}{oEP6}q`LX! zuqsFz!&v>t3%KwzKpwTgD4r2+Lz&jWr{K7HRP=#3`U?`Fuki@$k}t3|8Kj%B+`}cAVFzA3 zjC-DQm%;l_>RyW&>Frk97o*gG0T!G@h-g~iZV_ZFd6eyFoN)5duZHVc-gC%w8qUaD zT6g{g{Wz=$d_yxUs{)vvu3q_FujUUegAE%8M>z7!e$rUbzkZ7^sW3bF2jBG{AVY@# zlJ`D24QA(^_;5aS0h&&0s7GJZ_@65Q33n^rva%>v+sk)nxFzQOHu!WiMu%cqF zUTuN=VvvTMKGgUO{)m=%WIIU1ov-KtGfh^`r9OE0@EvK%L_JF-bJG@>4mOaMTZar| zbgzGb9Vs55ILT>uL+J%;tCLU>8|CPy9AhFdib3)`>}vV|xdf7s3%URs2s&nN6L_hD zUTA!Hd=}1qClkFd4kULqp(?0oJS#YbD=>EnGaFPtzvY&d8E2 zJT{nXT+~ldw0>O@feqjrwx5|TW#gJPYpRH$vn5@VUWzIE^;%sKDFb$)JZ=C2hGi>B zBfC-kV|H8#`X++dO>F98%fk;FKd@D*TZ^=43c#jJ85}DGr}tUAiPKQ5kepM!J#Jsl zU|9AAyQy!rIIh_i5Vj+o9kXVT=qUjW5kdc(r3#Nmg}DCz+3|z6;wI+KN#mn$Lib|Q z@kL2eQa?#8IH-*TwmOdHAdZj;;2v{dIgf9zb_)bqf6Ah4$5L>Zo)4sFWITYPix$R} zb+29xK|{9OISo9TcIUCDc`lL?*{{Ht-bU7HBn?-bz`Wo}Dutp$PU4k^R-u7xm=dHa zvYnKf(GcYQL$}kyTJk6tVw7NJPYihrja^Fu1VVzb+lO(y++;mrh4B2#paVRJB|W7W zSyfX)h&c4lyVCUh?5+&A!u?)1#I|oYN&-N~=>VL12;C{Djm1W!vZG*Kc2HYmzlp-n zC%XztOU(h3Bqg;6+emS}!t-qJoU9>xUsA%D#0In6h;;|=+zNL}4976LTFi#dpGfZH zlSMz+$R%DH7OM*}h0?6_{3uAMNI8CFW(u~S+O2Kf0m?aroapp4;O$s<`B8A84e0<% zSg(|#JiLI}SG^!z%4KC|uW_z7UMziKx9`lE0y3oVO;X8Y#2?8|%Y1ry_e2$bwJY&> z3roFXe51E*UKBM>A*I=YS~v%mY_6D&`BHsW+-*YDxu>E>p9o%WeOrSw7CyMnpteiT zF9qGz6&d?d*@Xf`6KKaO!obWdM-c@t+cquUjUgzKH--z*U8bQHG7@>r3Vd*!&Fqt# zg(ok|2f71j8tz|vTwFLuH(^6LhFP@&wP_^lIc)9Sf1_Z)Qmbh|EVT6Ke`sh7^#XZRGY<-4wtes{D(&cXKTXuZIjlq7x?{3s(^0D%1(cCu^(%Zv z4HHHU?wW=lMyWOo=}zP4jT#QX#4sr>O^v4Ggx2YN5vm_qfI=CUdL;>NoPi)jyWwQI zgFwA@;n%ojSfUinXv9br`H!}vO3yp_R};-Mil``a&?gV=h6!6Xd7RKzBap-_u2dM0 zZ)nfb-pEWS^BuX|y&wItwnW9Uusz5P&%YWkyE;^X6luk)%!-aU+ByEK7HYlVJQwOm zT(%tW?EyvPIFUJCE}{+hh-m2o%vNEPUzHcRJT@Z zsm(!Mu}o^sbi_Tta+n3KU4Y`LuD3Ht7Q@hfAP@J=L9>NtRPF8PZ0fHwcR-^^W2FUf z`#5!24#^mM+f3no6*HcvZE3w5I5B zR-k1zf(bf*}dKfj>W3fY#s|j-n?t)j-v*!E=q905$FZ z+Ht72njdW8TWBx^geq{n}jcUn8NwguXOh7+R})%doP_Nj8Q|z<_7E(@1NMm@7{=| z$QTneR=wWzop;bcW?g<}sfX^D)A%uV7$fnFY(8|TeL-lZT-_*o2lED9NRe&t8b%Hc z=rOuNS;?NpSGdI$Dc@R*-m3QH*7fV@{olWvBQ?7RYrdzXE>%%gbvf&rEqbvt+P6e* zqeSJoA>;7h6#%DtUq3mfsZ?MV`)lZ(Ey}$|&6tV*TNREH7=YCDLZIp+D9XDa1Aj?= zJbE_Wo=`_5xA>98%zr6{o_hr|J)2xh=8m%4-;8^oKe{kYu9ygFjsYM{=X(@qUvc~O z#CRyHA@Sj`xLS+s*$Y8I%Wnr4Qawe+Cxo-U%v#yT4_VzZ8k-*-YYWSuu3!Nw-lrsP zVWK-O#XG+xzGt%DrKpl)9!eW?9J$mgpLOh8GDa8r@N^l~t=D4x)?UFmAjbfqJDtF* zQ@jZ@Ka(5#k8K)BY|e!RknH6b@3vGUh)5C@MhzJ3I%`uj*N3BP3%&O>4X;<^7m`-m z`J`tvFk(kHq)ZP%r&t14;pGFfd{vllAIKEb=Yy{==-?qM7M4Y@9b*dKWHexJhpH zh_&1*00(HRF@lEn;|3L)0&wa5sX5&=CfKtT(49@<4hh6#0*jG<1fw zLG)2eD<+$yPP-Ed0gG~|&#Zo!n^XUvdR32_Gxln3@s?d=io#}ifyw#GP-cc$B-sr> zr#dXhiLR>0t+1dV`n`8|I)&16T6O`06CmpL^ta7zJ-?ZVhs$J|R;qUZ#0$E&inSd<&cIO}WZh@;0K&PpGOni0eYW@Kic<%u=wS5k}` zDzTZz<`&^W-0reFp&~KoJQOGfhU^%PB=1^13LayA&k|E z;{9egy@u!BeZBkl`?$B_sPp=uzNL*c7tfG4KQ@R(7XvRzW~&7ajjF@wbXyWV;bhH^ zSEh`IE6-k5wv@F99Wo1n&kf@D<9tP^`a*^(t+Qz9-C4UeQ0h5_5~)`7_mhTvW48ug z$umS^HkPgV_ix8D1ra!dp8$l^)eZpa;n z7UmK`uyd*kTB^V(59wu?NneEvZ#haJuUlfw5Up651ywm zF>p0S(is$6e~$#u&AQIZ@2lVF&$vDq5dsuU=_RAd_P9gkvBOF_giml3$M$qyiI+RSD9y zWIO3cPN1A6y)&=^mF467;0G*hS>z zmA@(=;0Qor*7smZb?45VBoC&Z#5@b}21Zq%e@nshB6*)=&?9>M%LNrHO|{+c*?L_l zCuIrfz#EWHz)SkHg5Qty<8X%maIeT44l1*!<~FF?Aw3!(c?iMF^N#mwPqfgaKYbck zS@LhaWY*%>N-BtFgX3gKd?imrSO>ezwhX?iuXhnGuftk61I?no23diTL@yf?lPpxS zGFm$4ALstv(rj|h*wbP{;JU;lTBk?? zlk*|P9N8-oQaH;0_tsF&JrMZ9LRo2jyS?Kbfdok7u0&3fP(qH=2^?^DkR${|F!PwL zBgp(JpArdwfrGQKzHOMCi^7;x)8f-Df~8m}E>L~jSXhXYd<1$;!yjNS@f=W*|Mv)b zB4zL+PXSmkTY9}MmYwIC);#RlcQ}(JIC8I@GBT}41N70>2QjepJ>n)0$si;bG$TQB z;l!kqn$CgxCfyjns{~}e_cADdA|V}(=t#SPh>Mq(G+V9*3tNl!I4o!Y&bNtX&j<+% zXGi?MA)$6NnPdNbU0Y3-NB=HP>k`(-zn9@QGTr|7%DX`p`2SvQhtQ(?-{1fL=U>!A bOTjBouGAh}n*%95L1=60YZR*6UHX3jiTCeA literal 0 HcmV?d00001 diff --git a/docs/sections/how_to_guides/basic/pipeline/index.md b/docs/sections/how_to_guides/basic/pipeline/index.md index 2d03f9ea87..f592082191 100644 --- a/docs/sections/how_to_guides/basic/pipeline/index.md +++ b/docs/sections/how_to_guides/basic/pipeline/index.md @@ -421,7 +421,7 @@ with Pipeline("pipe-name", description="My first pipe") as pipeline: VertexAILLM(model="gemini-1.5-pro"), ): task = TextGeneration( - name=f"text_generation_with_{llm.model_name}", + name=f"text_generation_with_{llm.model_name.replace('.', '-')}", llm=llm, input_batch_size=5, ) @@ -459,6 +459,30 @@ To load the pipeline, we can use the `from_yaml` or `from_json` methods: Serializing the pipeline is very useful when we want to share the pipeline with others, or when we want to store the pipeline for future use. It can even be hosted online, so the pipeline can be executed directly using the [CLI](../../advanced/cli/index.md). +## Visualizing the pipeline + +We can visualize the pipeline using the `Pipeline.draw()` method. This will create a `mermaid` graph, and return the path to the image. + +```python +path_to_image = pipeline.draw( + top_to_bottom=True, + show_edge_labels=True, +) +``` + +Within notebooks, we can simply call `pipeline` and the graph will be displayed. Alternatively, we can use the `Pipeline.draw()` method to have more control over the graph visualization and use `IPython` to display it. + +```python +from IPython.display import Image, display + +display(Image(path_to_image)) +``` + +Let's now see how the pipeline of the [fully working example](#fully-working-example) looks like. + +![Pipeline](../../../../assets/images/sections/how_to_guides/basic/pipeline.png) + + ## Fully working example To sum up, here is the full code of the pipeline we have created in this section. Note that you will need to change the name of the Hugging Face repository where the resulting will be pushed, set `OPENAI_API_KEY` environment variable, set `MISTRAL_API_KEY` and have `gcloud` installed and configured: @@ -487,7 +511,9 @@ To sum up, here is the full code of the pipeline we have created in this section MistralLLM(model="mistral-large-2402"), VertexAILLM(model="gemini-1.0-pro"), ): - task = TextGeneration(name=f"text_generation_with_{llm.model_name}", llm=llm) + task = TextGeneration( + name=f"text_generation_with_{llm.model_name.replace('.', '-')}", llm=llm + ) load_dataset.connect(task) task.connect(combine_generations) diff --git a/src/distilabel/pipeline/_dag.py b/src/distilabel/pipeline/_dag.py index 3ca2b569df..3cc22a5348 100644 --- a/src/distilabel/pipeline/_dag.py +++ b/src/distilabel/pipeline/_dag.py @@ -11,7 +11,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. - +import base64 import inspect from collections import defaultdict from functools import cached_property @@ -29,6 +29,7 @@ ) import networkx as nx +import requests from distilabel.constants import ( CONVERGENCE_STEP_ATTR_NAME, @@ -48,6 +49,8 @@ from distilabel.mixins.runtime_parameters import RuntimeParametersNames from distilabel.steps.base import GeneratorStep, Step, _Step +_MERMAID_URL = "https://mermaid.ink/img/" + class DAG(_Serializable): """A Directed Acyclic Graph (DAG) to represent the pipeline. @@ -452,7 +455,7 @@ def _validate_convergence_step( # Check if the `input_batch_size` of the step is equal or lower than the for predecessor in predecessors: - prev_step: "Step" = self.get_step(predecessor)[STEP_ATTR_NAME] + prev_step: "Step" = self.get_step(predecessor)[STEP_ATTR_NAME] # type: ignore if step.input_batch_size > prev_step.input_batch_size: # type: ignore raise ValueError( "A convergence step should have an `input_batch_size` equal or lower" @@ -749,3 +752,186 @@ def from_dict(cls, data: Dict[str, Any]) -> "DAG": ) return dag + + def _get_graph_info_for_draw( + self, + ) -> Tuple[ + Set[str], + Dict[str, str], + List[Dict[str, Any]], + Dict[str, Dict[str, Any]], + Dict[str, Dict[str, Any]], + Dict[str, Dict[str, Any]], + ]: + """Returns the graph info. + + Returns: + all_steps: The set of all steps in the graph. + step_name_to_class: The mapping of step names to their classes. + connections: The list of connections in the graph. + step_outputs: The mapping of step names to their outputs. + step_output_mappings: The mapping of step names to their output mappings. + step_input_mappings: The mapping of step names to their input mappings. + """ + dump = self.dump() + step_name_to_class = { + step["step"].get("name"): step["step"].get("type_info", {}).get("name") + for step in dump["steps"] + } + connections = dump["connections"] + + step_outputs = {} + for step in dump["steps"]: + try: + step_outputs[step["name"]] = self.get_step(step["name"])[ + STEP_ATTR_NAME + ].get_outputs() + except AttributeError: + step_outputs[step["name"]] = {"dynamic": True} + step_inputs = {} + for step in dump["steps"]: + try: + step_inputs[step["name"]] = self.get_step(step["name"])[ + STEP_ATTR_NAME + ].get_inputs() + except AttributeError: + step_inputs[step["name"]] = {"dynamic": True} + + # Add Argilla and Distiset steps to the graph + leaf_steps = self.leaf_steps + for idx, leaf_step in enumerate(leaf_steps): + if "to_argilla" in leaf_step: + connections.append({"from": leaf_step, "to": [f"to_argilla_{idx}"]}) + step_name_to_class[f"to_argilla_{idx}"] = "Argilla" + step_outputs[leaf_step] = {"records": True} + else: + connections.append({"from": leaf_step, "to": [f"distiset_{idx}"]}) + step_name_to_class[f"distiset_{idx}"] = "Distiset" + + # Create a set of all steps in the graph + all_steps = {con["from"] for con in connections} | { + to_step for con in connections for to_step in con["to"] + } + + # Create a mapping of step outputs + step_output_mappings = { + step["name"]: { + k: v + for k, v in { + **{output: output for output in step_outputs[step["name"]]}, + **step["step"]["output_mappings"], + }.items() + if list( + dict( + { + **{output: output for output in step_outputs[step["name"]]}, + **step["step"]["output_mappings"], + }.items() + ).values() + ).count(v) + == 1 + or k != v + } + for step in dump["steps"] + } + step_input_mappings = { + step["name"]: dict( + { + **{input: input for input in step_inputs[step["name"]]}, + **step["step"]["input_mappings"], + }.items() + ) + for step in dump["steps"] + } + + return ( + all_steps, + step_name_to_class, + connections, + step_outputs, + step_output_mappings, + step_input_mappings, + ) + + def draw(self, top_to_bottom: bool = False, show_edge_labels: bool = True) -> str: # noqa: C901 + """Draws the DAG and returns the image content. + + Parameters: + top_to_bottom: Whether to draw the DAG top to bottom. Defaults to `False`. + show_edge_labels: Whether to show the edge labels. Defaults to `True`. + + Returns: + The image content. + """ + ( + all_steps, + step_name_to_class, + connections, + step_outputs, + step_output_mappings, + step_input_mappings, + ) = self._get_graph_info_for_draw() + graph = [f"flowchart {'TD' if top_to_bottom else 'LR'}"] + for step in all_steps: + graph.append(f' {step}["{step_name_to_class[step]}"]') + + if show_edge_labels: + for connection in connections: + from_step = connection["from"] + from_mapping = step_output_mappings[from_step] + for to_step in connection["to"]: + for from_column in set( + list(step_outputs[from_step].keys()) + + list(step_output_mappings[from_step].keys()) + ): + if from_column not in from_mapping: + continue + to_column = from_mapping.get(from_column) + + # walk through mappings + to_mapping = step_input_mappings.get(to_step, {}) + edge_label = [from_column] + if from_column != to_column: + edge_label.append(to_column) + if edge_label[-1] in to_mapping: + edge_label.append(to_mapping[edge_label[-1]]) + + if ( + edge_label[-1] not in to_mapping + and from_step not in self.leaf_steps + ): + edge_label.append("**_pass_**") + edge_label = ":".join(list(dict.fromkeys(edge_label))) + graph.append(f" {from_step} --> |{edge_label}| {to_step}") + + else: + for connection in connections: + from_step = connection["from"] + for to_step in connection["to"]: + graph.append(f" {from_step} --> {to_step}") + + graph.append("classDef component text-align:center;") + graph_styled = "\n".join(graph) + return _to_mermaid_image(graph_styled) + + +def _to_mermaid_image(graph_styled: str) -> str: + """Converts a Mermaid graph to an image using the Mermaid Ink service. + + Parameters: + graph_styled: The Mermaid graph to convert to an image. + + Returns: + The image content. + """ + base64_string = base64.b64encode(graph_styled.encode("ascii")).decode("ascii") + url = f"{_MERMAID_URL}{base64_string}?type=png" + + try: + response = requests.get(url, timeout=10) + response.raise_for_status() + return response.content + except requests.RequestException as e: + raise ValueError( + "Error accessing https://mermaid.ink/. See stacktrace for details." + ) from e diff --git a/src/distilabel/pipeline/base.py b/src/distilabel/pipeline/base.py index a854806651..1b4a2d8271 100644 --- a/src/distilabel/pipeline/base.py +++ b/src/distilabel/pipeline/base.py @@ -11,7 +11,6 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. - import hashlib import logging import os @@ -50,6 +49,7 @@ from distilabel.steps.base import GeneratorStep from distilabel.steps.generators.utils import make_generator_step from distilabel.utils.logging import setup_logging, stop_logging +from distilabel.utils.notebook import in_notebook from distilabel.utils.serialization import ( TYPE_INFO_KEY, _Serializable, @@ -638,6 +638,45 @@ def _model_dump(self, obj: Any, **kwargs: Any) -> Dict[str, Any]: """ return self.dag.dump() + def draw( + self, + path: Optional[Union[str, Path]] = "pipeline.png", + top_to_bottom: bool = False, + show_edge_labels: bool = True, + ) -> str: + """ + Draws the pipeline. + + Parameters: + path: The path to save the image to. + top_to_bottom: Whether to draw the DAG top to bottom. Defaults to `False`. + show_edge_labels: Whether to show the edge labels. Defaults to `True`. + + Returns: + The path to the saved image. + """ + png = self.dag.draw( + top_to_bottom=top_to_bottom, show_edge_labels=show_edge_labels + ) + with open(path, "wb") as f: + f.write(png) + return path + + def __repr__(self) -> str: + """ + If running in a Jupyter notebook, display an image representing this `Pipeline`. + """ + if in_notebook(): + try: + from IPython.display import Image, display + + image_data = self.dag.draw() + + display(Image(image_data)) + except Exception: + pass + return super().__repr__() + def dump(self, **kwargs: Any) -> Dict[str, Any]: return { "distilabel": {"version": __version__}, diff --git a/src/distilabel/steps/generators/huggingface.py b/src/distilabel/steps/generators/huggingface.py index ea72c9a0c6..f6e782a751 100644 --- a/src/distilabel/steps/generators/huggingface.py +++ b/src/distilabel/steps/generators/huggingface.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import warnings from collections import defaultdict from functools import cached_property from pathlib import Path @@ -247,10 +248,10 @@ def _dataset_info(self) -> Dict[str, DatasetInfo]: try: return get_dataset_infos(self.repo_id) except Exception as e: - # The previous could fail in case of a internet connection issues. - # Assuming the dataset is already loaded and we can get the info from the loaded dataset, otherwise it will fail anyway. - self._logger.warning( - f"Failed to get dataset info from Hugging Face Hub, trying to get it loading the dataset. Error: {e}" + warnings.warn( + f"Failed to get dataset info from Hugging Face Hub, trying to get it loading the dataset. Error: {e}", + UserWarning, + stacklevel=2, ) ds = load_dataset(self.repo_id, config=self.config, split=self.split) if self.config: diff --git a/tests/unit/pipeline/test_dag.py b/tests/unit/pipeline/test_dag.py index b1e14cf836..a5b55520f4 100644 --- a/tests/unit/pipeline/test_dag.py +++ b/tests/unit/pipeline/test_dag.py @@ -33,6 +33,11 @@ StepOutput, ) +import base64 +from unittest.mock import MagicMock, patch + +import requests + class TestDAG: def test_add_step(self, dummy_step_1: "Step") -> None: @@ -807,3 +812,126 @@ def test_dag_to_from_file_format( with Pipeline(name="unit-test-pipeline"): dag_from_file = loader(filename) assert isinstance(dag_from_file, DAG) + + +class TestDAGDraw: + @patch("distilabel.pipeline._dag.requests.get") + def test_draw_basic(self, mock_get): + # Mock the response from mermaid.ink + mock_response = MagicMock() + mock_response.content = b"mocked_image_content" + mock_get.return_value = mock_response + + dag = DAG() + generator_step = DummyGeneratorStep(name="generator") + step1 = DummyStep1(name="step1") + step2 = DummyStep2(name="step2") + + dag.add_step(generator_step) + dag.add_step(step1) + dag.add_step(step2) + dag.add_edge("generator", "step1") + dag.add_edge("step1", "step2") + + image_content = dag.draw() + + assert image_content == b"mocked_image_content" + mock_get.assert_called_once() + called_url = mock_get.call_args[0][0] + assert "https://mermaid.ink/img/" in called_url + + @patch("distilabel.pipeline._dag.requests.get") + def test_draw_top_to_bottom(self, mock_get): + mock_response = MagicMock() + mock_response.content = b"mocked_image_content" + mock_get.return_value = mock_response + + dag = DAG() + generator_step = DummyGeneratorStep(name="generator") + step1 = DummyStep1(name="step1") + dag.add_step(generator_step) + dag.add_step(step1) + dag.add_edge("generator", "step1") + + dag.draw(top_to_bottom=True) + + called_url = mock_get.call_args[0][0] + decoded_graph = base64.b64decode( + called_url.split("/")[-1].split("?")[0] + ).decode("ascii") + assert "flowchart TD" in decoded_graph + + @patch("distilabel.pipeline._dag.requests.get") + def test_draw_without_edge_labels(self, mock_get): + mock_response = MagicMock() + mock_response.content = b"mocked_image_content" + mock_get.return_value = mock_response + + dag = DAG() + generator_step = DummyGeneratorStep(name="generator") + step1 = DummyStep1(name="step1") + dag.add_step(generator_step) + dag.add_step(step1) + dag.add_edge("generator", "step1") + + dag.draw(show_edge_labels=False) + + called_url = mock_get.call_args[0][0] + decoded_graph = base64.b64decode( + called_url.split("/")[-1].split("?")[0] + ).decode("ascii") + assert "generator --> step1" in decoded_graph + assert "|" not in decoded_graph # No edge labels + + @patch("distilabel.pipeline._dag.requests.get") + def test_draw_with_argilla_step(self, mock_get): + mock_response = MagicMock() + mock_response.content = b"mocked_image_content" + mock_get.return_value = mock_response + + dag = DAG() + generator_step = DummyGeneratorStep(name="generator") + step1 = DummyStep1(name="to_argilla") + dag.add_step(generator_step) + dag.add_step(step1) + dag.add_edge("generator", "to_argilla") + + dag.draw() + + called_url = mock_get.call_args[0][0] + decoded_graph = base64.b64decode( + called_url.split("/")[-1].split("?")[0] + ).decode("ascii") + assert 'to_argilla_0["Argilla"]' in decoded_graph + + @patch("distilabel.pipeline._dag.requests.get") + def test_draw_with_distiset_step(self, mock_get): + mock_response = MagicMock() + mock_response.content = b"mocked_image_content" + mock_get.return_value = mock_response + + dag = DAG() + generator_step = DummyGeneratorStep(name="generator") + step1 = DummyStep1(name="step1") + dag.add_step(generator_step) + dag.add_step(step1) + dag.add_edge("generator", "step1") + + dag.draw() + + called_url = mock_get.call_args[0][0] + decoded_graph = base64.b64decode( + called_url.split("/")[-1].split("?")[0] + ).decode("ascii") + assert 'distiset_0["Distiset"]' in decoded_graph + + @patch("distilabel.pipeline._dag.requests.get") + def test_draw_error_handling(self, mock_get): + mock_get.side_effect = requests.RequestException("Mocked error") + + dag = DAG() + generator_step = DummyGeneratorStep(name="generator") + dag.add_step(generator_step) + + with pytest.raises(ValueError, match="Error accessing https://mermaid.ink/"): + dag.draw() From d7e61b5219c30a0bd287d46cd85a90ca937f4a45 Mon Sep 17 00:00:00 2001 From: David Berenstein Date: Mon, 23 Sep 2024 12:54:10 +0200 Subject: [PATCH 64/82] Fix schema inference structured generation (#994) * fix: converting ModelMetaClass to model_json_schema * fix: allow for adding optional literal format json to instructor to make methods more inter-changable * docs: emphasize usability with any framework * fix: first check if structured_output has been defined * Update docs/sections/how_to_guides/advanced/structured_generation.md Co-authored-by: Agus --------- Co-authored-by: Agus --- .../how_to_guides/advanced/structured_generation.md | 11 ++++++----- .../llms/huggingface/inference_endpoints.py | 7 +++++++ src/distilabel/steps/tasks/typing.py | 2 ++ 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/docs/sections/how_to_guides/advanced/structured_generation.md b/docs/sections/how_to_guides/advanced/structured_generation.md index 02b8cc8e4a..6f907951c1 100644 --- a/docs/sections/how_to_guides/advanced/structured_generation.md +++ b/docs/sections/how_to_guides/advanced/structured_generation.md @@ -129,7 +129,7 @@ These were some simple examples, but one can see the options this opens. ## Instructor -When working with model providers behind an API, there's no direct way of accessing the internal logit processor like `outlines` does, but thanks to [`instructor`](https://python.useinstructor.com/) we can generate structured output from LLM providers based on `pydantic.BaseModel` objects. We have integrated `instructor` to deal with the [`AsyncLLM`][distilabel.llms.AsyncLLM], so you can work with the following LLMs: [`OpenAILLM`][distilabel.llms.OpenAILLM], [`AzureOpenAILLM`][distilabel.llms.AzureOpenAILLM], [`CohereLLM`][distilabel.llms.CohereLLM], [`GroqLLM`][distilabel.llms.GroqLLM], [`LiteLLM`][distilabel.llms.LiteLLM] and [`MistralLLM`][distilabel.llms.MistralLLM]. +For other LLM providers behind APIs, there's no direct way of accessing the internal logit processor like `outlines` does, but thanks to [`instructor`](https://python.useinstructor.com/) we can generate structured output from LLM providers based on `pydantic.BaseModel` objects. We have integrated `instructor` to deal with the [`AsyncLLM`][distilabel.llms.AsyncLLM]. !!! Note For `instructor` integration to work you may need to install the corresponding dependencies: @@ -155,14 +155,15 @@ class User(BaseModel): And then we provide that schema to the `structured_output` argument of the LLM: -!!! Note - In this example we are using *open-mixtral-8x22b*, keep in mind not all the models work with the function calling functionality required for this example to work. +!!! NOTE + In this example we are using *Meta Llama 3.1 8B Instruct*, keep in mind not all the models support structured outputs. ```python from distilabel.llms import MistralLLM -llm = MistralLLM( - model="open-mixtral-8x22b", +llm = InferenceEndpointsLLM( + model_id="meta-llama/Meta-Llama-3.1-8B-Instruct", + tokenizer_id="meta-llama/Meta-Llama-3.1-8B-Instruct", structured_output={"schema": User} ) llm.load() diff --git a/src/distilabel/llms/huggingface/inference_endpoints.py b/src/distilabel/llms/huggingface/inference_endpoints.py index 42cf1b4345..3566228f56 100644 --- a/src/distilabel/llms/huggingface/inference_endpoints.py +++ b/src/distilabel/llms/huggingface/inference_endpoints.py @@ -26,6 +26,7 @@ model_validator, validate_call, ) +from pydantic._internal._model_construction import ModelMetaclass from typing_extensions import Annotated, override from distilabel.llms.base import AsyncLLM @@ -363,6 +364,12 @@ def _get_structured_output( "the `structured_output` attribute." ) from e + if structured_output: + if isinstance(structured_output["value"], ModelMetaclass): + structured_output["value"] = structured_output[ + "value" + ].model_json_schema() + return structured_output async def _generate_with_text_generation( diff --git a/src/distilabel/steps/tasks/typing.py b/src/distilabel/steps/tasks/typing.py index ae9fd9519e..920a94c3b9 100644 --- a/src/distilabel/steps/tasks/typing.py +++ b/src/distilabel/steps/tasks/typing.py @@ -49,6 +49,8 @@ class OutlinesStructuredOutputType(TypedDict, total=False): class InstructorStructuredOutputType(TypedDict, total=False): """TypedDict to represent the structured output configuration from `instructor`.""" + format: Optional[Literal["json"]] + """One of "json".""" schema: Union[Type[BaseModel], Dict[str, Any]] """The schema to use for the structured output, a `pydantic.BaseModel` class. """ mode: Optional[str] From a178109b62db95c154b0dbae0a4b5efb7dd095f9 Mon Sep 17 00:00:00 2001 From: Agus Date: Wed, 25 Sep 2024 16:30:30 +0200 Subject: [PATCH 65/82] [DOCS] Add developer documentation section in the docs (#999) * Add new section with developer docs * Fix name of link * Add help for PR body --- docs/sections/community/contributor.md | 12 +- .../community/developer_documentation.md | 104 ++++++++++++++++++ mkdocs.yml | 1 + 3 files changed, 112 insertions(+), 5 deletions(-) create mode 100644 docs/sections/community/developer_documentation.md diff --git a/docs/sections/community/contributor.md b/docs/sections/community/contributor.md index bfc5f287c4..180c46929e 100644 --- a/docs/sections/community/contributor.md +++ b/docs/sections/community/contributor.md @@ -7,12 +7,12 @@ hide: Thank you for investing your time in contributing to the project! Any contribution you make will be reflected in the most recent version of distilabel 🤩. ??? Question "New to contributing in general?" - If you're a new contributor, read the [README](https://github.com/argilla-io/distilabel/blob/develop/README.md) to get an overview of the project. In addition, here are some resources to help you get started with open-source contributions: + If you're a new contributor, read the [README](https://github.com/argilla-io/distilabel/blob/develop/README.md) to get an overview of the project. In addition, here are some resources to help you get started with open-source contributions: - * **Discord**: You are welcome to join the [distilabel Discord community](http://hf.co/join/discord), where you can keep in touch with other users, contributors and the distilabel team. In the following [section](#first-contact-in-discord), you can find more information on how to get started in Discord. - * **Git**: This is a very useful tool to keep track of the changes in your files. Using the command-line interface (CLI), you can make your contributions easily. For that, you need to have it [installed and updated](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) on your computer. - * **GitHub**: It is a platform and cloud-based service that uses git and allows developers to collaborate on projects. To contribute to distilabel, you'll need to create an account. Check the [Contributor Workflow with Git and Github](#contributor-workflow-with-git-and-github) for more info. - * **Developer Documentation**: To collaborate, you'll need to set up an efficient environment. Check the [Installation](../getting_started/installation.md) guide to know how to do it. + * **Discord**: You are welcome to join the [distilabel Discord community](http://hf.co/join/discord), where you can keep in touch with other users, contributors and the distilabel team. In the following [section](#first-contact-in-discord), you can find more information on how to get started in Discord. + * **Git**: This is a very useful tool to keep track of the changes in your files. Using the command-line interface (CLI), you can make your contributions easily. For that, you need to have it [installed and updated](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) on your computer. + * **GitHub**: It is a platform and cloud-based service that uses git and allows developers to collaborate on projects. To contribute to distilabel, you'll need to create an account. Check the [Contributor Workflow with Git and Github](#contributor-workflow-with-git-and-github) for more info. + * **Developer Documentation**: To collaborate, you'll need to set up an efficient environment. Check the [Installation](../getting_started/installation.md) guide to know how to do it. ## First Contact in Discord @@ -129,6 +129,8 @@ In addition, on the right side, you can select a reviewer (for instance, if you Finally, fill in the template carefully and follow the guidelines. Remember to link the original issue and enable the checkbox to allow maintainer edits so the branch can be updated for a merge. Then, click on `Create pull request`. +For the PR body, ensure you give a description of what the PR contains, and add examples if possible (and if they apply to the contribution) to help with the review process. You can take a look at [#PR 974](https://github.com/argilla-io/distilabel/pull/974) or [#PR 983](https://github.com/argilla-io/distilabel/pull/983) for examples of typical PRs. + ### Review your pull request diff --git a/docs/sections/community/developer_documentation.md b/docs/sections/community/developer_documentation.md new file mode 100644 index 0000000000..ccc0795f87 --- /dev/null +++ b/docs/sections/community/developer_documentation.md @@ -0,0 +1,104 @@ +--- +description: This is a step-by-step guide to help you develop distilabel. +hide: + - footer +--- + +Thank you for investing your time in contributing to the project! + +If you don't have the repository locally, and need any help, go to the [contributor guide](../community/contributor.md) and read the contributor workflow with Git and GitHub first. + +## Set up the Python environment + +To work on the `distilabel`, you must install the package on your system. + +!!! Tip + This guide will use `uv`, but `pip` and `venv` can be used as well, this guide can work quite similar with both options. + +From the root of the cloned Distilabel repository, you should move to the distilabel folder in your terminal. + +```bash +cd distilabel +``` + +### Create a virtual environment + +The first step will be creating a virtual environment to keep our dependencies isolated. Here we are choosing `python 3.11` ([uv venv](https://docs.astral.sh/uv/pip/environments/) documentation), and then activate it: + +```bash +uv venv .venv --python 3.11 +source .venv/bin/activate +``` + +### Install the project + +Installing from local (we are using [`uv pip`](https://docs.astral.sh/uv/pip/packages/)): + +```bash +uv pip install -e . +``` + +We have extra dependencies with their name, depending on the part you are working on, you may want to install some dependency (take a look at `pyproject.toml` in the repo to see all the extra dependencies): + +```bash +uv pip install -e ".[vllm,outlines]" +``` + +### Linting and formatting + +To maintain a consistent code format, install the pre-commit hooks to run before each commit automatically (we rely heavily on [`ruff`](https://docs.astral.sh/ruff/)): + +```bash +uv pip install -e ".[dev]" +pre-commit install +``` + +### Running tests + +All the changes you add to the codebase should come with tests, either `unit` or `integration` tests, depending on the type of change, which are placed under `tests/unit` and `tests/integration` respectively. + +Start by installing the tests dependencies: + +```bash +uv pip install ".[tests]" +``` + +Running the whole tests suite may take some time, and you will need all the dependencies installed, so just run your tests, and the whole tests suite will be run for you in the CI: + +```bash +# Run specific tests +pytest tests/unit/steps/generators/test_data.py +``` + +## Set up the documentation + +To contribute to the documentation and generate it locally, ensure you have installed the development dependencies: + +```bash +uv pip install -e ".[docs]" +``` + +And run the following command to create the development server with `mkdocs`: + +```bash +mkdocs serve +``` + +### Documentation guidelines + +As mentioned, we use mkdocs to build the documentation. You can write the documentation in `markdown` format, and it will automatically be converted to HTML. In addition, you can include elements such as tables, tabs, images, and others, as shown in this guide. We recommend following these guidelines: + +- Use clear and concise language: Ensure the documentation is easy to understand for all users by using straightforward language and including meaningful examples. Images are not easy to maintain, so use them only when necessary and place them in the appropriate folder within the docs/assets/images directory. + +- Verify code snippets: Double-check that all code snippets are correct and runnable. + +- Review spelling and grammar: Check the spelling and grammar of the documentation. + +- Update the table of contents: If you add a new page, include it in the relevant index.md or the mkdocs.yml file. + +### Components gallery + +The components gallery section of the documentation is automatically generated thanks to a custom plugin, it will be run when `mkdocs serve` is called. This guide to the steps helps us visualize each step, as well as examples of use. + +!!! Note + Changes done to the docstrings of `Steps/Tasks/LLMs` won't appear in the components gallery automatically, you will have to stop the `mkdocs` server and run it again to see the changes, everything else is reloaded automatically. diff --git a/mkdocs.yml b/mkdocs.yml index b91cd814fa..2ef6234673 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -252,4 +252,5 @@ nav: - Community: - sections/community/index.md - How to contribute?: sections/community/contributor.md + - Developer Documentation: sections/community/developer_documentation.md - Issue dashboard: sections/community/popular_issues.md From a49242d7bf3c1b6bf09257478ef51282f0cf99b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Mart=C3=ADn=20Bl=C3=A1zquez?= Date: Mon, 30 Sep 2024 11:51:31 +0200 Subject: [PATCH 66/82] Fix `vllm` installation in CI (#1009) --- scripts/install_cpu_vllm.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/install_cpu_vllm.sh b/scripts/install_cpu_vllm.sh index 7535c88213..bdaa7ad74e 100755 --- a/scripts/install_cpu_vllm.sh +++ b/scripts/install_cpu_vllm.sh @@ -15,7 +15,7 @@ which python echo "Installing Python build dependencies..." python -m pip install --upgrade pip -python -m pip install wheel packaging ninja "setuptools>=49.4.0" numpy +python -m pip install wheel packaging ninja "setuptools>=49.4.0" numpy setuptools-scm echo "Cloning 'vllm-project/vllm' GitHub repository..." git clone https://github.com/vllm-project/vllm.git From 3244c05e353f9d00cff6c88102fe82d255fe8ea3 Mon Sep 17 00:00:00 2001 From: zye1996 Date: Mon, 30 Sep 2024 03:25:34 -0700 Subject: [PATCH 67/82] Fix writing `distilabel_metadata` column when `LLM` error (#1003) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix metadata writeout when llm error * linter reformat --------- Co-authored-by: Gabriel Martín Blázquez --- src/distilabel/pipeline/step_wrapper.py | 8 +------- src/distilabel/pipeline/write_buffer.py | 9 ++++++--- src/distilabel/steps/base.py | 14 ++++++++++++++ src/distilabel/steps/tasks/base.py | 24 +++++++++++++++++++++++- 4 files changed, 44 insertions(+), 11 deletions(-) diff --git a/src/distilabel/pipeline/step_wrapper.py b/src/distilabel/pipeline/step_wrapper.py index 661f99b1f7..2ddfb10daa 100644 --- a/src/distilabel/pipeline/step_wrapper.py +++ b/src/distilabel/pipeline/step_wrapper.py @@ -277,13 +277,7 @@ def _impute_step_outputs(self, batch: "_Batch") -> List[Dict[str, Any]]: Args: batch: The batch to impute. """ - result = [] - for row in batch.data[0]: - data = row.copy() - for output in self.step.outputs: - data[output] = None - result.append(data) - return result + return self.step.impute_step_outputs(batch.data[0]) def _send_batch(self, batch: _Batch) -> None: """Sends a batch to the `output_queue`.""" diff --git a/src/distilabel/pipeline/write_buffer.py b/src/distilabel/pipeline/write_buffer.py index a71ffdd9b2..e53d6faebf 100644 --- a/src/distilabel/pipeline/write_buffer.py +++ b/src/distilabel/pipeline/write_buffer.py @@ -130,9 +130,12 @@ def _write(self, step_name: str) -> None: self._buffer_last_schema[step_name] = table.schema else: if not last_schema.equals(table.schema): - new_schema = pa.unify_schemas([last_schema, table.schema]) - self._buffer_last_schema[step_name] = new_schema - table = table.cast(new_schema) + if set(last_schema.names) == set(table.schema.names): + table = table.select(last_schema.names) + else: + new_schema = pa.unify_schemas([last_schema, table.schema]) + self._buffer_last_schema[step_name] = new_schema + table = table.cast(new_schema) next_file_number = self._buffers_last_file[step_name] self._buffers_last_file[step_name] = next_file_number + 1 diff --git a/src/distilabel/steps/base.py b/src/distilabel/steps/base.py index cc0c0b2e1a..a3040133a0 100644 --- a/src/distilabel/steps/base.py +++ b/src/distilabel/steps/base.py @@ -582,6 +582,20 @@ def save_artifact( ) write_json(filename=metadata_path, data=metadata or {}) + def impute_step_outputs( + self, step_output: List[Dict[str, Any]] + ) -> List[Dict[str, Any]]: + """ + Imputes the output columns of the step that are not present in the step output. + """ + result = [] + for row in step_output: + data = row.copy() + for output in self.outputs: + data[output] = None + result.append(data) + return result + def _model_dump(self, obj: Any, **kwargs: Any) -> Dict[str, Any]: dump = super()._model_dump(obj, **kwargs) dump["runtime_parameters_info"] = self.get_runtime_parameters_info() diff --git a/src/distilabel/steps/tasks/base.py b/src/distilabel/steps/tasks/base.py index a0afb74c32..33b0330466 100644 --- a/src/distilabel/steps/tasks/base.py +++ b/src/distilabel/steps/tasks/base.py @@ -117,6 +117,28 @@ def unload(self) -> None: self._logger.debug("Executing task unload logic.") self.llm.unload() + @override + def impute_step_outputs( + self, step_output: List[Dict[str, Any]] + ) -> List[Dict[str, Any]]: + """ + Imputes the outputs of the task in case the LLM failed to generate a response. + """ + result = [] + for row in step_output: + data = row.copy() + for output in self.outputs: + data[output] = None + data = self._maybe_add_raw_input_output( + data, + None, + None, + add_raw_output=self.add_raw_output, + add_raw_input=self.add_raw_input, + ) + result.append(data) + return result + @abstractmethod def format_output( self, @@ -201,7 +223,7 @@ def _maybe_add_raw_input_output( if add_raw_output: meta[f"raw_output_{self.name}"] = raw_output if add_raw_input: - meta[f"raw_input_{self.name}"] = self.format_input(input) + meta[f"raw_input_{self.name}"] = self.format_input(input) if input else None if meta: output[DISTILABEL_METADATA_KEY] = meta From 3fd680c587114ec78a2344a2ac9a1aa98cec52f6 Mon Sep 17 00:00:00 2001 From: Agus Date: Mon, 30 Sep 2024 13:03:07 +0200 Subject: [PATCH 68/82] Add example of custom text generation step in quickstart (#984) --- docs/sections/getting_started/quickstart.md | 22 ++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/docs/sections/getting_started/quickstart.md b/docs/sections/getting_started/quickstart.md index 04d84d9786..7af9bca8f0 100644 --- a/docs/sections/getting_started/quickstart.md +++ b/docs/sections/getting_started/quickstart.md @@ -30,7 +30,7 @@ pip install distilabel[hf-inference-endpoints] --upgrade ## Define a pipeline -In this guide we will walk you through the process of creating a simple pipeline that uses the [`InferenceEndpointsLLM`][distilabel.llms.InferenceEndpointsLLM] class to generate text. The [`Pipeline`][distilabel.pipeline.Pipeline] will load a dataset that contains a column named `prompt` from the Hugging Face Hub via the step [`LoadDataFromHub`][distilabel.steps.LoadDataFromHub] and then use the [`InferenceEndpointsLLM`][distilabel.llms.InferenceEndpointsLLM] class to generate text based on the dataset using the [`TextGeneration`][distilabel.steps.tasks.TextGeneration] task. +In this guide we will walk you through the process of creating a simple pipeline that uses the [`InferenceEndpointsLLM`][distilabel.llms.InferenceEndpointsLLM] class to generate text. The [`Pipeline`][distilabel.pipeline.Pipeline] will load a dataset that contains a column named `prompt` from the Hugging Face Hub via the step [`LoadDataFromHub`][distilabel.steps.LoadDataFromHub] and then use the [`InferenceEndpointsLLM`][distilabel.llms.InferenceEndpointsLLM] class to generate text based on the dataset using the [`TextGeneration`](https://distilabel.argilla.io/dev/components-gallery/tasks/textgeneration/) task. > You can check the available models in the [Hugging Face Model Hub](https://huggingface.co/models?pipeline_tag=text-generation&sort=trending) and filter by `Inference status`. @@ -53,12 +53,14 @@ with Pipeline( # (1) model_id="meta-llama/Meta-Llama-3.1-8B-Instruct", tokenizer_id="meta-llama/Meta-Llama-3.1-8B-Instruct", ), # (5) + system_prompt="You are a creative AI Assistant writer.", + template="Follow the following instruction: {{ instruction }}" # (6) ) - load_dataset >> text_generation # (6) + load_dataset >> text_generation # (7) if __name__ == "__main__": - distiset = pipeline.run( # (7) + distiset = pipeline.run( # (8) parameters={ load_dataset.name: { "repo_id": "distilabel-internal-testing/instruction-dataset-mini", @@ -74,7 +76,7 @@ if __name__ == "__main__": }, }, ) - distiset.push_to_hub(repo_id="distilabel-example") # (8) + distiset.push_to_hub(repo_id="distilabel-example") # (9) ``` 1. We define a [`Pipeline`][distilabel.pipeline.Pipeline] with the name `simple-text-generation-pipeline` and a description `A simple text generation pipeline`. Note that the `name` is mandatory and will be used to calculate the `cache` signature path, so changing the name will change the cache path and will be identified as a different pipeline. @@ -83,12 +85,14 @@ if __name__ == "__main__": 3. We define a [`LoadDataFromHub`][distilabel.steps.LoadDataFromHub] step named `load_dataset` that will load a dataset from the Hugging Face Hub, as provided via runtime parameters in the `pipeline.run` method below, but it can also be defined within the class instance via the arg `repo_id=...`. This step will produce output batches with the rows from the dataset, and the column `prompt` will be mapped to the `instruction` field. -4. We define a [`TextGeneration`][distilabel.steps.tasks.TextGeneration] task named `text_generation` that will generate text based on the `instruction` field from the dataset. This task will use the [`InferenceEndpointsLLM`][distilabel.llms.InferenceEndpointsLLM] class with the model `Meta-Llama-3.1-8B-Instruct`. +4. We define a [`TextGeneration`](https://distilabel.argilla.io/dev/components-gallery/tasks/textgeneration/) task named `text_generation` that will generate text based on the `instruction` field from the dataset. This task will use the [`InferenceEndpointsLLM`][distilabel.llms.InferenceEndpointsLLM] class with the model `Meta-Llama-3.1-8B-Instruct`. -5. We define the [`InferenceEndpointsLLM`][distilabel.llms.InferenceEndpointsLLM] class with the model `Meta-Llama-3.1-8B-Instruct` that will be used by the [`TextGeneration`][distilabel.steps.tasks.TextGeneration] task. In this case, since the [`InferenceEndpointsLLM`][distilabel.llms.InferenceEndpointsLLM] is used, we assume that the `HF_TOKEN` environment variable is set. +5. We define the [`InferenceEndpointsLLM`][distilabel.llms.InferenceEndpointsLLM] class with the model `Meta-Llama-3.1-8B-Instruct` that will be used by the [`TextGeneration`](https://distilabel.argilla.io/dev/components-gallery/tasks/textgeneration/) task. In this case, since the [`InferenceEndpointsLLM`][distilabel.llms.InferenceEndpointsLLM] is used, we assume that the `HF_TOKEN` environment variable is set. -6. We connect the `load_dataset` step to the `text_generation` task using the `rshift` operator, meaning that the output from the `load_dataset` step will be used as input for the `text_generation` task. +6. Both `system_prompt` and `template` are optional fields. The `template` must be informed as a string following the [Jinja2](https://jinja.palletsprojects.com/en/3.1.x/templates/#synopsis) template format, and the fields that appear there ("instruction" in this case, which corresponds to the default) must be informed in the `columns` attribute. The component gallery for [`TextGeneration`](https://distilabel.argilla.io/dev/components-gallery/tasks/textgeneration/) has examples to get you started. -7. We run the pipeline with the parameters for the `load_dataset` and `text_generation` steps. The `load_dataset` step will use the repository `distilabel-internal-testing/instruction-dataset-mini` and the `test` split, and the `text_generation` task will use the `generation_kwargs` with the `temperature` set to `0.7` and the `max_new_tokens` set to `512`. +7. We connect the `load_dataset` step to the `text_generation` task using the `rshift` operator, meaning that the output from the `load_dataset` step will be used as input for the `text_generation` task. -8. Optionally, we can push the generated [`Distiset`][distilabel.distiset.Distiset] to the Hugging Face Hub repository `distilabel-example`. This will allow you to share the generated dataset with others and use it in other pipelines. +8. We run the pipeline with the parameters for the `load_dataset` and `text_generation` steps. The `load_dataset` step will use the repository `distilabel-internal-testing/instruction-dataset-mini` and the `test` split, and the `text_generation` task will use the `generation_kwargs` with the `temperature` set to `0.7` and the `max_new_tokens` set to `512`. + +9. Optionally, we can push the generated [`Distiset`][distilabel.distiset.Distiset] to the Hugging Face Hub repository `distilabel-example`. This will allow you to share the generated dataset with others and use it in other pipelines. From a46489e7cc83f34ed5935e26affbf551111ceb33 Mon Sep 17 00:00:00 2001 From: David Berenstein Date: Thu, 3 Oct 2024 13:17:35 +0200 Subject: [PATCH 69/82] feat: 985 feature argillalabeller task (#986) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: add initial version of argilla labeller task * fix: arguments in runtime parameters * feat: add field descriptions * feat: Update record formatting logic during structured generation * feat: update workflows * refactor: work based off server payloads * fix: resolve serializatione xample records * fix: only convert examples w when provided * fix: set to basically zero * fix: add temperature fix * fix: revert changes * fix: example records with formatted responses * fix: set max new tokens manually * fix: some fixes in formatting * refactor: some code quality improvements * feat: improv * refactor: remove unused code * fix: wrong prompt template * fix: remove print statement * fix: added pydantic rtuntimeparameter definition * fix: creating new characters per line examples * fix: add nuance on example in prompt template * feat: Add guidelines to prompt template * fix: remove pdb trace * fix: avoid using records without correct responses * feat: add ability to forward different questions * test: add tests for argilla labeller * fix: wrong docstring * fix: wrong docstring * refactor: rename suggestions -> suggestion * docs: update examples * tests: remove span question * docs: update the examples * Apply suggestions from code review Co-authored-by: Gabriel Martín Blázquez * refactor: apply suggestions code review * fix: type hinting Record import * fix: tests * tests: fix failing tests --------- Co-authored-by: Gabriel Martín Blázquez --- src/distilabel/llms/openai.py | 2 +- .../steps/clustering/text_clustering.py | 1 - src/distilabel/steps/tasks/__init__.py | 2 + .../steps/tasks/argilla_labeller.py | 589 ++++++++++++++++++ .../tasks/templates/argillalabeller.jinja2 | 13 + .../unit/steps/tasks/test_argilla_labeller.py | 210 +++++++ 6 files changed, 815 insertions(+), 2 deletions(-) create mode 100644 src/distilabel/steps/tasks/argilla_labeller.py create mode 100644 src/distilabel/steps/tasks/templates/argillalabeller.jinja2 create mode 100644 tests/unit/steps/tasks/test_argilla_labeller.py diff --git a/src/distilabel/llms/openai.py b/src/distilabel/llms/openai.py index b73efc223b..48cac8a50e 100644 --- a/src/distilabel/llms/openai.py +++ b/src/distilabel/llms/openai.py @@ -667,7 +667,7 @@ def _create_jsonl_row( """Creates a JSONL formatted row to be used by the OpenAI Batch API. Args: - inputs: a list of inputs in chat format to generate responses for, optionally + input: a list of inputs in chat format to generate responses for, optionally including structured output. custom_id: a custom ID to use for the row. kwargs: the keyword arguments to use for the generation. diff --git a/src/distilabel/steps/clustering/text_clustering.py b/src/distilabel/steps/clustering/text_clustering.py index 4bf583c167..7e640bf5c1 100644 --- a/src/distilabel/steps/clustering/text_clustering.py +++ b/src/distilabel/steps/clustering/text_clustering.py @@ -223,7 +223,6 @@ def _create_figure( inputs: The inputs of the step, as we will extract information from them again. label2docs: Map from each label to the list of documents (texts) that belong to that cluster. cluster_summaries: The summaries of the clusters, obtained from the LLM. - labels: The labels of the clusters (integers representing each predicted class). """ self._logger.info("🖼️ Creating figure for the clusters...") diff --git a/src/distilabel/steps/tasks/__init__.py b/src/distilabel/steps/tasks/__init__.py index eb90c6dbad..c7b7d72395 100644 --- a/src/distilabel/steps/tasks/__init__.py +++ b/src/distilabel/steps/tasks/__init__.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +from distilabel.steps.tasks.argilla_labeller import ArgillaLabeller from distilabel.steps.tasks.base import GeneratorTask, Task from distilabel.steps.tasks.complexity_scorer import ComplexityScorer from distilabel.steps.tasks.evol_instruct.base import EvolInstruct @@ -52,6 +53,7 @@ __all__ = [ "GeneratorTask", "Task", + "ArgillaLabeller", "ComplexityScorer", "EvolInstruct", "EvolComplexity", diff --git a/src/distilabel/steps/tasks/argilla_labeller.py b/src/distilabel/steps/tasks/argilla_labeller.py new file mode 100644 index 0000000000..46a12748ff --- /dev/null +++ b/src/distilabel/steps/tasks/argilla_labeller.py @@ -0,0 +1,589 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import sys +import warnings +from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union + +import orjson as json +from jinja2 import Template +from pydantic import BaseModel, Field, PrivateAttr +from typing_extensions import override + +from distilabel.mixins.runtime_parameters import RuntimeParameter +from distilabel.steps.base import StepInput +from distilabel.steps.tasks.base import Task + +if sys.version_info < (3, 9): + import importlib_resources +else: + import importlib.resources as importlib_resources + +if TYPE_CHECKING: + from argilla import ( + LabelQuestion, + MultiLabelQuestion, + RatingQuestion, + Record, + TextField, + TextQuestion, + ) + + from distilabel.steps.tasks.typing import ChatType + from distilabel.steps.typing import StepOutput + + +class ArgillaLabeller(Task): + """ + Annotate Argilla records based on input fields, example records and question settings. + + This task is designed to facilitate the annotation of Argilla records by leveraging a pre-trained LLM. + It uses a system prompt that guides the LLM to understand the input fields, the question type, + and the question settings. The task then formats the input data and generates a response based on the question. + The response is validated against the question's value model, and the final suggestion is prepared for annotation. + + Attributes: + _template: a Jinja2 template used to format the input for the LLM. + + Input columns: + - record (`argilla.Record`): The record to be annotated. + - fields (`Optional[List[Dict[str, Any]]]`): The list of field settings for the input fields. + - question (`Optional[Dict[str, Any]]`): The question settings for the question to be answered. + - example_records (`Optional[List[Dict[str, Any]]]`): The few shot example records with responses to be used to answer the question. + - guidelines (`Optional[str]`): The guidelines for the annotation task. + + Output columns: + - suggestion (`Dict[str, Any]`): The final suggestion for annotation. + + Categories: + - text-classification + - scorer + - text-generation + + References: + - [`Argilla: Argilla is a collaboration tool for AI engineers and domain experts to build high-quality datasets`](https://github.com/argilla-io/argilla/) + + Examples: + Annotate a record with the same dataset and question: + + ```python + import argilla as rg + from distilabel.steps.tasks import ArgillaLabeller + from distilabel.llms.huggingface import InferenceEndpointsLLM + + # Get information from Argilla dataset definition + dataset = rg.Dataset("my_dataset") + pending_records_filter = rg.Filter(("status", "==", "pending")) + completed_records_filter = rg.Filter(("status", "==", "completed")) + pending_records = list( + dataset.records( + query=rg.Query(filter=pending_records_filter), + limit=5, + ) + ) + example_records = list( + dataset.records( + query=rg.Query(filter=completed_records_filter), + limit=5, + ) + ) + field = dataset.settings.fields["text"] + question = dataset.settings.questions["label"] + + # Initialize the labeller with the model and fields + labeller = ArgillaLabeller( + llm=InferenceEndpointsLLM( + model_id="mistralai/Mistral-7B-Instruct-v0.2", + ) + fields=[field], + question=question, + example_records=example_records, + guidelines=dataset.guidelines + ) + labeller.load() + + # Process the pending records + result = next( + labeller.process( + [ + { + "record": record + } for record in pending_records + ] + ) + ) + + # Add the suggestions to the records + for record, suggestion in zip(pending_records, result): + record.suggestions.add(suggestion["suggestion"]) + + # Log the updated records + dataset.records.log(pending_records) + ``` + + Annotate a record with alternating datasets and questions: + + ```python + import argilla as rg + from distilabel.steps.tasks import ArgillaLabeller + from distilabel.llms.huggingface import InferenceEndpointsLLM + + # Get information from Argilla dataset definition + dataset = rg.Dataset("my_dataset") + field = dataset.settings.fields["text"] + question = dataset.settings.questions["label"] + question2 = dataset.settings.questions["label2"] + + # Initialize the labeller with the model and fields + labeller = ArgillaLabeller( + llm=InferenceEndpointsLLM( + model_id="mistralai/Mistral-7B-Instruct-v0.2", + ) + ) + labeller.load() + + # Process the record + record = next(dataset.records()) + result = next( + labeller.process( + [ + { + "record": record, + "fields": [field], + "question": question, + }, + { + "record": record, + "fields": [field], + "question": question2, + } + ] + ) + ) + + # Add the suggestions to the record + record.suggestions.add(result[0]["suggestion"]) + + # Log the updated record + dataset.records.log(record) + ``` + + Overwrite default prompts and instructions: + + ```python + import argilla as rg + from distilabel.steps.tasks import ArgillaLabeller + from distilabel.llms.huggingface import InferenceEndpointsLLM + + # Overwrite default prompts and instructions + labeller = ArgillaLabeller( + llm=InferenceEndpointsLLM( + model_id="mistralai/Mistral-7B-Instruct-v0.2", + ), + system_prompt="You are an expert annotator and labelling assistant that understands complex domains and natural language processing.", + question_to_label_instruction={ + "label_selection": "Select the appropriate label from the list of provided labels.", + "multi_label_selection": "Select none, one or multiple labels from the list of provided labels.", + "text": "Provide a text response to the question.", + "rating": "Provide a rating for the question.", + }, + ) + labeller.load() + ``` + """ + + system_prompt: str = ( + "You are an expert annotator and labelling assistant that understands complex domains and natural language processing. " + "You are given input fields and a question. " + "You should create a valid JSON object as an answer to the question based on the input fields. " + "1. Understand the input fields and optional guidelines. " + "2. Understand the question type and the question settings. " + "3. Reason through your response step-by-step. " + "4. Provide a valid JSON object as an answer to the question." + ) + question_to_label_instruction: Dict[str, str] = { + "label_selection": "Select the appropriate label from the list of provided labels.", + "multi_label_selection": "Select none, one or multiple labels from the list of provided labels.", + "text": "Provide a text response to the question.", + "rating": "Provide a rating for the question.", + } + example_records: Optional[ + RuntimeParameter[Union[List[Union[Dict[str, Any], BaseModel]], None]] + ] = Field( + default=None, + description="The few shot serialized example records or `BaseModel`s with responses to be used to answer the question.", + ) + fields: Optional[ + RuntimeParameter[Union[List[Union[BaseModel, Dict[str, Any]]], None]] + ] = Field( + default=None, + description="The field serialized field settings or `BaseModel` for the fields to be used to answer the question.", + ) + question: Optional[ + RuntimeParameter[ + Union[ + Dict[str, Any], + BaseModel, + None, + ] + ] + ] = Field( + default=None, + description="The question serialized question settings or `BaseModel` for the question to be answered.", + ) + guidelines: Optional[RuntimeParameter[str]] = Field( + default=None, + description="The guidelines for the annotation task.", + ) + + _template: Union[Template, None] = PrivateAttr(...) + _client: Optional[Any] = PrivateAttr(None) + + def load(self) -> None: + """Loads the Jinja2 template.""" + super().load() + + _path = str( + importlib_resources.files("distilabel") + / "steps" + / "tasks" + / "templates" + / "argillalabeller.jinja2" + ) + + self._template = Template(open(_path).read()) + + @property + def inputs(self) -> Dict[str, bool]: + return { + "record": True, + "fields": False, + "question": False, + "example_records": False, + "guidelines": False, + } + + def _format_record( + self, record: Dict[str, Any], fields: List[Dict[str, Any]] + ) -> str: + """Format the record fields into a string. + + Args: + record (Dict[str, Any]): The record to format. + fields (List[Dict[str, Any]]): The fields to format. + + Returns: + str: The formatted record fields. + """ + output = [] + for field in fields: + if title := field.get("title"): + output.append(f"title: {title}") + if description := field.get("description"): + output.append(f"description: {description}") + output.append(record.get("fields", {}).get(field.get("name", ""))) + return "\n".join(output) + + def _get_label_instruction(self, question: Dict[str, Any]) -> str: + """Get the label instruction for the question. + + Args: + question (Dict[str, Any]): The question to get the label instruction for. + + Returns: + str: The label instruction for the question. + """ + question_type = question["settings"]["type"] + return self.question_to_label_instruction[question_type] + + def _format_question(self, question: Dict[str, Any]) -> str: + """Format the question settings into a string. + + Args: + question (Dict[str, Any]): The question to format. + + Returns: + str: The formatted question. + """ + output = [ + f"title: {question.get('title', '')}", + f"description: {question.get('description', '')}", + f"label_instruction: {self._get_label_instruction(question)}", + ] + settings = question.get("settings", {}) + if "options" in settings: + output.append( + f"labels: {[option['value'] for option in settings.get('options', [])]}" + ) + return "\n".join(output) + + def _format_example_records( + self, + records: List[Dict[str, Any]], + fields: List[Dict[str, Any]], + question: Dict[str, Any], + ) -> str: + """Format the example records into a string. + + Args: + records (List[Dict[str, Any]]): The records to format. + fields (List[Dict[str, Any]]): The fields to format. + question (Dict[str, Any]): The question to format. + + Returns: + str: The formatted example records. + """ + base = [] + for record in records: + responses = record.get("responses", {}) + if responses.get(question["name"]): + base.append(self._format_record(record, fields)) + value = responses[question["name"]][0]["value"] + formatted_value = self._assign_value_to_question_value_model( + value, question + ) + base.append(f"Response: {formatted_value}") + base.append("") + else: + warnings.warn( + f"Record {record} has no response for question {question['name']}. Skipping example record.", + stacklevel=2, + ) + return "\n".join(base) + + def format_input( + self, + input: Dict[ + str, + Union[ + Dict[str, Any], + "Record", + "TextField", + "MultiLabelQuestion", + "LabelQuestion", + "RatingQuestion", + "TextQuestion", + ], + ], + ) -> "ChatType": + """Format the input into a chat message. + + Args: + input (Dict[str, Union[Dict[str, Any], Record, TextField, MultiLabelQuestion, LabelQuestion, RatingQuestion, TextQuestion]]): The input to format. + + Returns: + ChatType: The formatted chat message. + """ + input_keys = list(self.inputs.keys()) + record = input[input_keys[0]] + fields = input.get(input_keys[1], self.fields) + question = input.get(input_keys[2], self.question) + examples = input.get(input_keys[3], self.example_records) + guidelines = input.get(input_keys[4], self.guidelines) + + if any([fields is None, question is None]): + raise ValueError( + "Fields and question must be provided during init or through `process` method." + ) + + record = record.to_dict() if not isinstance(record, dict) else record + question = question.serialize() if not isinstance(question, dict) else question + fields = [ + field.serialize() if not isinstance(field, dict) else field + for field in fields + ] + examples = ( + [ + example.to_dict() if not isinstance(example, dict) else example + for example in examples + ] + if examples + else None + ) + + formatted_fields = self._format_record(record, fields) + formatted_question = self._format_question(question) + formatted_examples = ( + self._format_example_records(examples, fields, question) + if examples + else False + ) + prompt = self._template.render( + fields=formatted_fields, + question=formatted_question, + examples=formatted_examples, + guidelines=guidelines, + ) + + messages = [] + if self.system_prompt: + messages.append({"role": "system", "content": self.system_prompt}) + messages.append({"role": "user", "content": prompt}) + return messages + + @property + def outputs(self) -> List[str]: + return ["suggestion"] + + def format_output( + self, output: Union[str, None], input: Dict[str, Any] + ) -> Dict[str, Any]: + """Format the output into a dictionary. + + Args: + output (Union[str, None]): The output to format. + input (Dict[str, Any]): The input to format. + + Returns: + Dict[str, Any]: The formatted output. + """ + from argilla import Suggestion + + question: Union[ + Any, + Dict[str, Any], + LabelQuestion, + MultiLabelQuestion, + RatingQuestion, + TextQuestion, + None, + ] = input.get(list(self.inputs.keys())[2], self.question) or self.question + question = question.serialize() if not isinstance(question, dict) else question + model = self._get_pydantic_model_of_structured_output(question) + validated_output = model(**json.loads(output)) + value = self._get_value_from_question_value_model(validated_output) + suggestion = Suggestion( + value=value, + question_name=question["name"], + type="model", + agent=self.llm.model_name, + ).serialize() + return {self.outputs[0]: suggestion} + + def _set_llm_structured_output_for_question(self, question: Dict[str, Any]) -> None: + runtime_parameters = self.llm._runtime_parameters + runtime_parameters.update( + { + "structured_output": { + "format": "json", + "schema": self._get_pydantic_model_of_structured_output(question), + }, + } + ) + self.llm.set_runtime_parameters(runtime_parameters) + + @override + def process(self, inputs: StepInput) -> "StepOutput": + """Process the input through the task. + + Args: + inputs (StepInput): The input to process. + + Returns: + StepOutput: The output of the task. + """ + questions = [input.get("question", self.question) for input in inputs] + questions = [ + question.serialize() if not isinstance(question, dict) else question + for question in questions + ] + if not all(question == questions[0] for question in questions): + warnings.warn( + "Not all questions are the same. Processing each question separately by setting the structured output for each question. This may impact performance.", + stacklevel=2, + ) + for input, question in zip(inputs, questions): + self._set_llm_structured_output_for_question(question) + yield from super().process([input]) + else: + question = questions[0] + self._set_llm_structured_output_for_question(question) + yield from super().process(inputs) + + def _get_value_from_question_value_model( + self, question_value_model: BaseModel + ) -> Any: + """Get the value from the question value model. + + Args: + question_value_model (BaseModel): The question value model to get the value from. + + Returns: + Any: The value from the question value model. + """ + for attr in ["label", "labels", "rating", "text"]: + if hasattr(question_value_model, attr): + return getattr(question_value_model, attr) + raise ValueError(f"Unsupported question type: {question_value_model}") + + def _assign_value_to_question_value_model( + self, value: Any, question: Dict[str, Any] + ) -> BaseModel: + """Assign the value to the question value model. + + Args: + value (Any): The value to assign. + question (Dict[str, Any]): The question to assign the value to. + + Returns: + BaseModel: The question value model with the assigned value. + """ + question_value_model = self._get_pydantic_model_of_structured_output(question) + for attr in ["label", "labels", "rating", "text"]: + try: + model_dict = {attr: value} + question_value_model = question_value_model(**model_dict) + return question_value_model.model_dump_json() + except AttributeError: + pass + return value + + def _get_pydantic_model_of_structured_output( + self, + question: Dict[str, Any], + ) -> BaseModel: + """Get the Pydantic model of the structured output. + + Args: + question (Dict[str, Any]): The question to get the Pydantic model of the structured output for. + + Returns: + BaseModel: The Pydantic model of the structured output. + """ + + question_type = question["settings"]["type"] + + if question_type == "multi_label_selection": + + class QuestionValueModel(BaseModel): + labels: Optional[List[str]] = Field(default_factory=list) + + elif question_type == "label_selection": + + class QuestionValueModel(BaseModel): + label: str + + elif question_type == "text": + + class QuestionValueModel(BaseModel): + text: str + + elif question_type == "rating": + + class QuestionValueModel(BaseModel): + rating: int + else: + raise ValueError(f"Unsupported question type: {question}") + + return QuestionValueModel diff --git a/src/distilabel/steps/tasks/templates/argillalabeller.jinja2 b/src/distilabel/steps/tasks/templates/argillalabeller.jinja2 new file mode 100644 index 0000000000..d5afa75d27 --- /dev/null +++ b/src/distilabel/steps/tasks/templates/argillalabeller.jinja2 @@ -0,0 +1,13 @@ +Please provide an answer to the question based on the input fields{% if examples %} and examples{% endif %}. +{% if guidelines %} +# Guidelines +{{ guidelines }} +{% endif %} +# Input Fields +{{ fields }} +# Question +{{ question }} +{% if examples %} +# Examples +{{ examples }} +{% endif %} \ No newline at end of file diff --git a/tests/unit/steps/tasks/test_argilla_labeller.py b/tests/unit/steps/tasks/test_argilla_labeller.py new file mode 100644 index 0000000000..926118dd6c --- /dev/null +++ b/tests/unit/steps/tasks/test_argilla_labeller.py @@ -0,0 +1,210 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import json +from typing import Any, Dict, List + +import pytest + +from distilabel.pipeline.local import Pipeline +from distilabel.steps.tasks.argilla_labeller import ArgillaLabeller +from distilabel.steps.tasks.typing import ChatItem +from tests.unit.conftest import DummyAsyncLLM + + +@pytest.fixture +def fields() -> Dict[str, Any]: + return [ + { + "name": "text", + "description": "The text of the question", + "title": "The text of the question", + "settings": {"type": "text"}, + } + ] + + +@pytest.fixture +def questions() -> List[Dict[str, Any]]: + return [ + { + "name": "label_selection", + "description": "The class of the question", + "title": "Is the question a question?", + "settings": { + "type": "label_selection", + "options": [ + {"value": "yes", "text": "Yes"}, + {"value": "no", "text": "No"}, + ], + }, + }, + { + "name": "multi_label_selection", + "description": "The class of the question", + "title": "Is the question a question?", + "settings": { + "type": "multi_label_selection", + "options": [ + {"value": "yes", "text": "Yes"}, + {"value": "no", "text": "No"}, + ], + }, + }, + { + "name": "rating", + "description": "The class of the question", + "title": "Is the question a question?", + "settings": { + "type": "rating", + "options": [ + {"value": "1", "text": "1"}, + ], + }, + }, + { + "name": "text", + "description": "The class of the question", + "title": "Is the question a question?", + "settings": { + "type": "text", + }, + }, + ] + + +@pytest.fixture +def outputs() -> List[Dict[str, Any]]: + return [ + { + "label": "yes", + }, + { + "labels": ["yes", "no"], + }, + { + "rating": "1", + }, + { + "text": "yes", + }, + ] + + +@pytest.fixture +def records() -> List[Dict[str, Any]]: + return [ + { + "fields": { + "text": "What is the capital of France?", + }, + "responses": [ + { + "quesion_name": "label_selection", + "value": "yes", + } + ], + } + ] + + +class TestArgillaLabeller: + def test_format_input( + self, + questions: List[Dict[str, Any]], + records: List[Dict[str, Any]], + fields: List[Dict[str, Any]], + ) -> None: + task = ArgillaLabeller( + name="argilla_labeller", + llm=DummyAsyncLLM(), + pipeline=Pipeline(name="unit-test-pipeline"), + ) + task.load() + + for question in questions: + result: List[ChatItem] = task.format_input( + input={ + "question": question, + "fields": fields, + "record": records[0], + } + ) + assert question["description"] in result[-1]["content"] + assert question["title"] in result[-1]["content"] + if question["settings"]["type"] in [ + "label_selection", + "multi_label_selection", + "span", + "rating", + ]: + assert ( + question["settings"]["options"][0]["value"] in result[-1]["content"] + ) + + def test_format_output( + self, + questions: List[Dict[str, Any]], + records: List[Dict[str, Any]], + fields: List[Dict[str, Any]], + outputs: List[Dict[str, Any]], + ) -> None: + task = ArgillaLabeller( + name="argilla_labeller", + llm=DummyAsyncLLM(), + pipeline=Pipeline(name="unit-test-pipeline"), + ) + task.load() + + for question, output in zip(questions, outputs): + task.format_output( + input={ + "question": question, + "fields": fields, + "record": records[0], + }, + output=json.dumps(output), + ) + + def test_fail_on_invalid_question_type( + self, questions: List[Dict[str, Any]], records: List[Dict[str, Any]] + ) -> None: + task = ArgillaLabeller( + name="argilla_labeller", + llm=DummyAsyncLLM(), + pipeline=Pipeline(name="unit-test-pipeline"), + ) + task.load() + + fake_question = questions[0] + fake_question["settings"]["type"] = "invalid_type" + + with pytest.raises(ValueError): + task.format_input( + input={ + "record": records[0], + "question": fake_question, + } + ) + + def test_fail_on_no_question(self, records: List[Dict[str, Any]]) -> None: + task = ArgillaLabeller( + name="argilla_labeller", + llm=DummyAsyncLLM(), + pipeline=Pipeline(name="unit-test-pipeline"), + ) + task.load() + + with pytest.raises(ValueError): + task.format_input(input={"record": records[0]}) From b4c13ba5d573dca178992ecd0e6ed3b546b0bc57 Mon Sep 17 00:00:00 2001 From: davidberenstein1957 Date: Thu, 3 Oct 2024 19:26:12 +0200 Subject: [PATCH 70/82] fix: validate fields and questions during process --- src/distilabel/steps/tasks/argilla_labeller.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/distilabel/steps/tasks/argilla_labeller.py b/src/distilabel/steps/tasks/argilla_labeller.py index 46a12748ff..a975e19dab 100644 --- a/src/distilabel/steps/tasks/argilla_labeller.py +++ b/src/distilabel/steps/tasks/argilla_labeller.py @@ -392,11 +392,6 @@ def format_input( examples = input.get(input_keys[3], self.example_records) guidelines = input.get(input_keys[4], self.guidelines) - if any([fields is None, question is None]): - raise ValueError( - "Fields and question must be provided during init or through `process` method." - ) - record = record.to_dict() if not isinstance(record, dict) else record question = question.serialize() if not isinstance(question, dict) else question fields = [ @@ -493,7 +488,19 @@ def process(self, inputs: StepInput) -> "StepOutput": Returns: StepOutput: The output of the task. """ + questions = [input.get("question", self.question) for input in inputs] + fields = [input.get("fields", self.fields) for input in inputs] + # check if any field for the field in fields is None + if any([item is None for item in field] for field in fields): + raise ValueError( + "Fields must be provided during init or through `process` method." + ) + # check if any question is None + if any(question is None for question in questions): + raise ValueError( + "Question must be provided during init or through `process` method." + ) questions = [ question.serialize() if not isinstance(question, dict) else question for question in questions From 1eb0524d30b7a06f7a8cc8d2e5727941444c1d6c Mon Sep 17 00:00:00 2001 From: davidberenstein1957 Date: Thu, 3 Oct 2024 19:30:34 +0200 Subject: [PATCH 71/82] fix: validation of fields and records passed --- .../steps/tasks/argilla_labeller.py | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/distilabel/steps/tasks/argilla_labeller.py b/src/distilabel/steps/tasks/argilla_labeller.py index a975e19dab..41dd0394ca 100644 --- a/src/distilabel/steps/tasks/argilla_labeller.py +++ b/src/distilabel/steps/tasks/argilla_labeller.py @@ -489,32 +489,33 @@ def process(self, inputs: StepInput) -> "StepOutput": StepOutput: The output of the task. """ - questions = [input.get("question", self.question) for input in inputs] - fields = [input.get("fields", self.fields) for input in inputs] + question_list = [input.get("question", self.question) for input in inputs] + fields_list = [input.get("fields", self.fields) for input in inputs] # check if any field for the field in fields is None - if any([item is None for item in field] for field in fields): - raise ValueError( - "Fields must be provided during init or through `process` method." - ) + for fields in fields_list: + if any(field is None for field in fields): + raise ValueError( + "Fields must be provided during init or through `process` method." + ) # check if any question is None - if any(question is None for question in questions): + if any(question is None for question in question_list): raise ValueError( "Question must be provided during init or through `process` method." ) - questions = [ + question_list = [ question.serialize() if not isinstance(question, dict) else question - for question in questions + for question in question_list ] - if not all(question == questions[0] for question in questions): + if not all(question == question_list[0] for question in question_list): warnings.warn( "Not all questions are the same. Processing each question separately by setting the structured output for each question. This may impact performance.", stacklevel=2, ) - for input, question in zip(inputs, questions): + for input, question in zip(inputs, question_list): self._set_llm_structured_output_for_question(question) yield from super().process([input]) else: - question = questions[0] + question = question_list[0] self._set_llm_structured_output_for_question(question) yield from super().process(inputs) From 7b5cbb04cd68de2b0d5080995b0c91b36836bb87 Mon Sep 17 00:00:00 2001 From: davidberenstein1957 Date: Thu, 3 Oct 2024 19:49:17 +0200 Subject: [PATCH 72/82] fix: suggestion serialisation argilla labeller --- src/distilabel/steps/tasks/argilla_labeller.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/distilabel/steps/tasks/argilla_labeller.py b/src/distilabel/steps/tasks/argilla_labeller.py index 41dd0394ca..dd4522813a 100644 --- a/src/distilabel/steps/tasks/argilla_labeller.py +++ b/src/distilabel/steps/tasks/argilla_labeller.py @@ -79,6 +79,7 @@ class ArgillaLabeller(Task): ```python import argilla as rg + from argilla import Suggestion from distilabel.steps.tasks import ArgillaLabeller from distilabel.llms.huggingface import InferenceEndpointsLLM @@ -105,7 +106,7 @@ class ArgillaLabeller(Task): labeller = ArgillaLabeller( llm=InferenceEndpointsLLM( model_id="mistralai/Mistral-7B-Instruct-v0.2", - ) + ), fields=[field], question=question, example_records=example_records, @@ -126,7 +127,7 @@ class ArgillaLabeller(Task): # Add the suggestions to the records for record, suggestion in zip(pending_records, result): - record.suggestions.add(suggestion["suggestion"]) + record.suggestions.add(Suggestion(**suggestion["suggestion"])) # Log the updated records dataset.records.log(pending_records) @@ -173,10 +174,11 @@ class ArgillaLabeller(Task): ) # Add the suggestions to the record - record.suggestions.add(result[0]["suggestion"]) + for suggestion in result: + record.suggestions.add(rg.Suggestion(**suggestion["suggestion"])) # Log the updated record - dataset.records.log(record) + dataset.records.log([record]) ``` Overwrite default prompts and instructions: @@ -464,7 +466,13 @@ def format_output( type="model", agent=self.llm.model_name, ).serialize() - return {self.outputs[0]: suggestion} + return { + self.outputs[0]: { + k: v + for k, v in suggestion.items() + if k in ["value", "question_name", "type", "agent"] + } + } def _set_llm_structured_output_for_question(self, question: Dict[str, Any]) -> None: runtime_parameters = self.llm._runtime_parameters From 4848dd233c5c95b6df4ac4edc65c936ada09a558 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Mart=C3=ADn=20Bl=C3=A1zquez?= Date: Mon, 7 Oct 2024 12:45:44 +0200 Subject: [PATCH 73/82] Fix`llvmlite` install with `uv` (#1018) * Add `numba >= 0.54.0` * Use `numpy < 2.0.0` * Install vLLM first * remove llm blender install --- pyproject.toml | 4 ++-- scripts/install_dependencies.sh | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 44404c683e..adab8d4fad 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -84,7 +84,7 @@ llama-cpp = ["llama-cpp-python >= 0.2.0"] mistralai = ["mistralai >= 1.0.0"] ollama = ["ollama >= 0.1.7"] openai = ["openai >= 1.0.0"] -outlines = ["outlines >= 0.0.40"] +outlines = ["outlines >= 0.0.40", "numba >= 0.54.0"] ray = ["ray[default] >= 2.31.0"] vertexai = ["google-cloud-aiplatform >= 1.38.0"] vllm = [ @@ -99,7 +99,7 @@ faiss-gpu = ["faiss-gpu >= 1.7.2"] text-clustering = [ "umap-learn >= 0.5.6", "scikit-learn >= 1.4.1", - "matplotlib >= 3.8.3" # For the figure (even though it's optional) + "matplotlib >= 3.8.3", # For the figure (even though it's optional) ] # minhash diff --git a/scripts/install_dependencies.sh b/scripts/install_dependencies.sh index 767f6e6dd0..0b2277f0fb 100755 --- a/scripts/install_dependencies.sh +++ b/scripts/install_dependencies.sh @@ -9,10 +9,9 @@ python -m pip install uv uv pip install --system -e ".[anthropic,argilla,cohere,groq,hf-inference-endpoints,hf-transformers,litellm,llama-cpp,ollama,openai,outlines,vertexai,mistralai,instructor,sentence-transformers,faiss-cpu,minhash,text-clustering]" if [ "${python_version}" != "(3, 12)" ]; then - uv pip install --system -e .[ray] + uv pip install --system -e .[ray] fi ./scripts/install_cpu_vllm.sh -uv pip install --system git+https://github.com/argilla-io/LLM-Blender.git uv pip install --system -e ".[dev,tests]" From d5c048459ada32ba7b3356f3f79c39fa0e61f706 Mon Sep 17 00:00:00 2001 From: David Berenstein Date: Mon, 7 Oct 2024 13:00:18 +0200 Subject: [PATCH 74/82] tests: validate passing questions and field within format_input too (#1017) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Gabriel Martín Blázquez --- src/distilabel/steps/tasks/argilla_labeller.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/distilabel/steps/tasks/argilla_labeller.py b/src/distilabel/steps/tasks/argilla_labeller.py index dd4522813a..d0874ed3de 100644 --- a/src/distilabel/steps/tasks/argilla_labeller.py +++ b/src/distilabel/steps/tasks/argilla_labeller.py @@ -382,10 +382,13 @@ def format_input( """Format the input into a chat message. Args: - input (Dict[str, Union[Dict[str, Any], Record, TextField, MultiLabelQuestion, LabelQuestion, RatingQuestion, TextQuestion]]): The input to format. + input: The input to format. Returns: - ChatType: The formatted chat message. + The formatted chat message. + + Raises: + ValueError: If question or fields are not provided. """ input_keys = list(self.inputs.keys()) record = input[input_keys[0]] @@ -394,6 +397,11 @@ def format_input( examples = input.get(input_keys[3], self.example_records) guidelines = input.get(input_keys[4], self.guidelines) + if question is None: + raise ValueError("Question must be provided.") + if fields is None or any(field is None for field in fields): + raise ValueError("Fields must be provided.") + record = record.to_dict() if not isinstance(record, dict) else record question = question.serialize() if not isinstance(question, dict) else question fields = [ @@ -416,6 +424,7 @@ def format_input( if examples else False ) + prompt = self._template.render( fields=formatted_fields, question=formatted_question, From 4b8903b2407e0432da8653a70e955d7393834487 Mon Sep 17 00:00:00 2001 From: zye1996 Date: Mon, 7 Oct 2024 05:04:24 -0700 Subject: [PATCH 75/82] Fix impute when `output_mapping` is not empty (#1015) --- src/distilabel/steps/base.py | 2 +- src/distilabel/steps/tasks/base.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/distilabel/steps/base.py b/src/distilabel/steps/base.py index a3040133a0..48fcaaa004 100644 --- a/src/distilabel/steps/base.py +++ b/src/distilabel/steps/base.py @@ -591,7 +591,7 @@ def impute_step_outputs( result = [] for row in step_output: data = row.copy() - for output in self.outputs: + for output in self.get_outputs().keys(): data[output] = None result.append(data) return result diff --git a/src/distilabel/steps/tasks/base.py b/src/distilabel/steps/tasks/base.py index 33b0330466..d73bafd60c 100644 --- a/src/distilabel/steps/tasks/base.py +++ b/src/distilabel/steps/tasks/base.py @@ -127,7 +127,7 @@ def impute_step_outputs( result = [] for row in step_output: data = row.copy() - for output in self.outputs: + for output in self.get_outputs().keys(): data[output] = None data = self._maybe_add_raw_input_output( data, From 4b056ff2aa9dcf4824272095167977224de80162 Mon Sep 17 00:00:00 2001 From: Agus Date: Mon, 7 Oct 2024 15:29:51 +0200 Subject: [PATCH 76/82] Add Tasks to replicate `APIGen` (#925) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add apigen task module * Add tests for apigen * Fix default name for dataset info when requesting the number of examples * checkpoint * Add tests for apigen generator * Create jinja template, split methods and add docstrings * Update string format * Simplify function setting and move it to load method * Add tests for semantic checker * Add prompt template for semantic checker * Redirect import for semantic checker * Fix docstrins for output columns * Add semantic checker task from apigen * Add notes for execution checker * Remove extra jump of line * Add first version of data sampler, step helper for apigen * Add tests for data sampler * Add integration test to check the sampler can be mixed with another generator step * Draft tests for new execution checker * Move helper functions * Draft for execution checker functionality * Add first version of execution checker and tests * Add tests for utils module of apigen * Remove unnecessary step for transformation and rename files for clarity * Fix import * Change function results name to show the original results from the execution * Remove print when the url for a reference doesn't contain https://arxiv * first working version * Fix tests including previous columns * Go back to previous name for dummy llm * Change dummy llm names on tests * Read the answers from the model parsed instead of dumped string * Add option to include the tools if available for few shot * Allow extra checks for the parameter types and tests for those * Add docs for the execution checker * Add new icon for execution * Fix return type for outputs column * Fix docstrings * Redirect imports to top level * Update docstrings to render on components gallery * Improve docstrings for fields in the data sampler * Remove unnecesary data from docstrings and remove TODO * Add missing data variable in example * Update src/distilabel/steps/tasks/apigen/execution_checker.py Co-authored-by: Gabriel Martín Blázquez * Refactor to return formatted json string instead of dict to simplify work with arrow * Draft tutorial to replicate paper * Allow number to be a dict with values and probabilities * Update pipeline run call * Add functionality to load functions from a folder with .py files * Fix comment for arg * Add example implementation * Add dependency for vllm * Fix dependency name * Add setuptools-scm in the script with the dependencies to install it prior to vllm * Another attempt with system * Add tests to take into account casting methods * Avoid casting and update prompt to ensure argument order is respected * Inform error type on generator * Add extra checks and safeguards for failed answer generation * Ensure the error is of the expected type * Fix unstructured generation * Remove json fences and fix semantic checker * Control case of functions without arguments * Add additional checks to run the execution checker * Remove additional dependency * Try fixing CI error with dependencies * Install dependency for the system * Undo fix attempt * Try fixing llvmlite dependency issue * Remove additional dependency as it breaks other tests --------- Co-authored-by: Gabriel Martín Blázquez --- .../tutorials-assets/overview-apigen.jpg | Bin 0 -> 408979 bytes docs/sections/pipeline_samples/index.md | 9 + .../pipeline_samples/papers/apigen.md | 239 ++++++++++ examples/lib_apigen.py | 146 ++++++ examples/pipeline_apigen.py | 116 +++++ mkdocs.yml | 1 + src/distilabel/distiset.py | 5 +- src/distilabel/llms/_dummy.py | 70 +++ src/distilabel/steps/__init__.py | 2 + .../steps/generators/data_sampler.py | 179 +++++++ .../steps/generators/huggingface.py | 10 +- src/distilabel/steps/tasks/__init__.py | 6 + src/distilabel/steps/tasks/apigen/__init__.py | 14 + .../steps/tasks/apigen/execution_checker.py | 268 +++++++++++ .../steps/tasks/apigen/generator.py | 448 ++++++++++++++++++ .../steps/tasks/apigen/semantic_checker.py | 308 ++++++++++++ src/distilabel/steps/tasks/apigen/utils.py | 194 ++++++++ .../tasks/templates/apigen/generator.jinja2 | 10 + .../templates/apigen/semantic_checker.jinja2 | 13 + .../utils/mkdocs/components_gallery.py | 2 + .../integration/test_generator_and_sampler.py | 55 +++ .../steps/generators/test_data_sampler.py | 45 ++ tests/unit/steps/tasks/apigen/__init__.py | 14 + .../apigen/_sample_lib/final_velocity.py | 27 ++ .../tasks/apigen/_sample_lib/get_value.py | 33 ++ .../unit/steps/tasks/apigen/_sample_module.py | 47 ++ .../tasks/apigen/test_execution_checker.py | 140 ++++++ .../unit/steps/tasks/apigen/test_generator.py | 172 +++++++ .../tasks/apigen/test_semantic_checker.py | 113 +++++ tests/unit/steps/tasks/apigen/test_utils.py | 77 +++ 30 files changed, 2756 insertions(+), 7 deletions(-) create mode 100644 docs/assets/tutorials-assets/overview-apigen.jpg create mode 100644 docs/sections/pipeline_samples/papers/apigen.md create mode 100644 examples/lib_apigen.py create mode 100644 examples/pipeline_apigen.py create mode 100644 src/distilabel/llms/_dummy.py create mode 100644 src/distilabel/steps/generators/data_sampler.py create mode 100644 src/distilabel/steps/tasks/apigen/__init__.py create mode 100644 src/distilabel/steps/tasks/apigen/execution_checker.py create mode 100644 src/distilabel/steps/tasks/apigen/generator.py create mode 100644 src/distilabel/steps/tasks/apigen/semantic_checker.py create mode 100644 src/distilabel/steps/tasks/apigen/utils.py create mode 100644 src/distilabel/steps/tasks/templates/apigen/generator.jinja2 create mode 100644 src/distilabel/steps/tasks/templates/apigen/semantic_checker.jinja2 create mode 100644 tests/integration/test_generator_and_sampler.py create mode 100644 tests/unit/steps/generators/test_data_sampler.py create mode 100644 tests/unit/steps/tasks/apigen/__init__.py create mode 100644 tests/unit/steps/tasks/apigen/_sample_lib/final_velocity.py create mode 100644 tests/unit/steps/tasks/apigen/_sample_lib/get_value.py create mode 100644 tests/unit/steps/tasks/apigen/_sample_module.py create mode 100644 tests/unit/steps/tasks/apigen/test_execution_checker.py create mode 100644 tests/unit/steps/tasks/apigen/test_generator.py create mode 100644 tests/unit/steps/tasks/apigen/test_semantic_checker.py create mode 100644 tests/unit/steps/tasks/apigen/test_utils.py diff --git a/docs/assets/tutorials-assets/overview-apigen.jpg b/docs/assets/tutorials-assets/overview-apigen.jpg new file mode 100644 index 0000000000000000000000000000000000000000..61deefac9aca1b784bdbf60fdf5e5904d42fc886 GIT binary patch literal 408979 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

85KHitX<0y)!T7UpI8;G|as^}&g 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 {1>RVq|f|Upu@ktXD5MsfKMQB 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 ## Examples diff --git a/docs/sections/pipeline_samples/papers/apigen.md b/docs/sections/pipeline_samples/papers/apigen.md new file mode 100644 index 0000000000..8cb034c182 --- /dev/null +++ b/docs/sections/pipeline_samples/papers/apigen.md @@ -0,0 +1,239 @@ +--- +hide: toc +--- + +# Create Function-Calling datasets with APIGen + +This example will introduce [APIGen: Automated Pipeline for Generating Verifiable and Diverse Function-Calling Datasets](https://arxiv.org/abs/2406.18518), a data generation pipeline designed to synthesize verifiable high-quality datasets for function-calling applications. + +## Replication + +The following figure showcases the APIGen framework: + +![APIGen framework](../../../assets/tutorials-assets/overview-apigen.jpg) + +Now, let's walk through the key steps illustrated in the figure: + +- [`DataSampler`](https://distilabel.argilla.io/dev/components-gallery/step/datasampler/): With the help of this step and the original [Salesforce/xlam-function-calling-60k](https://huggingface.co/datasets/Salesforce/xlam-function-calling-60k) we are getting the Seed QA Data Sampler for the prompt template. + +- [`APIGenGenerator`](https://distilabel.argilla.io/dev/components-gallery/task/apigengenerator/): This step does the job of the *Query-Answer Generator*, including the format checker from *Stage 1: Format Checker* thanks to the structured output generation. + +- [`APIGenExecutionChecker`](https://distilabel.argilla.io/dev/components-gallery/task/apigenexecutionchecker/): This step is in charge of the *Stage 2: Execution Checker*. + +- [`APIGenSemanticChecker`](https://distilabel.argilla.io/dev/components-gallery/task/apigensemanticchecker/): Step in charge of running *Stage 3: Semantic Checker*, can use the same or a different LLM, we are using the same as in [`APIGenGenerator`](https://distilabel.argilla.io/dev/components-gallery/task/apigengenerator/) step. + +The current implementation hasn't utilized the *Diverse Prompt Library*. To incorporate it, one could either adjust the prompt template within the [`APIGenGenerator`](https://distilabel.argilla.io/dev/components-gallery/task/apigengenerator/) or develop a new sampler specifically for this purpose. As for the *API Sampler*, while no specific data is shared here, we've created illustrative examples to demonstrate the pipeline's functionality. These examples represent a mix of data that could be used to replicate the sampler's output. + +## Data preparation + +The original paper tells about the data they used and give some hints, but nothing was shared. In this example, we will write a bunch of examples by hand to showcase how this pipeline can be built. + +Assume we have the following function names, and corresponding descriptions of their behaviour: + +```python +data = [ + { + "func_name": "final_velocity", + "func_desc": "Calculates the final velocity of an object given its initial velocity, acceleration, and time.", + }, + { + "func_name": "permutation_count", + "func_desc": "Calculates the number of permutations of k elements from a set of n elements.", + }, + { + "func_name": "getdivision", + "func_desc": "Divides two numbers by making an API call to a division service.", + }, + { + "func_name": "binary_addition", + "func_desc": "Adds two binary numbers and returns the result as a binary string.", + }, + { + "func_name": "swapi_planet_resource", + "func_desc": "get a specific planets resource", + }, + { + "func_name": "disney_character", + "func_desc": "Find a specific character using this endpoint", + } +] +``` + +The original paper refers to both python functions and APIs, but we will make use of python functions exclusively for simplicity. In order to execute and check this functions/APIs, we need access to the code, which we have moved to a python file: [lib_apigen.py](../../../../examples/lib_apigen.py). All this functions are executable, but we also need access to their *tool* representation. For this, we will make use of transformers' *get_json_schema* function[^1]. + +[^1]: Read this nice blog post for more information on tools and the reasoning behind `get_json_schema`: [Tool Use, Unified](https://huggingface.co/blog/unified-tool-use). + +We have all the machinery prepared in our libpath, except from the *tool* definition. With the help of our helper function `load_module_from_path` we will load this python module, collect all the tools, and add them to each row in our `data` variable. + +```python +from distilabel.steps.tasks.apigen.utils import load_module_from_path + +libpath_module = load_module_from_path(libpath) +tools = getattr(libpath_module, "get_tools")() # call get_tools() + +for row in data: + # The tools should have a mix where both the correct and irrelevant tools are present. + row.update({"tools": [tools[row["func_name"]]]}) +``` + +Now we have all the necessary data for our prompt. Additionally, we will make use of the original dataset as few-shot examples to enhance the model: + +```python +ds_og = ( + load_dataset("Salesforce/xlam-function-calling-60k", split="train") + .shuffle(seed=42) + .select(range(500)) + .to_list() +) +``` + +We have just loaded a subset and transformed it to a list of dictionaries, as we will use it in the [`DataSampler`](https://distilabel.argilla.io/dev/components-gallery/steps/datasampler/) `GeneratorStep`, grabbing random examples from the original dataset. + +## Building the Pipeline + +Now that we've walked through each component, it's time to see how it all comes together, here's the Pipeline code: + +```python +with Pipeline(name="apigen-example") as pipeline: + loader_seeds = LoadDataFromDicts(data=data) # (1) + + sampler = DataSampler( # (2) + data=ds_og, + size=2, + samples=len(data), + batch_size=8, + ) + + prep_examples = PrepareExamples() # This step will add the 'examples' column + + combine_steps = CombineOutputs() # (3) + + model_id = "meta-llama/Meta-Llama-3.1-70B-Instruct" + llm=InferenceEndpointsLLM( # (4) + model_id=model_id, + tokenizer_id=model_id, + generation_kwargs={ + "temperature": 0.7, + "max_new_tokens": 2048, + }, + ) + apigen = APIGenGenerator( # (5) + llm=llm, + use_default_structured_output=True, + ) + + execution_checker = APIGenExecutionChecker(libpath=str(libpath)) # (6) + semantic_checker = APIGenSemanticChecker(llm=llm) # (7) + + sampler >> prep_examples + ( + [loader_seeds, prep_examples] + >> combine_steps + >> apigen + >> execution_checker + >> semantic_checker + ) +``` + +1. Load the data seeds we are going to use to generate our function calling dataset. + +2. The `DataSampler` together with `PrepareExamples` will be used to help us create the few-shot +examples from the original dataset to be fed in our prompt. + +3. Combine both columns to obtain a single stream of data + +4. Will reuse the same LLM for the generation and the semantic checks. + +5. Creates the `query` and `answers` that will be used together with the `tools` to fine-tune a new model. Will generate the structured outputs to ensure we have valid JSON formatted answers. + +6. Adds columns `keep_row_after_execution_check` and `execution_result`. + +7. Adds columns `keep_row_after_semantic_check` and `thought`. + +## Script and final dataset + +To see all the pieces in place, take a look at the full pipeline, as well as an example row that would be generated from this pipeline. + +??? Run + + ```python + python examples/pipeline_apigen.py + ``` + +```python title="pipeline_apigen.py" +--8<-- "examples/pipeline_apigen.py" +``` + +Example row: + +```json +{ + "func_name": "final_velocity", + "func_desc": "Calculates the final velocity of an object given its initial velocity, acceleration, and time.", + "tools": [ + { + "function": { + "description": "Calculates the final velocity of an object given its initial velocity, acceleration, and time.", + "name": "final_velocity", + "parameters": { + "properties": { + "acceleration": { + "description": "The acceleration of the object.", + "type": "number" + }, + "initial_velocity": { + "description": "The initial velocity of the object.", + "type": "number" + }, + "time": { + "description": "The time elapsed.", + "type": "number" + } + }, + "required": [ + "initial_velocity", + "acceleration", + "time" + ], + "type": "object" + } + }, + "type": "function" + } + ], + "examples": "## Query:\nRetrieve the first 15 comments for post ID '12345' from the Tokapi mobile API.\n## Answers:\n[{\"name\": \"v1_post_post_id_comments\", \"arguments\": {\"post_id\": \"12345\", \"count\": 15}}]\n\n## Query:\nRetrieve the detailed recipe for the cake with ID 'cake101'.\n## Answers:\n[{\"name\": \"detailed_cake_recipe_by_id\", \"arguments\": {\"is_id\": \"cake101\"}}]\n\n## Query:\nWhat are the frequently asked questions and their answers for Coca-Cola Company? Also, what are the suggested tickers based on Coca-Cola Company?\n## Answers:\n[{\"name\": \"symbols_faq\", \"arguments\": {\"ticker_slug\": \"KO\"}}, {\"name\": \"symbols_suggested\", \"arguments\": {\"ticker_slug\": \"KO\"}}]", + "query": "What would be the final velocity of an object that starts at rest and accelerates at 9.8 m/s^2 for 10 seconds.", + "answers": "[{\"arguments\": {\"acceleration\": \"9.8\", \"initial_velocity\": \"0\", \"time\": \"10\"}, \"name\": \"final_velocity\"}]", + "distilabel_metadata": { + "raw_input_a_p_i_gen_generator_0": [ + { + "content": "You are a data labeler. Your responsibility is to generate a set of diverse queries and corresponding answers for the given functions in JSON format.\n\nConstruct queries and answers that exemplify how to use these functions in a practical scenario. Include in each query specific, plausible values for each parameter. For instance, if the function requires a date, use a typical and reasonable date.\n\nEnsure the query:\n- Is clear and concise\n- Demonstrates typical use cases\n- Includes all necessary parameters in a meaningful way. For numerical parameters, it could be either numbers or words\n- Across a variety level of difficulties, ranging from beginner and advanced use cases\n- The corresponding result's parameter types and ranges match with the function's descriptions\n\nEnsure the answer:\n- Is a list of function calls in JSON format\n- The length of the answer list should be equal to the number of requests in the query\n- Can solve all the requests in the query effectively", + "role": "system" + }, + { + "content": "Here are examples of queries and the corresponding answers for similar functions:\n## Query:\nRetrieve the first 15 comments for post ID '12345' from the Tokapi mobile API.\n## Answers:\n[{\"name\": \"v1_post_post_id_comments\", \"arguments\": {\"post_id\": \"12345\", \"count\": 15}}]\n\n## Query:\nRetrieve the detailed recipe for the cake with ID 'cake101'.\n## Answers:\n[{\"name\": \"detailed_cake_recipe_by_id\", \"arguments\": {\"is_id\": \"cake101\"}}]\n\n## Query:\nWhat are the frequently asked questions and their answers for Coca-Cola Company? Also, what are the suggested tickers based on Coca-Cola Company?\n## Answers:\n[{\"name\": \"symbols_faq\", \"arguments\": {\"ticker_slug\": \"KO\"}}, {\"name\": \"symbols_suggested\", \"arguments\": {\"ticker_slug\": \"KO\"}}]\n\nNote that the query could be interpreted as a combination of several independent requests.\n\nBased on these examples, generate 1 diverse query and answer pairs for the function `final_velocity`.\nThe detailed function description is the following:\nCalculates the final velocity of an object given its initial velocity, acceleration, and time.\n\nThese are the available tools to help you:\n[{'type': 'function', 'function': {'name': 'final_velocity', 'description': 'Calculates the final velocity of an object given its initial velocity, acceleration, and time.', 'parameters': {'type': 'object', 'properties': {'initial_velocity': {'type': 'number', 'description': 'The initial velocity of the object.'}, 'acceleration': {'type': 'number', 'description': 'The acceleration of the object.'}, 'time': {'type': 'number', 'description': 'The time elapsed.'}}, 'required': ['initial_velocity', 'acceleration', 'time']}}}]\n\nThe output MUST strictly adhere to the following JSON format, and NO other text MUST be included:\n```json\n[\n {\n \"query\": \"The generated query.\",\n \"answers\": [\n {\n \"name\": \"api_name\",\n \"arguments\": {\n \"arg_name\": \"value\"\n ... (more arguments as required)\n }\n },\n ... (more API calls as required)\n ]\n }\n]\n```\n\nNow please generate 1 diverse query and answer pairs following the above format.", + "role": "user" + } + ], + "raw_input_a_p_i_gen_semantic_checker_0": [ + { + "content": "As a data quality evaluator, you must assess the alignment between a user query, corresponding function calls, and their execution results.\nThese function calls and results are generated by other models, and your task is to ensure these results accurately reflect the user\u2019s intentions.\n\nDo not pass if:\n1. The function call does not align with the query\u2019s objective, or the input arguments appear incorrect.\n2. The function call and arguments are not properly chosen from the available functions.\n3. The number of function calls does not correspond to the user\u2019s intentions.\n4. The execution results are irrelevant and do not match the function\u2019s purpose.\n5. The execution results contain errors or reflect that the function calls were not executed successfully.", + "role": "system" + }, + { + "content": "Given Information:\n- All Available Functions:\nCalculates the final velocity of an object given its initial velocity, acceleration, and time.\n- User Query: What would be the final velocity of an object that starts at rest and accelerates at 9.8 m/s^2 for 10 seconds.\n- Generated Function Calls: [{\"arguments\": {\"acceleration\": \"9.8\", \"initial_velocity\": \"0\", \"time\": \"10\"}, \"name\": \"final_velocity\"}]\n- Execution Results: ['9.8']\n\nNote: The query may have multiple intentions. Functions may be placeholders, and execution results may be truncated due to length, which is acceptable and should not cause a failure.\n\nThe main decision factor is wheather the function calls accurately reflect the query's intentions and the function descriptions.\nProvide your reasoning in the thought section and decide if the data passes (answer yes or no).\nIf not passing, concisely explain your reasons in the thought section; otherwise, leave this section blank.\n\nYour response MUST strictly adhere to the following JSON format, and NO other text MUST be included.\n```\n{\n \"thought\": \"Concisely describe your reasoning here\",\n \"passes\": \"yes\" or \"no\"\n}\n```\n", + "role": "user" + } + ], + "raw_output_a_p_i_gen_generator_0": "{\"pairs\": [\n {\n \"answers\": [\n {\n \"arguments\": {\n \"acceleration\": \"9.8\",\n \"initial_velocity\": \"0\",\n \"time\": \"10\"\n },\n \"name\": \"final_velocity\"\n }\n ],\n \"query\": \"What would be the final velocity of an object that starts at rest and accelerates at 9.8 m/s^2 for 10 seconds.\"\n }\n]}", + "raw_output_a_p_i_gen_semantic_checker_0": "{\n \"thought\": \"\",\n \"passes\": \"yes\"\n}" + }, + "model_name": "meta-llama/Meta-Llama-3.1-70B-Instruct", + "keep_row_after_execution_check": true, + "execution_result": [ + "9.8" + ], + "thought": "", + "keep_row_after_semantic_check": true +} +``` diff --git a/examples/lib_apigen.py b/examples/lib_apigen.py new file mode 100644 index 0000000000..d49f414e68 --- /dev/null +++ b/examples/lib_apigen.py @@ -0,0 +1,146 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import Any, Dict, Optional + + +def final_velocity(initial_velocity: float, acceleration: float, time: float) -> int: + """Calculates the final velocity of an object given its initial velocity, acceleration, and time. + + Args: + initial_velocity: The initial velocity of the object. + acceleration: The acceleration of the object. + time: The time elapsed. + + Returns: + The final velocity + """ + # Tool: + # {"name": "final_velocity", "description": "Calculates the final velocity of an object given its initial velocity, acceleration, and time.", "parameters": {"initial_velocity": {"description": "The initial velocity of the object.", "type": "float"}, "acceleration": {"description": "The acceleration of the object.", "type": "float"}, "time": {"description": "The time elapsed.", "type": "float"}}} + # Answer: + # {"name": "final_velocity", "arguments": {"initial_velocity": 5, "acceleration": 1.5, "time": 40}} + return initial_velocity + acceleration * time + + +def permutation_count(n: int, k: int) -> int: + """Calculates the number of permutations of k elements from a set of n elements. + + Args: + n: The total number of elements in the set. + k: The number of elements to choose for the permutation. + + Returns: + The number of permutations. + """ + # Tool: + # {"name": "permutation_count", "description": "Calculates the number of permutations of k elements from a set of n elements.", "parameters": {"n": {"description": "The total number of elements in the set.", "type": "int"}, "k": {"description": "The number of elements to choose for the permutation.", "type": "int"}}} + # Answer: + # {"name": "permutation_count", "arguments": {"n": 10, "k": 3}} + import math + + return math.factorial(n) / math.factorial(n - k) + + +def getdivision(dividend: int, divisor: int) -> float: + """Divides two numbers by making an API call to a division service. + + Args: + dividend: The dividend in the division operation. + divisor: The divisor in the division operation. + + Returns: + Division of the 2 numbers. + """ + # Tool: + # {"name": "getdivision", "description": "Divides two numbers by making an API call to a division service.", "parameters": {"divisor": {"description": "The divisor in the division operation.", "type": "int", "default": ""}, "dividend": {"description": "The dividend in the division operation.", "type": "int", "default": ""}}} + # Answer: + # {"name": "getdivision", "arguments": {"divisor": 25, "dividend": 100}} + return dividend / divisor + + +def binary_addition(a: str, b: str) -> str: + """Adds two binary numbers and returns the result as a binary string. + + Args: + a: The first binary number. + b: The second binary number. + + Raises: + ValueError: On invalid binary number. + + Returns: + Binary string of the sum of the two numbers. + """ + # Tool: + # {"name": "binary_addition", "description": "Adds two binary numbers and returns the result as a binary string.", "parameters": {"a": {"description": "The first binary number.", "type": "str"}, "b": {"description": "The second binary number.", "type": "str"}}} + # Answer: + # {"name": "binary_addition", "arguments": {"a": "1010", "b": "1101"}} + if not set(a).issubset("01") or not set(b).issubset("01"): + raise ValueError("Invalid binary number") + + return bin(int(a, 2) + int(b, 2))[2:] + + +def _make_request(url: str, params: Optional[Dict[str, Any]] = None): + import requests + + req = requests.get(url, params=params) + return req.json() + + +def swapi_planet_resource(id: str) -> Dict[str, Any]: + """get a specific planets resource + + Args: + id: identifier of the planet + + Returns: + Information about the planet. + """ + # url = "https://swapi.dev/api/planets/1" + return _make_request(r"https://swapi.dev/api/planets/", params={"id": id}) + + +def disney_character(name: str) -> Dict[str, Any]: + """Find a specific character using this endpoint + + Args: + name: Name of the character to look for. + + Returns: + Infrmation about the character. + """ + # Example: + # url = "https://api.disneyapi.dev/character" + # params = {"name": "mulan"} + return _make_request(r"https://api.disneyapi.dev/character", params={"name": name}) + + +def get_lib(): + return { + "swapi_planet_resource": swapi_planet_resource, + "disney_character": disney_character, + "final_velocity": final_velocity, + "permutation_count": permutation_count, + "getdivision": getdivision, + "binary_addition": binary_addition, + } + + +def get_tools() -> Dict[str, Dict[str, Any]]: + """Returns the tool representation of the functions in the library.""" + # TODO: Improve the `get_json_schema`, it fails on a lot of examples. + from transformers.utils import get_json_schema + + return {name: get_json_schema(func) for name, func in get_lib().items()} diff --git a/examples/pipeline_apigen.py b/examples/pipeline_apigen.py new file mode 100644 index 0000000000..e63e16e39e --- /dev/null +++ b/examples/pipeline_apigen.py @@ -0,0 +1,116 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from pathlib import Path + +from datasets import load_dataset + +from distilabel.llms import InferenceEndpointsLLM +from distilabel.pipeline import Pipeline +from distilabel.steps import CombineOutputs, DataSampler, LoadDataFromDicts +from distilabel.steps.tasks import ( + APIGenExecutionChecker, + APIGenGenerator, + APIGenSemanticChecker, +) +from distilabel.steps.tasks.apigen.utils import PrepareExamples, load_module_from_path + +libpath = Path(__file__).parent / "lib_apigen.py" + +data = [ + { + "func_name": "final_velocity", + "func_desc": "Calculates the final velocity of an object given its initial velocity, acceleration, and time.", + }, + { + "func_name": "permutation_count", + "func_desc": "Calculates the number of permutations of k elements from a set of n elements.", + }, + { + "func_name": "getdivision", + "func_desc": "Divides two numbers by making an API call to a division service.", + }, + { + "func_name": "binary_addition", + "func_desc": "Adds two binary numbers and returns the result as a binary string.", + }, + { + "func_name": "swapi_planet_resource", + "func_desc": "get a specific planets resource", + }, + { + "func_name": "disney_character", + "func_desc": "Find a specific character using this endpoint", + }, +] + +libpath_module = load_module_from_path(libpath) +tools = libpath_module.get_tools() # call get_tools() + +# TODO: Add in the tools between 0 and 2 extra tools to make the task more challenging. +for row in data: + # The tools should have a mix where both the correct and irrelevant tools are present. + row.update({"tools": [tools[row["func_name"]]]}) + + +ds_og = ( + load_dataset("Salesforce/xlam-function-calling-60k", split="train") + .shuffle(seed=42) + .select(range(500)) + .to_list() +) + + +with Pipeline(name="APIGenPipeline") as pipeline: + loader_seeds = LoadDataFromDicts(data=data) + sampler = DataSampler( + data=ds_og, + size=2, + samples=len(data), + batch_size=8, + ) + + prep_examples = PrepareExamples() + + model_id = "meta-llama/Meta-Llama-3.1-70B-Instruct" + llm = InferenceEndpointsLLM( + model_id=model_id, + tokenizer_id=model_id, + generation_kwargs={ + "temperature": 0.7, + "max_new_tokens": 2048, + }, + ) + apigen = APIGenGenerator( + llm=llm, + use_default_structured_output=True, + ) + combine_steps = CombineOutputs() + + execution_checker = APIGenExecutionChecker(libpath=str(libpath)) + semantic_checker = APIGenSemanticChecker(llm=llm) + + sampler >> prep_examples + ( + [loader_seeds, prep_examples] + >> combine_steps + >> apigen + >> execution_checker + >> semantic_checker + ) + + +if __name__ == "__main__": + distiset = pipeline.run() + print(distiset["default"]["train"][0]) diff --git a/mkdocs.yml b/mkdocs.yml index 2ef6234673..3c26a6cc17 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -208,6 +208,7 @@ nav: - Instruction Backtranslation: "sections/pipeline_samples/papers/instruction_backtranslation.md" - Prometheus 2: "sections/pipeline_samples/papers/prometheus.md" - UltraFeedback: "sections/pipeline_samples/papers/ultrafeedback.md" + - APIGen: "sections/pipeline_samples/papers/apigen.md" - Examples: - Benchmarking with distilabel: "sections/pipeline_samples/examples/benchmarking_with_distilabel.md" - Structured generation with outlines: "sections/pipeline_samples/examples/llama_cpp_with_outlines.md" diff --git a/src/distilabel/distiset.py b/src/distilabel/distiset.py index b6b31f7a5a..8e52c667d3 100644 --- a/src/distilabel/distiset.py +++ b/src/distilabel/distiset.py @@ -695,8 +695,9 @@ def _grab_citations(dag: "DAG") -> List[str]: for ref in references.values(): try: bibtex_refs.append(get_bibtex(ref)) - except ValueError as e: - print(f"Error: {e}") + except ValueError: + # No need to inform in this case, it's noise + pass except AttributeError as e: print( f"Couldn't obtain the bibtex format for the ref: '{ref}', error: {e}" diff --git a/src/distilabel/llms/_dummy.py b/src/distilabel/llms/_dummy.py new file mode 100644 index 0000000000..740f98cd46 --- /dev/null +++ b/src/distilabel/llms/_dummy.py @@ -0,0 +1,70 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import TYPE_CHECKING, Any, List + +from distilabel.llms.base import LLM, AsyncLLM +from distilabel.llms.mixins.magpie import MagpieChatTemplateMixin + +if TYPE_CHECKING: + from distilabel.llms.typing import GenerateOutput + from distilabel.steps.tasks.typing import FormattedInput + + +class DummyAsyncLLM(AsyncLLM): + structured_output: Any = None + + def load(self) -> None: + pass + + @property + def model_name(self) -> str: + return "test" + + async def agenerate( # type: ignore + self, input: "FormattedInput", num_generations: int = 1 + ) -> "GenerateOutput": + return ["output" for _ in range(num_generations)] + + +class DummySyncLLM(LLM): + structured_output: Any = None + + def load(self) -> None: + super().load() + + @property + def model_name(self) -> str: + return "test" + + def generate( # type: ignore + self, inputs: "FormattedInput", num_generations: int = 1 + ) -> "GenerateOutput": + return [["output" for _ in range(num_generations)] for _ in range(len(inputs))] + + +class DummyMagpieLLM(LLM, MagpieChatTemplateMixin): + def load(self) -> None: + pass + + @property + def model_name(self) -> str: + return "test" + + def generate( + self, inputs: List["FormattedInput"], num_generations: int = 1, **kwargs: Any + ) -> List["GenerateOutput"]: + return [ + ["Hello Magpie" for _ in range(num_generations)] for _ in range(len(inputs)) + ] diff --git a/src/distilabel/steps/__init__.py b/src/distilabel/steps/__init__.py index 79c10a268e..cc1be59f92 100644 --- a/src/distilabel/steps/__init__.py +++ b/src/distilabel/steps/__init__.py @@ -45,6 +45,7 @@ FormatTextGenerationSFT, ) from distilabel.steps.generators.data import LoadDataFromDicts +from distilabel.steps.generators.data_sampler import DataSampler from distilabel.steps.generators.huggingface import ( LoadDataFromDisk, LoadDataFromFileSystem, @@ -83,6 +84,7 @@ "FormatChatGenerationSFT", "FormatTextGenerationSFT", "LoadDataFromDicts", + "DataSampler", "LoadDataFromDisk", "LoadDataFromFileSystem", "LoadDataFromHub", diff --git a/src/distilabel/steps/generators/data_sampler.py b/src/distilabel/steps/generators/data_sampler.py new file mode 100644 index 0000000000..6b2e55bf02 --- /dev/null +++ b/src/distilabel/steps/generators/data_sampler.py @@ -0,0 +1,179 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import random +from itertools import islice +from typing import TYPE_CHECKING, Any, Dict, List + +from pydantic import Field +from typing_extensions import override + +from distilabel.steps.base import GeneratorStep + +if TYPE_CHECKING: + from distilabel.steps.base import GeneratorStepOutput + + +class DataSampler(GeneratorStep): + """Step to sample from a dataset. + + `GeneratorStep` that samples from a dataset and yields it in batches. + This step is useful when you have a pipeline that can benefit from using examples + in the prompts for example as few-shot learning, that can be changing on each row. + For example, you can pass a list of dictionaries with N examples and generate M samples + from it (assuming you have another step loading data, this M should have the same size + as the data being loaded in that step). The size S argument is the number of samples per + row generated, so each example would contain S examples to be used as examples. + + Attributes: + data: The list of dictionaries to sample from. + size: Number of samples per example. For example in a few-shot learning scenario, + the number of few-shot examples that will be generated per example. Defaults to 2. + samples: Number of examples that will be generated by the step in total. + If used with another loader step, this should be the same as the number + of samples in the loader step. Defaults to 100. + + Output columns: + - dynamic (based on the keys found on the first dictionary of the list): The columns + of the dataset. + + Categories: + - load + + Examples: + Sample data from a list of dictionaries: + + ```python + from distilabel.steps import DataSampler + + sampler = DataSampler( + data=[{"sample": f"sample {i}"} for i in range(30)], + samples=10, + size=2, + batch_size=4 + ) + sampler.load() + + result = next(sampler.process()) + # >>> result + # ([{'sample': ['sample 7', 'sample 0']}, {'sample': ['sample 2', 'sample 21']}, {'sample': ['sample 17', 'sample 12']}, {'sample': ['sample 2', 'sample 14']}], False) + ``` + + Pipeline with a loader and a sampler combined in a single stream: + + ```python + from datasets import load_dataset + + from distilabel.steps import LoadDataFromDicts, DataSampler + from distilabel.steps.tasks.apigen.utils import PrepareExamples + from distilabel.pipeline import Pipeline + + ds = ( + load_dataset("Salesforce/xlam-function-calling-60k", split="train") + .shuffle(seed=42) + .select(range(500)) + .to_list() + ) + data = [ + { + "func_name": "final_velocity", + "func_desc": "Calculates the final velocity of an object given its initial velocity, acceleration, and time.", + }, + { + "func_name": "permutation_count", + "func_desc": "Calculates the number of permutations of k elements from a set of n elements.", + }, + { + "func_name": "getdivision", + "func_desc": "Divides two numbers by making an API call to a division service.", + }, + ] + with Pipeline(name="APIGenPipeline") as pipeline: + loader_seeds = LoadDataFromDicts(data=data) + sampler = DataSampler( + data=ds, + size=2, + samples=len(data), + batch_size=8, + ) + prep_examples = PrepareExamples() + + sampler >> prep_examples + ( + [loader_seeds, prep_examples] + >> combine_steps + ) + # Now we have a single stream of data with the loader and the sampler data + ``` + """ + + data: List[Dict[str, Any]] = Field(default_factory=list, exclude=True) + size: int = Field( + default=2, + description=( + "Number of samples per example. For example in a few-shot learning scenario, the number " + "of few-shot examples that will be generated per example." + ), + ) + samples: int = Field( + default=100, + description=( + "Number of examples that will be generated by the step in total. " + "If used with another loader step, this should be the same as the number of " + "samples in the loader step." + ), + ) + + @override + def process(self, offset: int = 0) -> "GeneratorStepOutput": # type: ignore + """Yields batches from a list of dictionaries. + + Args: + offset: The offset to start the generation from. Defaults to `0`. + + Yields: + A list of Python dictionaries as read from the inputs (propagated in batches) + and a flag indicating whether the yield batch is the last one. + """ + + total_samples = 0 + + while total_samples < self.samples: + batch = [] + bs = min(self.batch_size, self.samples - total_samples) + for _ in range(self.batch_size): + choices = random.choices(self.data, k=self.size) + choices = self._transform_data(choices) + batch.extend(choices) + total_samples += bs + batch = list(islice(batch, bs)) + yield (batch, True if total_samples >= self.samples else False) + batch = [] + + @staticmethod + def _transform_data(data: List[Dict[str, Any]]) -> List[Dict[str, Any]]: + if not data: + return [] + + result = {key: [] for key in data[0].keys()} + + for item in data: + for key, value in item.items(): + result[key].append(value) + + return [result] + + @property + def outputs(self) -> List[str]: + return list(self.data[0].keys()) diff --git a/src/distilabel/steps/generators/huggingface.py b/src/distilabel/steps/generators/huggingface.py index f6e782a751..721b3d4081 100644 --- a/src/distilabel/steps/generators/huggingface.py +++ b/src/distilabel/steps/generators/huggingface.py @@ -219,11 +219,11 @@ def _get_dataset_num_examples(self) -> int: Returns: The number of examples in the dataset. """ - return ( - self._dataset_info[self.config if self.config else "default"] - .splits[self.split] - .num_examples - ) + default_config = self.config + if not default_config: + default_config = list(self._dataset_info.keys())[0] + + return self._dataset_info[default_config].splits[self.split].num_examples def _get_dataset_columns(self) -> List[str]: """Get the columns of the dataset, based on the `config` runtime parameter provided. diff --git a/src/distilabel/steps/tasks/__init__.py b/src/distilabel/steps/tasks/__init__.py index c7b7d72395..065567a572 100644 --- a/src/distilabel/steps/tasks/__init__.py +++ b/src/distilabel/steps/tasks/__init__.py @@ -12,6 +12,9 @@ # See the License for the specific language governing permissions and # limitations under the License. +from distilabel.steps.tasks.apigen.execution_checker import APIGenExecutionChecker +from distilabel.steps.tasks.apigen.generator import APIGenGenerator +from distilabel.steps.tasks.apigen.semantic_checker import APIGenSemanticChecker from distilabel.steps.tasks.argilla_labeller import ArgillaLabeller from distilabel.steps.tasks.base import GeneratorTask, Task from distilabel.steps.tasks.complexity_scorer import ComplexityScorer @@ -54,6 +57,9 @@ "GeneratorTask", "Task", "ArgillaLabeller", + "APIGenExecutionChecker", + "APIGenGenerator", + "APIGenSemanticChecker", "ComplexityScorer", "EvolInstruct", "EvolComplexity", diff --git a/src/distilabel/steps/tasks/apigen/__init__.py b/src/distilabel/steps/tasks/apigen/__init__.py new file mode 100644 index 0000000000..20ce00bda7 --- /dev/null +++ b/src/distilabel/steps/tasks/apigen/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + diff --git a/src/distilabel/steps/tasks/apigen/execution_checker.py b/src/distilabel/steps/tasks/apigen/execution_checker.py new file mode 100644 index 0000000000..7d30dd1f75 --- /dev/null +++ b/src/distilabel/steps/tasks/apigen/execution_checker.py @@ -0,0 +1,268 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# - Try to import the function from a given module +# - If function, try to import it and run it +# - If fails, track the error message, and return it + +import inspect +import json +from pathlib import Path +from typing import TYPE_CHECKING, Callable, Union + +from pydantic import Field, PrivateAttr +from typing_extensions import override + +from distilabel.steps.base import Step, StepInput +from distilabel.steps.tasks.apigen.utils import ( + execute_from_response, + load_module_from_path, +) + +if TYPE_CHECKING: + from types import ModuleType + + from distilabel.steps.typing import StepColumns, StepOutput + + +class APIGenExecutionChecker(Step): + """Executes the generated function calls. + + This step checks if a given answer from a model as generated by `APIGenGenerator` + can be executed against the given library (given by `libpath`, which is a string + pointing to a python .py file with functions). + + Attributes: + libpath: The path to the library where we will retrieve the functions. + It can also point to a folder with the functions. In this case, the folder + layout should be a folder with .py files, each containing a single function, + the name of the function being the same as the filename. + check_is_dangerous: Bool to exclude some potentially dangerous functions, it contains + some heuristics found while testing. This functions can run subprocesses, deal with + the OS, or have other potentially dangerous operations. Defaults to True. + + Input columns: + - answers (`str`): List with arguments to be passed to the function, + dumped as a string from a list of dictionaries. Should be loaded using + `json.loads`. + + Output columns: + - keep_row_after_execution_check (`bool`): Whether the function should be kept or not. + - execution_result (`str`): The result from executing the function. + + Categories: + - filtering + - execution + + References: + - [APIGen: Automated Pipeline for Generating Verifiable and Diverse Function-Calling Datasets](https://arxiv.org/abs/2406.18518) + - [Salesforce/xlam-function-calling-60k](https://huggingface.co/datasets/Salesforce/xlam-function-calling-60k) + + Examples: + Execute a function from a given library with the answer from an LLM: + + ```python + from distilabel.steps.tasks import APIGenExecutionChecker + + # For the libpath you can use as an example the file at the tests folder: + # ../distilabel/tests/unit/steps/tasks/apigen/_sample_module.py + task = APIGenExecutionChecker( + libpath="../distilabel/tests/unit/steps/tasks/apigen/_sample_module.py", + ) + task.load() + + res = next( + task.process( + [ + { + "answers": [ + { + "arguments": { + "initial_velocity": 0.2, + "acceleration": 0.1, + "time": 0.5, + }, + "name": "final_velocity", + } + ], + } + ] + ) + ) + res + #[{'answers': [{'arguments': {'initial_velocity': 0.2, 'acceleration': 0.1, 'time': 0.5}, 'name': 'final_velocity'}], 'keep_row_after_execution_check': True, 'execution_result': ['0.25']}] + ``` + """ + + libpath: str = Field( + default=..., + description=( + "The path to the library where we will retrieve the functions, " + "or a folder with python files named the same as the functions they contain.", + ), + ) + check_is_dangerous: bool = Field( + default=True, + description=( + "Bool to exclude some potentially dangerous functions, it contains " + "some heuristics found while testing. This functions can run subprocesses, " + "deal with the OS, or have other potentially dangerous operations.", + ), + ) + + _toolbox: Union["ModuleType", None] = PrivateAttr(None) + + def load(self) -> None: + """Loads the library where the functions will be extracted from.""" + super().load() + if Path(self.libpath).suffix == ".py": + self._toolbox = load_module_from_path(self.libpath) + + def unload(self) -> None: + self._toolbox = None + + @property + def inputs(self) -> "StepColumns": + """The inputs for the task are those found in the original dataset.""" + return ["answers"] + + @property + def outputs(self) -> "StepColumns": + """The outputs are the columns required by `APIGenGenerator` task.""" + return ["keep_row_after_execution_check", "execution_result"] + + def _get_function(self, function_name: str) -> Callable: + """Retrieves the function from the toolbox. + + Args: + function_name: The name of the function to retrieve. + + Returns: + Callable: The function to be executed. + """ + if self._toolbox: + return getattr(self._toolbox, function_name, None) + try: + toolbox = load_module_from_path( + str(Path(self.libpath) / f"{function_name}.py") + ) + return getattr(toolbox, function_name, None) + except FileNotFoundError: + return None + except Exception as e: + self._logger.warning(f"Error loading function '{function_name}': {e}") + return None + + def _is_dangerous(self, function: Callable) -> bool: + """Checks if a function is dangerous to remove it. + Contains a list of heuristics to avoid executing possibly dangerous functions. + """ + source_code = inspect.getsource(function) + # We don't want to execute functions that use subprocess + if ( + ("subprocess." in source_code) + or ("os.system(" in source_code) + or ("input(" in source_code) + # Avoiding threading + or ("threading.Thread(" in source_code) + or ("exec(" in source_code) + # Avoiding argparse (not sure why) + or ("argparse.ArgumentParser(" in source_code) + # Avoiding logging changing the levels to not mess with the logs + or (".setLevel(" in source_code) + # Don't run a test battery + or ("unittest.main(" in source_code) + # Avoid exiting the program + or ("sys.exit(" in source_code) + or ("exit(" in source_code) + or ("raise SystemExit(" in source_code) + or ("multiprocessing.Pool(" in source_code) + ): + return True + return False + + @override + def process(self, inputs: StepInput) -> "StepOutput": + """Checks the answer to see if it can be executed. + Captures the possible errors and returns them. + + If a single example is provided, it is copied to avoid raising an error. + + Args: + inputs: A list of dictionaries with the input data. + + Yields: + A list of dictionaries with the output data. + """ + for input in inputs: + output = [] + if input["answers"]: + answers = json.loads(input["answers"]) + else: + input.update( + **{ + "keep_row_after_execution_check": False, + "execution_result": ["No answers were provided."], + } + ) + continue + for answer in answers: + if answer is None: + output.append( + { + "keep": False, + "execution_result": "Nothing was generated for this answer.", + } + ) + continue + + function_name = answer.get("name", None) + arguments = answer.get("arguments", None) + + self._logger.debug( + f"Executing function '{function_name}' with arguments: {arguments}" + ) + function = self._get_function(function_name) + + if self.check_is_dangerous: + if function and self._is_dangerous(function): + function = None + + if function is None: + output.append( + { + "keep": False, + "execution_result": f"Function '{function_name}' not found.", + } + ) + else: + execution = execute_from_response(function, arguments) + output.append( + { + "keep": execution["keep"], + "execution_result": execution["execution_result"], + } + ) + # We only consider a good response if all the answers were executed successfully, + # but keep the reasons for further review if needed. + input.update( + **{ + "keep_row_after_execution_check": all( + o["keep"] is True for o in output + ), + "execution_result": [o["execution_result"] for o in output], + } + ) + + yield inputs diff --git a/src/distilabel/steps/tasks/apigen/generator.py b/src/distilabel/steps/tasks/apigen/generator.py new file mode 100644 index 0000000000..c1c691e378 --- /dev/null +++ b/src/distilabel/steps/tasks/apigen/generator.py @@ -0,0 +1,448 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import importlib.resources as importlib_resources +import json +import random +from typing import TYPE_CHECKING, Any, Callable, Dict, Final, List, Union + +import orjson +from jinja2 import Template +from pydantic import PrivateAttr +from typing_extensions import override + +from distilabel.steps.tasks.apigen.utils import remove_fences +from distilabel.steps.tasks.base import Task + +if TYPE_CHECKING: + from distilabel.steps.tasks.typing import ChatType + from distilabel.steps.typing import StepColumns + + +SYSTEM_PROMPT_API_GEN: Final[str] = """\ +You are a data labeler. Your responsibility is to generate a set of diverse queries and corresponding answers for the given functions in JSON format. + +Construct queries and answers that exemplify how to use these functions in a practical scenario. Include in each query specific, plausible values for each parameter. For instance, if the function requires a date, use a typical and reasonable date. + +Ensure the query: +- Is clear and concise +- Demonstrates typical use cases +- Includes all necessary parameters in a meaningful way. For numerical parameters, it could be either numbers or words +- Across a variety level of difficulties, ranging from beginner and advanced use cases +- The corresponding result's parameter types and ranges match with the function's descriptions + +Ensure the answer: +- Is a list of function calls in JSON format +- The length of the answer list should be equal to the number of requests in the query +- Can solve all the requests in the query effectively""" + + +class APIGenGenerator(Task): + """Generate queries and answers for the given functions in JSON format. + + The `APIGenGenerator` is inspired by the APIGen pipeline, which was designed to generate + verifiable and diverse function-calling datasets. The task generates a set of diverse queries + and corresponding answers for the given functions in JSON format. + + Attributes: + system_prompt: The system prompt to guide the user in the generation of queries and answers. + use_tools: Whether to use the tools available in the prompt to generate the queries and answers. + In case the tools are given in the input, they will be added to the prompt. + number: The number of queries to generate. It can be a list, where each number will be + chosen randomly, or a dictionary with the number of queries and the probability of each. + I.e: `number=1`, `number=[1, 2, 3]`, `number={1: 0.5, 2: 0.3, 3: 0.2}` are all valid inputs. + It corresponds to the number of parallel queries to generate. + use_default_structured_output: Whether to use the default structured output or not. + + Input columns: + - examples (`str`): Examples used as few shots to guide the model. + - func_name (`str`): Name for the function to generate. + - func_desc (`str`): Description of what the function should do. + - tools (`str`): JSON formatted string containing the tool representation of the function. + + Output columns: + - query (`str`): The list of queries. + - answers (`str`): JSON formatted string with the list of answers, containing the info as + a dictionary to be passed to the functions. + + Categories: + - text-generation + + References: + - [APIGen: Automated Pipeline for Generating Verifiable and Diverse Function-Calling Datasets](https://arxiv.org/abs/2406.18518) + - [Salesforce/xlam-function-calling-60k](https://huggingface.co/datasets/Salesforce/xlam-function-calling-60k) + + Examples: + Generate without structured output (original implementation): + + ```python + from distilabel.steps.tasks import ApiGenGenerator + from distilabel.llms import InferenceEndpointsLLM + + llm=InferenceEndpointsLLM( + model_id="meta-llama/Meta-Llama-3.1-70B-Instruct", + generation_kwargs={ + "temperature": 0.7, + "max_new_tokens": 1024, + }, + ) + apigen = ApiGenGenerator( + use_default_structured_output=False, + llm=llm + ) + apigen.load() + + res = next( + apigen.process( + [ + { + "examples": 'QUERY:\nWhat is the binary sum of 10010 and 11101?\nANSWER:\n[{"name": "binary_addition", "arguments": {"a": "10010", "b": "11101"}}]', + "func_name": "getrandommovie", + "func_desc": "Returns a list of random movies from a database by calling an external API." + } + ] + ) + ) + res + # [{'examples': 'QUERY:\nWhat is the binary sum of 10010 and 11101?\nANSWER:\n[{"name": "binary_addition", "arguments": {"a": "10010", "b": "11101"}}]', + # 'number': 1, + # 'func_name': 'getrandommovie', + # 'func_desc': 'Returns a list of random movies from a database by calling an external API.', + # 'queries': ['I want to watch a movie tonight, can you recommend a random one from your database?', + # 'Give me 5 random movie suggestions from your database to plan my weekend.'], + # 'answers': [[{'name': 'getrandommovie', 'arguments': {}}], + # [{'name': 'getrandommovie', 'arguments': {}}, + # {'name': 'getrandommovie', 'arguments': {}}, + # {'name': 'getrandommovie', 'arguments': {}}, + # {'name': 'getrandommovie', 'arguments': {}}, + # {'name': 'getrandommovie', 'arguments': {}}]], + # 'raw_input_api_gen_generator_0': [{'role': 'system', + # 'content': "You are a data labeler. Your responsibility is to generate a set of diverse queries and corresponding answers for the given functions in JSON format.\n\nConstruct queries and answers that exemplify how to use these functions in a practical scenario. Include in each query specific, plausible values for each parameter. For instance, if the function requires a date, use a typical and reasonable date.\n\nEnsure the query:\n- Is clear and concise\n- Demonstrates typical use cases\n- Includes all necessary parameters in a meaningful way. For numerical parameters, it could be either numbers or words\n- Across a variety level of difficulties, ranging from beginner and advanced use cases\n- The corresponding result's parameter types and ranges match with the function's descriptions\n\nEnsure the answer:\n- Is a list of function calls in JSON format\n- The length of the answer list should be equal to the number of requests in the query\n- Can solve all the requests in the query effectively"}, + # {'role': 'user', + # 'content': 'Here are examples of queries and the corresponding answers for similar functions:\nQUERY:\nWhat is the binary sum of 10010 and 11101?\nANSWER:\n[{"name": "binary_addition", "arguments": {"a": "10010", "b": "11101"}}]\n\nNote that the query could be interpreted as a combination of several independent requests.\nBased on these examples, generate 2 diverse query and answer pairs for the function `getrandommovie`\nThe detailed function description is the following:\nReturns a list of random movies from a database by calling an external API.\n\nThe output MUST strictly adhere to the following JSON format, and NO other text MUST be included:\n```json\n[\n {\n "query": "The generated query.",\n "answers": [\n {\n "name": "api_name",\n "arguments": {\n "arg_name": "value"\n ... (more arguments as required)\n }\n },\n ... (more API calls as required)\n ]\n }\n]\n```\n\nNow please generate 2 diverse query and answer pairs following the above format.'}]}, + # 'model_name': 'meta-llama/Meta-Llama-3.1-70B-Instruct'}] + ``` + + Generate with structured output: + + ```python + from distilabel.steps.tasks import ApiGenGenerator + from distilabel.llms import InferenceEndpointsLLM + + llm=InferenceEndpointsLLM( + model_id="meta-llama/Meta-Llama-3.1-70B-Instruct", + tokenizer="meta-llama/Meta-Llama-3.1-70B-Instruct", + generation_kwargs={ + "temperature": 0.7, + "max_new_tokens": 1024, + }, + ) + apigen = ApiGenGenerator( + use_default_structured_output=True, + llm=llm + ) + apigen.load() + + res_struct = next( + apigen.process( + [ + { + "examples": 'QUERY:\nWhat is the binary sum of 10010 and 11101?\nANSWER:\n[{"name": "binary_addition", "arguments": {"a": "10010", "b": "11101"}}]', + "func_name": "getrandommovie", + "func_desc": "Returns a list of random movies from a database by calling an external API." + } + ] + ) + ) + res_struct + # [{'examples': 'QUERY:\nWhat is the binary sum of 10010 and 11101?\nANSWER:\n[{"name": "binary_addition", "arguments": {"a": "10010", "b": "11101"}}]', + # 'number': 1, + # 'func_name': 'getrandommovie', + # 'func_desc': 'Returns a list of random movies from a database by calling an external API.', + # 'queries': ["I'm bored and want to watch a movie. Can you suggest some movies?", + # "My family and I are planning a movie night. We can't decide on what to watch. Can you suggest some random movie titles?"], + # 'answers': [[{'arguments': {}, 'name': 'getrandommovie'}], + # [{'arguments': {}, 'name': 'getrandommovie'}]], + # 'raw_input_api_gen_generator_0': [{'role': 'system', + # 'content': "You are a data labeler. Your responsibility is to generate a set of diverse queries and corresponding answers for the given functions in JSON format.\n\nConstruct queries and answers that exemplify how to use these functions in a practical scenario. Include in each query specific, plausible values for each parameter. For instance, if the function requires a date, use a typical and reasonable date.\n\nEnsure the query:\n- Is clear and concise\n- Demonstrates typical use cases\n- Includes all necessary parameters in a meaningful way. For numerical parameters, it could be either numbers or words\n- Across a variety level of difficulties, ranging from beginner and advanced use cases\n- The corresponding result's parameter types and ranges match with the function's descriptions\n\nEnsure the answer:\n- Is a list of function calls in JSON format\n- The length of the answer list should be equal to the number of requests in the query\n- Can solve all the requests in the query effectively"}, + # {'role': 'user', + # 'content': 'Here are examples of queries and the corresponding answers for similar functions:\nQUERY:\nWhat is the binary sum of 10010 and 11101?\nANSWER:\n[{"name": "binary_addition", "arguments": {"a": "10010", "b": "11101"}}]\n\nNote that the query could be interpreted as a combination of several independent requests.\nBased on these examples, generate 2 diverse query and answer pairs for the function `getrandommovie`\nThe detailed function description is the following:\nReturns a list of random movies from a database by calling an external API.\n\nNow please generate 2 diverse query and answer pairs following the above format.'}]}, + # 'model_name': 'meta-llama/Meta-Llama-3.1-70B-Instruct'}] + ``` + """ + + system_prompt: str = SYSTEM_PROMPT_API_GEN + use_default_structured_output: bool = False + number: Union[int, List[int], Dict[int, float]] = 1 + use_tools: bool = True + + _number: Union[int, None] = PrivateAttr(None) + _fn_parallel_queries: Union[Callable[[], str], None] = PrivateAttr(None) + _format_inst: Union[str, None] = PrivateAttr(None) + + def load(self) -> None: + """Loads the template for the generator prompt.""" + super().load() + _path = str( + importlib_resources.files("distilabel") + / "steps" + / "tasks" + / "templates" + / "apigen" + / "generator.jinja2" + ) + self._template = Template(open(_path).read()) + self._format_inst = self._set_format_inst() + + def _parallel_queries(self, number: int) -> Callable[[int], str]: + """Prepares the function to update the parallel queries guide in the prompt. + + Raises: + ValueError: if `is_parallel` is not a boolean or a list of floats. + + Returns: + The function to generate the parallel queries guide. + """ + if number > 1: + return ( + "It can contain multiple parallel queries in natural language for the given functions. " + "They could use either the same function with different arguments or different functions.\n" + ) + return "" + + def _get_number(self) -> int: + """Generates the number of queries to generate in a single call. + The number must be set to `_number` to avoid changing the original value + when calling `_default_error`. + """ + if isinstance(self.number, list): + self._number = random.choice(self.number) + elif isinstance(self.number, dict): + self._number = random.choices( + list(self.number.keys()), list(self.number.values()) + )[0] + else: + self._number = self.number + return self._number + + def _set_format_inst(self) -> str: + """Prepares the function to generate the formatted instructions for the prompt. + + If the default structured output is used, returns an empty string because nothing + else is needed, otherwise, returns the original addition to the prompt to guide the model + to generate a formatted JSON. + """ + return ( + "\nThe output MUST strictly adhere to the following JSON format, and NO other text MUST be included:\n" + "```\n" + "[\n" + " {\n" + ' "query": "The generated query.",\n' + ' "answers": [\n' + " {\n" + ' "name": "api_name",\n' + ' "arguments": {\n' + ' "arg_name": "value"\n' + " ... (more arguments as required)\n" + " }\n" + " },\n" + " ... (more API calls as required)\n" + " ]\n" + " }\n" + "]\n" + "```\n" + ) + + def _get_func_desc(self, input: Dict[str, Any]) -> str: + """If available and required, will use the info from the tools in the + prompt for extra information. Otherwise will use jut the function description. + """ + if not self.use_tools: + return input["func_desc"] + extra = "" # Extra information from the tools (if available will be added) + if "tools" in input: + extra = f"\n\nThis is the available tool to guide you (respect the order of the parameters):\n{input['tools']}" + return input["func_desc"] + extra + + @property + def inputs(self) -> "StepColumns": + """The inputs for the task.""" + return { + "examples": True, + "func_name": True, + "func_desc": True, + "tools": False, + } + + def format_input(self, input: Dict[str, Any]) -> "ChatType": + """The input is formatted as a `ChatType`.""" + number = self._get_number() + parallel_queries = self._parallel_queries(number) + return [ + {"role": "system", "content": self.system_prompt}, + { + "role": "user", + "content": self._template.render( + examples=input["examples"], + parallel_queries=parallel_queries, + number=number, + func_name=input["func_name"], + func_desc=self._get_func_desc(input), + format_inst=self._format_inst, + ), + }, + ] + + @property + def outputs(self) -> "StepColumns": + """The output for the task are the queries and corresponding answers.""" + return ["query", "answers", "model_name"] + + def format_output( + self, output: Union[str, None], input: Dict[str, Any] + ) -> Dict[str, Any]: + """The output is formatted as a list with the score of each instruction. + + Args: + output: the raw output of the LLM. + input: the input to the task. Used for obtaining the number of responses. + + Returns: + A dict with the queries and answers pairs. + The answers are an array of answers corresponding to the query. + Each answer is represented as an object with the following properties: + - name (string): The name of the tool used to generate the answer. + - arguments (object): An object representing the arguments passed to the tool to generate the answer. + Each argument is represented as a key-value pair, where the key is the parameter name and the + value is the corresponding value. + """ + if output is None: + return self._default_error(input) + + if not self.use_default_structured_output: + output = remove_fences(output) + + try: + pairs = orjson.loads(output) + except orjson.JSONDecodeError: + return self._default_error(input) + + pairs = pairs["pairs"] if self.use_default_structured_output else pairs + + return self._format_output(pairs, input) + + def _format_output( + self, pairs: Dict[str, Any], input: Dict[str, Any] + ) -> Dict[str, Any]: + """Parses the response, returning a dictionary with queries and answers. + + Args: + pairs: The parsed dictionary from the LLM's output. + input: The input from the `LLM`. + + Returns: + Formatted output, where the `queries` are a list of strings, and the `answers` + are a list of objects. + """ + try: + input.update( + **{ + "query": pairs[0]["query"], + "answers": json.dumps(pairs[0]["answers"]), + } + ) + return input + except Exception as e: + self._logger.error(f"Error formatting output: {e}, pairs: '{pairs}'") + return self._default_error(input) + + def _default_error(self, input: Dict[str, Any]) -> Dict[str, Any]: + """Returns a default error output, to fill the responses in case of failure.""" + input.update( + **{ + "query": None, + "answers": json.dumps([None] * self._number), + } + ) + return input + + @override + def get_structured_output(self) -> Dict[str, Any]: + """Creates the json schema to be passed to the LLM, to enforce generating + a dictionary with the output which can be directly parsed as a python dictionary. + + The schema corresponds to the following: + + ```python + from typing import Dict, List + from pydantic import BaseModel + + + class Answer(BaseModel): + name: str + arguments: Dict[str, str] + + class QueryAnswer(BaseModel): + query: str + answers: List[Answer] + + class QueryAnswerPairs(BaseModel): + pairs: List[QueryAnswer] + + json.dumps(QueryAnswerPairs.model_json_schema(), indent=4) + ``` + + Returns: + JSON Schema of the response to enforce. + """ + return { + "$defs": { + "Answer": { + "properties": { + "name": {"title": "Name", "type": "string"}, + "arguments": { + "additionalProperties": {"type": "string"}, + "title": "Arguments", + "type": "object", + }, + }, + "required": ["name", "arguments"], + "title": "Answer", + "type": "object", + }, + "QueryAnswer": { + "properties": { + "query": {"title": "Query", "type": "string"}, + "answers": { + "items": {"$ref": "#/$defs/Answer"}, + "title": "Answers", + "type": "array", + }, + }, + "required": ["query", "answers"], + "title": "QueryAnswer", + "type": "object", + }, + }, + "properties": { + "pairs": { + "items": {"$ref": "#/$defs/QueryAnswer"}, + "title": "Pairs", + "type": "array", + } + }, + "required": ["pairs"], + "title": "QueryAnswerPairs", + "type": "object", + } diff --git a/src/distilabel/steps/tasks/apigen/semantic_checker.py b/src/distilabel/steps/tasks/apigen/semantic_checker.py new file mode 100644 index 0000000000..5ec7cdc57d --- /dev/null +++ b/src/distilabel/steps/tasks/apigen/semantic_checker.py @@ -0,0 +1,308 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import importlib.resources as importlib_resources +from typing import TYPE_CHECKING, Any, Dict, Final, Union + +import orjson +from jinja2 import Template +from pydantic import PrivateAttr +from typing_extensions import override + +from distilabel.steps.tasks.apigen.utils import remove_fences +from distilabel.steps.tasks.base import Task + +if TYPE_CHECKING: + from distilabel.steps.tasks.typing import ChatType + from distilabel.steps.typing import StepColumns + + +SYSTEM_PROMPT_SEMANTIC_CHECKER: Final[str] = """\ +As a data quality evaluator, you must assess the alignment between a user query, corresponding function calls, and their execution results. +These function calls and results are generated by other models, and your task is to ensure these results accurately reflect the user’s intentions. + +Do not pass if: +1. The function call does not align with the query’s objective, or the input arguments appear incorrect. +2. The function call and arguments are not properly chosen from the available functions. +3. The number of function calls does not correspond to the user’s intentions. +4. The execution results are irrelevant and do not match the function’s purpose. +5. The execution results contain errors or reflect that the function calls were not executed successfully. +""".rstrip() + + +class APIGenSemanticChecker(Task): + r"""Generate queries and answers for the given functions in JSON format. + + The `APIGenGenerator` is inspired by the APIGen pipeline, which was designed to generate + verifiable and diverse function-calling datasets. The task generates a set of diverse queries + and corresponding answers for the given functions in JSON format. + + Attributes: + system_prompt: System prompt for the task. Has a default one. + exclude_failed_execution: Whether to exclude failed executions (won't run on those + rows that have a False in `keep_row_after_execution_check` column, which + comes from running `APIGenExecutionChecker`). Defaults to True. + + Input columns: + - func_desc (`str`): Description of what the function should do. + - query (`str`): Instruction from the user. + - answers (`str`): JSON encoded list with arguments to be passed to the function/API. + Should be loaded using `json.loads`. + - execution_result (`str`): Result of the function/API executed. + + Output columns: + - thought (`str`): Reasoning for the output on whether to keep this output or not. + - keep_row_after_semantic_check (`bool`): True or False, can be used to filter + afterwards. + + Categories: + - filtering + - text-generation + + References: + - [APIGen: Automated Pipeline for Generating Verifiable and Diverse Function-Calling Datasets](https://arxiv.org/abs/2406.18518) + - [Salesforce/xlam-function-calling-60k](https://huggingface.co/datasets/Salesforce/xlam-function-calling-60k) + + Examples: + + Semantic checker for generated function calls (original implementation): + + ```python + from distilabel.steps.tasks import APIGenSemanticChecker + from distilabel.llms import InferenceEndpointsLLM + + llm=InferenceEndpointsLLM( + model_id="meta-llama/Meta-Llama-3.1-70B-Instruct", + generation_kwargs={ + "temperature": 0.7, + "max_new_tokens": 1024, + }, + ) + semantic_checker = APIGenSemanticChecker( + use_default_structured_output=False, + llm=llm + ) + semantic_checker.load() + + res = next( + semantic_checker.process( + [ + { + "func_desc": "Fetch information about a specific cat breed from the Cat Breeds API.", + "query": "What information can be obtained about the Maine Coon cat breed?", + "answers": json.dumps([{"name": "get_breed_information", "arguments": {"breed": "Maine Coon"}}]), + "execution_result": "The Maine Coon is a big and hairy breed of cat", + } + ] + ) + ) + res + # [{'func_desc': 'Fetch information about a specific cat breed from the Cat Breeds API.', + # 'query': 'What information can be obtained about the Maine Coon cat breed?', + # 'answers': [{"name": "get_breed_information", "arguments": {"breed": "Maine Coon"}}], + # 'execution_result': 'The Maine Coon is a big and hairy breed of cat', + # 'thought': '', + # 'keep_row_after_semantic_check': True, + # 'raw_input_a_p_i_gen_semantic_checker_0': [{'role': 'system', + # 'content': 'As a data quality evaluator, you must assess the alignment between a user query, corresponding function calls, and their execution results.\nThese function calls and results are generated by other models, and your task is to ensure these results accurately reflect the user’s intentions.\n\nDo not pass if:\n1. The function call does not align with the query’s objective, or the input arguments appear incorrect.\n2. The function call and arguments are not properly chosen from the available functions.\n3. The number of function calls does not correspond to the user’s intentions.\n4. The execution results are irrelevant and do not match the function’s purpose.\n5. The execution results contain errors or reflect that the function calls were not executed successfully.\n'}, + # {'role': 'user', + # 'content': 'Given Information:\n- All Available Functions:\nFetch information about a specific cat breed from the Cat Breeds API.\n- User Query: What information can be obtained about the Maine Coon cat breed?\n- Generated Function Calls: [{"name": "get_breed_information", "arguments": {"breed": "Maine Coon"}}]\n- Execution Results: The Maine Coon is a big and hairy breed of cat\n\nNote: The query may have multiple intentions. Functions may be placeholders, and execution results may be truncated due to length, which is acceptable and should not cause a failure.\n\nThe main decision factor is wheather the function calls accurately reflect the query\'s intentions and the function descriptions.\nProvide your reasoning in the thought section and decide if the data passes (answer yes or no).\nIf not passing, concisely explain your reasons in the thought section; otherwise, leave this section blank.\n\nYour response MUST strictly adhere to the following JSON format, and NO other text MUST be included.\n```\n{\n "thought": "Concisely describe your reasoning here",\n "pass": "yes" or "no"\n}\n```\n'}]}, + # 'model_name': 'meta-llama/Meta-Llama-3.1-70B-Instruct'}] + ``` + + Semantic checker for generated function calls (structured output): + + ```python + from distilabel.steps.tasks import APIGenSemanticChecker + from distilabel.llms import InferenceEndpointsLLM + + llm=InferenceEndpointsLLM( + model_id="meta-llama/Meta-Llama-3.1-70B-Instruct", + generation_kwargs={ + "temperature": 0.7, + "max_new_tokens": 1024, + }, + ) + semantic_checker = APIGenSemanticChecker( + use_default_structured_output=True, + llm=llm + ) + semantic_checker.load() + + res = next( + semantic_checker.process( + [ + { + "func_desc": "Fetch information about a specific cat breed from the Cat Breeds API.", + "query": "What information can be obtained about the Maine Coon cat breed?", + "answers": json.dumps([{"name": "get_breed_information", "arguments": {"breed": "Maine Coon"}}]), + "execution_result": "The Maine Coon is a big and hairy breed of cat", + } + ] + ) + ) + res + # [{'func_desc': 'Fetch information about a specific cat breed from the Cat Breeds API.', + # 'query': 'What information can be obtained about the Maine Coon cat breed?', + # 'answers': [{"name": "get_breed_information", "arguments": {"breed": "Maine Coon"}}], + # 'execution_result': 'The Maine Coon is a big and hairy breed of cat', + # 'keep_row_after_semantic_check': True, + # 'thought': '', + # 'raw_input_a_p_i_gen_semantic_checker_0': [{'role': 'system', + # 'content': 'As a data quality evaluator, you must assess the alignment between a user query, corresponding function calls, and their execution results.\nThese function calls and results are generated by other models, and your task is to ensure these results accurately reflect the user’s intentions.\n\nDo not pass if:\n1. The function call does not align with the query’s objective, or the input arguments appear incorrect.\n2. The function call and arguments are not properly chosen from the available functions.\n3. The number of function calls does not correspond to the user’s intentions.\n4. The execution results are irrelevant and do not match the function’s purpose.\n5. The execution results contain errors or reflect that the function calls were not executed successfully.\n'}, + # {'role': 'user', + # 'content': 'Given Information:\n- All Available Functions:\nFetch information about a specific cat breed from the Cat Breeds API.\n- User Query: What information can be obtained about the Maine Coon cat breed?\n- Generated Function Calls: [{"name": "get_breed_information", "arguments": {"breed": "Maine Coon"}}]\n- Execution Results: The Maine Coon is a big and hairy breed of cat\n\nNote: The query may have multiple intentions. Functions may be placeholders, and execution results may be truncated due to length, which is acceptable and should not cause a failure.\n\nThe main decision factor is wheather the function calls accurately reflect the query\'s intentions and the function descriptions.\nProvide your reasoning in the thought section and decide if the data passes (answer yes or no).\nIf not passing, concisely explain your reasons in the thought section; otherwise, leave this section blank.\n'}]}, + # 'model_name': 'meta-llama/Meta-Llama-3.1-70B-Instruct'}] + ``` + """ + + system_prompt: str = SYSTEM_PROMPT_SEMANTIC_CHECKER + use_default_structured_output: bool = False + + _format_inst: Union[str, None] = PrivateAttr(None) + + def load(self) -> None: + """Loads the template for the generator prompt.""" + super().load() + _path = str( + importlib_resources.files("distilabel") + / "steps" + / "tasks" + / "templates" + / "apigen" + / "semantic_checker.jinja2" + ) + + self._template = Template(open(_path).read()) + self._format_inst = self._set_format_inst() + + def _set_format_inst(self) -> str: + """Prepares the function to generate the formatted instructions for the prompt. + + If the default structured output is used, returns an empty string because nothing + else is needed, otherwise, returns the original addition to the prompt to guide the model + to generate a formatted JSON. + """ + return ( + "\nYour response MUST strictly adhere to the following JSON format, and NO other text MUST be included.\n" + "```\n" + "{\n" + ' "thought": "Concisely describe your reasoning here",\n' + ' "passes": "yes" or "no"\n' + "}\n" + "```\n" + ) + + @property + def inputs(self) -> "StepColumns": + """The inputs for the task.""" + return { + "func_desc": True, + "query": True, + "answers": True, + "execution_result": True, + "keep_row_after_execution_check": True, + } + + def format_input(self, input: Dict[str, Any]) -> "ChatType": + """The input is formatted as a `ChatType`.""" + return [ + {"role": "system", "content": self.system_prompt}, + { + "role": "user", + "content": self._template.render( + func_desc=input["func_desc"], + query=input["query"] or "", + func_call=input["answers"] or "", + execution_result=input["execution_result"], + format_inst=self._format_inst, + ), + }, + ] + + @property + def outputs(self) -> "StepColumns": + """The output for the task are the queries and corresponding answers.""" + return ["keep_row_after_semantic_check", "thought"] + + def format_output( + self, output: Union[str, None], input: Dict[str, Any] + ) -> Dict[str, Any]: + """The output is formatted as a list with the score of each instruction. + + Args: + output: the raw output of the LLM. + input: the input to the task. Used for obtaining the number of responses. + + Returns: + A dict with the queries and answers pairs. + The answers are an array of answers corresponding to the query. + Each answer is represented as an object with the following properties: + - name (string): The name of the tool used to generate the answer. + - arguments (object): An object representing the arguments passed to the tool to generate the answer. + Each argument is represented as a key-value pair, where the key is the parameter name and the + value is the corresponding value. + """ + if output is None: + return self._default_error(input) + + output = remove_fences(output) + + try: + result = orjson.loads(output) + # Update the column name and change to bool + result["keep_row_after_semantic_check"] = ( + result.pop("passes").lower() == "yes" + ) + input.update(**result) + return input + except orjson.JSONDecodeError: + return self._default_error(input) + + def _default_error(self, input: Dict[str, Any]) -> Dict[str, Any]: + """Default error message for the task.""" + input.update({"thought": None, "keep_row_after_semantic_check": None}) + return input + + @override + def get_structured_output(self) -> Dict[str, Any]: + """Creates the json schema to be passed to the LLM, to enforce generating + a dictionary with the output which can be directly parsed as a python dictionary. + + The schema corresponds to the following: + + ```python + from typing import Literal + from pydantic import BaseModel + import json + + class Checker(BaseModel): + thought: str + passes: Literal["yes", "no"] + + json.dumps(Checker.model_json_schema(), indent=4) + ``` + + Returns: + JSON Schema of the response to enforce. + """ + return { + "properties": { + "thought": {"title": "Thought", "type": "string"}, + "passes": {"enum": ["yes", "no"], "title": "Passes", "type": "string"}, + }, + "required": ["thought", "passes"], + "title": "Checker", + "type": "object", + } diff --git a/src/distilabel/steps/tasks/apigen/utils.py b/src/distilabel/steps/tasks/apigen/utils.py new file mode 100644 index 0000000000..85ff0b764c --- /dev/null +++ b/src/distilabel/steps/tasks/apigen/utils.py @@ -0,0 +1,194 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import importlib.util +import re +import signal +from typing import TYPE_CHECKING, Any, Callable, Dict, TypedDict, Union + +from distilabel.steps.base import Step, StepInput + +if TYPE_CHECKING: + from types import ModuleType + + from distilabel.steps.typing import StepColumns, StepOutput + + +class PrepareExamples(Step): + r"""Helper step to create examples from `query` and `answers` pairs used as Few Shots in APIGen. + + Attributes: + template (str): The template to format the examples. + + Input columns: + - query (`str`): The query to generate examples from. + - answers (`str`): The answers to the query. + + Output columns: + - examples (`str`): The formatted examples. + + Categories: + - format + + Examples: + Generate examples for APIGen: + + ```python + from distilabel.steps.tasks.apigen.utils import PrepareExamples + + prepare_examples = PrepareExamples() + result = next(prepare_examples.process( + [ + { + "query": ['I need the area of circles with radius 2.5, 5, and 7.5 inches, please.', 'Can you provide the current locations of buses and trolleys on route 12?'], + "answers": ['[{"name": "circle_area", "arguments": {"radius": 2.5}}, {"name": "circle_area", "arguments": {"radius": 5}}, {"name": "circle_area", "arguments": {"radius": 7.5}}]', '[{"name": "bus_trolley_locations", "arguments": {"route": "12"}}]'] + } + ] + ) + # result + # [{'examples': '## Query:\nI need the area of circles with radius 2.5, 5, and 7.5 inches, please.\n## Answers:\n[{"name": "circle_area", "arguments": {"radius": 2.5}}, {"name": "circle_area", "arguments": {"radius": 5}}, {"name": "circle_area", "arguments": {"radius": 7.5}}]\n\n## Query:\nCan you provide the current locations of buses and trolleys on route 12?\n## Answers:\n[{"name": "bus_trolley_locations", "arguments": {"route": "12"}}]'}, {'examples': '## Query:\nI need the area of circles with radius 2.5, 5, and 7.5 inches, please.\n## Answers:\n[{"name": "circle_area", "arguments": {"radius": 2.5}}, {"name": "circle_area", "arguments": {"radius": 5}}, {"name": "circle_area", "arguments": {"radius": 7.5}}]\n\n## Query:\nCan you provide the current locations of buses and trolleys on route 12?\n## Answers:\n[{"name": "bus_trolley_locations", "arguments": {"route": "12"}}]'}] + ``` + """ + + template: str = "## Query:\n{query}\n## Answers:\n{answers}" + + @property + def inputs(self) -> "StepColumns": + return ["query", "answers"] + + @property + def outputs(self) -> "StepColumns": + return ["examples"] + + def process(self, inputs: StepInput) -> "StepOutput": + """The process prepares the data for the `APIGenGenerator` task. + + If a single example is provided, it is copied to avoid raising an error. + + Args: + inputs: A list of dictionaries with the input data. + + Yields: + A list of dictionaries with the output data. + """ + outputs = [] + for input in inputs: + example_list = [] + for query, answers in zip(input["query"], input["answers"]): + example_list.append(self.template.format(query=query, answers=answers)) + outputs.append({"examples": "\n\n".join(example_list)}) + + yield outputs + + +def load_module_from_path(path: str) -> "ModuleType": + """Loads a python module from a given path. + + Args: + path: Path pointing to the module. + + Returns: + ModuleType + + Example: + ```python + path = "/path/to/module.py" + module = load_module_from_path(path) + # And you can load functions from the module like this: + function = getattr(module, "function_name") + function(*args, **kwargs) + ``` + """ + spec = importlib.util.spec_from_file_location("module.name", path) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + + +class FunctionResult(TypedDict): + keep: bool + execution_result: str + + +def execute_from_response( + function: Callable, call_answer: Union[Dict[str, Any], None] +) -> FunctionResult: + """Executes a function with the given arguments as generated by `APIGenGenerator`. + + Given that we cannot cast all the arguments arbitrarily, we try to evaluate them, + which ensures the strings can be converted to the correct type if possible (say + a list of lists of ints will be passed as such instead of its string representation). + + Args: + function: A callable object. + call_answer: The arguments to call the function, as generated by the model. + + Returns: + A container with the result of the execution and if the row should be kept. + """ + if not function: + return FunctionResult(keep=False, execution_result="Function not found") + + if call_answer: + for key, value in call_answer.items(): + if isinstance(value, str): + try: + call_answer[key] = eval(value) + except Exception: + # Leave as is and expect the function to handle it + pass + + try: + if call_answer: + result = run_function_with_timeout(function, 5, *call_answer.values()) + else: + # There can be functions that do not require arguments + result = run_function_with_timeout(function, 5) + return FunctionResult(keep=True, execution_result=str(result)) + except Exception as e: + return FunctionResult(keep=False, execution_result=str(e)) + + +def remove_json_fences(text: str) -> str: + pattern = r"^```json\n([\s\S]*)\n```$" + match = re.match(pattern, text, re.MULTILINE) + if match: + return match.group(1) + return text + + +def remove_fences(text: str) -> str: + pattern = r"^```\n([\s\S]*)\n```$" + match = re.match(pattern, text, re.MULTILINE) + if match: + return match.group(1) + return text + + +def timeout_handler(signum, frame): + raise TimeoutError("Function execution timed out") + + +def run_function_with_timeout(function: Callable, timeout: int = 5, *args: Any) -> Any: + """Run a function with a timeout, to limit the total time waiting for a result.""" + signal.signal(signal.SIGALRM, timeout_handler) + signal.alarm(timeout) + + try: + result = function(*args) + finally: + # Cancel the alarm + signal.alarm(0) + + return result diff --git a/src/distilabel/steps/tasks/templates/apigen/generator.jinja2 b/src/distilabel/steps/tasks/templates/apigen/generator.jinja2 new file mode 100644 index 0000000000..cc92c725c3 --- /dev/null +++ b/src/distilabel/steps/tasks/templates/apigen/generator.jinja2 @@ -0,0 +1,10 @@ +Here are examples of queries and the corresponding answers for similar functions: +{{ examples }} + +Note that the query could be interpreted as a combination of several independent requests. +{{ parallel_queries }} +Based on these examples, generate {{ number }} diverse query and answer pairs for the function `{{ func_name }}`. +The detailed function description is the following: +{{ func_desc }} +{{ format_inst }} +Now please generate {{ number }} diverse query and answer pairs following the above format. \ No newline at end of file diff --git a/src/distilabel/steps/tasks/templates/apigen/semantic_checker.jinja2 b/src/distilabel/steps/tasks/templates/apigen/semantic_checker.jinja2 new file mode 100644 index 0000000000..8d94357e7e --- /dev/null +++ b/src/distilabel/steps/tasks/templates/apigen/semantic_checker.jinja2 @@ -0,0 +1,13 @@ +Given Information: +- All Available Functions: +{{ func_desc }} +- User Query: {{ query }} +- Generated Function Calls: {{ func_call }} +- Execution Results: {{ execution_result }} + +Note: The query may have multiple intentions. Functions may be placeholders, and execution results may be truncated due to length, which is acceptable and should not cause a failure. + +The main decision factor is wheather the function calls accurately reflect the query's intentions and the function descriptions. +Provide your reasoning in the thought section and decide if the data passes (answer yes or no). +If not passing, concisely explain your reasons in the thought section; otherwise, leave this section blank. +{{ format_inst }} \ No newline at end of file diff --git a/src/distilabel/utils/mkdocs/components_gallery.py b/src/distilabel/utils/mkdocs/components_gallery.py index 9d5d9b59ee..621f4b61dc 100644 --- a/src/distilabel/utils/mkdocs/components_gallery.py +++ b/src/distilabel/utils/mkdocs/components_gallery.py @@ -90,6 +90,7 @@ "filtering": ":material-filter:", "format": ":material-format-list-bulleted:", "load": ":material-file-download:", + "execution": ":octicons-code-16:", "save": ":material-content-save:", } @@ -108,6 +109,7 @@ "filtering": "Filtering steps are used to filter the data based on some criteria.", "format": "Format steps are used to format the data.", "load": "Load steps are used to load the data.", + "execution": "Executes python functions.", "save": "Save steps are used to save the data.", } diff --git a/tests/integration/test_generator_and_sampler.py b/tests/integration/test_generator_and_sampler.py new file mode 100644 index 0000000000..1bb0a457b5 --- /dev/null +++ b/tests/integration/test_generator_and_sampler.py @@ -0,0 +1,55 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from distilabel.llms._dummy import DummyAsyncLLM +from distilabel.pipeline import Pipeline +from distilabel.steps import CombineOutputs, LoadDataFromDicts +from distilabel.steps.generators.data_sampler import DataSampler +from distilabel.steps.tasks import TextGeneration + + +def get_pipeline(): + with Pipeline() as pipe: + size_dataset_1 = 10 + loader_1 = LoadDataFromDicts( + data=[{"instruction": f"instruction {i}"} for i in range(size_dataset_1)] + ) + sampler = DataSampler( + data=[{"sample": f"sample {i}"} for i in range(30)], + size=2, + samples=size_dataset_1, + batch_size=8, + ) + text_generation = TextGeneration(llm=DummyAsyncLLM(), input_batch_size=8) + + combine = CombineOutputs() + [loader_1, sampler] >> combine >> text_generation + return pipe + + +def test_sampler(): + pipe = get_pipeline() + distiset = pipe.run(use_cache=False) + assert len(distiset["default"]["train"]) == 10 + row = distiset["default"]["train"][0] + assert isinstance(row["sample"], list) + assert len(row["sample"]) == 2 + assert isinstance(row["instruction"], str) + + +if __name__ == "__main__": + pipe = get_pipeline() + distiset = pipe.run(use_cache=False) + print(distiset) + print(distiset["default"]["train"][0]) diff --git a/tests/unit/steps/generators/test_data_sampler.py b/tests/unit/steps/generators/test_data_sampler.py new file mode 100644 index 0000000000..32882e0379 --- /dev/null +++ b/tests/unit/steps/generators/test_data_sampler.py @@ -0,0 +1,45 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import List + +import pytest + +from distilabel.steps.generators.data_sampler import DataSampler + + +@pytest.mark.parametrize( + "samples, size, batch_size, expected", + [ + (10, 2, 4, [4, 4, 2]), + (7, 5, 6, [6, 1]), + (20, 5, 20, [20]), + (20, 50, 8, [8, 8, 4]), + ], +) +def test_generator_and_sampler( + samples: int, size: int, batch_size: int, expected: List[int] +): + sampler = DataSampler( + data=[{"sample": f"sample {i}"} for i in range(30)], + size=size, + samples=samples, + batch_size=batch_size, + ) + sampler.load() + results = [item[0] for item in sampler.process()] + assert len(results) == len(expected) + assert len(results[0]) == batch_size + for i, result in enumerate(results): + assert len(result) == expected[i] diff --git a/tests/unit/steps/tasks/apigen/__init__.py b/tests/unit/steps/tasks/apigen/__init__.py new file mode 100644 index 0000000000..20ce00bda7 --- /dev/null +++ b/tests/unit/steps/tasks/apigen/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + diff --git a/tests/unit/steps/tasks/apigen/_sample_lib/final_velocity.py b/tests/unit/steps/tasks/apigen/_sample_lib/final_velocity.py new file mode 100644 index 0000000000..abcc66214c --- /dev/null +++ b/tests/unit/steps/tasks/apigen/_sample_lib/final_velocity.py @@ -0,0 +1,27 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +def final_velocity(initial_velocity: float, acceleration: float, time: float) -> int: + """Calculates the final velocity of an object given its initial velocity, acceleration, and time. + + Args: + initial_velocity: The initial velocity of the object. + acceleration: The acceleration of the object. + time: The time elapsed. + + Returns: + The final velocity + """ + return initial_velocity + acceleration * time diff --git a/tests/unit/steps/tasks/apigen/_sample_lib/get_value.py b/tests/unit/steps/tasks/apigen/_sample_lib/get_value.py new file mode 100644 index 0000000000..db3bd1bccf --- /dev/null +++ b/tests/unit/steps/tasks/apigen/_sample_lib/get_value.py @@ -0,0 +1,33 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import List, Optional, Tuple + + +def get_value(matrix: List[List[int]], indices: Tuple[int, int]) -> Optional[int]: + """Gets the value at the specified index in the matrix. + + Args: + matrix: A list of lists representing the matrix. + indices: A tuple containing the row and column indices. + """ + row_index, col_index = indices + if ( + row_index < 0 + or row_index >= len(matrix) + or col_index < 0 + or col_index >= len(matrix[row_index]) + ): + return None + return matrix[row_index][col_index] diff --git a/tests/unit/steps/tasks/apigen/_sample_module.py b/tests/unit/steps/tasks/apigen/_sample_module.py new file mode 100644 index 0000000000..6e9e085023 --- /dev/null +++ b/tests/unit/steps/tasks/apigen/_sample_module.py @@ -0,0 +1,47 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import List, Optional, Tuple + + +def final_velocity(initial_velocity: float, acceleration: float, time: float) -> int: + """Calculates the final velocity of an object given its initial velocity, acceleration, and time. + + Args: + initial_velocity: The initial velocity of the object. + acceleration: The acceleration of the object. + time: The time elapsed. + + Returns: + The final velocity + """ + return initial_velocity + acceleration * time + + +def get_value(matrix: List[List[int]], indices: Tuple[int, int]) -> Optional[int]: + """Gets the value at the specified index in the matrix. + + Args: + matrix: A list of lists representing the matrix. + indices: A tuple containing the row and column indices. + """ + row_index, col_index = indices + if ( + row_index < 0 + or row_index >= len(matrix) + or col_index < 0 + or col_index >= len(matrix[row_index]) + ): + return None + return matrix[row_index][col_index] diff --git a/tests/unit/steps/tasks/apigen/test_execution_checker.py b/tests/unit/steps/tasks/apigen/test_execution_checker.py new file mode 100644 index 0000000000..d70e422715 --- /dev/null +++ b/tests/unit/steps/tasks/apigen/test_execution_checker.py @@ -0,0 +1,140 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import json +from pathlib import Path +from typing import Any, Dict + +import pytest + +from distilabel.steps.tasks.apigen.execution_checker import APIGenExecutionChecker + +SAMPLE_LIB = Path(__file__).parent / "_sample_module.py" +SAMPLE_LIB_FOLDER = Path(__file__).parent / "_sample_lib" + + +class TestAPIGenExecutionChecker: + @pytest.mark.parametrize("lib", (SAMPLE_LIB, SAMPLE_LIB_FOLDER)) + @pytest.mark.parametrize( + "answers, expected", + [ + ( + { + "query": "Whats the velocity of X?", + "answers": json.dumps( + [ + { + "arguments": { + "initial_velocity": 0.2, + "acceleration": "0.1", + "time": 5, + }, + "name": "final_velocity", + } + ] + ), + }, + [ + { + "query": "Whats the velocity of X?", + "answers": json.dumps( + [ + { + "arguments": { + "initial_velocity": 0.2, + "acceleration": "0.1", + "time": 5, + }, + "name": "final_velocity", + } + ] + ), + "keep_row_after_execution_check": True, + "execution_result": ["0.7"], + } + ], + ), + ( + { + "query": "Other query", + "answers": json.dumps( + [ + { + "arguments": { + "initial_velocity": 0.2, + "acceleration": 0.1, + "time": 0.5, + }, + "name": "unknown_function", + } + ] + ), + }, + [ + { + "query": "Other query", + "answers": json.dumps( + [ + { + "arguments": { + "initial_velocity": 0.2, + "acceleration": 0.1, + "time": 0.5, + }, + "name": "unknown_function", + } + ] + ), + "keep_row_after_execution_check": False, + "execution_result": ["Function 'unknown_function' not found."], + } + ], + ), + ( + { + "query": "Other query", + "answers": '[{"arguments": {"matrix": "[[1, 2, 3], [4, 5, 6], [7, 8, 9]]", "indices": "[1, 2]"}, "name": "get_value"}]', + }, + [ + { + "query": "Other query", + "answers": '[{"arguments": {"matrix": "[[1, 2, 3], [4, 5, 6], [7, 8, 9]]", "indices": "[1, 2]"}, "name": "get_value"}]', + "keep_row_after_execution_check": True, + "execution_result": ["6"], + } + ], + ), + ( + { + "query": "Other query", + "answers": None, + }, + [ + { + "query": "Other query", + "answers": None, + "keep_row_after_execution_check": False, + "execution_result": ["No answers were provided."], + } + ], + ), + ], + ) + def test_process( + self, lib: str, answers: Dict[str, str], expected: Dict[str, Any] + ) -> None: + task = APIGenExecutionChecker(libpath=str(lib)) + task.load() + result = next(task.process([answers])) + assert result == expected diff --git a/tests/unit/steps/tasks/apigen/test_generator.py b/tests/unit/steps/tasks/apigen/test_generator.py new file mode 100644 index 0000000000..a290666a60 --- /dev/null +++ b/tests/unit/steps/tasks/apigen/test_generator.py @@ -0,0 +1,172 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import random +from typing import TYPE_CHECKING, List, Union + +import pytest + +from distilabel.steps.tasks.apigen.generator import APIGenGenerator +from tests.unit.conftest import DummyLLM + +if TYPE_CHECKING: + from distilabel.llms.typing import GenerateOutput + from distilabel.steps.tasks.typing import FormattedInput + +import json + + +class DummyAPIGenLLM(DummyLLM): + use_structured_output: bool = False + number: int = 1 + + def generate( + self, inputs: List["FormattedInput"], num_generations: int = 1 + ) -> "GenerateOutput": + query_answers = [ + { + "query": "What information can be obtained about the Maine Coon cat breed?", + "answers": [ + { + "name": "get_breed_information", + "arguments": {"breed": "Maine Coon"}, + } + ] + * self.number, + } + ] + if self.use_structured_output: + query_answers = {"pairs": query_answers} + return [ + [json.dumps(query_answers) for _ in range(num_generations)] + for _ in range(len(inputs)) + ] + + +# Example of 3 rows from Salesforce/xlam-function-calling-60k +SAMPLE_DATA = [ + { + "answers": '[{"name": "get_breed_information", "arguments": {"breed": "Maine Coon"}}]', + "query": "What information can be obtained about the Maine Coon cat breed?", + "id": 3493, + "tools": '[{"name": "get_breed_information", "description": "Fetch information about a specific cat breed from the Cat Breeds API.", "parameters": {"breed": {"description": "The name of the cat breed to fetch information for.", "type": "str", "default": "aegean"}}}, {"name": "country_region_cities", "description": "Fetches a list of cities within a specified region of a given country from the GeoDB API.", "parameters": {"countryid": {"description": "An ISO-3166 country code or WikiData ID.", "type": "str", "default": "US"}, "regioncode": {"description": "An ISO-3166 or FIPS region code.", "type": "str", "default": "CA"}, "limit": {"description": "The maximum number of results to retrieve. Defaults to None.", "type": "int, optional", "default": ""}, "hateoasmode": {"description": "Include HATEOAS-style links in results. Defaults to None.", "type": "bool, optional", "default": ""}, "asciimode": {"description": "Display results using ASCII characters. Defaults to None.", "type": "bool, optional", "default": ""}, "nameprefixdefaultlangresults": {"description": "Match on names in the default language if a non-default language is requested when prefix-matching. Defaults to None.", "type": "bool, optional", "default": ""}, "timezoneids": {"description": "Only include cities in these time zones. Comma-separated values. Defaults to None.", "type": "str, optional", "default": ""}, "nameprefix": {"description": "Only include cities whose names start with this prefix. If languagecode is set, the prefix will be matched on the name as it appears in that language. Defaults to None.", "type": "str, optional", "default": ""}, "types": {"description": "Only include cities of these types (comma-separated): CITY, ADM2. Defaults to None.", "type": "str, optional", "default": ""}, "minpopulation": {"description": "Only include cities with at least this population. Defaults to None.", "type": "int, optional", "default": ""}, "languagecode": {"description": "Display results in this language. Defaults to None.", "type": "str, optional", "default": ""}, "offset": {"description": "The zero-based offset into the results. Defaults to None.", "type": "int, optional", "default": ""}, "maxpopulation": {"description": "Only include cities with no more than this population. Defaults to None.", "type": "int, optional", "default": ""}, "includedeleted": {"description": "Whether to include any cities marked deleted. Options are: ALL, SINCE_YESTERDAY, SINCE_LAST_WEEK, NONE. Defaults to None.", "type": "str, optional", "default": ""}, "sort": {"description": "How to sort the results. Format: \\u00b1SORT_FIELD,\\u00b1SORT_FIELD where SORT_FIELD = elevation, name, population. Defaults to None.", "type": "str, optional", "default": ""}}}, {"name": "company_details", "description": "Fetch details of a company from Indeed\'s API.", "parameters": {"company_id": {"description": "The unique identifier of the company to fetch details for.", "type": "str", "default": "Microsoft"}, "locality": {"description": "The locality or country code for Indeed\'s subdomain. Default is \'us\' if not provided.", "type": "str, optional", "default": ""}}}]', + }, + { + "answers": '[{"name": "mailcheck", "arguments": {"domain": "protonmail.com"}}, {"name": "mailcheck", "arguments": {"domain": "mail.com"}}, {"name": "get_products_in_category", "arguments": {"skip": 20, "limit": 25, "category": "furniture"}}]', + "query": "Check if the email domains 'protonmail.com' and 'mail.com' are valid and not temporary. Get the products from category 'furniture' in my store, skipping the first 20 items and limiting to 25 items.", + "id": 57546, + "tools": '[{"name": "mailcheck", "description": "Checks if an email domain is valid or a disposable/temporary address.", "parameters": {"domain": {"description": "The email or domain to check for validity. It is recommended to enter just the domain for user privacy.", "type": "str", "default": "mailinator.com"}}}, {"name": "get_products_in_category", "description": "Fetches a list of products from a specified category in a store with pagination.", "parameters": {"skip": {"description": "The number of items to skip before starting to collect the result set.", "type": "int", "default": ""}, "limit": {"description": "The number of items to return in the result set.", "type": "int", "default": ""}, "category": {"description": "The category from which to fetch products.", "type": "str", "default": ""}}}, {"name": "product_by_id", "description": "Fetches detailed information about a specific product from the AliExpress API using the provided product ID.", "parameters": {"product_id": {"description": "The unique identifier for the product on AliExpress.", "type": "int", "default": "32841070485"}}}]', + }, + { + "answers": '[{"name": "navigations_get_node_content", "arguments": {"is_id": 8899, "cat_id": 8899, "language": "en"}}, {"name": "navigations_get_node_content", "arguments": {"is_id": 7766, "cat_id": 7766, "language": "en"}}, {"name": "navigations_get_node_content", "arguments": {"is_id": 5544, "cat_id": 5544, "language": "fr"}}, {"name": "navigations_get_node_content", "arguments": {"is_id": 3322, "cat_id": 3322, "language": "fr"}}]', + "query": "What are the node contents for category IDs 8899 and 7766 in English and for category IDs 5544 and 3322 in French?", + "id": 8815, + "tools": '[{"name": "navigations_get_node_content", "description": "Fetches the content of a node in a navigation hierarchy.", "parameters": {"is_id": {"description": "The \'id\' field value returned from the /navigations/get-root endpoint.", "type": "int", "default": "26066300130"}, "cat_id": {"description": "The \'cat_id\' field value returned from the /navigations/get-tabs endpoint.", "type": "int", "default": "2026"}, "language": {"description": "The 2-letter language code (default is \'en\').", "type": "str, optional", "default": "en"}, "currency": {"description": "The 3-letter currency code (default is \'USD\').", "type": "str, optional", "default": "USD"}, "country": {"description": "The 2-letter country code (default is \'US\').", "type": "str, optional", "default": "US"}}}, {"name": "products_get_reviews", "description": "Fetches brief reviews of a product from the Shein API.", "parameters": {"goods_spu": {"description": "The value of \'productRelationID\' returned in the /products/list or /products/search endpoints. Defaults to \'m22022854841\'.", "type": "str, optional", "default": "m22022854841"}, "cat_id": {"description": "The value of \'cat_id\' returned in the /products/list or /products/search endpoints. Defaults to \'1727\'.", "type": "str, optional", "default": "1727"}, "sku": {"description": "The value of \'goods_sn\' returned in the /products/list or /products/search endpoints. Defaults to \'rm2202285484176751\'.", "type": "str, optional", "default": "rm2202285484176751"}, "currency": {"description": "The 3-letter currency code. Defaults to \'USD\'.", "type": "str, optional", "default": "USD"}, "goods_id": {"description": "The value of \'goods_id\' field returned in the /products/list or /products/search endpoints. Defaults to \'10196865\'.", "type": "str, optional", "default": "10196865"}, "language": {"description": "The 2-letter language code. Defaults to \'en\'.", "type": "str, optional", "default": "en"}, "country": {"description": "The 2-letter country code. Defaults to \'US\'.", "type": "str, optional", "default": "US"}}}]', + }, +] + + +class TestApiGenGenerator: + @pytest.mark.parametrize("number", [1, 2, [3]]) + @pytest.mark.parametrize("use_default_structured_output", [True, False]) + @pytest.mark.parametrize("use_tools", [True, False]) + def test_format_input( + self, + number: Union[int, List[int]], + use_default_structured_output: bool, + use_tools: bool, + ) -> None: + random.seed(42) + task = APIGenGenerator( + llm=DummyLLM(), + number=number, + use_tools=use_tools, + use_default_structured_output=use_default_structured_output, + ) + task.load() + formatted = task.format_input( + input={ + "examples": '## Query:\nWhat information can be obtained about the Maine Coon cat breed?\n## Answer:\n[{"name": "get_breed_information", "arguments": {"breed": "Maine Coon"}}]', + "func_name": "get_breed_information", + "func_desc": "Fetch information about a specific cat breed from the Cat Breeds API.", + "tools": '[{"name": "navigations_get_node_content", "description": "Fetches the content of a node in a navigation hierarchy.", "parameters": {"is_id": {"description": "The \'id\' field value returned from the /navigations/get-root endpoint.", "type": "int", "default": "26066300130"}, "cat_id": {"description": "The \'cat_id\' field value returned from the /navigations/get-tabs endpoint.", "type": "int", "default": "2026"}, "language": {"description": "The 2-letter language code (default is \'en\').", "type": "str, optional", "default": "en"}, "currency": {"description": "The 3-letter currency code (default is \'USD\').", "type": "str, optional", "default": "USD"}, "country": {"description": "The 2-letter country code (default is \'US\').", "type": "str, optional", "default": "US"}}}, {"name": "products_get_reviews", "description": "Fetches brief reviews of a product from the Shein API.", "parameters": {"goods_spu": {"description": "The value of \'productRelationID\' returned in the /products/list or /products/search endpoints. Defaults to \'m22022854841\'.", "type": "str, optional", "default": "m22022854841"}, "cat_id": {"description": "The value of \'cat_id\' returned in the /products/list or /products/search endpoints. Defaults to \'1727\'.", "type": "str, optional", "default": "1727"}, "sku": {"description": "The value of \'goods_sn\' returned in the /products/list or /products/search endpoints. Defaults to \'rm2202285484176751\'.", "type": "str, optional", "default": "rm2202285484176751"}, "currency": {"description": "The 3-letter currency code. Defaults to \'USD\'.", "type": "str, optional", "default": "USD"}, "goods_id": {"description": "The value of \'goods_id\' field returned in the /products/list or /products/search endpoints. Defaults to \'10196865\'.", "type": "str, optional", "default": "10196865"}, "language": {"description": "The 2-letter language code. Defaults to \'en\'.", "type": "str, optional", "default": "en"}, "country": {"description": "The 2-letter country code. Defaults to \'US\'.", "type": "str, optional", "default": "US"}}}]', + } + ) + + assert isinstance(formatted, list) + # Check only the user prompt, the system one should be fixed + formatted_prompt = formatted[1]["content"] + + if isinstance(number, list): + # Fix the number for the tests for simplicity + number = 3 + assert f"Now please generate {number} diverse" in formatted_prompt + + assert ( + "The output MUST strictly adhere to the following JSON format, and NO other text MUST be included:" + in formatted_prompt + ) + + tools_entry = "This is the available tool to guide you (respect the order of the parameters):" + if use_tools: + assert tools_entry in formatted_prompt + else: + assert tools_entry not in formatted_prompt + + is_parallel_check = "It can contain multiple parallel queries in natural language for the given functions. They could use either the same function with different arguments or different functions." + if number > 1: + assert is_parallel_check in formatted_prompt + else: + assert is_parallel_check not in formatted_prompt + + @pytest.mark.parametrize("number", [1, 2]) + @pytest.mark.parametrize("use_default_structured_output", [True, False]) + @pytest.mark.parametrize("use_tools", [True, False]) + def test_process( + self, + number: Union[int, List[int]], + use_default_structured_output: bool, + use_tools: bool, + ) -> None: + # Is parallel is not relevant in this case, it's only relevant for the format_input + # as it will be multiple questions in the prompt + random.seed(42) + task = APIGenGenerator( + llm=DummyAPIGenLLM( + use_structured_output=use_default_structured_output, number=number + ), + number=number, + use_tools=use_tools, + use_default_structured_output=use_default_structured_output, + ) + task.load() + result = next( + task.process( + [ + { + "examples": '## Query:\nWhat information can be obtained about the Maine Coon cat breed?\n## Answer:\n[{"name": "get_breed_information", "arguments": {"breed": "Maine Coon"}}]', + "func_name": "get_breed_information", + "func_desc": "Fetch information about a specific cat breed from the Cat Breeds API.", + "tools": '[{"name": "navigations_get_node_content", "description": "Fetches the content of a node in a navigation hierarchy.", "parameters": {"is_id": {"description": "The \'id\' field value returned from the /navigations/get-root endpoint.", "type": "int", "default": "26066300130"}, "cat_id": {"description": "The \'cat_id\' field value returned from the /navigations/get-tabs endpoint.", "type": "int", "default": "2026"}, "language": {"description": "The 2-letter language code (default is \'en\').", "type": "str, optional", "default": "en"}, "currency": {"description": "The 3-letter currency code (default is \'USD\').", "type": "str, optional", "default": "USD"}, "country": {"description": "The 2-letter country code (default is \'US\').", "type": "str, optional", "default": "US"}}}, {"name": "products_get_reviews", "description": "Fetches brief reviews of a product from the Shein API.", "parameters": {"goods_spu": {"description": "The value of \'productRelationID\' returned in the /products/list or /products/search endpoints. Defaults to \'m22022854841\'.", "type": "str, optional", "default": "m22022854841"}, "cat_id": {"description": "The value of \'cat_id\' returned in the /products/list or /products/search endpoints. Defaults to \'1727\'.", "type": "str, optional", "default": "1727"}, "sku": {"description": "The value of \'goods_sn\' returned in the /products/list or /products/search endpoints. Defaults to \'rm2202285484176751\'.", "type": "str, optional", "default": "rm2202285484176751"}, "currency": {"description": "The 3-letter currency code. Defaults to \'USD\'.", "type": "str, optional", "default": "USD"}, "goods_id": {"description": "The value of \'goods_id\' field returned in the /products/list or /products/search endpoints. Defaults to \'10196865\'.", "type": "str, optional", "default": "10196865"}, "language": {"description": "The 2-letter language code. Defaults to \'en\'.", "type": "str, optional", "default": "en"}, "country": {"description": "The 2-letter country code. Defaults to \'US\'.", "type": "str, optional", "default": "US"}}}]', + } + ] + ) + )[0] + assert "query" in result + assert "answers" in result + query = result["query"] + assert isinstance(query, str) + answers = json.loads(result["answers"]) + assert isinstance(answers, list) + assert len(answers) == number diff --git a/tests/unit/steps/tasks/apigen/test_semantic_checker.py b/tests/unit/steps/tasks/apigen/test_semantic_checker.py new file mode 100644 index 0000000000..e73b71c3a0 --- /dev/null +++ b/tests/unit/steps/tasks/apigen/test_semantic_checker.py @@ -0,0 +1,113 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import Any, Dict + +import pytest + +from distilabel.steps.tasks.apigen.semantic_checker import APIGenSemanticChecker +from tests.unit.conftest import DummyLLM + +SAMPLE_DATA = [ + # The info can for the function description can be obtained from the tool itself + { + "func_desc": "Fetch information about a specific cat breed from the Cat Breeds API.", + "query": "What information can be obtained about the Maine Coon cat breed?", + "answers": '[{"name": "get_breed_information", "arguments": {"breed": "Maine Coon"}}]', + "execution_result": "Hopefully some info about the Maine Coon", + }, + { + "func_desc": "Checks if an email domain is valid or a disposable/temporary address.", + "query": "Check if the email domains 'protonmail.com' and 'mail.com' are valid and not temporary. Get the products from category 'furniture' in my store, skipping the first 20 items and limiting to 25 items.", + "answers": '[{"name": "mailcheck", "arguments": {"domain": "protonmail.com"}}, {"name": "mailcheck", "arguments": {"domain": "mail.com"}}, {"name": "get_products_in_category", "arguments": {"skip": 20, "limit": 25, "category": "furniture"}}]', + "execution_result": "Response for the emails", + }, + { + "func_desc": "Fetches the content of a node in a navigation hierarchy.", + "query": "What are the node contents for category IDs 8899 and 7766 in English and for category IDs 5544 and 3322 in French?", + "answers": '[{"name": "navigations_get_node_content", "arguments": {"is_id": 8899, "cat_id": 8899, "language": "en"}}, {"name": "navigations_get_node_content", "arguments": {"is_id": 7766, "cat_id": 7766, "language": "en"}}, {"name": "navigations_get_node_content", "arguments": {"is_id": 5544, "cat_id": 5544, "language": "fr"}}, {"name": "navigations_get_node_content", "arguments": {"is_id": 3322, "cat_id": 3322, "language": "fr"}}]', + "execution_result": "Response for the node contents", + }, +] + + +class TestAPIGenSemanticChecker: + @pytest.mark.parametrize("use_default_structured_output", [True, False]) + def test_format_input(self, use_default_structured_output: bool) -> None: + task = APIGenSemanticChecker( + llm=DummyLLM(), + use_default_structured_output=use_default_structured_output, + ) + task.load() + result = task.format_input(SAMPLE_DATA[0]) + assert isinstance(result, list) + formatted_prompt = result[1]["content"] + + default_structured_output_check = "Your response MUST strictly adhere to the following JSON format, and NO other text MUST be included" + assert default_structured_output_check in formatted_prompt + assert ( + '- Generated Function Calls: [{"name": "get_breed_information", "arguments": {"breed": "Maine Coon"}}]' + in formatted_prompt + ) + assert ( + "- All Available Functions:\nFetch information about a specific cat breed from the Cat Breeds API." + in formatted_prompt + ) + assert ( + "- Execution Results: Hopefully some info about the Maine Coon" + in formatted_prompt + ) + + @pytest.mark.parametrize( + "result, expected", + [ + ( + '{"thought": "thought", "keep_row_after_semantic_check": "no", "passes": "no"}', + { + "thought": "thought", + "keep_row_after_semantic_check": False, + "answers": '[{"name": "get_breed_information", "arguments": {"breed": "Maine Coon"}}]', + "execution_result": "Hopefully some info about the Maine Coon", + "func_desc": "Fetch information about a specific cat breed from the Cat Breeds API.", + "query": "What information can be obtained about the Maine Coon cat breed?", + }, + ), + ( + None, + { + "thought": None, + "keep_row_after_semantic_check": None, + "answers": '[{"name": "get_breed_information", "arguments": {"breed": "Maine Coon"}}]', + "execution_result": "Hopefully some info about the Maine Coon", + "func_desc": "Fetch information about a specific cat breed from the Cat Breeds API.", + "query": "What information can be obtained about the Maine Coon cat breed?", + }, + ), + ( + "wrong", + { + "thought": None, + "keep_row_after_semantic_check": None, + "answers": '[{"name": "get_breed_information", "arguments": {"breed": "Maine Coon"}}]', + "execution_result": "Hopefully some info about the Maine Coon", + "func_desc": "Fetch information about a specific cat breed from the Cat Breeds API.", + "query": "What information can be obtained about the Maine Coon cat breed?", + }, + ), + ], + ) + def test_format_output(self, result: str, expected: Dict[str, Any]) -> None: + task = APIGenSemanticChecker(llm=DummyLLM()) + task.load() + assert task.format_output(result, SAMPLE_DATA[0]) == expected diff --git a/tests/unit/steps/tasks/apigen/test_utils.py b/tests/unit/steps/tasks/apigen/test_utils.py new file mode 100644 index 0000000000..00707f17a9 --- /dev/null +++ b/tests/unit/steps/tasks/apigen/test_utils.py @@ -0,0 +1,77 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from pathlib import Path +from typing import Any, Dict + +import pytest + +from distilabel.steps.tasks.apigen.utils import ( + execute_from_response, + load_module_from_path, +) + + +@pytest.mark.parametrize( + "function_name, answer, expected_result", + [ + ( + "final_velocity", + {"initial_velocity": 10, "acceleration": 5, "time": 2}, + {"execution_result": "20", "keep": True}, + ), + # In this case, internally we should cast the arguments + ( + "final_velocity", + {"initial_velocity": "10", "acceleration": "5", "time": "2"}, + {"execution_result": "20", "keep": True}, + ), + # Different names for the arguments but correctly positioned + ( + "final_velocity", + {"v0": "10", "a": "5", "t": "2"}, + {"execution_result": "20", "keep": True}, + ), + # Fail casting one of the values + ( + "final_velocity", + {"initial_velocity": "10", "acceleration": "5", "time": "1m/s"}, + { + "execution_result": "unsupported operand type(s) for +: 'int' and 'str'", + "keep": False, + }, + ), + ( + "final_velocity", + {"initial_velocity": 10, "acceleration": 5}, + { + "execution_result": "final_velocity() missing 1 required positional argument: 'time'", + "keep": False, + }, + ), + ( + "unknwown_function", + {"initial_velocity": 10, "acceleration": 5, "time": 2}, + {"execution_result": "Function not found", "keep": False}, + ), + ], +) +def test_execute_from_response( + function_name: str, answer: Dict[str, Any], expected_result: Dict[str, Any] +): + libpath = Path(__file__).parent / "_sample_module.py" + libpath = load_module_from_path(libpath) + final_velocity = getattr(libpath, function_name, None) + result = execute_from_response(final_velocity, answer) + assert result == expected_result From 87683f04d809be1005589bd74b1ca2ad2b1e73da Mon Sep 17 00:00:00 2001 From: Agus Date: Mon, 7 Oct 2024 15:45:13 +0200 Subject: [PATCH 77/82] Pretty print (#934) * Add integration test to showcase the prompts * Add a base print method so the Tasks can pretty print their prompts easily * Update base method to allow automatic pretty printing * Add optional argument instead of the default, update method name and return type * Fix type hint * Add example in docstrings * Add section in docs for the print method --- .../how_to_guides/tasks/task_print.png | Bin 0 -> 140892 bytes .../how_to_guides/basic/task/index.md | 69 +++++++++++++ src/distilabel/steps/tasks/base.py | 91 +++++++++++++++++- .../steps/tasks/complexity_scorer.py | 14 +++ .../steps/tasks/evol_instruct/base.py | 6 ++ .../steps/tasks/evol_instruct/generator.py | 4 + .../steps/tasks/evol_quality/base.py | 4 + .../steps/tasks/improving_text_embeddings.py | 15 +-- .../steps/tasks/magpie/generator.py | 6 ++ src/distilabel/steps/tasks/quality_scorer.py | 11 +++ src/distilabel/steps/tasks/ultrafeedback.py | 11 +++ tests/integration/test_prints.py | 71 ++++++++++++++ 12 files changed, 294 insertions(+), 8 deletions(-) create mode 100644 docs/assets/images/sections/how_to_guides/tasks/task_print.png create mode 100644 tests/integration/test_prints.py diff --git a/docs/assets/images/sections/how_to_guides/tasks/task_print.png b/docs/assets/images/sections/how_to_guides/tasks/task_print.png new file mode 100644 index 0000000000000000000000000000000000000000..95498c8c6c1518ba1d3e8ec077d5aaf099c78fd3 GIT binary patch literal 140892 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 SojC;mlfAAal`HYbqyGc#DRU73 literal 0 HcmV?d00001 diff --git a/docs/sections/how_to_guides/basic/task/index.md b/docs/sections/how_to_guides/basic/task/index.md index 5104599700..7aa2049f4b 100644 --- a/docs/sections/how_to_guides/basic/task/index.md +++ b/docs/sections/how_to_guides/basic/task/index.md @@ -57,6 +57,75 @@ As shown above, the [`TextGeneration`][distilabel.steps.tasks.TextGeneration] ta ) ``` +### Task.print + +!!! Info + New since version `1.4.0`, [`Task.print`][distilabel.steps.tasks.base._Task.print] `Task.print` method. + +The `Tasks` include a handy method to show what the prompt formatted for an `LLM` would look like, let's see an example with [`UltraFeedback`][distilabel.steps.tasks.ultrafeedback.UltraFeedback], but it applies to any other `Task`. + +```python +from distilabel.steps.tasks import UltraFeedback +from distilabel.llms.huggingface import InferenceEndpointsLLM + +uf = UltraFeedback( + llm=InferenceEndpointsLLM( + model_id="meta-llama/Meta-Llama-3.1-70B-Instruct", + ), +) +uf.load() +uf.print() +``` + +The result will be a rendered prompt, with the System prompt (if contained for the task) and the User prompt, rendered with rich (it will show exactly the same in a jupyter notebook). + +![task-print](../../../../assets/images/sections/how_to_guides/tasks/task_print.png) + +In case you want to test with a custom input, you can pass an example to the tasks` `format_input` method (or generate it on your own depending on the task), and pass it to the print method so that it shows your example: + + +```python +uf.print( + uf.format_input({"instruction": "test", "generations": ["1", "2"]}) +) +``` + +??? "Using a DummyLLM to avoid loading one" + + In case you don't want to load an LLM to render the template, you can create a dummy one like the ones we could use for testing. + + ```python + from distilabel.llms.base import LLM + from distilabel.llms.mixins.magpie import MagpieChatTemplateMixin + + class DummyLLM(AsyncLLM, MagpieChatTemplateMixin): + structured_output: Any = None + magpie_pre_query_template: str = "llama3" + + def load(self) -> None: + pass + + @property + def model_name(self) -> str: + return "test" + + def generate( + self, input: "FormattedInput", num_generations: int = 1 + ) -> "GenerateOutput": + return ["output" for _ in range(num_generations)] + ``` + + You can use this `LLM` just as any of the other ones to `load` your task and call `print`: + + ```python + uf = UltraFeedback(llm=DummyLLM()) + uf.load() + uf.print() + ``` + +!!! Note + When creating a custom task, the `print` method will be available by default, but it is limited to the most common scenarios for the inputs. If you test your new task and find it's not working as expected (for example, if your task contains one input consisting of a list of texts instead of a single one), you should override the `_sample_input` method. You can inspect the [`UltraFeedback`][distilabel.steps.tasks.ultrafeedback.UltraFeedback] source code for this. + ## Specifying the number of generations and grouping generations All the `Task`s have a `num_generations` attribute that allows defining the number of generations that we want to have per input. We can update the example above to generate 3 completions per input: diff --git a/src/distilabel/steps/tasks/base.py b/src/distilabel/steps/tasks/base.py index d73bafd60c..0524749e26 100644 --- a/src/distilabel/steps/tasks/base.py +++ b/src/distilabel/steps/tasks/base.py @@ -14,7 +14,7 @@ import importlib from abc import ABC, abstractmethod -from typing import TYPE_CHECKING, Any, Dict, List, Union +from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union from pydantic import Field, PrivateAttr from typing_extensions import override @@ -34,7 +34,7 @@ if TYPE_CHECKING: from distilabel.llms.typing import GenerateOutput - from distilabel.steps.tasks.typing import FormattedInput + from distilabel.steps.tasks.typing import ChatType, FormattedInput from distilabel.steps.typing import StepOutput @@ -276,6 +276,93 @@ def get_structured_output(self) -> Union[Dict[str, Any], None]: """ return None + def _sample_input(self) -> "ChatType": + """Returns a sample input to be used in the `print` method. + Tasks that don't adhere to a format input that returns a map of the type + str -> str should override this method to return a sample input. + """ + return self.format_input( + {input: f"" for input in self.inputs} + ) + + def print(self, sample_input: Optional["ChatType"] = None) -> None: + """Prints a sample input to the console using the `rich` library. + Helper method to visualize the prompt of the task. + + Args: + sample_input: A sample input to be printed. If not provided, a default will be + generated using the `_sample_input` method, which can be overriden by + subclasses. This should correspond to the same example you could pass to + the `format_input` method. + The variables be named by default. + + Examples: + Print the URIAL prompt: + + ```python + from distilabel.steps.tasks import URIAL + from distilabel.llms.huggingface import InferenceEndpointsLLM + + # Consider this as a placeholder for your actual LLM. + urial = URIAL( + llm=InferenceEndpointsLLM( + model_id="meta-llama/Meta-Llama-3.1-70B-Instruct", + ), + ) + urial.load() + urial.print() + ╭─────────────────────────────────────── Prompt: URIAL ────────────────────────────────────────╮ + │ ╭────────────────────────────────────── User Message ───────────────────────────────────────╮ │ + │ │ # Instruction │ │ + │ │ │ │ + │ │ Below is a list of conversations between a human and an AI assistant (you). │ │ + │ │ Users place their queries under "# User:", and your responses are under "# Assistant:". │ │ + │ │ You are a helpful, respectful, and honest assistant. │ │ + │ │ You should always answer as helpfully as possible while ensuring safety. │ │ + │ │ Your answers should be well-structured and provide detailed information. They should also │ │ + │ │ have an engaging tone. │ │ + │ │ Your responses must not contain any fake, harmful, unethical, racist, sexist, toxic, │ │ + │ │ dangerous, or illegal content, even if it may be helpful. │ │ + │ │ Your response must be socially responsible, and thus you can refuse to answer some │ │ + │ │ controversial topics. │ │ + │ │ │ │ + │ │ │ │ + │ │ # User: │ │ + │ │ │ │ + │ │ │ │ + │ │ │ │ + │ │ # Assistant: │ │ + │ ╰───────────────────────────────────────────────────────────────────────────────────────────╯ │ + ╰───────────────────────────────────────────────────────────────────────────────────────────────╯ + ``` + """ + from rich.console import Console, Group + from rich.panel import Panel + from rich.text import Text + + console = Console() + sample_input = sample_input or self._sample_input() + + panels = [] + for item in sample_input: + content = Text.assemble((item.get("content", ""),)) + panel = Panel( + content, + title=f"[bold][magenta]{item.get('role', '').capitalize()} Message[/magenta][/bold]", + border_style="light_cyan3", + ) + panels.append(panel) + + # Create a group of panels + # Wrap the group in an outer panel + outer_panel = Panel( + Group(*panels), + title=f"[bold][magenta]Prompt: {type(self).__name__} [/magenta][/bold]", + border_style="light_cyan3", + expand=False, + ) + console.print(outer_panel) + class Task(_Task, Step): """Task is a class that implements the `_Task` abstract class and adds the `Step` diff --git a/src/distilabel/steps/tasks/complexity_scorer.py b/src/distilabel/steps/tasks/complexity_scorer.py index 9ee8befa5a..401e3b760f 100644 --- a/src/distilabel/steps/tasks/complexity_scorer.py +++ b/src/distilabel/steps/tasks/complexity_scorer.py @@ -239,3 +239,17 @@ def _format_structured_output( return orjson.loads(output) except orjson.JSONDecodeError: return {"scores": [None] * len(input["instructions"])} + + @override + def _sample_input(self) -> "ChatType": + """Returns a sample input to be used in the `print` method. + Tasks that don't adhere to a format input that returns a map of the type + str -> str should override this method to return a sample input. + """ + return self.format_input( + { + "instructions": [ + f"" for i in range(2) + ], + } + ) diff --git a/src/distilabel/steps/tasks/evol_instruct/base.py b/src/distilabel/steps/tasks/evol_instruct/base.py index ea73f25038..95f271a117 100644 --- a/src/distilabel/steps/tasks/evol_instruct/base.py +++ b/src/distilabel/steps/tasks/evol_instruct/base.py @@ -388,3 +388,9 @@ def process(self, inputs: StepInput) -> "StepOutput": # type: ignore ): input.update(self.format_output(instruction, answers[idx])) yield inputs + + @override + def _sample_input(self) -> ChatType: + return self.format_input( + self._apply_random_mutation("") + ) diff --git a/src/distilabel/steps/tasks/evol_instruct/generator.py b/src/distilabel/steps/tasks/evol_instruct/generator.py index bc15655ba2..1f56c866a3 100644 --- a/src/distilabel/steps/tasks/evol_instruct/generator.py +++ b/src/distilabel/steps/tasks/evol_instruct/generator.py @@ -347,3 +347,7 @@ def process(self, offset: int = 0) -> "GeneratorStepOutput": # type: ignore ], True, ) + + @override + def _sample_input(self) -> "ChatType": + return self._apply_random_mutation(iter_no=0)[0] diff --git a/src/distilabel/steps/tasks/evol_quality/base.py b/src/distilabel/steps/tasks/evol_quality/base.py index 743deeb4f2..5c899aa680 100644 --- a/src/distilabel/steps/tasks/evol_quality/base.py +++ b/src/distilabel/steps/tasks/evol_quality/base.py @@ -271,3 +271,7 @@ def process(self, inputs: StepInput) -> "StepOutput": # type: ignore yield inputs self._logger.info(f"🎉 Finished evolving {len(responses)} instructions!") + + @override + def _sample_input(self) -> ChatType: + return self.format_input("") diff --git a/src/distilabel/steps/tasks/improving_text_embeddings.py b/src/distilabel/steps/tasks/improving_text_embeddings.py index a23b9dbbac..d806e3aded 100644 --- a/src/distilabel/steps/tasks/improving_text_embeddings.py +++ b/src/distilabel/steps/tasks/improving_text_embeddings.py @@ -12,17 +12,12 @@ # See the License for the specific language governing permissions and # limitations under the License. +import importlib.resources as importlib_resources import random import re -import sys from abc import ABC, abstractmethod from typing import Any, Dict, List, Literal, Optional, Union -if sys.version_info < (3, 9): - import importlib_resources -else: - import importlib.resources as importlib_resources - from jinja2 import Template from pydantic import Field, PrivateAttr from typing_extensions import override @@ -232,6 +227,10 @@ def process(self, offset: int = 0) -> GeneratorStepOutput: # type: ignore ) yield task_outputs, True + @override + def _sample_input(self) -> ChatType: + return self.prompt + # IMPLEMENTED TASKS class EmbeddingTaskGenerator(GeneratorTask): @@ -402,6 +401,10 @@ def format_output( pass return {"tasks": output} + @override + def _sample_input(self) -> ChatType: + return self.prompt + class GenerateTextRetrievalData(_EmbeddingDataGeneration): """Generate text retrieval data with an `LLM` to later on train an embedding model. diff --git a/src/distilabel/steps/tasks/magpie/generator.py b/src/distilabel/steps/tasks/magpie/generator.py index 2df8bf6217..c1e413d32c 100644 --- a/src/distilabel/steps/tasks/magpie/generator.py +++ b/src/distilabel/steps/tasks/magpie/generator.py @@ -15,6 +15,7 @@ from typing import TYPE_CHECKING, Any, Dict, Union from pydantic import Field +from typing_extensions import override from distilabel.errors import DistilabelUserError from distilabel.llms.mixins.magpie import MagpieChatTemplateMixin @@ -23,6 +24,7 @@ from distilabel.steps.tasks.magpie.base import MagpieBase if TYPE_CHECKING: + from distilabel.steps.tasks.typing import ChatType from distilabel.steps.typing import GeneratorStepOutput, StepColumns @@ -312,3 +314,7 @@ def process(self, offset: int = 0) -> "GeneratorStepOutput": ) generated += rows_to_generate # type: ignore yield (conversations, generated == self.num_rows) + + @override + def _sample_input(self) -> "ChatType": + return self._generate_with_pre_query_template(inputs=[{}]) diff --git a/src/distilabel/steps/tasks/quality_scorer.py b/src/distilabel/steps/tasks/quality_scorer.py index 57b7e38e2d..604f2a0276 100644 --- a/src/distilabel/steps/tasks/quality_scorer.py +++ b/src/distilabel/steps/tasks/quality_scorer.py @@ -262,3 +262,14 @@ def _format_structured_output( return orjson.loads(output) except orjson.JSONDecodeError: return {"scores": [None] * len(input["responses"])} + + @override + def _sample_input(self) -> ChatType: + return self.format_input( + { + "instruction": f"", + "responses": [ + f"" for i in range(2) + ], + } + ) diff --git a/src/distilabel/steps/tasks/ultrafeedback.py b/src/distilabel/steps/tasks/ultrafeedback.py index 04af9c1775..aeb57bda36 100644 --- a/src/distilabel/steps/tasks/ultrafeedback.py +++ b/src/distilabel/steps/tasks/ultrafeedback.py @@ -480,3 +480,14 @@ def _format_structured_output( "types": [None] * len(input["generations"]), "rationales-for-ratings": [None] * len(input["generations"]), } + + @override + def _sample_input(self) -> ChatType: + return self.format_input( + { + "instruction": f"", + "generations": [ + f"" for i in range(2) + ], + } + ) diff --git a/tests/integration/test_prints.py b/tests/integration/test_prints.py new file mode 100644 index 0000000000..1deda9df7e --- /dev/null +++ b/tests/integration/test_prints.py @@ -0,0 +1,71 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from functools import partial + +import pytest + +from distilabel.llms.mixins.magpie import MagpieChatTemplateMixin +from distilabel.steps import tasks as tasks_ +from tests.unit.conftest import DummyLLM + +# The tasks not listed here don't have a print method (or don't have a print method that works) +tasks = [ + tasks_.ComplexityScorer, + partial(tasks_.EvolInstruct, num_evolutions=1), + partial(tasks_.EvolComplexity, num_evolutions=1), + partial(tasks_.EvolComplexityGenerator, num_instructions=1), + partial(tasks_.EvolInstructGenerator, num_instructions=1), + partial(tasks_.EvolQuality, num_evolutions=1), + tasks_.Genstruct, + partial( + tasks_.BitextRetrievalGenerator, + source_language="English", + target_language="Spanish", + unit="sentence", + difficulty="elementary school", + high_score="4", + low_score="2.5", + ), + partial(tasks_.EmbeddingTaskGenerator, category="text-retrieval"), + tasks_.GenerateLongTextMatchingData, + tasks_.GenerateShortTextMatchingData, + tasks_.GenerateTextClassificationData, + tasks_.GenerateTextRetrievalData, + tasks_.MonolingualTripletGenerator, + tasks_.InstructionBacktranslation, + tasks_.Magpie, + tasks_.MagpieGenerator, + partial(tasks_.PrometheusEval, mode="absolute", rubric="factual-validity"), + tasks_.QualityScorer, + tasks_.SelfInstruct, + partial(tasks_.GenerateSentencePair, action="paraphrase"), + tasks_.UltraFeedback, + tasks_.URIAL, +] + + +class TestLLM(DummyLLM, MagpieChatTemplateMixin): + magpie_pre_query_template: str = "llama3" + + +llm = TestLLM() + + +@pytest.mark.parametrize("task", tasks) +def test_prints(task): + t = task(llm=llm) + t.load() + t.print() + t.unload() From e027f99393d14a6f51ae508eab663274b233ed29 Mon Sep 17 00:00:00 2001 From: Agus Date: Mon, 7 Oct 2024 15:52:58 +0200 Subject: [PATCH 78/82] Add `CLAIR` task (#926) * Redirect import of CLAIR * Add jinja2 template for CLAIR * Add CLAIR task * Add tests for CLAIR task * Update example in docstrings * Add tutorial to reproduce CLAIR * Show new tutorial in the gallery and fix rendering issue in docstrings --- docs/assets/pipelines/clair.png | Bin 0 -> 527548 bytes docs/sections/pipeline_samples/index.md | 15 ++ .../sections/pipeline_samples/papers/clair.md | 84 ++++++++ mkdocs.yml | 1 + src/distilabel/steps/tasks/__init__.py | 2 + src/distilabel/steps/tasks/clair.py | 199 ++++++++++++++++++ .../steps/tasks/templates/clair.jinja2 | 7 + tests/unit/steps/tasks/test_clair.py | 74 +++++++ 8 files changed, 382 insertions(+) create mode 100644 docs/assets/pipelines/clair.png create mode 100644 docs/sections/pipeline_samples/papers/clair.md create mode 100644 src/distilabel/steps/tasks/clair.py create mode 100644 src/distilabel/steps/tasks/templates/clair.jinja2 create mode 100644 tests/unit/steps/tasks/test_clair.py diff --git a/docs/assets/pipelines/clair.png b/docs/assets/pipelines/clair.png new file mode 100644 index 0000000000000000000000000000000000000000..c80e801f9075d04fc1e44a3d5f1f641962278b94 GIT binary patch literal 527548 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∈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

diff --git a/docs/sections/pipeline_samples/papers/clair.md b/docs/sections/pipeline_samples/papers/clair.md new file mode 100644 index 0000000000..8c0887460b --- /dev/null +++ b/docs/sections/pipeline_samples/papers/clair.md @@ -0,0 +1,84 @@ +# Contrastive Learning From AI Revisions (CLAIR) + +["Anchored Preference Optimization and Contrastive Revisions: Addressing Underspecification in Alignment"](https://huggingface.co/papers/2408.06266) introduces both Contrastive +Learning from AI Revisions (CLAIR), a data-creation method which leads to more contrastive preference pairs, and Anchored Preference Optimization (APO), a controllable and more stable alignment objective. While APO can be found in [TRL](https://huggingface.co/docs/trl/dpo_trainer#loss-functions), we have implemented a task for CLAIR in `distilabel`. + +CLAIR is a method for creating preference pairs which minimally revises one output to express a preference, resulting in a more precise learning signal as opposed to conventional methods which use a judge to select a preferred response. + +![CLAIR overview](../../../assets/pipelines/clair.png) + +The athors from the original paper shared a [collection of datasets from CLAIR and APO](https://huggingface.co/collections/ContextualAI/clair-and-apo-66b52868672bb1c984d1f3d5), where [ContextualAI/ultrafeedback_clair_32k](https://huggingface.co/datasets/ContextualAI/ultrafeedback_clair_32k) corresponds to the CLAIR implementation. + +### Replication + +!!! NOTE + The section is named `Replication` but in this case we are showing how to use the [`CLAIR`][distilabel.steps.tasks.clair.CLAIR] task create revisions for your generations using `distilabel`. + +To showcase CLAIR we will be using the [`CLAIR`][distilabel.steps.tasks.PrometheusEval] task implemented in `distilabel` and we are reusing a small sample of the already generated dataset by ContextualAI [`ContextualAI/ultrafeedback_clair_32k`](https://huggingface.co/datasets/ContextualAI/ultrafeedback_clair_32k) for testing. + +#### Installation + +To reproduce the code below, one will need to install `distilabel` as follows: + +```bash +pip install "distilabel>=1.4.0" +``` + +Depending on the LLM provider you want to use, the requirements may vary, take a look at the dependencies in that case, we are using for the example the free inference endpoints from Hugging Face, but that won't apply for a bigger dataset. + +#### Building blocks + +In this case where we already have instructions and their generations, we will just need to load the data and the corresponding CLAIR task for the revisions: + +- [`CLAIR`](https://distilabel.argilla.io/dev/components-gallery/tasks/clair/) to generate the revisions. + +#### Code + +Let's see the full pipeline applied to `ContextualAI/ultrafeedback_clair_32k` in `distilabel`: + +```python +from typing import Any, Dict + +from datasets import load_dataset + +from distilabel.pipeline import Pipeline +from distilabel.steps.tasks import CLAIR +from distilabel.llms import InferenceEndpointsLLM + + +def transform_ultrafeedback(example: Dict[str, Any]) -> Dict[str, Any]: + return { + "task": example["prompt"], + "student_solution": example["rejected"][1]["content"], + } + +dataset = ( + load_dataset("ContextualAI/ultrafeedback_clair_32k", split="train") + .select(range(10)) # We collect just 10 examples + .map(transform_ultrafeedback) # Apply the transformation to get just the text +) + +with Pipeline(name="CLAIR UltraFeedback sample") as pipeline: + clair = CLAIR( # (1) + llm=InferenceEndpointsLLM( + model_id="meta-llama/Meta-Llama-3.1-70B-Instruct", + generation_kwargs={ + "temperature": 0.7, + "max_new_tokens": 4096 + } + ) + ) + + +if __name__ == "__main__": + distiset = pipeline.run(dataset=dataset) # (2) + distiset.push_to_hub(repo_id="username/clair-test", include_script=True) # (3) +``` + +1. This Pipeline uses just CLAIR because we already have the generations, but one can just include a first task to create generations from instructions, and then the revisions with CLAIR. + +2. Include the dataset directly in the run method for simplicity. + +3. Push the distiset to the hub with the script for reproducibility. + +An example dataset can be found at: [distilabel-internal-testing/clair-test](https://huggingface.co/datasets/distilabel-internal-testing/clair-test). diff --git a/mkdocs.yml b/mkdocs.yml index 3c26a6cc17..c43332994d 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -209,6 +209,7 @@ nav: - Prometheus 2: "sections/pipeline_samples/papers/prometheus.md" - UltraFeedback: "sections/pipeline_samples/papers/ultrafeedback.md" - APIGen: "sections/pipeline_samples/papers/apigen.md" + - CLAIR: "sections/pipeline_samples/papers/clair.md" - Examples: - Benchmarking with distilabel: "sections/pipeline_samples/examples/benchmarking_with_distilabel.md" - Structured generation with outlines: "sections/pipeline_samples/examples/llama_cpp_with_outlines.md" diff --git a/src/distilabel/steps/tasks/__init__.py b/src/distilabel/steps/tasks/__init__.py index 065567a572..725fd065fd 100644 --- a/src/distilabel/steps/tasks/__init__.py +++ b/src/distilabel/steps/tasks/__init__.py @@ -17,6 +17,7 @@ from distilabel.steps.tasks.apigen.semantic_checker import APIGenSemanticChecker from distilabel.steps.tasks.argilla_labeller import ArgillaLabeller from distilabel.steps.tasks.base import GeneratorTask, Task +from distilabel.steps.tasks.clair import CLAIR from distilabel.steps.tasks.complexity_scorer import ComplexityScorer from distilabel.steps.tasks.evol_instruct.base import EvolInstruct from distilabel.steps.tasks.evol_instruct.evol_complexity.base import EvolComplexity @@ -89,6 +90,7 @@ "TextGeneration", "ChatItem", "ChatType", + "CLAIR", "UltraFeedback", "URIAL", ] diff --git a/src/distilabel/steps/tasks/clair.py b/src/distilabel/steps/tasks/clair.py new file mode 100644 index 0000000000..cbf189ab72 --- /dev/null +++ b/src/distilabel/steps/tasks/clair.py @@ -0,0 +1,199 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import importlib.resources as importlib_resources +from typing import TYPE_CHECKING, Any, Dict, Final, Union + +from jinja2 import Template +from pydantic import PrivateAttr + +from distilabel.steps.tasks.base import Task + +if TYPE_CHECKING: + from distilabel.steps.tasks.typing import ChatType + from distilabel.steps.typing import StepColumns + + +SYSTEM_PROMPT: Final[str] = ( + "You are a teacher and your task is to minimally improve a student's answer. I will give you a {task} and a {student_solution}. Your job is to revise the {student_solution} such that it is clearer, more correct, and more engaging. Copy all non-corrected parts of the student's answer. Do not allude to the {corrected_student_solution} being a revision or a correction in your final solution." +) + + +class CLAIR(Task): + r"""Contrastive Learning from AI Revisions (CLAIR). + + CLAIR uses an AI system to minimally revise a solution A→A´ such that the resulting + preference A `preferred` A’ is much more contrastive and precise. + + Input columns: + - task (`str`): The task or instruction. + - student_solution (`str`): An answer to the task that is to be revised. + + Output columns: + - revision (`str`): The revised text. + - rational (`str`): The rational for the provided revision. + - model_name (`str`): The name of the model used to generate the revision and rational. + + Categories: + - preference + - text-generation + + References: + - [`Anchored Preference Optimization and Contrastive Revisions: Addressing Underspecification in Alignment`](https://arxiv.org/abs/2408.06266v1) + - [`APO and CLAIR - GitHub Repository`](https://github.com/ContextualAI/CLAIR_and_APO) + + Examples: + Create contrastive preference pairs: + + ```python + from distilabel.steps.tasks import CLAIR + from distilabel.llms.huggingface import InferenceEndpointsLLM + + llm=InferenceEndpointsLLM( + model_id="meta-llama/Meta-Llama-3.1-70B-Instruct", + tokenizer_id="meta-llama/Meta-Llama-3.1-70B-Instruct", + generation_kwargs={ + "temperature": 0.7, + "max_new_tokens": 4096, + }, + ) + clair_task = CLAIR(llm=llm) + + clair_task.load() + + result = next( + clair_task.process( + [ + { + "task": "How many gaps are there between the earth and the moon?", + "student_solution": 'There are no gaps between the Earth and the Moon. The Moon is actually in a close orbit around the Earth, and it is held in place by gravity. The average distance between the Earth and the Moon is about 384,400 kilometers (238,900 miles), and this distance is known as the "lunar distance" or "lunar mean distance."\n\nThe Moon does not have a gap between it and the Earth because it is a natural satellite that is gravitationally bound to our planet. The Moon's orbit is elliptical, which means that its distance from the Earth varies slightly over the course of a month, but it always remains within a certain range.\n\nSo, to summarize, there are no gaps between the Earth and the Moon. The Moon is simply a satellite that orbits the Earth, and its distance from our planet varies slightly due to the elliptical shape of its orbit.' + } + ] + ) + ) + # result + # [{'task': 'How many gaps are there between the earth and the moon?', + # 'student_solution': 'There are no gaps between the Earth and the Moon. The Moon is actually in a close orbit around the Earth, and it is held in place by gravity. The average distance between the Earth and the Moon is about 384,400 kilometers (238,900 miles), and this distance is known as the "lunar distance" or "lunar mean distance."\n\nThe Moon does not have a gap between it and the Earth because it is a natural satellite that is gravitationally bound to our planet. The Moon\'s orbit is elliptical, which means that its distance from the Earth varies slightly over the course of a month, but it always remains within a certain range.\n\nSo, to summarize, there are no gaps between the Earth and the Moon. The Moon is simply a satellite that orbits the Earth, and its distance from our planet varies slightly due to the elliptical shape of its orbit.', + # 'revision': 'There are no physical gaps or empty spaces between the Earth and the Moon. The Moon is actually in a close orbit around the Earth, and it is held in place by gravity. The average distance between the Earth and the Moon is about 384,400 kilometers (238,900 miles), and this distance is known as the "lunar distance" or "lunar mean distance."\n\nThe Moon does not have a significant separation or gap between it and the Earth because it is a natural satellite that is gravitationally bound to our planet. The Moon\'s orbit is elliptical, which means that its distance from the Earth varies slightly over the course of a month, but it always remains within a certain range. This variation in distance is a result of the Moon\'s orbital path, not the presence of any gaps.\n\nIn summary, the Moon\'s orbit is continuous, with no intervening gaps, and its distance from the Earth varies due to the elliptical shape of its orbit.', + # 'rational': 'The student\'s solution provides a clear and concise answer to the question. However, there are a few areas where it can be improved. Firstly, the term "gaps" can be misleading in this context. The student should clarify what they mean by "gaps." Secondly, the student provides some additional information about the Moon\'s orbit, which is correct but could be more clearly connected to the main point. Lastly, the student\'s conclusion could be more concise.', + # 'distilabel_metadata': {'raw_output_c_l_a_i_r_0': '{teacher_reasoning}: The student\'s solution provides a clear and concise answer to the question. However, there are a few areas where it can be improved. Firstly, the term "gaps" can be misleading in this context. The student should clarify what they mean by "gaps." Secondly, the student provides some additional information about the Moon\'s orbit, which is correct but could be more clearly connected to the main point. Lastly, the student\'s conclusion could be more concise.\n\n{corrected_student_solution}: There are no physical gaps or empty spaces between the Earth and the Moon. The Moon is actually in a close orbit around the Earth, and it is held in place by gravity. The average distance between the Earth and the Moon is about 384,400 kilometers (238,900 miles), and this distance is known as the "lunar distance" or "lunar mean distance."\n\nThe Moon does not have a significant separation or gap between it and the Earth because it is a natural satellite that is gravitationally bound to our planet. The Moon\'s orbit is elliptical, which means that its distance from the Earth varies slightly over the course of a month, but it always remains within a certain range. This variation in distance is a result of the Moon\'s orbital path, not the presence of any gaps.\n\nIn summary, the Moon\'s orbit is continuous, with no intervening gaps, and its distance from the Earth varies due to the elliptical shape of its orbit.', + # 'raw_input_c_l_a_i_r_0': [{'role': 'system', + # 'content': "You are a teacher and your task is to minimally improve a student's answer. I will give you a {task} and a {student_solution}. Your job is to revise the {student_solution} such that it is clearer, more correct, and more engaging. Copy all non-corrected parts of the student's answer. Do not allude to the {corrected_student_solution} being a revision or a correction in your final solution."}, + # {'role': 'user', + # 'content': '{task}: How many gaps are there between the earth and the moon?\n\n{student_solution}: There are no gaps between the Earth and the Moon. The Moon is actually in a close orbit around the Earth, and it is held in place by gravity. The average distance between the Earth and the Moon is about 384,400 kilometers (238,900 miles), and this distance is known as the "lunar distance" or "lunar mean distance."\n\nThe Moon does not have a gap between it and the Earth because it is a natural satellite that is gravitationally bound to our planet. The Moon\'s orbit is elliptical, which means that its distance from the Earth varies slightly over the course of a month, but it always remains within a certain range.\n\nSo, to summarize, there are no gaps between the Earth and the Moon. The Moon is simply a satellite that orbits the Earth, and its distance from our planet varies slightly due to the elliptical shape of its orbit.\n\n-----------------\n\nLet\'s first think step by step with a {teacher_reasoning} to decide how to improve the {student_solution}, then give the {corrected_student_solution}. Mention the {teacher_reasoning} and {corrected_student_solution} identifiers to structure your answer.'}]}, + # 'model_name': 'meta-llama/Meta-Llama-3.1-70B-Instruct'}] + ``` + + Citations: + + ``` + @misc{doosterlinck2024anchoredpreferenceoptimizationcontrastive, + title={Anchored Preference Optimization and Contrastive Revisions: Addressing Underspecification in Alignment}, + author={Karel D'Oosterlinck and Winnie Xu and Chris Develder and Thomas Demeester and Amanpreet Singh and Christopher Potts and Douwe Kiela and Shikib Mehri}, + year={2024}, + eprint={2408.06266}, + archivePrefix={arXiv}, + primaryClass={cs.LG}, + url={https://arxiv.org/abs/2408.06266}, + } + ``` + """ + + system_prompt: str = SYSTEM_PROMPT + _template: Union[Template, None] = PrivateAttr(...) + + def load(self) -> None: + super().load() + _path = str( + importlib_resources.files("distilabel") + / "steps" + / "tasks" + / "templates" + / "clair.jinja2" + ) + with open(_path, "r") as f: + self._template = Template(f.read()) + + @property + def inputs(self) -> "StepColumns": + return ["task", "student_solution"] + + @property + def outputs(self) -> "StepColumns": + return ["revision", "rational", "model_name"] + + def format_input(self, input: Dict[str, Any]) -> "ChatType": + """The input is formatted as a `ChatType` assuming that the instruction + is the first interaction from the user within a conversation.""" + return [ + {"role": "system", "content": self.system_prompt}, + { + "role": "user", + "content": self._template.render( + task=input["task"], student_solution=input["student_solution"] + ), + }, + ] + + def format_output( + self, output: Union[str, None], input: Dict[str, Any] + ) -> Dict[str, Any]: + """The output is formatted as a list with the score of each instruction-response pair. + + Args: + output: the raw output of the LLM. + input: the input to the task. Used for obtaining the number of responses. + + Returns: + A dict with the key `scores` containing the scores for each instruction-response pair. + """ + if output is None: + return self._default_error() + + return self._format_output(output) + + def _format_output(self, output: Union[str, None]) -> Dict[str, Any]: + if "**Corrected Student Solution:**" in output: + splits = output.split("**Corrected Student Solution:**") + elif "{corrected_student_solution}:" in output: + splits = output.split("{corrected_student_solution}:") + elif "{corrected_student_solution}" in output: + splits = output.split("{corrected_student_solution}") + elif "**Worsened Student Solution:**" in output: + splits = output.split("**Worsened Student Solution:**") + elif "{worsened_student_solution}:" in output: + splits = output.split("{worsened_student_solution}:") + elif "{worsened_student_solution}" in output: + splits = output.split("{worsened_student_solution}") + else: + splits = None + + # Safety check when the output doesn't follow the expected format + if not splits: + return self._default_error() + + if len(splits) >= 2: + revision = splits[1] + revision = revision.strip("\n\n").strip() # noqa: B005 + + rational = splits[0] + if "{teacher_reasoning}" in rational: + rational = rational.split("{teacher_reasoning}")[1].strip(":").strip() + rational = rational.strip("\n\n").strip() # noqa: B005 + else: + return self._default_error() + return {"revision": revision, "rational": rational} + + def _default_error(self) -> Dict[str, None]: + return {"revision": None, "rational": None} diff --git a/src/distilabel/steps/tasks/templates/clair.jinja2 b/src/distilabel/steps/tasks/templates/clair.jinja2 new file mode 100644 index 0000000000..3815c6db83 --- /dev/null +++ b/src/distilabel/steps/tasks/templates/clair.jinja2 @@ -0,0 +1,7 @@ +{task}: {{ task }} + +{student_solution}: {{ student_solution }} + +----------------- + +Let's first think step by step with a {teacher_reasoning} to decide how to improve the {student_solution}, then give the {corrected_student_solution}. Mention the {teacher_reasoning} and {corrected_student_solution} identifiers to structure your answer. \ No newline at end of file diff --git a/tests/unit/steps/tasks/test_clair.py b/tests/unit/steps/tasks/test_clair.py new file mode 100644 index 0000000000..3d16c0bf48 --- /dev/null +++ b/tests/unit/steps/tasks/test_clair.py @@ -0,0 +1,74 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import Any, Dict, Union + +import pytest + +from distilabel.steps.tasks.clair import CLAIR +from tests.unit.conftest import DummyLLM + + +class TestCLAIR: + def test_format_input(self) -> None: + task = CLAIR(llm=DummyLLM()) + task.load() + + result = task.format_input( + input={"task": "TASK", "student_solution": "SOLUTION"} + ) + # System prompt + assert ( + result[0]["content"] + == "You are a teacher and your task is to minimally improve a student's answer. I will give you a {task} and a {student_solution}. Your job is to revise the {student_solution} such that it is clearer, more correct, and more engaging. Copy all non-corrected parts of the student's answer. Do not allude to the {corrected_student_solution} being a revision or a correction in your final solution." + ) + # User prompt + assert ( + result[1]["content"] + == """\ +{task}: TASK + +{student_solution}: SOLUTION + +----------------- + +Let's first think step by step with a {teacher_reasoning} to decide how to improve the {student_solution}, then give the {corrected_student_solution}. Mention the {teacher_reasoning} and {corrected_student_solution} identifiers to structure your answer. +""".strip() + ) + + @pytest.mark.parametrize( + "output, expected", + [ + (None, {"revision": None, "rational": None}), + ("WRONG", {"revision": None, "rational": None}), + ( + "{teacher_reasoning}\n\nreasoning\n\n{corrected_student_solution}\n\ncorrected", + {"revision": "corrected", "rational": "reasoning"}, + ), + ], + ) + def test_format_output( + self, + output: Union[str, None], + expected: Dict[str, Any], + ) -> None: + task = CLAIR(llm=DummyLLM()) + task.load() + + result = task.format_output( + output=output, + input={}, + ) + + assert result == expected From ebab004bffde58f123c22c324bd629358adb8818 Mon Sep 17 00:00:00 2001 From: Agus Date: Mon, 7 Oct 2024 18:33:49 +0200 Subject: [PATCH 79/82] Add cache at `Step` level (#766) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add signature method for Serializable objects * Update signature to only keep track of the step names and not it's internal info * Refactor hash generation * Add dummy batch manager from dag * Update batch manager cache tests to start batch manager from a DAG * Draft of integration tests for new caching * Checkpoint draft * Add cache directory location * Add use_cache argument to Step for future use * Change output names to keep track of them while debugging * Make use of use_cache at the step level * Add docstrings for internal batch manager arguments * Remove path from add_batch method * Move step caching to get_batch method in batch manager step * Read batches from cached dir * Set every step cache to False if the pipeline has the cache as False * Comment for the batch manager * Move back to caching from add_step * Checkpoint current status * Add use_cache on step * If there's previous data saved, concatenate the content of the parquet files * Only read the distiset from cache if all the steps are the same, otherwise overwrite * Add changes to make loading a new and modified step feasible * Set use cache to True by default * Move logic of registering the batches to BasePipeline._register_batch to do it before calling _manage_batch_flows * Avoid reading parquet file from cache when any of the steps has use_cach=False * Add is_convergence method to DAG and cleanup batch_manager * Add integration tests for the new caching mechanism * Update unit tests related to register_batch * Fix signature serialization case of void list * Add use_cache to argilla tests * Fix tests related to use_cache * Fix tests * Remove undefined object input * Add `_invalidate_steps_cache_if_required` method * Initial work for loading batches from `batch_manager_data` directory * Draft cache updates * Update pipeline signature * Add signature mixin from other PR * Moved pipeline cache to executions folder with different data per pipeline * Testing new updates to read from cache * Checkpoint with loading working while adding new steps * Point of control * Fix not all the batches where being saved * Sort batches after loaded * Fix `load_from_cache` to load batches from `steps_data` directory correctly * Update test * Add `step_has_finished` method * Update invalidate cache function * Update integration caching tests * Refactor to extract logic to methods * Refactor to remove `cached_data_dir` * Update stages message * Refactor `invalidate_cache_for` method * Fix `_BatchManager` unit tests * Update to not serialize `exclude_from_signature` attribute * Fix pipeline unit tests * Remove write buffer data if `use_cache=False` * Fix offline batch generation attributes were being not ignored by signature * Fix print test * Fix routing batch function --------- Co-authored-by: Gabriel Martín Blázquez --- src/distilabel/mixins/signature.py | 83 ++ src/distilabel/pipeline/_dag.py | 16 + src/distilabel/pipeline/base.py | 202 +++-- src/distilabel/pipeline/batch.py | 10 +- src/distilabel/pipeline/batch_manager.py | 360 +++++++-- src/distilabel/pipeline/local.py | 2 +- src/distilabel/pipeline/ray.py | 2 +- src/distilabel/pipeline/step_wrapper.py | 1 + src/distilabel/pipeline/write_buffer.py | 25 +- src/distilabel/steps/base.py | 11 +- tests/integration/test_caching_steps.py | 499 ++++++++++++ tests/integration/test_prints.py | 5 +- tests/unit/conftest.py | 2 +- tests/unit/pipeline/conftest.py | 26 + tests/unit/pipeline/test_base.py | 136 +++- tests/unit/pipeline/test_batch_manager.py | 746 ++++++------------ tests/unit/pipeline/utils.py | 2 + tests/unit/steps/argilla/test_base.py | 1 + tests/unit/steps/argilla/test_preference.py | 1 + .../steps/argilla/test_text_generation.py | 1 + .../steps/tasks/evol_instruct/test_base.py | 1 + .../tasks/evol_instruct/test_generator.py | 1 + .../steps/tasks/evol_quality/test_base.py | 1 + tests/unit/steps/tasks/magpie/test_base.py | 1 + .../unit/steps/tasks/magpie/test_generator.py | 1 + tests/unit/steps/tasks/test_base.py | 1 + tests/unit/steps/tasks/test_pair_rm.py | 1 + tests/unit/steps/test_base.py | 14 + 28 files changed, 1509 insertions(+), 643 deletions(-) create mode 100644 src/distilabel/mixins/signature.py create mode 100644 tests/integration/test_caching_steps.py diff --git a/src/distilabel/mixins/signature.py b/src/distilabel/mixins/signature.py new file mode 100644 index 0000000000..b014f03e90 --- /dev/null +++ b/src/distilabel/mixins/signature.py @@ -0,0 +1,83 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import hashlib +from typing import TYPE_CHECKING, Any, List, Set + +from pydantic import BaseModel, Field + +from distilabel.utils.serialization import TYPE_INFO_KEY + +if TYPE_CHECKING: + pass + +# Add here the name of the attributes that shouldn't be used to generate the signature. +# Attributes from a `BaseModel` that is an attribute from the root class must be prefixed +# with the name of the attribute followed by an underscore. For example, if the attribute +# `jobs_ids` is an attribute from the `llm` attribute of the root class it should be added +# as `llm_jobs_ids`. +_EXCLUDE_FROM_SIGNATURE_DEFAULTS = { + TYPE_INFO_KEY, + "disable_cuda_device_placement", + "input_batch_size", + "gpu_memory_utilization", + "resources", + "exclude_from_signature", + "llm_jobs_ids", + "llm_offline_batch_generation_block_until_done", +} + + +class SignatureMixin(BaseModel): + """Mixin for creating a signature (for cache) of the class. + + Attributes: + exclude_from_signature: list of attributes to exclude from the signature. + """ + + exclude_from_signature: Set[str] = Field( + default=_EXCLUDE_FROM_SIGNATURE_DEFAULTS, exclude=True + ) + + @property + def signature(self) -> str: + """Makes a signature (hash) of the class, using its attributes. + + Returns: + signature of the class. + """ + + def flatten_dump(d: Any, parent_key: str = "", sep: str = "_") -> List: + items = [] + for k, v in d.items(): + new_key = parent_key + sep + k if parent_key else k + if isinstance(v, dict): + items.extend(flatten_dump(v, new_key, sep=sep)) + elif isinstance(v, list): + if len(v) == 0: + items.append((new_key, "")) + elif isinstance(v[0], str): + items.append((new_key, "-".join(v))) + else: + for i, x in enumerate(v): + items.extend(flatten_dump(x, f"{new_key}-{i}", sep=sep)) + elif new_key not in self.exclude_from_signature: + items.append((new_key, v)) + return items + + info = [] + for name, value in flatten_dump(self.dump()): + info.append(f"{name}-{str(value)}") + + return hashlib.sha1("-".join(info).encode()).hexdigest() diff --git a/src/distilabel/pipeline/_dag.py b/src/distilabel/pipeline/_dag.py index 3cc22a5348..5962ecc4f0 100644 --- a/src/distilabel/pipeline/_dag.py +++ b/src/distilabel/pipeline/_dag.py @@ -33,6 +33,7 @@ from distilabel.constants import ( CONVERGENCE_STEP_ATTR_NAME, + RECEIVES_ROUTED_BATCHES_ATTR_NAME, ROUTING_BATCH_FUNCTION_ATTR_NAME, STEP_ATTR_NAME, ) @@ -253,6 +254,21 @@ def is_step_in_trophic_level(self, step_name: str, trophic_level: int) -> bool: """ return self.get_step_trophic_level(step_name) == trophic_level + def is_convergence_step(self, step_name: str) -> bool: + """Checks if a given step is a convegence step. + + Args: + step_name: Name of the step to check if a convergence step. + + Returns: + True if it is, False otherwise. + """ + predecessors = list(self.get_step_predecessors(step_name)) + return all( + self.get_step(predecessor).get(RECEIVES_ROUTED_BATCHES_ATTR_NAME, False) + for predecessor in predecessors + ) + def step_in_last_trophic_level(self, step_name: str) -> bool: """Checks if a step is in the last trophic level. diff --git a/src/distilabel/pipeline/base.py b/src/distilabel/pipeline/base.py index 1b4a2d8271..c3397c392c 100644 --- a/src/distilabel/pipeline/base.py +++ b/src/distilabel/pipeline/base.py @@ -14,6 +14,7 @@ import hashlib import logging import os +import shutil import signal import threading import time @@ -51,7 +52,6 @@ from distilabel.utils.logging import setup_logging, stop_logging from distilabel.utils.notebook import in_notebook from distilabel.utils.serialization import ( - TYPE_INFO_KEY, _Serializable, read_json, ) @@ -87,6 +87,7 @@ class _CacheLocation(TypedDict): pipeline: Path batch_manager: Path + steps_data: Path data: Path batch_input_data: Path log_file: Path @@ -125,7 +126,6 @@ def get_pipeline(cls) -> Union["BasePipeline", None]: _STEP_LOAD_FAILED_CODE = -666 _STEP_NOT_LOADED_CODE = -999 -_ATTRIBUTES_IGNORED_CACHE = ("disable_cuda_device_placement", "jobs_ids") _PIPELINE_DEFAULT_NAME = "__default_pipeline_name__" @@ -242,48 +242,18 @@ def _set_pipeline_name(self) -> None: if self.name == _PIPELINE_DEFAULT_NAME: self.name = f"pipeline_{'_'.join(self.dag)}" - def _create_signature(self) -> str: + @property + def signature(self) -> str: """Makes a signature (hash) of a pipeline, using the step ids and the adjacency between them. The main use is to find the pipeline in the cache folder. Returns: - int: Signature of the pipeline. + Signature of the pipeline. """ - hasher = hashlib.sha1() - steps_info = [] pipeline_dump = self.dump()["pipeline"] - - for step in pipeline_dump["steps"]: - step_info = step["name"] - for argument, value in sorted(step[constants.STEP_ATTR_NAME].items()): - if (argument == TYPE_INFO_KEY) or (value is None): - continue - - if isinstance(value, dict): - # input_mappings/output_mappings - step_info += "-".join( - [ - f"{str(k)}={str(v)}" - for k, v in value.items() - if k not in _ATTRIBUTES_IGNORED_CACHE - ] - ) - elif isinstance(value, (list, tuple)): - # runtime_parameters_info - step_info += "-".join([str(v) for v in value]) - elif isinstance(value, (int, str, float, bool)): - if argument not in _ATTRIBUTES_IGNORED_CACHE: - # batch_size/name - step_info += str(value) - else: - raise ValueError( - f"Field '{argument}' in step '{step['name']}' has type {type(value)}, explicitly cast the type to 'str'." - ) - - steps_info.append(step_info) - + steps_names = list(self.dag) connections_info = [ f"{c['from']}-{'-'.join(c['to'])}" for c in pipeline_dump["connections"] ] @@ -296,14 +266,13 @@ def _create_signature(self) -> str: ] if type_info := routing_batch_function._get_type_info(): step += f"-{type_info}" + routing_batch_functions_info.append(step) - hasher.update( + return hashlib.sha1( ",".join( - steps_info + connections_info + routing_batch_functions_info + steps_names + connections_info + routing_batch_functions_info ).encode() - ) - - return hasher.hexdigest() + ).hexdigest() def run( self, @@ -415,7 +384,7 @@ def run( stop_logging() return distiset - self._setup_write_buffer() + self._setup_write_buffer(use_cache) self._print_load_stages_info() @@ -716,16 +685,32 @@ def _cache_location(self) -> "_CacheLocation": Returns: Path: Filenames where the pipeline content will be serialized. """ - folder = self._cache_dir / self.name / self._create_signature() + folder = self._cache_dir / self.name / self.signature + pipeline_execution_dir = folder / "executions" / self.aggregated_steps_signature return { - "pipeline": folder / "pipeline.yaml", - "batch_manager": folder / "batch_manager.json", - "data": folder / "data", - "batch_input_data": folder / "batch_input_data", - "log_file": folder / "pipeline.log", - "stages_file": folder / "stages.json", + "pipeline": pipeline_execution_dir / "pipeline.yaml", + "batch_manager": pipeline_execution_dir / "batch_manager.json", + "steps_data": self._cache_dir / self.name / "steps_data", + "data": pipeline_execution_dir / "data", + "batch_input_data": pipeline_execution_dir / "batch_input_data", + "log_file": pipeline_execution_dir / "pipeline.log", + "stages_file": pipeline_execution_dir / "stages.json", } + @property + def aggregated_steps_signature(self) -> str: + """Creates an aggregated signature using `Step`s signature that will be used for + the `_BatchManager`. + + Returns: + The aggregated signature. + """ + signatures = [] + for step_name in self.dag: + step: "_Step" = self.dag.get_step(step_name)[constants.STEP_ATTR_NAME] + signatures.append(step.signature) + return hashlib.sha1("".join(signatures).encode()).hexdigest() + def _cache(self) -> None: """Saves the `BasePipeline` using the `_cache_filename`.""" if self._dry_run: @@ -737,7 +722,10 @@ def _cache(self) -> None: ) if self._batch_manager is not None: - self._batch_manager.cache(self._cache_location["batch_manager"]) + self._batch_manager.cache( + path=self._cache_location["batch_manager"], + steps_data_path=self._cache_location["steps_data"], + ) self._save_stages_status() @@ -814,30 +802,90 @@ def recursively_handle_secrets_and_excluded_attributes( def _load_batch_manager(self, use_cache: bool = True) -> None: """Will try to load the `_BatchManager` from the cache dir if found. Otherwise, it will create one from scratch. + + If the `_BatchManager` is loaded from cache, we check for invalid steps (those that + may have a different signature than the original in the pipeline folder), and + restart them, as well as their successors. + + Args: + use_cache: whether the cache should be used or not. """ batch_manager_cache_loc = self._cache_location["batch_manager"] + + # This first condition handles the case in which the pipeline is exactly the same + # no steps have been added, removed or changed. if use_cache and batch_manager_cache_loc.exists(): self._logger.info( f"💾 Loading `_BatchManager` from cache: '{batch_manager_cache_loc}'" ) - self._batch_manager = _BatchManager.load_from_cache(batch_manager_cache_loc) + self._batch_manager = _BatchManager.load_from_cache( + dag=self.dag, + batch_manager_path=batch_manager_cache_loc, + steps_data_path=self._cache_location["steps_data"], + ) + self._invalidate_steps_cache_if_required() + # In this other case, the pipeline has been changed. We need to create a new batch + # manager and if `use_cache==True` then check which outputs have we computed and + # cached for steps that haven't changed but that were executed in another pipeline, + # and therefore we can reuse else: - self._batch_manager = _BatchManager.from_dag(self.dag) + self._batch_manager = _BatchManager.from_dag( + dag=self.dag, + use_cache=use_cache, + steps_data_path=self._cache_location["steps_data"], + ) + + def _invalidate_steps_cache_if_required(self) -> None: + """Iterates over the steps of the pipeline and invalidates their cache if required.""" + for step_name in self.dag: + # `GeneratorStep`s doesn't receive input data so no need to check their + # `_BatchManagerStep` + if self.dag.get_step(step_name)[constants.STEP_ATTR_NAME].is_generator: + continue + + step: "_Step" = self.dag.get_step(step_name)[constants.STEP_ATTR_NAME] + if not step.use_cache: + self._batch_manager.invalidate_cache_for( + step_name=step.name, + dag=self.dag, + steps_data_path=self._cache_location["steps_data"], + ) # type: ignore + self._logger.info( + f"♻️ Step '{step.name}' won't use cache (`use_cache=False`). The cache of this step and their successors won't be" + " reused and the results will have to be recomputed." + ) + break - def _setup_write_buffer(self) -> None: + def _setup_write_buffer(self, use_cache: bool = True) -> None: """Setups the `_WriteBuffer` that will store the data of the leaf steps of the pipeline while running, so the `Distiset` can be created at the end. """ + if not use_cache and self._cache_location["data"].exists(): + shutil.rmtree(self._cache_location["data"]) buffer_data_path = self._cache_location["data"] / constants.STEPS_OUTPUTS_PATH self._logger.info(f"📝 Pipeline data will be written to '{buffer_data_path}'") - self._write_buffer = _WriteBuffer(buffer_data_path, self.dag.leaf_steps) + self._write_buffer = _WriteBuffer( + buffer_data_path, + self.dag.leaf_steps, + steps_cached={ + step_name: self.dag.get_step(step_name)[ + constants.STEP_ATTR_NAME + ].use_cache + for step_name in self.dag + }, + ) def _print_load_stages_info(self) -> None: """Prints the information about the load stages.""" stages, _ = self.dag.get_steps_load_stages() msg = "" for stage, steps in enumerate(stages): - msg += f"\n * Stage {stage}: {steps}" + steps_to_be_loaded = self._steps_to_be_loaded_in_stage(stage) + msg += f"\n * Stage {stage}:" + for step in steps: + msg += f"\n - '{step}'" + if step not in steps_to_be_loaded: + msg += " (results cached, won't be loaded and executed)" self._logger.info( f"⌛ The steps of the pipeline will be loaded in stages:{msg}" ) @@ -1094,6 +1142,26 @@ def _is_step_running(self, step_name: str) -> bool: with self._steps_load_status_lock: return self._steps_load_status[step_name] >= 1 + def _steps_to_be_loaded_in_stage(self, stage: int) -> List[str]: + """Returns the list of steps of the provided stage that should be loaded taking + into account if they have finished. + + Args: + stage: the stage number + + Returns: + A list containing the name of the steps that should be loaded in this stage. + """ + assert self._batch_manager, "Batch manager is not set" + + steps_stages, _ = self.dag.get_steps_load_stages() + + return [ + step + for step in steps_stages[stage] + if not self._batch_manager.step_has_finished(step) + ] + def _run_stage_steps_and_wait(self, stage: int) -> bool: """Runs the steps of the specified stage and waits for them to be ready. @@ -1103,9 +1171,10 @@ def _run_stage_steps_and_wait(self, stage: int) -> bool: Returns: `True` if all the steps have been loaded correctly, `False` otherwise. """ + assert self._batch_manager, "Batch manager is not set" - steps_stages, _ = self.dag.get_steps_load_stages() - steps = steps_stages[stage] + steps = self._steps_to_be_loaded_in_stage(stage) + self._logger.debug(f"Steps to be loaded in stage {stage}: {steps}") # Run the steps of the stage self._run_steps(steps=steps) @@ -1290,7 +1359,9 @@ def _add_batches_back_to_batch_manager(self) -> None: if not isinstance(batch, _Batch): continue self._batch_manager.add_batch( # type: ignore - to_step=step_name, batch=batch, prepend=True + to_step=step_name, + batch=batch, + prepend=True, ) self._logger.debug( f"Adding batch back to the batch manager: {batch}" @@ -1319,10 +1390,10 @@ def _manage_batch_flow(self, batch: "_Batch") -> None: """ assert self._batch_manager, "Batch manager is not set" - self._register_batch(batch) - route_to, do_not_route_to, routed = self._get_successors(batch) + self._register_batch(batch) + # Keep track of the steps that the batch was routed to if routed: batch.batch_routed_to = route_to @@ -1363,6 +1434,7 @@ def _manage_batch_flow(self, batch: "_Batch") -> None: while new_batch := self._batch_manager.get_batch(step.name): # type: ignore # if new_batch := self._batch_manager.get_batch(step.name): # type: ignore self._send_batch_to_step(new_batch) + else: self._request_more_batches_if_needed(step) else: @@ -1429,7 +1501,10 @@ def _register_batch(self, batch: "_Batch") -> None: Args: batch: The batch to register. """ - self._batch_manager.register_batch(batch) # type: ignore + assert self._batch_manager, "Batch manager is not set" + self._batch_manager.register_batch( + batch, steps_data_path=self._cache_location["steps_data"] + ) # type: ignore self._logger.debug( f"Batch {batch.seq_no} from step '{batch.step_name}' registered in batch" " manager" @@ -1453,7 +1528,6 @@ def _send_last_batch_flag_to_step(self, step_name: str) -> None: def _request_initial_batches(self) -> None: """Requests the initial batches to the generator steps.""" assert self._batch_manager, "Batch manager is not set" - for step in self._batch_manager._steps.values(): if not self._is_step_running(step.step_name): continue @@ -1513,7 +1587,9 @@ def _handle_batch_on_stop(self, batch: "_Batch") -> None: """ assert self._batch_manager, "Batch manager is not set" - self._batch_manager.register_batch(batch) + self._batch_manager.register_batch( + batch, steps_data_path=self._cache_location["steps_data"] + ) step: "Step" = self.dag.get_step(batch.step_name)[constants.STEP_ATTR_NAME] for successor in self.dag.get_step_successors(step.name): # type: ignore self._batch_manager.add_batch(successor, batch) diff --git a/src/distilabel/pipeline/batch.py b/src/distilabel/pipeline/batch.py index d8ad4312ae..684328f53e 100644 --- a/src/distilabel/pipeline/batch.py +++ b/src/distilabel/pipeline/batch.py @@ -37,8 +37,11 @@ class _Batch(_Serializable): data_hash: The hash of the data. Defaults to `None`. data_path: The path where the data of the batch is stored. Defaults to `None`. accumulated: A flag to indicate if the batch is accumulated. - created_from: A dictionary containing the `seq_no` of the batches of the steps that - were used to create this batch. + created_from: A dictionary containing which batches from which steps were used + to created this batch. The keys are the names of the steps and the values + are lists for each step containing the `seq_no` of each batch used, the original containing the `seq_no` of the batches of the steps that + size of the batch used and the number of rows used from the batch to create + this batch. size: The size of the batch. """ @@ -49,7 +52,7 @@ class _Batch(_Serializable): data_hash: Optional[str] = None data_path: Optional[str] = None accumulated: bool = False - created_from: Dict[str, List[Tuple[int, int]]] = field(default_factory=dict) + created_from: Dict[str, List[Tuple[int, int, int]]] = field(default_factory=dict) batch_routed_to: List[str] = field(default_factory=list) size: int = 0 _fs: Optional[fsspec.AbstractFileSystem] = None @@ -99,6 +102,7 @@ def get_data(self, num_rows: Union[int, None] = None) -> List[Dict[str, Any]]: data = self.data[0][:num_rows] self.data[0] = self.data[0][num_rows:] + # self.size = len(self.data[0]) self._update_data_hash() return data diff --git a/src/distilabel/pipeline/batch_manager.py b/src/distilabel/pipeline/batch_manager.py index 8ddfa30e09..9ca05e48e2 100644 --- a/src/distilabel/pipeline/batch_manager.py +++ b/src/distilabel/pipeline/batch_manager.py @@ -13,9 +13,10 @@ # limitations under the License. from collections import defaultdict +from copy import deepcopy from dataclasses import dataclass, field from pathlib import Path -from typing import TYPE_CHECKING, Any, Dict, Iterable, List, Tuple, Union +from typing import TYPE_CHECKING, Any, Dict, Iterable, List, Optional, Tuple, Union from distilabel.constants import ( RECEIVES_ROUTED_BATCHES_ATTR_NAME, @@ -70,6 +71,14 @@ class _BatchManagerStep(_Serializable): batch from step A used by steps B and C and obtained from the `created_from` of the batches created by them. It's used to avoid messing up the order of the batches. Only used if `convergence_step=True`. Defaults to `0`. + step_signature: The signature that defines a given `Step`. It will be used for the + caching mechanism. + use_cache: Flag from the original `Step` to indicate whether this step should make use of + the cached data. + step_offset: Dictionary with each key the predecessor/s step/s and as value a dict + with keys `batch` and `offset`, containing the name of the file for the corresponding + batch, and the number of rows that were read from that step, respectively. Used + for caching mechanism. """ step_name: str @@ -85,6 +94,9 @@ class _BatchManagerStep(_Serializable): ) next_expected_created_from_batch_seq_no: int = 0 next_expected_seq_no: Dict[str, Tuple[int, int]] = field(default_factory=dict) + step_signature: Optional[str] = None + use_cache: bool = False + step_offset: Dict[str, Tuple[int, int]] = field(default_factory=dict) def add_batch(self, batch: _Batch, prepend: bool = False) -> None: """Add a batch of data from `batch.step_name` to the step. It will accumulate the @@ -124,14 +136,22 @@ def get_batch(self) -> Union[_Batch, None]: if not self._ready_to_create_batch(): return None + seq_no = self._get_seq_no() + # `_last_batch` must be called before `_get_data`, as `_get_data` will update the # list of data which is used to determine if the batch to be created is the last one. - # TODO: remove `_last_batch` method and integrate logic in `_get_data` last_batch = self._last_batch() + + # Get the batch data and the information from which batches of the upstream steps + # the data was taken. data, created_from, batch_routed_to = self._get_data() + # Update the step offset i.e. which is the last batch and last row index from that + # batch that the step has consumed + self._update_offset(created_from) + return _Batch( - seq_no=self._get_seq_no(), + seq_no=seq_no, step_name=self.step_name, last_batch=last_batch, data=data, @@ -212,6 +232,9 @@ def from_step( data={predecessor: [] for predecessor in predecessors}, convergence_step=convergence_step, next_expected_seq_no={predecessor: (0, 0) for predecessor in predecessors}, + step_signature=step.signature, + use_cache=step.use_cache, + step_offset={predecessor: (0, 0) for predecessor in predecessors}, ) def _get_seq_no(self) -> int: @@ -226,7 +249,9 @@ def _get_seq_no(self) -> int: def _get_data( self, - ) -> Tuple[List[List[Dict[str, Any]]], Dict[str, List[Tuple[int, int]]], List[str]]: + ) -> Tuple[ + List[List[Dict[str, Any]]], Dict[str, List[Tuple[int, int, int]]], List[str] + ]: """Gets the data needed to create a batch for the step to process. If the step is accumulating data, then it will return a list with all the data received from the predecessors. Otherwise, it will return a list of data with the `input_batch_size` @@ -252,7 +277,7 @@ def _get_data( def _get_data_for_accumulate( self, - ) -> Tuple[List[List[Dict[str, Any]]], Dict[str, List[Tuple[int, int]]]]: + ) -> Tuple[List[List[Dict[str, Any]]], Dict[str, List[Tuple[int, int, int]]]]: """Gets the data needed to create a batch for the step to process when the step is accumulating data. It will return a list with all the data received from the predecessors. In addition, it will remove the data used to create the batch from @@ -268,7 +293,7 @@ def _get_data_for_accumulate( for step_name, batches in self.data.items(): batches_used[step_name] = [] for batch in batches: - batches_used[step_name].append((batch.seq_no, batch.size)) + batches_used[step_name].append((batch.seq_no, batch.size, batch.size)) data.append([row for batch in batches for row in batch.get_data()]) # Reset the data buffer self.data = {step_name: [] for step_name in self.data} @@ -276,7 +301,7 @@ def _get_data_for_accumulate( def _get_data_for_convergence_step( self, - ) -> Tuple[List[List[Dict[str, Any]]], Dict[str, List[Tuple[int, int]]]]: + ) -> Tuple[List[List[Dict[str, Any]]], Dict[str, List[Tuple[int, int, int]]]]: """Gets the data needed to create a batch for the step to process when the step is a convergence step. @@ -315,7 +340,7 @@ def _get_data_for_convergence_step( remaining_rows_per_step[batch.step_name] -= num_rows # type: ignore # Keep track of the batches used to create the batch - batches_used[batch.step_name].append((batch.seq_no, batch.size)) + batches_used[batch.step_name].append((batch.seq_no, batch.size, num_rows)) # If the batch was entirely consumed, then remove it from the buffer if len(batch.data[0]) == 0: @@ -336,7 +361,9 @@ def _get_data_for_convergence_step( def _get_data_normal( self, - ) -> Tuple[List[List[Dict[str, Any]]], Dict[str, List[Tuple[int, int]]], List[str]]: + ) -> Tuple[ + List[List[Dict[str, Any]]], Dict[str, List[Tuple[int, int, int]]], List[str] + ]: """Gets the data needed to create a batch for the step to process when the step is not accumulating data. It will return a list of data with the `input_batch_size` for each predecessor. In addition, it will remove the data used to create the batch @@ -374,7 +401,7 @@ def _get_data_normal( remaining_rows -= num_rows # Keep track of the batches used to create the batch - batches_used[step_name].append((batch.seq_no, batch.size)) + batches_used[step_name].append((batch.seq_no, batch.size, num_rows)) next_expected_seq_no = batch.seq_no @@ -552,6 +579,35 @@ def _last_batch(self) -> bool: return self._last_batch_normal() + def _update_offset( + self, created_from: Dict[str, List[Tuple[int, int, int]]] + ) -> None: + """Update the offset for the batch buffers of the upstream steps. + + Args: + created_from: A dictionary containing which batches from which steps were used + to created this batch. The keys are the names of the steps and the values + are lists for each step containing the `seq_no` of each batch used, the original containing the `seq_no` of the batches of the steps that + size of the batch used and the number of rows used from the batch to create + this batch. + """ + for predecessor, seq_no_and_batch in created_from.items(): + prev_last_batch_seq_no, prev_last_batch_offset = self.step_offset[ + predecessor + ] + last_batch_seq_no, _, last_batch_size = seq_no_and_batch[-1] + batch_offset = ( + prev_last_batch_offset + last_batch_size + if prev_last_batch_seq_no == last_batch_seq_no + else last_batch_size + ) + last_batch_seq_no = ( + last_batch_seq_no + if last_batch_seq_no > prev_last_batch_seq_no + else prev_last_batch_seq_no + ) + self.step_offset[predecessor] = (last_batch_seq_no, batch_offset) + def _last_batch_accumulate(self) -> bool: """Checks if the batch to be created is the last one for an step accumulating data. `True` if the last batch was received from all the predecessors. @@ -596,11 +652,7 @@ def _last_batch_normal(self) -> bool: num_rows = sum(len(batch.data[0]) for batch in batches) - if ( - self.input_batch_size - and num_rows > self.input_batch_size - and step_name in self.last_batch_received - ): + if self.input_batch_size and num_rows > self.input_batch_size: return False return True @@ -619,12 +671,12 @@ def _group_batches_by_created_from( for batches in self.data.values(): for batch in batches: first_key = next(iter(batch.created_from)) - batch_seq_no, batch_size = batch.created_from[first_key][0] + batch_seq_no, batch_size, _ = batch.created_from[first_key][0] grouped_batches[batch_seq_no].append((batch, batch_size)) return sorted((seq_no, batches) for seq_no, batches in grouped_batches.items()) def _model_dump(self, obj: Any, **kwargs: Any) -> Dict[str, Any]: - """Dumps the content of the `_BatchManagerStep` to a dictionary, using the `dataclass` helper function. + """Dumps the content of the `_BatchManagerStep` to a dictionary. Args: obj: Unused, just kept to match the signature of the parent method. @@ -648,8 +700,15 @@ def _model_dump(self, obj: Any, **kwargs: Any) -> Dict[str, Any]: "convergence_step_batches_consumed": self.convergence_step_batches_consumed, "next_expected_created_from_batch_seq_no": self.next_expected_created_from_batch_seq_no, "next_expected_seq_no": self.next_expected_seq_no, + "step_signature": self.step_signature, + "use_cache": self.use_cache, + "step_offset": self.step_offset, } + @property + def signature(self) -> str: + return f"{self.step_name}_{self.step_signature}" + class _BatchManager(_Serializable): """Class to manage the batches received from the steps. It keeps track of the @@ -675,9 +734,9 @@ def __init__( Args: steps: A dictionary with the step name as the key and a dictionary with the predecessor step name as the key and a list of batches as the value. - last_batch_received: A dictionary with the step name as the key and a the last + last_batch_received: A dictionary with the step name as the key and the last `_Batch` received from the step. - last_batch_sent: A dictionary with the step name as the key and a the last + last_batch_sent: A dictionary with the step name as the key and the last `_Batch` sent to the step. last_batch_flag_sent_to: A list with the names of the steps to which `LAST_BATCH_SENT_FLAG` was sent. @@ -695,7 +754,6 @@ def can_generate(self) -> bool: `True` if there are still batches to be processed by the steps. Otherwise, `False`. """ - for step_name, batch in self._last_batch_received.items(): if step_name not in self._last_batch_flag_sent_to: if not batch: @@ -709,17 +767,38 @@ def can_generate(self) -> bool: return False - def register_batch(self, batch: _Batch) -> None: + def register_batch( + self, batch: _Batch, steps_data_path: Optional["StrOrPath"] = None + ) -> None: """Method to register a batch received from a step. It will keep track of the sequence number and the last batch received from the step in the internal maps. Args: batch: _Batch from which we will register the sequence number and the last batch received. + steps_data_path: The path where the outputs of each `Step` (considering its + signature) will be saved for later reuse in another pipelines executions. """ last_batch = self._last_batch_received[batch.step_name] if not last_batch or (last_batch and last_batch.seq_no < batch.seq_no): self._last_batch_received[batch.step_name] = batch + if steps_data_path: + self.write_batch_data(batch, steps_data_path) + + def write_batch_data(self, batch: _Batch, steps_data_path: Path) -> None: + """Writes the batch to the steps data directory. + + Argument: + batch: the batch to be written. + steps_data_path: the steps data base directory. + """ + step = self._steps[batch.step_name] + batch_manager_data_dir = Path(steps_data_path) / step.signature + batch_manager_data_dir.mkdir(parents=True, exist_ok=True) + filename = batch_manager_data_dir / f"batch_{batch.seq_no}.json" + if not filename.exists(): + self.save(path=filename, format="json", dump=batch.dump()) + def get_last_batch(self, step_name: str) -> Union[_Batch, None]: """Gets the last batch received from a step. @@ -731,7 +810,12 @@ def get_last_batch(self, step_name: str) -> Union[_Batch, None]: """ return self._last_batch_received.get(step_name) - def add_batch(self, to_step: str, batch: _Batch, prepend: bool = False) -> None: + def add_batch( + self, + to_step: str, + batch: _Batch, + prepend: bool = False, + ) -> None: """Add an output batch from `batch.step_name` to `to_step`. Args: @@ -745,7 +829,6 @@ def add_batch(self, to_step: str, batch: _Batch, prepend: bool = False) -> None: """ if to_step not in self._steps: raise ValueError(f"Step '{to_step}' not found in the batch manager.") - step = self._steps[to_step] step.add_batch(batch, prepend) @@ -824,25 +907,46 @@ def set_last_batch_flag_sent_to(self, step_name: str) -> None: def set_next_expected_seq_no( self, step_name: str, from_step: str, next_expected_seq_no: int ) -> None: - """Sets the next expected sequence number of a `_Batch` received by `step` comming + """Sets the next expected sequence number of a `_Batch` received by `step` coming from `from_step`. Args: - step_name: The step name which next expected sequence number for `from_step` + step_name: The step name whose next expected sequence number for `from_step` has to be updated. from_step: The name of the step from which its next expected sequence number in step has to be updated. - next_expected_seq_no: the next expected sequence number of a `_Batch` comming + next_expected_seq_no: the next expected sequence number of a `_Batch` coming from `from_step`. """ self._steps[step_name].set_next_expected_seq_no(from_step, next_expected_seq_no) + def step_has_finished(self, step_name: str) -> bool: + """Indicates if the step has finished by checking if it sent a batch with `last_batch==True` + or it was sent the `LAST_BATCH_SENT_FLAG`. + + Args: + step_name: the name of the step to be checked. + + Returns: + `True` if step has finished generating batches, `False` otherwise. + """ + return step_name in self._last_batch_flag_sent_to or ( + self._last_batch_received[step_name] is not None + and self._last_batch_received[step_name].last_batch # type: ignore + ) + @classmethod - def from_dag(cls, dag: "DAG") -> "_BatchManager": + def from_dag( # noqa: C901 + cls, dag: "DAG", use_cache: bool = False, steps_data_path: Optional[Path] = None + ) -> "_BatchManager": """Create a `_BatchManager` instance from a `DAG` instance. Args: dag: The `DAG` instance. + use_cache: whether or not to try loading outputs from steps of previous pipelines + executions. Defaults to `False`. + steps_data_path: The path where the outputs of each `Step` (considering its + signature) will be saved for later reuse in another pipelines executions. Returns: A `_BatchManager` instance. @@ -850,12 +954,14 @@ def from_dag(cls, dag: "DAG") -> "_BatchManager": steps = {} last_batch_received = {} last_batch_sent = {} + last_batch_flag_sent_to = [] + + load_batches = {} + steps_to_load_data_from_previous_executions: Dict[str, Union[Path, None]] = {} for step_name in dag: step: "_Step" = dag.get_step(step_name)[STEP_ATTR_NAME] last_batch_received[step.name] = None last_batch_sent[step.name] = None - if step.is_generator: - continue predecessors = list(dag.get_step_predecessors(step_name)) convergence_step = all( dag.get_step(predecessor).get(RECEIVES_ROUTED_BATCHES_ATTR_NAME, False) @@ -866,8 +972,55 @@ def from_dag(cls, dag: "DAG") -> "_BatchManager": predecessors=predecessors, convergence_step=convergence_step, ) + + all_step_precessors_use_cache = all( + dag.get_step(step_name)[STEP_ATTR_NAME].use_cache + for step_name in predecessors + ) + if use_cache and step.use_cache and all_step_precessors_use_cache: + step_data_path = steps_data_path / batch_manager_step.signature + if step_data_path.exists(): + steps_to_load_data_from_previous_executions[step_name] = ( + step_data_path + ) + # We only want to load the outputs that are directly needed by the added + # steps, so if we need to load the outputs of one step and one of its + # predecessors it's in the list, then we remove it. + for predecessor in predecessors: + if predecessor in steps_to_load_data_from_previous_executions: + steps_to_load_data_from_previous_executions[predecessor] = ( + None + ) + steps[step_name] = batch_manager_step - return cls(steps, last_batch_received, last_batch_sent, []) + + for ( + step_name, + step_outputs_path, + ) in steps_to_load_data_from_previous_executions.items(): + last_batch_flag_sent_to.append(step_name) + if step_outputs_path is None: + continue + load_batches[step_name] = sorted( + [ + _Batch.from_json(batch_file) + for batch_file in step_outputs_path.glob("*.json") + if batch_file.is_file() and batch_file.suffix == ".json" + ], + key=lambda x: x.seq_no, + ) + last_batch_received[step_name] = load_batches[step_name][-1] + + # Load batches from previous steps in batch manager steps + for step_name, batch_manager_step in steps.items(): + for predecessor in dag.get_step_predecessors(step_name): + if predecessor in load_batches: + batch_manager_step.data[predecessor] = deepcopy( + load_batches[predecessor] + ) + batch_manager_step.last_batch_received.append(predecessor) + + return cls(steps, last_batch_received, last_batch_sent, last_batch_flag_sent_to) def _model_dump(self, obj: Any, **kwargs: Any) -> Dict[str, Any]: """Dumps the content of the `_BatchManager` to a dictionary. @@ -892,12 +1045,14 @@ def _model_dump(self, obj: Any, **kwargs: Any) -> Dict[str, Any]: "last_batch_flag_sent_to": self._last_batch_flag_sent_to, } - def cache(self, path: "StrOrPath") -> None: + def cache(self, path: Path, steps_data_path: Path) -> None: # noqa: C901 """Cache the `_BatchManager` to a file. Args: path: The path to the file where the `_BatchManager` will be cached. If `None`, then the `_BatchManager` will be cached in the default cache folder. + steps_data_path: The path where the outputs of each `Step` (considering its + signature) will be saved for later reuse in another pipelines executions. """ def save_batch( @@ -953,26 +1108,6 @@ def remove_files(keep_files: List[str], dir: Path) -> None: # Remove built `_Batch`es that were consumed from cache remove_files(step_dump["built_batches"], built_batches_dir) - # Store each `_BatchManagerStep` `_Batch`es in a separate file - for buffered_step_name in step_dump["data"]: - step_batches_dir = batch_manager_step_dir / buffered_step_name - step_batches_dir.mkdir(parents=True, exist_ok=True) - - # Store each `_Batch` in a separate file - step_dump["data"][buffered_step_name] = [ - str( - save_batch( - batches_dir=step_batches_dir, - batch_dump=batch_dump, - batch_list=self._steps[step_name].data[buffered_step_name], - ) - ) - for batch_dump in step_dump["data"][buffered_step_name] - ] - - # Remove `_Batch`es that were consumed from cache - remove_files(step_dump["data"][buffered_step_name], step_batches_dir) - # Store the `_BatchManagerStep` info batch_manager_step_file = str( path.parent / f"batch_manager_steps/{step_name}/batch_manager_step.json" @@ -986,29 +1121,138 @@ def remove_files(keep_files: List[str], dir: Path) -> None: self.save(path=path, format="json", dump=dump) @classmethod - def load_from_cache(cls, path: "StrOrPath") -> "_BatchManager": + def load_from_cache( + cls, dag: "DAG", batch_manager_path: "StrOrPath", steps_data_path: "StrOrPath" + ) -> "_BatchManager": """Loads the `_BatchManager` from a cache file. Args: path: The path to the cache file. """ - _check_is_dir(path) - content = read_json(path) + _check_is_dir(batch_manager_path) + content = read_json(batch_manager_path) # Read each `_BatchManagerStep` from file steps = {} for step_name, step_file in content["steps"].items(): steps[step_name] = read_json(step_file) + # When reading back from JSON, `next_expected_seq_no` and `step_offset` is a + # list (because JSON files do not have tuples). + steps[step_name]["next_expected_seq_no"] = { + k: tuple(v) for k, v in steps[step_name]["next_expected_seq_no"].items() + } + steps[step_name]["step_offset"] = { + k: tuple(v) for k, v in steps[step_name]["step_offset"].items() + } + + # TODO: where are we writing built batches now? xD # Read each `_Batch` from file steps[step_name]["built_batches"] = [ read_json(batch) for batch in steps[step_name]["built_batches"] ] - for buffered_step_name, batch_files in steps[step_name]["data"].items(): - steps[step_name]["data"][buffered_step_name] = [ - read_json(batch_file) for batch_file in batch_files - ] + # Read the batches from the `steps_data` directory to populate back the `_BatchManagerStep` + step_offset = steps[step_name]["step_offset"] + for successor_step_name, offset in step_offset.items(): + batch_offset, batch_row_offset = offset + step: "_Step" = dag.get_step(successor_step_name)[STEP_ATTR_NAME] + successor_step_data_path = ( + steps_data_path / f"{step.name}_{step.signature}" + ) + + # read batches from successor step from the step data directory taking into + # account offset + batches = [] + for batch_file in successor_step_data_path.glob("*.json"): + if not batch_file.is_file() or batch_file.suffix != ".json": + continue + + # If the batch number is lower than the batch offset then we should + # skip it as it has already been processed by the step + batch_no = int(batch_file.stem.split("batch_")[1]) + if batch_no < batch_offset: + continue + + # read the batch and skip the first N rows of the first batch + batch = read_json(batch_file) + if batch_no == batch_offset: + batch["data"][0] = batch["data"][0][batch_row_offset:] + + batches.append(batch) + + # sort batches by `seq_no` as it's a requirement for checking if ready to + # create next batch + batches.sort(key=lambda batch: batch["seq_no"]) + steps[step_name]["data"][successor_step_name] = batches content["steps"] = steps return cls.from_dict(content) + + def invalidate_cache_for( + self, step_name: str, dag: "DAG", steps_data_path: Path + ) -> None: + """Invalidates the cache for the given step and its predecessors. + + Args: + step_name: the name of the step for which the cache will be invalidated. + dag: the `DAG` of the pipeline containing the steps. + steps_data_path: the path where the output batches of each `Step` were saved + for reuse in another pipeline execution. + """ + invalidate_if_predecessor = [] + for sorted_step in dag: + if (sorted_step == step_name) or any( + predecessor in invalidate_if_predecessor + for predecessor in dag.get_step_predecessors(sorted_step) + ): + self._reset_batch_manager_for_step(sorted_step, dag) + invalidate_if_predecessor.append(sorted_step) + + self._load_predecessor_batches(step_name, dag, steps_data_path) + + def _reset_batch_manager_for_step(self, step_name: str, dag: "DAG") -> None: + """Resets the batch manager state for a given step i.e. creates a new clean `_BatchManagerStep` + for the step and removes the step name from the lists of states of the `BatchManager`. + + Args: + step_name: the name of step for which its batch manager state needs to be cleaned. + dag: the `DAG` of the pipeline containing the steps. + """ + predecessors = list(dag.get_step_predecessors(step_name)) + convergence_step = dag.is_convergence_step(step_name) + step = dag.get_step(step_name)[STEP_ATTR_NAME] + self._steps[step_name] = _BatchManagerStep.from_step( + step, predecessors=predecessors, convergence_step=convergence_step + ) + + self._last_batch_received[step_name] = None + self._last_batch_sent[step_name] = None + if step_name in self._last_batch_flag_sent_to: + self._last_batch_flag_sent_to.remove(step_name) + + def _load_predecessor_batches( + self, step_name: str, dag: "DAG", steps_data_path: Path + ) -> None: + """Loads the cached batches of the predecessors of the step in its `_BatchManagerStep`. + + Args: + step_name: the name of the step whose predecessors' batches will be loaded. + dag: the `DAG` of the pipeline containing the steps. + steps_data_path: the path where the output batches of each `Step` were saved + for reuse in another pipeline execution. + """ + for predecessor in dag.get_step_predecessors(step_name): + step_predecessor = dag.get_step(predecessor)[STEP_ATTR_NAME] + predecessor_step_data_path = ( + steps_data_path + / f"{step_predecessor.name}_{step_predecessor.signature}" + ) + batch_files = list_files_in_dir( + predecessor_step_data_path, key=lambda x: int(x.stem.split("_")[-1]) + ) + for file in batch_files: + batch = _Batch.from_file(file) + if batch.last_batch: + self._steps[step_name].last_batch_received.append(batch.step_name) + self._steps[step_name].data[predecessor].append(batch) diff --git a/src/distilabel/pipeline/local.py b/src/distilabel/pipeline/local.py index be7919d56d..c01cce303f 100644 --- a/src/distilabel/pipeline/local.py +++ b/src/distilabel/pipeline/local.py @@ -216,7 +216,7 @@ def run( initargs=( self._log_queue, self.name, - self._create_signature(), + self.signature, ), ) as pool, ): diff --git a/src/distilabel/pipeline/ray.py b/src/distilabel/pipeline/ray.py index cf9a26064a..70bf205ab3 100644 --- a/src/distilabel/pipeline/ray.py +++ b/src/distilabel/pipeline/ray.py @@ -310,7 +310,7 @@ def run(self) -> str: ), log_queue=self._log_queue, pipeline_name=self.name, - pipeline_cache_id=self._create_signature(), + pipeline_cache_id=self.signature, ) self._logger.debug( diff --git a/src/distilabel/pipeline/step_wrapper.py b/src/distilabel/pipeline/step_wrapper.py index 2ddfb10daa..844648f202 100644 --- a/src/distilabel/pipeline/step_wrapper.py +++ b/src/distilabel/pipeline/step_wrapper.py @@ -166,6 +166,7 @@ def _generator_step_process_loop(self) -> None: `process` method. """ step = cast("GeneratorStep", self.step) + try: if (batch := self.input_queue.get()) is None: self.step._logger.info( diff --git a/src/distilabel/pipeline/write_buffer.py b/src/distilabel/pipeline/write_buffer.py index e53d6faebf..3fdb037e14 100644 --- a/src/distilabel/pipeline/write_buffer.py +++ b/src/distilabel/pipeline/write_buffer.py @@ -15,7 +15,7 @@ import logging from os import PathLike from pathlib import Path -from typing import Any, Dict, List, Set +from typing import Any, Dict, List, Optional, Set import pyarrow as pa import pyarrow.parquet as pq @@ -33,12 +33,21 @@ class _WriteBuffer: is full, the content is written to a parquet file. """ - def __init__(self, path: "PathLike", leaf_steps: Set[str]) -> None: + def __init__( + self, + path: "PathLike", + leaf_steps: Set[str], + steps_cached: Optional[Dict[str, bool]] = None, + ) -> None: """ Args: path: Folder where the files will be written, the idea is for this path to be in the cache folder under /data. leaf_steps: Leaf steps from either the DAG of the Pipeline. + steps_cached: Dictionary with the name of a step and the variable + use_cache. We will use this to determine whether we have to read + a previous parquet table to concatenate before saving the cached + datasets. Raises: ValueError: If the path is not a directory. @@ -61,6 +70,7 @@ def __init__(self, path: "PathLike", leaf_steps: Set[str]) -> None: } self._buffer_last_schema = {} self._buffers_last_file: Dict[str, int] = {step: 1 for step in leaf_steps} + self._steps_cached = steps_cached or {} self._logger = logging.getLogger("distilabel.write_buffer") def _get_filename(self, step_name: str) -> Path: @@ -141,6 +151,17 @@ def _write(self, step_name: str) -> None: self._buffers_last_file[step_name] = next_file_number + 1 parquet_file = step_parquet_dir / f"{str(next_file_number).zfill(5)}.parquet" + if parquet_file.exists(): + # If the file already exists, due to some error in a pipeline that was cached + prev_table = pq.read_table(parquet_file) + # If some columns differ, it means some of the step changed, we won't load the previous table + # NOTE: If any step has use_cache=False, we cannot assume the previous parquet file is + # valid, so we will overwrite the previous parquet file. Is this the best option? + use_cache = False not in self._steps_cached.values() + + if prev_table.column_names == table.column_names and use_cache: + table = pa.concat_tables([prev_table, table]) + pq.write_table(table, parquet_file) self._logger.debug(f"Written to file '{parquet_file}'") diff --git a/src/distilabel/steps/base.py b/src/distilabel/steps/base.py index 48fcaaa004..c467005e0f 100644 --- a/src/distilabel/steps/base.py +++ b/src/distilabel/steps/base.py @@ -39,6 +39,7 @@ RuntimeParameter, RuntimeParametersMixin, ) +from distilabel.mixins.signature import SignatureMixin from distilabel.utils.serialization import _Serializable, write_json from distilabel.utils.typing_ import is_parameter_annotated_with @@ -133,7 +134,14 @@ class StepResources(RuntimeParametersMixin, BaseModel): ) -class _Step(RuntimeParametersMixin, RequirementsMixin, BaseModel, _Serializable, ABC): +class _Step( + RuntimeParametersMixin, + RequirementsMixin, + SignatureMixin, + BaseModel, + _Serializable, + ABC, +): """Base class for the steps that can be included in a `Pipeline`. A `Step` is a class defining some processing logic. The input and outputs for this @@ -193,6 +201,7 @@ def process(self, inputs: *StepInput) -> StepOutput: pipeline: Any = Field(default=None, exclude=True, repr=False) input_mappings: Dict[str, str] = {} output_mappings: Dict[str, str] = {} + use_cache: bool = True _pipeline_artifacts_path: Path = PrivateAttr(None) _built_from_decorator: bool = PrivateAttr(default=False) diff --git a/tests/integration/test_caching_steps.py b/tests/integration/test_caching_steps.py new file mode 100644 index 0000000000..5ed8af993f --- /dev/null +++ b/tests/integration/test_caching_steps.py @@ -0,0 +1,499 @@ +# Copyright 2023-present, Argilla, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from tempfile import TemporaryDirectory +from typing import TYPE_CHECKING, Any, Dict, Generator, List +from unittest import mock +from uuid import uuid4 + +from pydantic import PrivateAttr + +from distilabel.pipeline import Pipeline +from distilabel.steps import LoadDataFromDicts +from distilabel.steps.base import Step, StepInput + +if TYPE_CHECKING: + from distilabel.pipeline.batch import _Batch + + +class DummyStep(Step): + attr: int = 5 + do_fail: bool = False + _ctr: int = PrivateAttr(default=0) + + _random: str = PrivateAttr(default="") + + def load(self) -> None: + super().load() + self._random = str(uuid4()) + + @property + def inputs(self) -> List[str]: + return ["instruction"] + + def process(self, inputs: StepInput) -> Generator[List[Dict[str, Any]], None, None]: + for input in inputs: + input["response"] = f"I don't know - {self._ctr} - {self._random}" + self._ctr += 1 + + if self.do_fail: + raise ValueError("The step failed") + yield inputs + + @property + def outputs(self) -> List[str]: + return ["response"] + + +class DummyStep2(DummyStep): + def process( + self, *inputs: StepInput + ) -> Generator[List[Dict[str, Any]], None, None]: + outputs = [] + for input_a, input_b in zip(*inputs): + output = {**input_a, **input_b} + output["response"] = f"I don't know - {self._ctr}" + self._ctr += 1 + outputs.append(output) + yield outputs + + +class OtherDummyStep(DummyStep): + pass + + +def test_cache() -> None: + with TemporaryDirectory() as tmp_dir: + with Pipeline(name="test_pipeline_caching", cache_dir=tmp_dir) as pipeline: + initial_batch_size = 8 + step_generator = LoadDataFromDicts( + data=[{"instruction": "some text"}] * initial_batch_size * 6, + batch_size=initial_batch_size, + ) + + step_a = DummyStep( + name="step_a", + input_batch_size=4, + use_cache=True, + ) + step_b = DummyStep( + name="step_b", + input_batch_size=10, + input_mappings={"instruction": "response"}, + output_mappings={"response": "response_1"}, + do_fail=False, + use_cache=True, + ) + step_c = DummyStep( + name="step_c", + input_batch_size=12, + input_mappings={"instruction": "response"}, + output_mappings={"response": "response_2"}, + use_cache=True, + ) + + step_generator >> step_a >> step_b >> step_c + + distiset_0 = pipeline.run() + distiset_1 = pipeline.run() + assert ( + distiset_0["default"]["train"].to_list() + == distiset_1["default"]["train"].to_list() + ) + + distiset_2 = pipeline.run(use_cache=False) + assert len(distiset_2["default"]["train"]) == 48 + assert ( + distiset_0["default"]["train"].to_list() + != distiset_2["default"]["train"].to_list() + ) + + +def test_cache_with_step_cache_false() -> None: + with TemporaryDirectory() as tmp_dir: + with Pipeline(name="test_pipeline_caching", cache_dir=tmp_dir) as pipeline: + initial_batch_size = 8 + step_generator = LoadDataFromDicts( + data=[{"instruction": "some text"}] * initial_batch_size * 6, + batch_size=initial_batch_size, + ) + + step_a = DummyStep( + name="step_a", + input_batch_size=4, + use_cache=True, + ) + step_b = DummyStep( + name="step_b", + input_batch_size=10, + input_mappings={"instruction": "response"}, + output_mappings={"response": "response_1"}, + do_fail=False, + use_cache=False, + ) + + step_generator >> step_a >> step_b + + distiset_0 = pipeline.run() + + with mock.patch.object( + pipeline, "_run_step", wraps=pipeline._run_step + ) as run_step_spy: + distiset_1 = pipeline.run() + + # check that only `step_b` has been executed + assert run_step_spy.call_count == 1 + + assert ( + distiset_0["default"]["train"].to_list() + != distiset_1["default"]["train"].to_list() + ) + + +def test_cache_with_step_changing() -> None: + with TemporaryDirectory() as tmp_dir: + with Pipeline(name="test_pipeline_caching", cache_dir=tmp_dir) as pipeline: + initial_batch_size = 8 + step_generator = LoadDataFromDicts( + data=[{"instruction": "some text"}] * initial_batch_size * 6, + batch_size=initial_batch_size, + ) + + step_a = DummyStep( + name="step_a", + input_batch_size=4, + use_cache=True, + ) + step_b = DummyStep( + name="step_b", + input_batch_size=10, + input_mappings={"instruction": "response"}, + output_mappings={"response": "response_1"}, + do_fail=False, + use_cache=True, + ) + + step_generator >> step_a >> step_b + + distiset_0 = pipeline.run() + + with Pipeline(name="test_pipeline_caching", cache_dir=tmp_dir) as pipeline: + initial_batch_size = 8 + step_generator = LoadDataFromDicts( + data=[{"instruction": "some text"}] * initial_batch_size * 6, + batch_size=initial_batch_size, + ) + + step_a = DummyStep( + name="step_a", + input_batch_size=4, + use_cache=True, + ) + step_b = DummyStep( + name="step_b", + attr=103401234, # change attribute so step is not the same + input_batch_size=10, + input_mappings={"instruction": "response"}, + output_mappings={"response": "response_1"}, + do_fail=False, + use_cache=True, + ) + + step_generator >> step_a >> step_b + + with mock.patch.object( + pipeline, "_run_step", wraps=pipeline._run_step + ) as run_step_spy: + distiset_1 = pipeline.run() + + # check that only `step_b` has been executed + assert run_step_spy.call_count == 1 + + assert ( + distiset_0["default"]["train"].to_list() + != distiset_1["default"]["train"].to_list() + ) + + +def test_cache_with_intermediate_step_cache_false() -> None: + with TemporaryDirectory() as tmp_dir: + with Pipeline(name="test_pipeline_caching", cache_dir=tmp_dir) as pipeline: + initial_batch_size = 8 + step_generator = LoadDataFromDicts( + data=[{"instruction": "some text"}] * initial_batch_size * 6, + batch_size=initial_batch_size, + ) + + step_a = DummyStep( + name="step_a", + input_batch_size=4, + use_cache=True, + ) + step_b = DummyStep( + name="step_b", + input_batch_size=10, + input_mappings={"instruction": "response"}, + output_mappings={"response": "response_1"}, + do_fail=False, + use_cache=False, + ) + step_c = DummyStep( + name="step_c", + input_batch_size=12, + input_mappings={"instruction": "response"}, + output_mappings={"response": "response_2"}, + use_cache=True, + ) + + step_generator >> step_a >> step_b >> step_c + + distiset_0 = pipeline.run() + + with mock.patch.object( + pipeline, "_run_step", wraps=pipeline._run_step + ) as run_step_spy: + distiset_1 = pipeline.run() + + # check that only `step_b` and `step_c` has been executed + assert run_step_spy.call_count == 2 + + assert ( + distiset_0["default"]["train"].to_list() + != distiset_1["default"]["train"].to_list() + ) + + +def test_cache_adding_step() -> None: + with TemporaryDirectory() as tmp_dir: + with Pipeline(name="test_pipeline_caching", cache_dir=tmp_dir) as pipeline: + initial_batch_size = 8 + step_generator = LoadDataFromDicts( + data=[{"instruction": "some text"}] * initial_batch_size * 6, + batch_size=initial_batch_size, + ) + + step_a = DummyStep( + name="step_a", + input_batch_size=4, + use_cache=True, + ) + step_b = DummyStep( + name="step_b", + input_batch_size=10, + input_mappings={"instruction": "response"}, + output_mappings={"response": "response_1"}, + do_fail=False, + use_cache=True, + ) + + step_generator >> step_a >> step_b + + distiset_0 = pipeline.run() + + with Pipeline(name="test_pipeline_caching", cache_dir=tmp_dir) as pipeline: + initial_batch_size = 8 + step_generator = LoadDataFromDicts( + data=[{"instruction": "some text"}] * initial_batch_size * 6, + batch_size=initial_batch_size, + ) + + step_a = DummyStep( + name="step_a", + input_batch_size=4, + use_cache=True, + ) + step_b = DummyStep( + name="step_b", + input_batch_size=10, + input_mappings={"instruction": "response"}, + output_mappings={"response": "response_1"}, + do_fail=False, + use_cache=True, + ) + step_c = DummyStep( + name="step_c", + input_batch_size=12, + input_mappings={"instruction": "response"}, + output_mappings={"response": "response_2"}, + use_cache=True, + ) + + step_generator >> step_a >> step_b >> step_c + + with mock.patch.object( + pipeline, "_run_step", wraps=pipeline._run_step + ) as run_step_spy: + distiset_1 = pipeline.run() + + # check that only `step_c` has been executed + assert run_step_spy.call_count == 1 + + dict_0 = distiset_0["default"]["train"].to_dict() + dict_1 = distiset_1["default"]["train"].to_dict() + del dict_1["response_2"] + assert dict_0 == dict_1 + + +def test_cache_adding_step_with_multiple_predecessor() -> None: + with TemporaryDirectory() as tmp_dir: + with Pipeline(name="test_pipeline_caching", cache_dir=tmp_dir) as pipeline: + initial_batch_size = 8 + step_generator = LoadDataFromDicts( + data=[{"instruction": "some text"}] * initial_batch_size * 6, + batch_size=initial_batch_size, + ) + + step_a = DummyStep( + name="step_a", + input_batch_size=4, + output_mappings={"response": "response_1"}, + use_cache=True, + ) + step_b = DummyStep( + name="step_b", + input_batch_size=10, + output_mappings={"response": "response_2"}, + do_fail=False, + use_cache=True, + ) + + step_generator >> [step_a, step_b] + + distiset_0 = pipeline.run() + + with Pipeline(name="test_pipeline_caching", cache_dir=tmp_dir) as pipeline: + initial_batch_size = 8 + step_generator = LoadDataFromDicts( + data=[{"instruction": "some text"}] * initial_batch_size * 6, + batch_size=initial_batch_size, + ) + + step_a = DummyStep( + name="step_a", + input_batch_size=4, + output_mappings={"response": "response_1"}, + use_cache=True, + ) + step_b = DummyStep( + name="step_b", + input_batch_size=10, + output_mappings={"response": "response_2"}, + do_fail=False, + use_cache=True, + ) + step_c = DummyStep2( + name="step_c", + input_batch_size=12, + output_mappings={"response": "response_3"}, + use_cache=True, + ) + + step_generator >> [step_a, step_b] >> step_c + + with mock.patch.object( + pipeline, "_run_step", wraps=pipeline._run_step + ) as run_step_spy: + distiset_1 = pipeline.run() + + # check that only `step_c` has been executed + assert run_step_spy.call_count == 1 + + for row_1, row_0_a, row_0_b in zip( + distiset_1["default"]["train"], + distiset_0["step_a"]["train"], + distiset_0["step_b"]["train"], + ): + assert row_1["response_1"] == row_0_a["response_1"] + assert row_1["response_2"] == row_0_b["response_2"] + + +def test_cache_with_offset() -> None: + use_cache_per_step = True + do_fail = False + with TemporaryDirectory() as tmp_dir: + with Pipeline(name="test_pipeline_caching", cache_dir=tmp_dir) as pipeline_0: + initial_batch_size = 8 + step_generator = LoadDataFromDicts( + data=[{"instruction": "some text"}] * initial_batch_size * 6, + batch_size=initial_batch_size, + ) + + step_a = DummyStep( + name="step_a", input_batch_size=4, use_cache=use_cache_per_step + ) + step_b = DummyStep( + name="step_b", + input_batch_size=10, + input_mappings={"instruction": "response"}, + output_mappings={"response": "response_1"}, + do_fail=do_fail, + use_cache=use_cache_per_step, + ) + step_c = DummyStep( + name="step_c", + input_batch_size=12, + input_mappings={"instruction": "response"}, + output_mappings={"response": "response_2"}, + use_cache=use_cache_per_step, + ) + + step_generator >> step_a >> step_b >> step_c + + # Controlled failure of the Pipeline + original_process_batch = pipeline_0._process_batch + + def _process_batch_wrapper( + batch: "_Batch", send_last_batch_flag: bool = True + ) -> None: + if batch.step_name == step_b.name and batch.seq_no == 2: + pipeline_0._stop_called = True + original_process_batch(batch) + + # Run first time and stop the pipeline when specific batch received (simulate CTRL + C) + with mock.patch.object(pipeline_0, "_process_batch", _process_batch_wrapper): + distiset_0 = pipeline_0.run(use_cache=False) + + assert len(distiset_0["default"]["train"]) == 12 + + with Pipeline(name="test_pipeline_caching", cache_dir=tmp_dir) as pipeline_1: + initial_batch_size = 8 + step_generator = LoadDataFromDicts( + data=[{"instruction": "some text"}] * initial_batch_size * 6, + batch_size=initial_batch_size, + ) + + step_a = DummyStep( + name="step_a", input_batch_size=4, use_cache=use_cache_per_step + ) + step_b = DummyStep( + name="step_b", + input_batch_size=10, + input_mappings={"instruction": "response"}, + output_mappings={"response": "response_1"}, + do_fail=do_fail, + use_cache=use_cache_per_step, + ) + step_c = DummyStep( + name="step_c", + input_batch_size=12, + input_mappings={"instruction": "response"}, + output_mappings={"response": "response_2"}, + use_cache=use_cache_per_step, + ) + + step_generator >> step_a >> step_b >> step_c + + distiset_1 = pipeline_1.run() + + assert len(distiset_1["default"]["train"]) == 48 diff --git a/tests/integration/test_prints.py b/tests/integration/test_prints.py index 1deda9df7e..7db85caf8f 100644 --- a/tests/integration/test_prints.py +++ b/tests/integration/test_prints.py @@ -13,6 +13,7 @@ # limitations under the License. from functools import partial +from typing import Union import pytest @@ -57,14 +58,14 @@ class TestLLM(DummyLLM, MagpieChatTemplateMixin): - magpie_pre_query_template: str = "llama3" + magpie_pre_query_template: Union[str, None] = "llama3" llm = TestLLM() @pytest.mark.parametrize("task", tasks) -def test_prints(task): +def test_prints(task) -> None: t = task(llm=llm) t.load() t.print() diff --git a/tests/unit/conftest.py b/tests/unit/conftest.py index 1903d10e3c..8c7c240b09 100644 --- a/tests/unit/conftest.py +++ b/tests/unit/conftest.py @@ -53,7 +53,7 @@ def model_name(self) -> str: return "test" def generate( # type: ignore - self, input: "FormattedInput", num_generations: int = 1 + self, inputs: "FormattedInput", num_generations: int = 1 ) -> "GenerateOutput": return ["output" for _ in range(num_generations)] diff --git a/tests/unit/pipeline/conftest.py b/tests/unit/pipeline/conftest.py index b3e708a178..a2bf2b932d 100644 --- a/tests/unit/pipeline/conftest.py +++ b/tests/unit/pipeline/conftest.py @@ -14,7 +14,10 @@ import pytest +from distilabel.pipeline._dag import DAG +from distilabel.pipeline.batch_manager import _BatchManager from distilabel.pipeline.local import Pipeline +from distilabel.steps.base import GeneratorStep, GlobalStep, Step from .utils import DummyGeneratorStep, DummyGlobalStep, DummyStep1, DummyStep2 @@ -42,3 +45,26 @@ def dummy_generator_step_fixture(pipeline: "Pipeline") -> DummyGeneratorStep: @pytest.fixture(name="dummy_global_step") def dummy_global_step_fixture(pipeline: "Pipeline") -> DummyGlobalStep: return DummyGlobalStep(name="dummy_global_step", pipeline=pipeline) + + +@pytest.fixture(name="dummy_dag") +def dummy_dag_fixture( + dummy_generator_step: "GeneratorStep", + dummy_step_1: "Step", + dummy_step_2: "Step", + dummy_global_step: "GlobalStep", +) -> DAG: + dag = DAG() + dag.add_step(dummy_generator_step) + dag.add_step(dummy_step_1) + dag.add_step(dummy_step_2) + dag.add_step(dummy_global_step) + dag.add_edge("dummy_generator_step", "dummy_step_1") + dag.add_edge("dummy_generator_step", "dummy_global_step") + dag.add_edge("dummy_step_1", "dummy_step_2") + return dag + + +@pytest.fixture(name="dummy_batch_manager") +def dummy_batch_manager_from_dag_fixture(dummy_dag: DAG) -> _BatchManager: + return _BatchManager.from_dag(dummy_dag) diff --git a/tests/unit/pipeline/test_base.py b/tests/unit/pipeline/test_base.py index 5f38781a04..86db3f5cfb 100644 --- a/tests/unit/pipeline/test_base.py +++ b/tests/unit/pipeline/test_base.py @@ -95,6 +95,28 @@ def test_get_pipeline(self) -> None: class TestBasePipeline: + def test_aggregated_steps_signature(self) -> None: + with DummyPipeline(name="dummy") as pipeline_0: + generator = DummyGeneratorStep() + step = DummyStep1() + step2 = DummyStep1() + step3 = DummyStep2() + + generator >> [step, step2] >> step3 + + with DummyPipeline(name="dummy") as pipeline_1: + generator = DummyGeneratorStep() + step = DummyStep1() + step2 = DummyStep1() + step3 = DummyStep2() + + generator >> [step, step2] >> step3 + + assert ( + pipeline_0.aggregated_steps_signature + == pipeline_1.aggregated_steps_signature + ) + def test_context_manager(self) -> None: assert _GlobalPipelineManager.get_pipeline() is None @@ -123,12 +145,18 @@ def test_load_batch_manager(self, use_cache: bool) -> None: if use_cache: mock_load_from_cache.assert_called_once_with( - pipeline._cache_location["batch_manager"] + dag=pipeline.dag, + batch_manager_path=pipeline._cache_location["batch_manager"], + steps_data_path=pipeline._cache_location["steps_data"], ) mock_from_dag.assert_not_called() else: mock_load_from_cache.assert_not_called() - mock_from_dag.assert_called_once_with(pipeline.dag) + mock_from_dag.assert_called_once_with( + dag=pipeline.dag, + use_cache=use_cache, + steps_data_path=pipeline._cache_location["steps_data"], + ) def test_setup_write_buffer(self) -> None: pipeline = DummyPipeline(name="unit-test-pipeline") @@ -328,6 +356,7 @@ def test_run_stage_steps_and_wait(self, caplog) -> None: generator >> [step, step2] >> step3 >> step4 + pipeline._load_batch_manager() pipeline._steps_load_status = { # type: ignore generator.name: 1, step.name: 1, @@ -351,6 +380,7 @@ def test_run_stage_steps_and_wait_with_failing_step(self, caplog) -> None: generator >> [step, step2] >> step3 >> step4 pipeline._init_steps_load_status() + pipeline._load_batch_manager() pipeline._steps_load_status[generator.name] = _STEP_LOAD_FAILED_CODE # type: ignore caplog.set_level(logging.INFO) @@ -368,6 +398,7 @@ def test_run_stage_steps_and_wait_stop_called(self) -> None: generator >> [step, step2] >> step3 >> step4 pipeline._init_steps_load_status() + pipeline._load_batch_manager() pipeline._stop_called = True assert pipeline._run_stage_steps_and_wait(stage=0) is False @@ -626,7 +657,9 @@ def test_register_batch(self) -> None: batch = _Batch(seq_no=0, step_name=generator.name, last_batch=False) # type: ignore pipeline._register_batch(batch) - pipeline._batch_manager.register_batch.assert_called_once_with(batch) + pipeline._batch_manager.register_batch.assert_called_once_with( + batch, steps_data_path=pipeline._cache_location["steps_data"] + ) def test_send_last_batch_flag_to_step(self) -> None: with DummyPipeline(name="unit-test-pipeline") as pipeline: @@ -743,7 +776,9 @@ def test_handle_batch_on_stop(self) -> None: batch = _Batch(seq_no=0, step_name=generator.name, last_batch=False) # type: ignore pipeline._handle_batch_on_stop(batch) - batch_manager_mock.register_batch.assert_called_once_with(batch) + batch_manager_mock.register_batch.assert_called_once_with( + batch, steps_data_path=pipeline._cache_location["steps_data"] + ) batch_manager_mock.add_batch.assert_has_calls( [ mock.call(step.name, batch), @@ -1300,8 +1335,7 @@ def test_base_pipeline_signature(self) -> None: pipeline = DummyPipeline(name="unit-test-pipeline") # Doesn't matter if it's exactly this or not, the test should fail if we change the # way this is created. - signature = pipeline._create_signature() - assert signature == "da39a3ee5e6b4b0d3255bfef95601890afd80709" + assert pipeline.signature == "da39a3ee5e6b4b0d3255bfef95601890afd80709" # Maybe not the best place for this test, but does the work for now from distilabel.pipeline.local import Pipeline @@ -1311,11 +1345,28 @@ def test_base_pipeline_signature(self) -> None: sample_two_steps = sample_n_steps(2) with Pipeline(name="unit-test-pipeline") as pipeline: - dummy_generator = DummyGeneratorStep() - dummy_step_1_0 = DummyStep1() - dummy_step_1_1 = DummyStep1() - dummy_step_1_2 = DummyStep1() - dummy_step_2 = DummyStep2() + dummy_generator = DummyGeneratorStep(name="dummy_generator") + dummy_step_1_0 = DummyStep1(name="dummy_step_1_0") + dummy_step_1_1 = DummyStep1(name="dummy_step_1_1") + dummy_step_1_2 = DummyStep1(name="dummy_step_1_2") + dummy_step_2 = DummyStep2(name="dummy_step_2") + + ( + dummy_generator + >> sample_two_steps + >> [dummy_step_1_0, dummy_step_1_1, dummy_step_1_2] + >> dummy_step_2 + ) + + assert pipeline.signature == "edff8f5bb8b51da406ff274e640f87264f014e3b" + + # attributes shouldn't affect in pipeline signature + with Pipeline(name="unit-test-pipeline") as pipeline: + dummy_generator = DummyGeneratorStep(name="dummy_generator") + dummy_step_1_0 = DummyStep1(name="dummy_step_1_0", attr1=17238497128934) + dummy_step_1_1 = DummyStep1(name="dummy_step_1_1") + dummy_step_1_2 = DummyStep1(name="dummy_step_1_2") + dummy_step_2 = DummyStep2(name="dummy_step_2") ( dummy_generator @@ -1324,8 +1375,51 @@ def test_base_pipeline_signature(self) -> None: >> dummy_step_2 ) - signature = pipeline._create_signature() - assert signature == "d3c7c572fe31233aa1198174c6c793b67ef3744b" + assert pipeline.signature == "edff8f5bb8b51da406ff274e640f87264f014e3b" + + with Pipeline(name="unit-test-pipeline") as pipeline: + dummy_generator = DummyGeneratorStep(name="dummy_generator") + dummy_step_1_0 = DummyStep1(name="dummy_step_1_0") + dummy_step_1_1 = DummyStep1(name="dummy_step_1_1") + dummy_step_1_2 = DummyStep1(name="dummy_step_1_2") + dummy_step_2 = DummyStep2(name="dummy_step_2") + + ( + dummy_generator + >> [dummy_step_1_0, dummy_step_1_1, dummy_step_1_2] + >> dummy_step_2 + ) + + assert pipeline.signature == "5634172be496319d50848b1679b2a8781cc5581f" + + with Pipeline(name="unit-test-pipeline") as pipeline: + dummy_generator = DummyGeneratorStep(name="dummy_generator_second_time") + dummy_step_1_0 = DummyStep1( + name="dummy_step_1_0_second_time", attr1=17238497128934 + ) + dummy_step_1_1 = DummyStep1(name="dummy_step_1_1_second_time") + dummy_step_1_2 = DummyStep1(name="dummy_step_1_2_second_time") + dummy_step_2 = DummyStep2(name="dummy_step_2_second_time") + + ( + dummy_generator + >> sample_two_steps + >> [dummy_step_1_0, dummy_step_1_1, dummy_step_1_2] + >> dummy_step_2 + ) + + assert pipeline.signature == "806dad3fca0f8274af0f374660d4e3eb25d62d12" + + with Pipeline(name="unit-test-pipeline") as pipeline: + dummy_generator = DummyGeneratorStep(name="dummy_generator_second_time") + dummy_step_1_0 = DummyStep1( + name="dummy_step_1_0_second_time", attr1=17238497128934 + ) + dummy_step_1_1 = DummyStep1(name="dummy_step_1_1_second_time") + + (dummy_generator >> sample_two_steps >> [dummy_step_1_0, dummy_step_1_1]) + + assert pipeline.signature == "7222ce34c677bea3720ef3d08c2673b29b61ff9b" def test_binary_rshift_operator(self) -> None: # Tests the steps can be connected using the >> operator. @@ -1340,7 +1434,7 @@ def test_binary_rshift_operator(self) -> None: dummy_generator.connect(dummy_step_1) dummy_step_1.connect(dummy_step_2) - signature_1 = pipeline_1._create_signature() + signature_1 = pipeline_1.signature with Pipeline(name="unit-test-pipeline-3") as pipeline_2: dummy_generator = DummyGeneratorStep(name="dummy_generator_step") @@ -1349,7 +1443,7 @@ def test_binary_rshift_operator(self) -> None: dummy_generator >> dummy_step_1 >> dummy_step_2 - signature_2 = pipeline_2._create_signature() + signature_2 = pipeline_2.signature assert signature_1 == signature_2 @@ -1366,7 +1460,7 @@ def test_binary_rshift_operator_with_list(self) -> None: dummy_generator.connect(dummy_step_1) dummy_generator.connect(dummy_step_2) - signature_1 = pipeline_1._create_signature() + signature_1 = pipeline_1.signature with Pipeline(name="unit-test-pipeline-2") as pipeline_2: dummy_generator = DummyGeneratorStep(name="dummy_generator_step") @@ -1375,7 +1469,7 @@ def test_binary_rshift_operator_with_list(self) -> None: dummy_generator >> [dummy_step_1, dummy_step_2] - signature_2 = pipeline_2._create_signature() + signature_2 = pipeline_2.signature assert signature_1 == signature_2 @@ -1395,7 +1489,7 @@ def test_binary_rrshift_operator(self) -> None: dummy_step_1.connect(dummy_global) dummy_step_2.connect(dummy_global) - signature_1 = pipeline_1._create_signature() + signature_1 = pipeline_1.signature with Pipeline(name="unit-test-pipeline-2") as pipeline_2: dummy_step_1 = DummyStep1(name="dummy_step_1") @@ -1403,7 +1497,7 @@ def test_binary_rrshift_operator(self) -> None: dummy_global = DummyGlobalStep(name="dummy_global_step") [dummy_step_1, dummy_step_2] >> dummy_global - signature_2 = pipeline_2._create_signature() + signature_2 = pipeline_2.signature assert signature_1 == signature_2 @@ -1429,7 +1523,7 @@ def test_binary_operators(self) -> None: dummy_step_1.connect(dummy_global) dummy_step_2.connect(dummy_global) - signature_1 = pipeline_1._create_signature() + signature_1 = pipeline_1.signature with Pipeline(name="unit-test-pipeline-2") as pipeline_2: dummy_generator = DummyGeneratorStep(name="dummy_generator_step") @@ -1438,6 +1532,6 @@ def test_binary_operators(self) -> None: dummy_global = DummyGlobalStep(name="dummy_global_step") dummy_generator >> [dummy_step_1, dummy_step_2] >> dummy_global - signature_2 = pipeline_2._create_signature() + signature_2 = pipeline_2.signature assert signature_1 == signature_2 diff --git a/tests/unit/pipeline/test_batch_manager.py b/tests/unit/pipeline/test_batch_manager.py index c5023b2616..8801096ce8 100644 --- a/tests/unit/pipeline/test_batch_manager.py +++ b/tests/unit/pipeline/test_batch_manager.py @@ -15,14 +15,18 @@ import tempfile from pathlib import Path from typing import Dict, List +from unittest import mock import pytest from distilabel.pipeline._dag import DAG from distilabel.pipeline.batch import _Batch from distilabel.pipeline.batch_manager import _BatchManager, _BatchManagerStep +from distilabel.pipeline.local import Pipeline from distilabel.steps.base import GeneratorStep, GlobalStep, Step +from .utils import DummyGeneratorStep, DummyStep1, DummyStep2 + class TestBatchManagerStep: def test_add_batch(self) -> None: @@ -144,6 +148,7 @@ def test_get_batch(self) -> None: ) ], }, + step_offset={"step1": (0, 0), "step2": (0, 0)}, built_batches=[previously_built_batch], next_expected_seq_no={"step1": (1, 1), "step2": (1, 1)}, ) @@ -168,7 +173,7 @@ def test_get_batch(self) -> None: {"b": 2}, ], ], - created_from={"step1": [(1, 5)], "step2": [(1, 5)]}, + created_from={"step1": [(1, 5, 2)], "step2": [(1, 5, 2)]}, ) batch = batch_manager_step.get_batch() @@ -187,7 +192,7 @@ def test_get_batch(self) -> None: {"b": 4}, ], ], - created_from={"step1": [(1, 5)], "step2": [(1, 5)]}, + created_from={"step1": [(1, 5, 2)], "step2": [(1, 5, 2)]}, ) def test_get_batches_accumulate(self) -> None: @@ -231,6 +236,7 @@ def test_get_batches_accumulate(self) -> None: ) ], }, + step_offset={"step1": (0, 0), "step2": (0, 0)}, last_batch_received=["step1", "step2"], ) @@ -258,7 +264,7 @@ def test_get_batches_accumulate(self) -> None: {"b": 6}, ], ], - created_from={"step1": [(0, 5)], "step2": [(0, 6)]}, + created_from={"step1": [(0, 5, 5)], "step2": [(0, 6, 6)]}, ) def test_get_batches_not_enough_data(self) -> None: @@ -430,7 +436,7 @@ def test_get_data(self) -> None: [{"a": 1}, {"a": 2}, {"a": 3}, {"a": 4}, {"a": 5}], [{"b": 1}, {"b": 2}, {"b": 3}, {"b": 4}, {"b": 5}], ] - assert created_from == {"step1": [(0, 6)], "step2": [(0, 7)]} + assert created_from == {"step1": [(0, 6, 5)], "step2": [(0, 7, 5)]} assert routed_to == ["step1", "step2"] assert batch_manager_step.data == { @@ -502,7 +508,7 @@ def test_get_data_accumulate(self) -> None: [{"a": 1}, {"a": 2}, {"a": 3}, {"a": 4}, {"a": 5}, {"a": 6}], [{"b": 1}, {"b": 2}, {"b": 3}, {"b": 4}, {"b": 5}, {"b": 6}, {"b": 7}], ] - assert created_from == {"step1": [(0, 6)], "step2": [(0, 7)]} + assert created_from == {"step1": [(0, 6, 6)], "step2": [(0, 7, 7)]} assert routed_to == [] assert batch_manager_step.data == {"step1": [], "step2": []} @@ -520,7 +526,7 @@ def test_get_data_convergence_step(self) -> None: ] ], size=3, - created_from={"Z": [(0, 3)]}, + created_from={"Z": [(0, 3, 3)]}, ) batch_a_1 = _Batch( @@ -535,7 +541,7 @@ def test_get_data_convergence_step(self) -> None: ] ], size=3, - created_from={"Z": [(1, 3)]}, + created_from={"Z": [(1, 3, 3)]}, ) batch_b_0 = _Batch( @@ -550,7 +556,7 @@ def test_get_data_convergence_step(self) -> None: ] ], size=3, - created_from={"Z": [(0, 3)]}, + created_from={"Z": [(0, 3, 3)]}, ) batch_c_0 = _Batch( @@ -565,7 +571,7 @@ def test_get_data_convergence_step(self) -> None: ] ], size=3, - created_from={"Z": [(1, 3)]}, + created_from={"Z": [(1, 3, 3)]}, ) batch_manager_step = _BatchManagerStep( @@ -590,7 +596,7 @@ def test_get_data_convergence_step(self) -> None: {"generation": "Hello, I'm B 0"}, ], ] - assert created_from == {"A": [(0, 3)], "B": [(0, 3)]} + assert created_from == {"A": [(0, 3, 3)], "B": [(0, 3, 3)]} assert routed_to == [] assert batch_manager_step.next_expected_created_from_batch_seq_no == 1 @@ -608,7 +614,7 @@ def test_get_data_convergence_step(self) -> None: {"generation": "Hello, I'm C 0"}, ], ] - assert created_from == {"A": [(1, 3)], "C": [(0, 3)]} + assert created_from == {"A": [(1, 3, 3)], "C": [(0, 3, 3)]} assert routed_to == [] assert batch_manager_step.next_expected_created_from_batch_seq_no == 2 @@ -803,7 +809,7 @@ def test_last_batch_accumulate( step_name="step1", last_batch=False, data=[[{"a": 1}, {"a": 2}, {"a": 3}, {"a": 4}, {"a": 5}]], - created_from={"step0": [(0, 5)]}, + created_from={"step0": [(0, 5, 5)]}, ) ], "step2": [ @@ -812,7 +818,7 @@ def test_last_batch_accumulate( step_name="step1", last_batch=False, data=[[{"b": 1}, {"b": 2}, {"b": 3}, {"b": 4}, {"b": 5}]], - created_from={"step0": [(0, 5)]}, + created_from={"step0": [(0, 5, 5)]}, ) ], }, @@ -827,7 +833,7 @@ def test_last_batch_accumulate( step_name="step1", last_batch=True, data=[[{"a": 1}, {"a": 2}, {"a": 3}, {"a": 4}, {"a": 5}]], - created_from={"step0": [(0, 5)]}, + created_from={"step0": [(0, 5, 5)]}, ) ], "step2": [ @@ -836,7 +842,7 @@ def test_last_batch_accumulate( step_name="step1", last_batch=True, data=[[{"b": 1}, {"b": 2}, {"b": 3}, {"b": 4}, {"b": 5}]], - created_from={"step0": [(0, 5)]}, + created_from={"step0": [(0, 5, 5)]}, ) ], }, @@ -851,7 +857,7 @@ def test_last_batch_accumulate( step_name="step1", last_batch=True, data=[[{"a": 1}, {"a": 2}, {"a": 3}]], - created_from={"step0": [(0, 3)]}, + created_from={"step0": [(0, 3, 3)]}, ) ], "step2": [ @@ -860,7 +866,7 @@ def test_last_batch_accumulate( step_name="step1", last_batch=True, data=[[{"b": 1}, {"b": 2}, {"b": 3}]], - created_from={"step0": [(0, 3)]}, + created_from={"step0": [(0, 3, 3)]}, ) ], }, @@ -1217,6 +1223,9 @@ def test_dump(self) -> None: "step1": (0, 0), "step2": (0, 0), }, + "step_offset": {}, + "step_signature": None, + "use_cache": False, "type_info": { "module": "distilabel.pipeline.batch_manager", "name": "_BatchManagerStep", @@ -1235,7 +1244,7 @@ def test_dump(self) -> None: last_batch=False, data=[[{"a": 1}, {"a": 2}, {"a": 3}, {"a": 4}, {"a": 5}]], batch_routed_to=["step1", "step2"], - created_from={"step0": [(0, 5)]}, + created_from={"step0": [(0, 5, 5)]}, ) ], "step2": [], @@ -1252,7 +1261,7 @@ def test_dump(self) -> None: last_batch=False, data=[[{"a": 1}, {"a": 2}, {"a": 3}, {"a": 4}, {"a": 5}]], batch_routed_to=["step1", "step2"], - created_from={"step0": [(0, 5)]}, + created_from={"step0": [(0, 5, 5)]}, ) ], "step2": [ @@ -1262,7 +1271,7 @@ def test_dump(self) -> None: last_batch=False, data=[[{"b": 1}, {"b": 2}, {"b": 3}, {"b": 4}, {"b": 5}]], batch_routed_to=["step1", "step2"], - created_from={"step0": [(0, 5)]}, + created_from={"step0": [(0, 5, 5)]}, ) ], }, @@ -1278,7 +1287,7 @@ def test_dump(self) -> None: last_batch=False, data=[[{"a": 1}, {"a": 2}, {"a": 3}, {"a": 4}]], batch_routed_to=["step1", "step2"], - created_from={"step0": [(0, 4)]}, + created_from={"step0": [(0, 4, 4)]}, ) ], "step2": [ @@ -1288,7 +1297,7 @@ def test_dump(self) -> None: last_batch=False, data=[[{"b": 1}, {"b": 2}, {"b": 3}, {"b": 4}, {"b": 5}]], batch_routed_to=["step1", "step2"], - created_from={"step0": [(0, 5)]}, + created_from={"step0": [(0, 5, 5)]}, ) ], }, @@ -1304,7 +1313,7 @@ def test_dump(self) -> None: last_batch=True, data=[[{"a": 1}, {"a": 2}, {"a": 3}, {"a": 4}]], batch_routed_to=["step1", "step2"], - created_from={"step0": [(0, 4)]}, + created_from={"step0": [(0, 4, 4)]}, ) ], "step2": [ @@ -1314,7 +1323,7 @@ def test_dump(self) -> None: last_batch=True, data=[[{"b": 1}, {"b": 2}, {"b": 3}, {"b": 4}]], batch_routed_to=["step1", "step2"], - created_from={"step0": [(0, 4)]}, + created_from={"step0": [(0, 4, 4)]}, ) ], }, @@ -1330,7 +1339,7 @@ def test_dump(self) -> None: last_batch=False, data=[[{"a": 1}, {"a": 2}, {"a": 3}, {"a": 4}]], batch_routed_to=["step1", "step2"], - created_from={"step0": [(0, 4)]}, + created_from={"step0": [(0, 4, 4)]}, ) ], "step2": [ @@ -1340,7 +1349,7 @@ def test_dump(self) -> None: last_batch=False, data=[[{"b": 1}, {"b": 2}, {"b": 3}, {"b": 4}, {"b": 5}]], batch_routed_to=["step1", "step2"], - created_from={"step0": [(0, 5)]}, + created_from={"step0": [(0, 5, 5)]}, ) ], }, @@ -1467,6 +1476,41 @@ def test_add_batch(self) -> None: "step2": [], } + def test_step_hash_finished(self) -> None: + batch_manager = _BatchManager( + steps={ + "step1": _BatchManagerStep( + step_name="step1", + accumulate=False, + input_batch_size=5, + data={}, + ), + "step2": _BatchManagerStep( + step_name="step2", + accumulate=False, + input_batch_size=5, + data={"step_1": []}, + ), + "step3": _BatchManagerStep( + step_name="step3", + accumulate=False, + input_batch_size=5, + data={"step2": []}, + ), + }, + last_batch_received={ + "step1": _Batch(seq_no=0, step_name="step1", last_batch=True), + "step2": None, + "step3": None, + }, + last_batch_sent={"step1": None, "step2": None, "step3": None}, + last_batch_flag_sent_to=["step2"], + ) + + assert batch_manager.step_has_finished("step1") is True + assert batch_manager.step_has_finished("step2") is True + assert batch_manager.step_has_finished("step3") is False + def test_add_batch_with_prepend(self) -> None: batch_1 = _Batch( seq_no=1, @@ -1554,12 +1598,26 @@ def test_from_dag( batch_manager = _BatchManager.from_dag(dag) assert batch_manager._steps == { + "dummy_generator_step": _BatchManagerStep( + step_name="dummy_generator_step", + accumulate=False, + input_batch_size=None, + data={}, + convergence_step=True, + next_expected_seq_no={}, + step_signature="963a16b6081170f39eef011d64d992a0a6e9f0e9", + use_cache=True, + step_offset={}, + ), "dummy_step_1": _BatchManagerStep( step_name="dummy_step_1", accumulate=False, input_batch_size=50, data={"dummy_generator_step": []}, next_expected_seq_no={"dummy_generator_step": (0, 0)}, + step_signature="bc765d5801dc71c88a1a444e1b1e26035d309724", + use_cache=True, + step_offset={"dummy_generator_step": (0, 0)}, ), "dummy_global_step": _BatchManagerStep( step_name="dummy_global_step", @@ -1567,6 +1625,9 @@ def test_from_dag( input_batch_size=50, data={"dummy_generator_step": []}, next_expected_seq_no={"dummy_generator_step": (0, 0)}, + step_signature="6a0e9f45043fa7dc37e2b36269d660dfef63dbb7", + use_cache=True, + step_offset={"dummy_generator_step": (0, 0)}, ), "dummy_step_2": _BatchManagerStep( step_name="dummy_step_2", @@ -1574,9 +1635,73 @@ def test_from_dag( input_batch_size=50, data={"dummy_step_1": []}, next_expected_seq_no={"dummy_step_1": (0, 0)}, + step_signature="2d1076164acb43431aad1a54a781b7bad22c7037", + use_cache=True, + step_offset={"dummy_step_1": (0, 0)}, ), } + def test_cache(self, dummy_batch_manager: _BatchManager) -> None: + # We test the cache starting from the DAG because we need the signature + with tempfile.TemporaryDirectory() as tmp_dir: + batch_manager_path = Path(tmp_dir) / "batch_manager.json" + dummy_batch_manager.cache(batch_manager_path, Path(tmp_dir)) + + assert batch_manager_path.exists() and batch_manager_path.is_file() + + for step_name, step in dummy_batch_manager._steps.items(): + batch_manager_step_dir = ( + Path(tmp_dir) / "batch_manager_steps" / step_name + ) + assert ( + batch_manager_step_dir.exists() and batch_manager_step_dir.is_dir() + ) + + batch_manager_step_path = ( + batch_manager_step_dir / "batch_manager_step.json" + ) + assert ( + batch_manager_step_path.exists() + and batch_manager_step_path.is_file() + ) + + built_batches_dir = batch_manager_step_dir / "built_batches" + assert built_batches_dir.exists() + + for batch in step.built_batches: + batch_path = ( + built_batches_dir + / f"batch_{batch.seq_no}_{batch.data_hash}.json" + ) + assert batch_path.exists() and batch_path.is_file() + + # for buffered_step_name in step.data: + # buffered_step_dir = batch_manager_step_dir / buffered_step_name + # assert buffered_step_dir.exists() and buffered_step_dir.is_dir() + + # for batch in step.data[buffered_step_name]: + # batch_path = ( + # buffered_step_dir + # / f"batch_{batch.seq_no}_{batch.data_hash}.json" + # ) + # assert batch_path.exists() and batch_path.is_file() + + def test_load_from_cache( + self, dummy_dag: DAG, dummy_batch_manager: _BatchManager + ) -> None: + with tempfile.TemporaryDirectory() as tmp_dir: + from pathlib import Path + + tmp_dir = Path.home() / "Downloads/test_batch_manager" + + batch_manager_path = Path(tmp_dir) / "batch_manager.json" + dummy_batch_manager.cache(batch_manager_path, Path(tmp_dir)) + loaded_batch_manager = _BatchManager.load_from_cache( + dummy_dag, batch_manager_path, Path(tmp_dir) + ) + + assert dummy_batch_manager.dump() == loaded_batch_manager.dump() + def test_can_generate(self) -> None: batch_manager = _BatchManager( steps={}, @@ -1608,6 +1733,108 @@ def test_can_generate(self) -> None: assert not batch_manager.can_generate() + def test_invalidate_cache_for(self) -> None: + with Pipeline() as pipeline: + generator = DummyGeneratorStep() + step_a = DummyStep1() + step_b = DummyStep1() + step_c = DummyStep2() + + generator >> [step_a, step_b] >> step_c + + pipeline._load_batch_manager() + batch_manager: "_BatchManager" = pipeline._batch_manager # type: ignore + + with ( + mock.patch.object( + batch_manager, "_reset_batch_manager_for_step" + ) as reset_mock, + mock.patch.object(batch_manager, "_load_predecessor_batches") as load_mock, + ): + batch_manager.invalidate_cache_for( + step_name=step_a.name, # type: ignore + dag=pipeline.dag, + steps_data_path=pipeline._cache_location["steps_data"], + ) + + # shouldn't have been called for step b + reset_mock.assert_has_calls( + [ + mock.call(step_a.name, pipeline.dag), + mock.call(step_c.name, pipeline.dag), + ] + ) + + load_mock.assert_called_once_with( + step_a.name, pipeline.dag, pipeline._cache_location["steps_data"] + ) + + def test_reset_batch_manager_for_step(self) -> None: + batch_manager = _BatchManager( + steps={ + "step1": _BatchManagerStep( + step_name="step1", + accumulate=True, + input_batch_size=5, + data={ + "step0": [_Batch(seq_no=0, step_name="step0", last_batch=True)] + }, + ) + }, + last_batch_received={ + "step1": _Batch(seq_no=0, step_name="step1", last_batch=True) + }, + last_batch_sent={ + "step1": _Batch(seq_no=0, step_name="step1", last_batch=True) + }, + last_batch_flag_sent_to=["step1"], + ) + + dag = DAG() + dag.add_step(DummyStep1(name="step1")) + + batch_manager._reset_batch_manager_for_step("step1", dag) + assert batch_manager._steps["step1"].data == {} + assert batch_manager._last_batch_received["step1"] is None + assert batch_manager._last_batch_sent["step1"] is None + assert batch_manager._last_batch_flag_sent_to == [] + + def test_load_predecessor_batches(self) -> None: + with Pipeline() as pipeline: + generator = DummyGeneratorStep() + step_a = DummyStep1() + step_b = DummyStep1() + step_c = DummyStep2() + + generator >> [step_a, step_b] >> step_c + + pipeline._load_batch_manager() + batch_manager: "_BatchManager" = pipeline._batch_manager # type: ignore + + with tempfile.TemporaryDirectory() as tmp_dir: + previous_step_dir = ( + Path(tmp_dir) / f"{generator.name}_{generator.signature}" + ) # type: ignore + batches = [] + for i in range(3): + batch = _Batch( + seq_no=i, + step_name=generator.name, # type: ignore + data=[[{"a": i} for _ in range(5)]], + last_batch=i % 3 == 0, + ) + batches.append(batch) + batch.save(path=previous_step_dir / f"batch_{i}.json") + + batch_manager._load_predecessor_batches( + step_name=step_a.name, # type: ignore + dag=pipeline.dag, + steps_data_path=Path(tmp_dir), # type: ignore + ) + + assert batch_manager._steps[step_a.name].data[generator.name] == batches # type: ignore + assert generator.name in batch_manager._steps[step_a.name].last_batch_received # type: ignore + def test_dump(self) -> None: built_batch = _Batch( seq_no=0, @@ -1681,6 +1908,9 @@ def test_dump(self) -> None: "step1": (1, 1), "step2": (1, 1), }, + "step_offset": {}, + "step_signature": None, + "use_cache": False, "type_info": { "module": "distilabel.pipeline.batch_manager", "name": "_BatchManagerStep", @@ -1898,467 +2128,3 @@ def test_from_dict(self) -> None: assert isinstance(step, _Batch) assert batch_manager._last_batch_flag_sent_to == ["step3"] - - def test_cache(self) -> None: - batch_manager = _BatchManager.from_dict( - { - "steps": { - "step1": { - "step_name": "step1", - "accumulate": True, - "convergence_step": False, - "convergence_step_batches_consumed": {"0": {"Z": 1234}}, - "input_batch_size": None, - "data": { - "step2": [ - { - "seq_no": 0, - "step_name": "step2", - "last_batch": True, - "data": [ - [ - {"b": 1}, - {"b": 2}, - {"b": 3}, - {"b": 4}, - {"b": 5}, - {"b": 6}, - {"b": 7}, - ] - ], - "data_hash": "1234", - "size": 7, - "accumulated": False, - "created_from": {}, - "batch_routed_to": [], - "type_info": { - "module": "distilabel.pipeline.batch_manager", - "name": "_Batch", - }, - } - ], - }, - "built_batches": [ - { - "seq_no": 0, - "step_name": "step1", - "last_batch": False, - "data": [ - [ - {"a": 1}, - {"a": 2}, - {"a": 3}, - {"a": 4}, - {"a": 5}, - ] - ], - "data_hash": "1234", - "size": 5, - "accumulated": False, - "batch_routed_to": [], - "created_from": {}, - "type_info": { - "module": "distilabel.pipeline.batch", - "name": "_Batch", - }, - } - ], - "seq_no": 0, - "last_batch_received": [], - "type_info": { - "module": "distilabel.pipeline.batch_manager", - "name": "_BatchManagerStep", - }, - }, - "step2": { - "step_name": "step2", - "accumulate": False, - "convergence_step": False, - "convergence_step_batches_consumed": {"0": {"Z": 1234}}, - "input_batch_size": 50, - "data": { - "step2": [ - { - "seq_no": 0, - "step_name": "step2", - "last_batch": True, - "data": [ - [ - {"b": 1}, - {"b": 2}, - {"b": 3}, - {"b": 4}, - {"b": 5}, - {"b": 6}, - {"b": 7}, - ] - ], - "data_hash": "1234", - "size": 7, - "accumulated": False, - "created_from": {}, - "batch_routed_to": [], - "type_info": { - "module": "distilabel.pipeline.batch_manager", - "name": "_Batch", - }, - } - ], - }, - "built_batches": [ - { - "seq_no": 0, - "step_name": "step1", - "last_batch": False, - "data": [ - [ - {"a": 1}, - {"a": 2}, - {"a": 3}, - {"a": 4}, - {"a": 5}, - ] - ], - "data_hash": "1234", - "size": 5, - "accumulated": False, - "batch_routed_to": [], - "created_from": {}, - "type_info": { - "module": "distilabel.pipeline.batch", - "name": "_Batch", - }, - } - ], - "seq_no": 0, - "last_batch_received": [], - "type_info": { - "module": "distilabel.pipeline.batch_manager", - "name": "_BatchManagerStep", - }, - }, - }, - "last_batch_received": { - "step1": { - "seq_no": 0, - "step_name": "step1", - "last_batch": False, - "data": [], - "size": 0, - "accumulated": False, - "created_from": {}, - "batch_routed_to": [], - "type_info": { - "module": "distilabel.pipeline.batch_manager", - "name": "_Batch", - }, - }, - "step2": { - "seq_no": 0, - "step_name": "step2", - "last_batch": False, - "data": [], - "size": 0, - "accumulated": False, - "created_from": {}, - "batch_routed_to": [], - "type_info": { - "module": "distilabel.pipeline.batch_manager", - "name": "_Batch", - }, - }, - }, - "last_batch_sent": { - "step1": { - "seq_no": 0, - "step_name": "step1", - "last_batch": False, - "data": [], - "size": 0, - "accumulated": False, - "created_from": {}, - "batch_routed_to": [], - "type_info": { - "module": "distilabel.pipeline.batch_manager", - "name": "_Batch", - }, - }, - "step2": { - "seq_no": 0, - "step_name": "step2", - "last_batch": False, - "data": [], - "size": 0, - "accumulated": False, - "created_from": {}, - "batch_routed_to": [], - "type_info": { - "module": "distilabel.pipeline.batch_manager", - "name": "_Batch", - }, - }, - }, - "last_batch_flag_sent_to": ["step3"], - "type_info": { - "module": "distilabel.pipeline.batch_manager", - "name": "_BatchManager", - }, - } - ) - - with tempfile.TemporaryDirectory() as tmp_dir: - batch_manager_path = Path(tmp_dir) / "batch_manager.json" - batch_manager.cache(batch_manager_path) - - assert batch_manager_path.exists() and batch_manager_path.is_file() - - for step_name, step in batch_manager._steps.items(): - batch_manager_step_dir = ( - Path(tmp_dir) / "batch_manager_steps" / step_name - ) - assert ( - batch_manager_step_dir.exists() and batch_manager_step_dir.is_dir() - ) - - batch_manager_step_path = ( - batch_manager_step_dir / "batch_manager_step.json" - ) - assert ( - batch_manager_step_path.exists() - and batch_manager_step_path.is_file() - ) - - built_batches_dir = batch_manager_step_dir / "built_batches" - assert built_batches_dir.exists() - - for batch in step.built_batches: - batch_path = ( - built_batches_dir - / f"batch_{batch.seq_no}_{batch.data_hash}.json" - ) - assert batch_path.exists() and batch_path.is_file() - - for buffered_step_name in step.data: - buffered_step_dir = batch_manager_step_dir / buffered_step_name - assert buffered_step_dir.exists() and buffered_step_dir.is_dir() - - for batch in step.data[buffered_step_name]: - batch_path = ( - buffered_step_dir - / f"batch_{batch.seq_no}_{batch.data_hash}.json" - ) - assert batch_path.exists() and batch_path.is_file() - - def test_load_from_cache(self) -> None: - batch_manager = _BatchManager.from_dict( - { - "steps": { - "step1": { - "step_name": "step1", - "accumulate": True, - "convergence_step": False, - "convergence_step_batches_consumed": {"0": {"Z": 1234}}, - "input_batch_size": None, - "data": { - "step2": [ - { - "seq_no": 0, - "step_name": "step2", - "last_batch": True, - "data": [ - [ - {"b": 1}, - {"b": 2}, - {"b": 3}, - {"b": 4}, - {"b": 5}, - {"b": 6}, - {"b": 7}, - ] - ], - "data_hash": "1234", - "size": 7, - "accumulated": False, - "created_from": {}, - "batch_routed_to": [], - "type_info": { - "module": "distilabel.pipeline.batch", - "name": "_Batch", - }, - } - ], - }, - "built_batches": [ - { - "seq_no": 0, - "step_name": "step1", - "last_batch": False, - "data": [ - [ - {"a": 1}, - {"a": 2}, - {"a": 3}, - {"a": 4}, - {"a": 5}, - ] - ], - "data_hash": "1234", - "size": 5, - "accumulated": False, - "batch_routed_to": [], - "created_from": {}, - "type_info": { - "module": "distilabel.pipeline.batch", - "name": "_Batch", - }, - } - ], - "seq_no": 0, - "last_batch_received": [], - "type_info": { - "module": "distilabel.pipeline.batch_manager", - "name": "_BatchManagerStep", - }, - }, - "step2": { - "step_name": "step2", - "accumulate": False, - "convergence_step": False, - "convergence_step_batches_consumed": {"0": {"Z": 1234}}, - "input_batch_size": 50, - "data": { - "step2": [ - { - "seq_no": 0, - "step_name": "step2", - "last_batch": True, - "data": [ - [ - {"b": 1}, - {"b": 2}, - {"b": 3}, - {"b": 4}, - {"b": 5}, - {"b": 6}, - {"b": 7}, - ] - ], - "data_hash": "1234", - "size": 7, - "accumulated": False, - "created_from": {}, - "batch_routed_to": [], - "type_info": { - "module": "distilabel.pipeline.batch", - "name": "_Batch", - }, - } - ], - }, - "built_batches": [ - { - "seq_no": 0, - "step_name": "step1", - "last_batch": False, - "data": [ - [ - {"a": 1}, - {"a": 2}, - {"a": 3}, - {"a": 4}, - {"a": 5}, - ] - ], - "data_hash": "1234", - "size": 5, - "accumulated": False, - "batch_routed_to": [], - "created_from": {}, - "type_info": { - "module": "distilabel.pipeline.batch", - "name": "_Batch", - }, - } - ], - "seq_no": 0, - "last_batch_received": [], - "type_info": { - "module": "distilabel.pipeline.batch_manager", - "name": "_BatchManagerStep", - }, - }, - }, - "last_batch_received": { - "step1": { - "seq_no": 0, - "step_name": "step1", - "last_batch": False, - "data": [], - "size": 0, - "accumulated": False, - "created_from": {}, - "batch_routed_to": [], - "type_info": { - "module": "distilabel.pipeline.batch", - "name": "_Batch", - }, - }, - "step2": { - "seq_no": 0, - "step_name": "step2", - "last_batch": False, - "data": [], - "size": 0, - "accumulated": False, - "created_from": {}, - "batch_routed_to": [], - "type_info": { - "module": "distilabel.pipeline.batch", - "name": "_Batch", - }, - }, - }, - "last_batch_sent": { - "step1": { - "seq_no": 0, - "step_name": "step1", - "last_batch": False, - "data": [], - "size": 0, - "accumulated": False, - "created_from": {}, - "batch_routed_to": [], - "type_info": { - "module": "distilabel.pipeline.batch", - "name": "_Batch", - }, - }, - "step2": { - "seq_no": 0, - "step_name": "step2", - "last_batch": False, - "data": [], - "size": 0, - "accumulated": False, - "created_from": {}, - "batch_routed_to": [], - "type_info": { - "module": "distilabel.pipeline.batch", - "name": "_Batch", - }, - }, - }, - "last_batch_flag_sent_to": ["step3"], - "type_info": { - "module": "distilabel.pipeline.batch_manager", - "name": "_BatchManager", - }, - } - ) - - with tempfile.TemporaryDirectory() as tmp_dir: - batch_manager_path = Path(tmp_dir) / "batch_manager.json" - batch_manager.cache(batch_manager_path) - loaded_batch_manager = _BatchManager.load_from_cache(batch_manager_path) - - assert batch_manager.dump() == loaded_batch_manager.dump() diff --git a/tests/unit/pipeline/utils.py b/tests/unit/pipeline/utils.py index 937a9c68bd..cb223755aa 100644 --- a/tests/unit/pipeline/utils.py +++ b/tests/unit/pipeline/utils.py @@ -42,6 +42,8 @@ def outputs(self) -> List[str]: class DummyStep1(Step): + attr1: int = 5 + @property def inputs(self) -> List[str]: return ["instruction"] diff --git a/tests/unit/steps/argilla/test_base.py b/tests/unit/steps/argilla/test_base.py index 78d70dd162..c0a452e72b 100644 --- a/tests/unit/steps/argilla/test_base.py +++ b/tests/unit/steps/argilla/test_base.py @@ -188,6 +188,7 @@ def test_serialization(self) -> None: "description": "The API key to authenticate the requests to the Argilla API.", }, ], + "use_cache": True, "type_info": { "module": "tests.unit.steps.argilla.test_base", "name": "CustomArgilla", diff --git a/tests/unit/steps/argilla/test_preference.py b/tests/unit/steps/argilla/test_preference.py index 9df1df4613..ab63ee5419 100644 --- a/tests/unit/steps/argilla/test_preference.py +++ b/tests/unit/steps/argilla/test_preference.py @@ -180,6 +180,7 @@ def test_serialization(self) -> None: "description": "The API key to authenticate the requests to the Argilla API.", }, ], + "use_cache": True, "type_info": { "module": "distilabel.steps.argilla.preference", "name": "PreferenceToArgilla", diff --git a/tests/unit/steps/argilla/test_text_generation.py b/tests/unit/steps/argilla/test_text_generation.py index 689b8e0927..356bf5a2e7 100644 --- a/tests/unit/steps/argilla/test_text_generation.py +++ b/tests/unit/steps/argilla/test_text_generation.py @@ -155,6 +155,7 @@ def test_serialization(self) -> None: "description": "The API key to authenticate the requests to the Argilla API.", }, ], + "use_cache": True, "type_info": { "module": "distilabel.steps.argilla.text_generation", "name": "TextGenerationToArgilla", diff --git a/tests/unit/steps/tasks/evol_instruct/test_base.py b/tests/unit/steps/tasks/evol_instruct/test_base.py index 4f6e12c6f1..66f67347b1 100644 --- a/tests/unit/steps/tasks/evol_instruct/test_base.py +++ b/tests/unit/steps/tasks/evol_instruct/test_base.py @@ -241,6 +241,7 @@ def test_serialization(self, dummy_llm: LLM) -> None: "description": "As `numpy` is being used in order to randomly pick a mutation method, then is nice to seed a random seed.", }, ], + "use_cache": True, "type_info": { "module": "distilabel.steps.tasks.evol_instruct.base", "name": "EvolInstruct", diff --git a/tests/unit/steps/tasks/evol_instruct/test_generator.py b/tests/unit/steps/tasks/evol_instruct/test_generator.py index 77b4a8ea05..8f86b94908 100644 --- a/tests/unit/steps/tasks/evol_instruct/test_generator.py +++ b/tests/unit/steps/tasks/evol_instruct/test_generator.py @@ -246,6 +246,7 @@ def test_serialization(self, dummy_llm: LLM) -> None: "description": "As `numpy` is being used in order to randomly pick a mutation method, then is nice to seed a random seed.", }, ], + "use_cache": True, "type_info": { "module": EvolInstructGenerator.__module__, "name": EvolInstructGenerator.__name__, diff --git a/tests/unit/steps/tasks/evol_quality/test_base.py b/tests/unit/steps/tasks/evol_quality/test_base.py index a7346b1069..2ac460afc4 100644 --- a/tests/unit/steps/tasks/evol_quality/test_base.py +++ b/tests/unit/steps/tasks/evol_quality/test_base.py @@ -205,6 +205,7 @@ def test_serialization(self, dummy_llm: LLM) -> None: "description": "As `numpy` is being used in order to randomly pick a mutation method, then is nice to set a random seed.", }, ], + "use_cache": True, "type_info": { "module": task.__module__, "name": task.__class__.__name__, diff --git a/tests/unit/steps/tasks/magpie/test_base.py b/tests/unit/steps/tasks/magpie/test_base.py index 8b830a0db8..cc13681f9f 100644 --- a/tests/unit/steps/tasks/magpie/test_base.py +++ b/tests/unit/steps/tasks/magpie/test_base.py @@ -762,6 +762,7 @@ def test_serialization(self) -> None: "description": "The number of generations to be produced per input.", }, ], + "use_cache": True, "type_info": { "module": "distilabel.steps.tasks.magpie.base", "name": "Magpie", diff --git a/tests/unit/steps/tasks/magpie/test_generator.py b/tests/unit/steps/tasks/magpie/test_generator.py index b5e41c3554..d1d1426351 100644 --- a/tests/unit/steps/tasks/magpie/test_generator.py +++ b/tests/unit/steps/tasks/magpie/test_generator.py @@ -202,6 +202,7 @@ def test_serialization(self) -> None: "description": "The number of rows to generate.", }, ], + "use_cache": True, "type_info": { "module": "distilabel.steps.tasks.magpie.generator", "name": "MagpieGenerator", diff --git a/tests/unit/steps/tasks/test_base.py b/tests/unit/steps/tasks/test_base.py index d00b8edac8..c56991d471 100644 --- a/tests/unit/steps/tasks/test_base.py +++ b/tests/unit/steps/tasks/test_base.py @@ -576,6 +576,7 @@ def test_serialization(self) -> None: "optional": True, }, ], + "use_cache": True, "type_info": { "module": "tests.unit.conftest", "name": "DummyTask", diff --git a/tests/unit/steps/tasks/test_pair_rm.py b/tests/unit/steps/tasks/test_pair_rm.py index 1903ccfb2f..104726307d 100644 --- a/tests/unit/steps/tasks/test_pair_rm.py +++ b/tests/unit/steps/tasks/test_pair_rm.py @@ -111,5 +111,6 @@ def test_serialization(self, _: MagicMock) -> None: "optional": True, }, ], + "use_cache": True, "type_info": {"module": "distilabel.steps.tasks.pair_rm", "name": "PairRM"}, } diff --git a/tests/unit/steps/test_base.py b/tests/unit/steps/test_base.py index 4791c5be28..6e8297bb06 100644 --- a/tests/unit/steps/test_base.py +++ b/tests/unit/steps/test_base.py @@ -29,6 +29,8 @@ class DummyStep(Step): + attr1: int = 5 + @property def inputs(self) -> List[str]: return ["instruction"] @@ -66,6 +68,16 @@ def process(self, inputs: StepInput) -> StepOutput: class TestStep: + def test_signature(self) -> None: + step = DummyStep(attr1=5) + assert step.signature == "a0ce83adedabec3fba270ec7bc8a52a62cbbee40" + + step = DummyStep(attr1=5) + assert step.signature == "a0ce83adedabec3fba270ec7bc8a52a62cbbee40" + + step = DummyStep(attr1=1234) + assert step.signature == "c00e67df4f7ed97a2bf8d9b1178d6c728e577c3b" + def test_create_step_with_invalid_name(self) -> None: pipeline = Pipeline(name="unit-test-pipeline") @@ -397,6 +409,7 @@ def test_step_dump(self) -> None: step = DummyStep(name="dummy", pipeline=pipeline) assert step.dump() == { "name": "dummy", + "attr1": 5, "input_batch_size": 50, "input_mappings": {}, "output_mappings": {}, @@ -444,6 +457,7 @@ def test_step_dump(self) -> None: "optional": True, }, ], + "use_cache": True, TYPE_INFO_KEY: { "module": "tests.unit.steps.test_base", "name": "DummyStep", From 4cbcb90ac3e0810c3dca871e43be0fcc9c4d2e1e Mon Sep 17 00:00:00 2001 From: Agus Date: Tue, 8 Oct 2024 12:10:18 +0200 Subject: [PATCH 80/82] Fix `IndexError` when overriding inputs and `group_generations=False` (#1022) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix processing num_generations when applying input mappings in steps process * Add unit test * Update comment --------- Co-authored-by: Gabriel Martín Blázquez --- src/distilabel/steps/base.py | 17 ++++-- tests/unit/steps/tasks/test_base.py | 88 +++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+), 4 deletions(-) diff --git a/src/distilabel/steps/base.py b/src/distilabel/steps/base.py index c467005e0f..b98c0e8275 100644 --- a/src/distilabel/steps/base.py +++ b/src/distilabel/steps/base.py @@ -667,10 +667,19 @@ def process_applying_mappings(self, *args: List[Dict[str, Any]]) -> "StepOutput" ) for output_rows in generator: - yield [ - self._apply_mappings_and_restore_overriden(row, overriden_inputs[i]) - for i, row in enumerate(output_rows) - ] + restored = [] + for i, row in enumerate(output_rows): + # Correct the index here because we don't know the num_generations from the llm + # ahead of time. For example, if we have `len(overriden_inputs)==5` and `len(row)==10`, + # from `num_generations==2` and `group_generations=False` in the LLM: + # The loop will use indices 0, 1, 2, 3, 4, 0, 1, 2, 3, 4 + ntimes_i = i % len(overriden_inputs) + restored.append( + self._apply_mappings_and_restore_overriden( + row, overriden_inputs[ntimes_i] + ) + ) + yield restored def _apply_input_mappings( self, inputs: Tuple[List[Dict[str, Any]], ...] diff --git a/tests/unit/steps/tasks/test_base.py b/tests/unit/steps/tasks/test_base.py index c56991d471..29341052fb 100644 --- a/tests/unit/steps/tasks/test_base.py +++ b/tests/unit/steps/tasks/test_base.py @@ -414,6 +414,94 @@ def test_process( result = next(task.process(input)) assert result == expected + def test_process_overriding_inputs(self) -> None: + llm = DummyAsyncLLM() + task = DummyTask( + name="task", + llm=llm, + group_generations=False, + num_generations=3, + input_mappings={"instruction": "instruction_2"}, + ) + + result = next( + task.process_applying_mappings( + [ + { + "instruction": "instruction that won't be used but overriden by input mapping", + "instruction_2": "instruction that will be used as input", + "additional_info": "info", + } + ] + ) + ) + + assert result == [ + { + "additional_info": "info", + "distilabel_metadata": { + "raw_input_task": [ + { + "content": "", + "role": "system", + }, + { + "content": "instruction that will be used as input", + "role": "user", + }, + ], + "raw_output_task": "output", + }, + "info_from_input": "info", + "instruction": "instruction that won't be used but overriden by input mapping", + "instruction_2": "instruction that will be used as input", + "model_name": "test", + "output": "output", + }, + { + "additional_info": "info", + "distilabel_metadata": { + "raw_input_task": [ + { + "content": "", + "role": "system", + }, + { + "content": "instruction that will be used as input", + "role": "user", + }, + ], + "raw_output_task": "output", + }, + "info_from_input": "info", + "instruction": "instruction that won't be used but overriden by input mapping", + "instruction_2": "instruction that will be used as input", + "model_name": "test", + "output": "output", + }, + { + "additional_info": "info", + "distilabel_metadata": { + "raw_input_task": [ + { + "content": "", + "role": "system", + }, + { + "content": "instruction that will be used as input", + "role": "user", + }, + ], + "raw_output_task": "output", + }, + "info_from_input": "info", + "instruction": "instruction that won't be used but overriden by input mapping", + "instruction_2": "instruction that will be used as input", + "model_name": "test", + "output": "output", + }, + ] + def test_process_with_runtime_parameters(self) -> None: # 1. Runtime parameters provided llm = DummyRuntimeLLM() # type: ignore From d99011c8476717c556a0ac3f06382a32932eb857 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Mart=C3=ADn=20Bl=C3=A1zquez?= Date: Tue, 8 Oct 2024 16:06:01 +0200 Subject: [PATCH 81/82] Update `Pipeline cache` docs (#1023) * Update link * Update cache section * Add step to fail if warnings * Fix dependency name --- .github/workflows/docs.yml | 3 + .../images/sections/caching/caching_1.png | Bin 0 -> 265332 bytes .../images/sections/caching/caching_2.png | Bin 0 -> 368648 bytes .../sections/caching/caching_pipe_1.png | Bin 301103 -> 0 bytes .../sections/caching/caching_pipe_2.png | Bin 329256 -> 0 bytes .../sections/caching/caching_pipe_3.png | Bin 129190 -> 0 bytes .../sections/caching/caching_pipe_4.png | Bin 24695 -> 0 bytes .../how_to_guides/advanced/caching.md | 157 +++++------------- .../pipeline_samples/papers/apigen.md | 2 +- mkdocs.yml | 3 +- pyproject.toml | 1 + 11 files changed, 48 insertions(+), 118 deletions(-) create mode 100644 docs/assets/images/sections/caching/caching_1.png create mode 100644 docs/assets/images/sections/caching/caching_2.png delete mode 100644 docs/assets/images/sections/caching/caching_pipe_1.png delete mode 100644 docs/assets/images/sections/caching/caching_pipe_2.png delete mode 100644 docs/assets/images/sections/caching/caching_pipe_3.png delete mode 100644 docs/assets/images/sections/caching/caching_pipe_4.png diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 62c1412486..374bd7ed4d 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -42,6 +42,9 @@ jobs: if: steps.cache.outputs.cache-hit != 'true' run: pip install -e .[docs] + - name: Check no warnings + run: mkdocs build --strict + - name: Set git credentials run: | git config --global user.name "${{ github.actor }}" diff --git a/docs/assets/images/sections/caching/caching_1.png b/docs/assets/images/sections/caching/caching_1.png new file mode 100644 index 0000000000000000000000000000000000000000..cde228769b0a409c756138b56e5ef3ee1acf3339 GIT binary patch literal 265332 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
    dAltOkBt7X6EjKQs>iX*u&cYjuPwQ{}? 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 zℜ1$2;~ku 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 z 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
    VCg{K;0jIw`rVkS!Pq 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

    I82 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�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

    Q+CNWlm(9zNfM*<+|3Xg*n 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%0DlN4e;qTK50&>P3rHVOl? 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 literal 0 HcmV?d00001 diff --git a/docs/assets/images/sections/caching/caching_pipe_1.png b/docs/assets/images/sections/caching/caching_pipe_1.png deleted file mode 100644 index f41f38a601c3342e62c33add7bc19f68d4f8747b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 301103 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%0ArId_yl<8dEOV#QRP!v{WnD6$Z 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%0A~X^D&mEVOc!yY!uBynMuPPgYA3`T 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

    s3vCVfq-Bgk~zE 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

    cU2gUW)6IdKjAA9yO)Eq1I-*s(r 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 z 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


    0c>Y-iS^Z?}7S zUT1x^I1Wy#L>V<3>uA7bjLd}}0VZ}j6i9tk1^khA4SF$8`}fk>`}U+D5US45SQl5I zaTZ6iYwnqh5-&a*aQlOY@y(7lqQ5%{0J_N^O9{+LnO%Xth>B)VrK14%)D&ls*CQa-YiIq{Y@@ zqstxnmM-n&huwBN_857^hR(S#rDKN0k9ij!X;;U$#jWyC~{Vg1P$#UYUY>5GRGp?Wy$ z7Z=mX$5z{@`7HkG6Aiyx7rD1+w)H;PiJODPLd{1cl2t#i6bw`EWfLNZ9+^|vNm?&y zX;>#FHm1XFdIwx|Ntv{LiXjfhKIWs>u5m_>sfP>*Gz;(8b>C(O(&qI6>DcVa31XXl*=c@n5r`FXSsxUnpG7 z8VqpsVnA6?#G@i%4im@sa%E`0Z9kCB34yyJnce=q*uMZ4``c2$VxJ;+2GKoZMO(W5 z9n+q^#*Tf&Ix-!|*UY?=r5qxcg*5_{!OSi}_PSI6gut%2-;TBCyR!{x>pRA^oG!F= z7B4{KLNQ}M%uAkbn4l*5<~uPAgO*&v=_wrom zr-Z76*8Kf26qS72B;(m*oaVFcn5r>Qvc`}dU~Bz=1%ka&ZRxnW!%^|~aexH&j+ydudp6S*be znxxm1o#prVHLZ4+0=9m3=B8_I&NVHERoeqo`Q^8V$6s&{oN?y-U;P$)EnZlkNcR@N z;Z?&jpMcH)zW|1vKrnIty_SfzjX&SldBoN-zoWU{uw0lfI)R`~waFfHeIZ4frABdD zY1nAYSZ=tGqDNu8R{JE%hWJ3^<~pm<5~4mZf7>l;s_T1d@9Q_Lbi8@SuL3>Zx#%e@@u`;CIVW0egh_Q^5DK4rW!g z`DnSw3};4ohO~OF?87&%uzfW|0vwr;sl zWmOnbAqQG72>|w#3p1FpfphK_s6ZsY9t5^{@>K=Bz_^3~5K|YN^bR9(yx%~^T~N>y zH);<+%EveXdf};jNzy#=+mRbT0M z172sw>#Xuv9=kOWJPzx$a@p~b3MC27GnbM6rwJBM2M{nJtCkAWmrK8U0J8rtbcnz* z1_h}^7r@%85^PMc9*8o~H_Q*MHlWJ9Sf|Vcx)+;sz+%X}m*!}*owRSNW%JImTguw%?y-Y0xmjJn49yk#!(PL4s_BgfjCJ+&Obi8zZ+c!{Lo zYIA#LYh8AGP;2BLD^61GKa&9l?8s2F&7_Fi8QY%$GhEiWPlAF3fFWD#3+PA;wuCv^ zSK9gZR>?IQZ%ptb-~5#f5Msi`EW5+)K7GqzeNdk`Qw+!nAPyt%;V^ObHRWMmZntxy z3pgqfA657TU<)|=5En)LJr;19F|~`+-7JYdCXEotxC&`*o%JLq;u^{)>H6ke3j_hE z)r1b|3FZged|^+AIjQx&MfJWLyd^o`e`Vpuat zF7->@F9v@1hV9}KOM8beV4CYuXi(@l?H#EmB=W$o3^S@zm2tbqcNNK9HfnjZ5ANe4 zS(G_86U%$csG*Y7IZNE7uJf0tyKnAK>wYf_0UQzIn=X7Qx5Np=A_Kn=F-()QE&KctT)tOCafP`=XmPZCO~}UA+kss z3`?2=v-8rmJ5WXbS*KjQt-1)@mE97ej3Z(>@=|9{r&HcL(`I3BVtJkkph)VoW;PRZ zM5QVLujftNMhlwZU7S}0rCV01UvKmYrz@U5m3#K6SC+i3=E3jG%e%3>5ZOFLi z$MJH+E+ z#!mJO5{?QFwEQ#_$D1gKvoie{g(O6rX{sTwN|pPiDZp!@=qaGT^=$v%!62Z!ydG1^ z?F6Mm2~XzjDx%LoqJ_CTJC`E(^MlS<#+QdRqd^=z2abm$;9p z)VM6b33 z518+~>PE3N+6XkKv*ED7OtC^1z zTRVHdV>9bozat*vQWF|`Kb?ivXuKXur8#f=9-F~+hw3_lW9h4A%gi}oAW(Q?w?d^2 zbO=j{*9&UDYH|JgB<>kpAHU2*bT1-)hIOcly|sRER2V5&cXp1yocRP3_#qM!m#O9v z!l4rnvjhK+w8vNE=VcYa%j)6l3LN`{1@JE!Y}-q0)k(|lcI4)zi&3~tIdwvz0B#mw zj^k9$+6A3M^X-QMg;oR2@-TeOMF7 zKYH-pgzVGe_1O8AR@S#l)<7>p-X3SNh1Bk6Vm=7Ra6uc_bl-$O3+4#nl9|Jenu!Ft|kw{D=bueq!t2e?U6c8*FIk)VlN zya|!j`{Ozjm4D?5L?YTGERi!}VrU%rZ$_{DoT^~T`+xsm`~*Lu+G4OgGBsu8qUdXA z04ci3_zx92Eg8FnSdXJo!$2(4q?Rq`;*gt$&2?wl5E2gM-zns1urQu9+Fln^CQ4RM zC;0GbpN_iW^_*E(OVCfW45AB zft#JTRg(7r&sS$C&}-5>n*1Xb+Bb?6&5_9vt-L_x{~m4rNe=i7_Z(f|H!RyvvFaj4 z(Up&+(X(jm<|_BUNsy<4g%GLo9?vr$lS0hp5LvG%7-hQIPgfQPkO~4am~WiZxg1QC^SB&T0ScYQ?#~cqwjCLkb7%`m@I0C{om?}dK^2SH*jdES{ucs2&7$Q zr++1Ml*rD$Yc(w1eT`#MJLrAw#tTFj&qCd5%?dG!2wfm{|G&Br;Ow%Rc*jPhv zHXi2#YC!8A7lJ|65BxD-)3x*%jjoW94e*tDK3T(rxmy58{TYA?=znUm{=WEZyfd0* z&wYu+ZMhbdTsv~L8kOVKovCr5l%JQ^2fTsj0;p>;>$zMaBnAS|VZE^l+e&ZFQji3OIPm zLpYvD@7VZNoAHW;N~sN8rq@Z@x0H_m=(|_^cvS+2JQHXzm5=d3{MN{uQ!+74`4mwMs?T??OH@vKTVaM||Yhq;x}KkPPL6Ib;a@L5r?CY<6Ch-D^jt zUGVCxh-cjukP_tZiD>~+0=wIy@0T61o8jVj<8GTcNUUX!^qC|Xy83^7#E`bsOn@bW zOhuD}RZ5w~H1!mOaO!#rLbQ{2Me`uOCX7}tevp@vO9<#R;;MZ-t#sV|GYP@qQ*XbK zpK1+;N5Sp|GPpLY{UHF^;YC@QPSZ4|1(1g5_SHPICy?b`9D z*?`H7-{01)Mli@duxpIpKl%DmR#x^sfna0TnFU7paeTteVnBjG9mGCN>*G#J;xK?d z`j?ad=aVOMT=(&llt6#sHBRZrwR3F7m<%j_)Xt#R66-Y;#Jc)EYT<>k916VS0Ju0- z>=0$S>857LVb6#_qU*RQtu*g(v;_D4=Qr|1-VFA*<#UnkHExVYC)yi~D=m!HwLBTsiCO`x+W8JW5;;?rY6-)G+ z*c;A|%^&fNGH4q-bWeH<&T=IU+)X8=cVWl3ZIa*_;>Z1tH$HT4k8`d&&S`N@35zqd z4F14cljl60z|+Mp>j~0t(POTn9c-D$vTYHw!0$J86_i7%T;KBeqlu5Ri#U)0JIX_% zDmdXBY6NHqtc#nifi8aeD?XmD#T@~2pXySK)(Tj0JMNF)+ip8k_U6jSBrtSuELQP| zsB&s=rUjJ`C@eV!jPzr<*{gk&7mhP!Q_Vu{{cx|4;dA)L%_Gx;cuY6q&(K3jultG! zP}<#sDg+l0x{7PDoANeJHkIb}GP`EstPt}BH@3pI0KMM(d69QDx1RuW)OAhA{h0-m z?yfpa*=;!vbPi=-;##3>rmDRhmjW+f_>X+T>{C7=WbQxt1Zg3m>;neI*z~4RT@2W9 z>{KWg4#~;;EUP(mDCQq(@Ji!>!E4|+X7CkX^Bw|F`b{d+V%q=~NprDQot$;+y)#A` zu;dZqP=Q5wiB|f-4N&oH0*S@<`as1jRyLI#4;+e7c+ge}7Wo$o08Iu6sRoGNVW0w+ zXVK5Z&)>Z+0n5S8kCR!$sP|B>e--AJhzn(lbz3wex%a5`n+#LypF9Fu`(ru5;RGCV z-#vg1MSMyxAkL-pM?__x;lDj#bvvl&5n8o0)#V2IvlbGAj5>`0e>w?bzDks8Hv%_G zSwv@G)eHEKqmtO@-$`-6zAe0w;JqCeV0r3T*khWKZrd2<`2-RG{#WNp=Iu`# zRD}@$36lrx4NO|w0Cri=4%DOdszrEhYF91h&#;2i5kv#fI_NJ|0r&r4PawCo7EjVa zUgK5=JU6!0*p3m0!X6Uyd}a}NTD18s=5@!S z^mGssOrRk2loE2N&O|`I3Hpb>23)BWk(q^w9!;4*U;%)yIxDO*_YfLxMIAuU2F99P zY+O_3=ojipv^z&wN;@FY6s?!XJ%rYQSAk~pWQ`zI|Avq!ym@nZrbu|{&WY;61`?N{uUv18)(J4dL#Lpw+< zoeeG=QN)~kNj_kYQ9%BREJVL#qnr=NTL~Ghc?0T3-boE8$0H3WzS^y}gG3H|;9W#* zsk1~Y3~nxcWw^tW7s~7F$v=qEfLpqTpjtj-h*pL=T;L~?99CXIT9iWWO_LO73h?Emz041Pz+breV2$4o~V6CCrM8i0~g(cm*i<3olMpQ-RK?W^DrKm zCaU!AaHn}bH<4{_s8Q!s=eXzgk}{>GWa)2F0|oEL+hr6gfx}!*9!kesF$@#u*PIH6 z)d_m|&L$F9gQ*-NGPnybAoOXrb3;;GF%NN<->4@n5{r#~Aq6T`$jr)uf zloEdGkIgajodC=F^9zyMWY%HMj(X0V_<^USLC^W0NrT}(Jp@3s2k;P>0v>|@BW#eI z{8!lEyBE(zK&C@H`T}DJB|1wrX_l=IS)(F|>;oOSW{(0n9~~%$vFU)tPy-5(Gm`nd zc2MS!LlW>q4{nlm<5WvE*%_wk9jiz;dZF+Pt?xqj-Wu@ajbUMoCbKumoh6^1>? zlIap_iILE+$4?3ZcB}a$528YP@8f7!C-*7;%{4v>8YuAE;3uxTzEwt&eVq8>>hPQ( ziiVtOt8>kr*k|KZ?<=@ecl_yXB{aT$0E7MLj))TXyJ@Zhq^##OS1T+K4OT?!)o*j3 zO}{JW27(6fxWWWi4odde#V`F=%)xNz6z~BGF}<*Q;9UUW`Iy*&ZpzB<5%WNh`?wUd zBwxUKd~$wG8iT#w;VEhGyAv)g7ANcdxc0kgCBd}OE^V9UuejdGkv4v9H_tM6zmt^2 zMh2Utjp0*(F&XtUn5&xpf|H=D0 zFPd|0syG8cEUE9%#V|{CLo^M3$t)DydK3oD zQqXHt66H>zFtkY!i%n2wxNYHO!4L5qHyftFKu=eHkjNEJOsxR7c&p;zw8Mbh zb>L-ZwPCzZmg=HHNm4CO=5{rhB!xv>*x;QFnALK|6MJ?w-$NAnVzfO3gsCRz)Kw)) zhuvx#xNElCp`rq+mG}|OTQ!2-VcsPolSI|k&>GW#c3D6pvyu9e*r3$f{fuV!F0m>1 zK7vD%um1CIj8^-s&3Z}>zX0v0= z=9^P9TqhcK$N#w@0P6PTVT(KSnJE1dwYF&nJ+6cu3=hmqsHFr{ z?Ti0iwNLodMF70)38CDp^MeJd*(!zTNFuM3mymnrKs6Fe<*$GLZWnC@#oo0aw2L$1 zQ)G47@ej1Kok~QDg;C`JS9!K5=|{%vz^Yt@`w39=hx0gpMa5c$+QS=%9ieZIWpQwq zZP9rwlFcP~)hjf*-wLiX@z(r3KBB3RVjeO{SoCRH&ho297YS0`OuVch?UO=Iu4 z&$b&KPbSMcAxKUoWhWW4VW8;#iu+|IgEsB`0633h&HJH?cLe?P$wd$)@K6to5+G=T zIbsnkjLFUDb_VJ8eH4jFQ4u{MhH?i=2mF+2pzR-ToZ&u;RC^KMK*1vs*#wnivsytO z!WFQ$fU6x(&8(stsVO^TZWc?d`dNs$C6B}2e_R9_rs))pQCC26MI=6ON;1}kp9E0P zln9y8j>Jn!N{+Ss=^_C7c!j1*>;h|e5!HSd(eajOwO~Se>BQCtm7fpCkF|o#M2Fwn zmk@b&Rz9;yPd3nm16%*n6ASFb6z|}Z2Sv`45YcS&bL*Qm+3kF3pmOqGXSxrEf?vWw zT&lpC5i8QPKJ)CUN)ICiRO#EhDz&vGgaY6~7LO+xtea>4)ut~L7_liEUNNOgJvP()xFyvj#w9uJg=`$!WJL4YgWMCB%$!mhLg%s zLH&D~UgM4G5~9?9%JiP;MnIX~-WfOMA7%QIZZ4|%ocM^O1ANr80%r7x&d^QiR6P<> z^(jxatXOx0vF$_~o4DiRpg$}G&=Ye0=Lz)Cm7n!U4aK#3nXL$`6$JqD7h9IKqBR2R z?}>mSKXrq4;F^=xXp_d9ac?42caIO;w2LhvWgqysWf}J$4Z(J>X|q*3Q~D?G;mY8; zM2X$zqmF!J2>BxP`qm-vpP^*|R{c-72t?W{It8fa3R7`0JzC<1N z1qBL}lHpCRqe8M#jX4vZE&R;(i$|>6w9&079-g<_hjKXD2bO>20qzHf)3_$o5oh1= zC|Ce_fUp;()T^QK_;0K-1*N51q2pwhnQSTrUC1H4=yagR9h_OY6!8hlw7)C_er>D99P`}_d_v{E?R@7_9x9MW%3biL=ouLY*H<>e%jC zA>wgYA&CDUd2ba}XS%g*RuTvnBxndu(4YZAaCdhJ?hq`vySuv+B)I#+-CY*$vT%3k zm)f=WU*A`|d-O>k^ zZ;0@%eOFl>@`@Y9REbg%kAqL|{+(G=C(oN`$2X6@I~N4HB2G&fafAJJz~AUEGKp6P z)o8W>Wu@u2uD?0T`D5fah0&W-g5M(Fpk{cewEG!Q?n!gj*#Q5*pV5G=8~7Lh0HV;% zJ};xh=mX#%0I~r%GNS}P*BZmf8hI2K?IIK~XLLvhIP0y(+*fW?uTN+_D=wW9_5+NLy#C>e3tU_s#>*@*F+r5%7y}$6F!|S*beSQ35J%2HyRb!c6c7)@z*F??x zE;Q!}BC>leiU+XGg{Sgeo4vRO#68+bWag5>1W5i&0d&7-k2`Qwv$4;&D6?ra)LW=2 z%GjL=>!JX%AzFrwLOJC=7M-B<7Xbm|-vk5^!ZXXy4&a+Eg&3w9QTaWDgpk6vkoew(}DmMv|nE0RsG4jSli)I5%a0Arzy6;X={#Iy9u zBapJx=>X6aiTIYrX!&2f8}mVb4*vNjd{#$uS>iP*c4fUf8qmYZMO)}o zeGz&;{k7D8`4KCMH3+Njftulhf%h2wVwJ~XM{4c$x9wig%l!WX6-Y|_Q=__GL)yO` z1l4~!2r3pyDp<&BUEbXKS&amdp#7ieKj>4Mo|r_BaB$9AuqVNv34pw?=fD7J4Ti}8 z@n8PRBATc9s1$>}w;ug156apNHar6{gQTC#J<&aHC@CL!qbJ!#nqxnG;k~p^yt4*t z-O;FbhQ;QEJ;9hrMW=duh|0EH=a?mWcV5LIuf8lfN+2}!!e|l3zrUh}s>cVvliQ$< ze1p~TBS4x+BgZW;u38On5Fm?0a03nksy`hBQH1JO-b{0WO!=N7H;78~TE4czUSHi2 z(9Eh>HZ+OYQFd6$NtBtfSkVM}zU$Y%C7?^A@{wpf@T?KiO5~BbZs@c8Vq4l#(?fw< z5=<-PPLcxgg8EF-?qiF^!`)vXzB+O!qVlHM@2oZWOeUW25ssoa_g@kMqS7rziL<7y zIy}4lYVVPDLOrD{TAL{U=%9-~gYmy)m{EVU@;!#XO}ET^VB}R&wW10^JTH6$mr>=3 zNK?tyeiq;Y>-LTzh$ICoeNni-OmdE+6WB{KAw5b^b_aQ&*IiY9sCUU#BgjI$(>aU- zb}=GOdOcR#ybPmLN@FqUG^wj&Ph!?S`HS8&gB@YwL|D}u?M6%K%opmSKx9j~?)J)i zi_KLJ*OVoj^|gC)zKS?-uezw{V&Yax;#H4Zb_$bY(g1$JtVMO`?m}G|_&DG`uUwv2 zr#DBd)x84|BF%otbWXdv`BmBHr7>;Da1CsRFsHpVA`?Y_TIi3){n^$%T^VQ=*L1%% zm}Tg)m0d8JcF;$xmM(MYh!cAmR9p$N(e+%|B1?hiSZv8s3l$oltVi5aK=GkjFencr?g=z!U`yh@yt; zBV-uqUYB`1igVqp`iMXzjFzbIR{riESe63~tY0GqDd#H-ved6<`r*W++l;UpwtlX) zQkgeydrG#3rbyB)9fXYW=YRBK;BVhQR0HPxMqD?(`BP|{1tgn zF~h0uC~oaHTb~%Yp+D09Gu&Sfj`?(tSg}uJ?B1d_hjuAk*G5cb(!-y%*JYYaEO_DKt9z zcEUC!hUg<*5aBPs)65AQz6`|4un+zvAizWCXd6%Xian~Kgli`**l3m`e(GdV@U4@4 zv8b6*qqCgh)a6&O=@7fRrJk>W^Jcg0y9iS59`j(P^627dU}Ee*H8_~|`lk_Aaf~t^ zXaFz}_+t+)R_-e7WKA0sf&WC6d(z3NR47vQ>G#YYYSgOw0Zt<1g~ni{@yHsFL)22kZHP{} zh17M1T`RvuRohs@1VTU^)cy%$B+ID>3 zS1p_6UonBSx2OxZEhkZq7jkK8Y>fex9iUD`CTrX^=5)^Q%GOsdH{&$iJ>ZS3sz0Lw z9L-L_%Bp6s>)6U~*f}Aq0r_ev^{?MOZt=!18=8dQYZHC_VSW;=#)bo? zZ5NLR(AR%@2bwbg??A@}%dl_fi+4b%3lZ=RJjpcaY-e;Z#wyXS8Lp1>0e*1oQKykO z3H3c&3SX)q<0PnT;d=7-CCbKfPdzx60wv2uNHZlk81G?Bv3C>>E{n%97`bZ%7YE?* z+18udN*>%_j04ffhIN(n1Z55)cD9Xi8M8O|`Z!QQ@}y!p51@H*&|V=r58dB)!~caL zE)i%m1vC&>8#f6VKeKWpF!s4d-0OtP9*j*kNoV|08B=L@oG&2;#crS;J$JSZ#PiN> zRjyW>5of0%sXNk1`akml?r&k$7g=`WkM0Y(iEHxg7quu6j*dJ0?zu}!enw#7Um-km zq27n5vb?+BYI93@^kPIuZaHaU?ah$+087~DtX8ib^{}!(F6~GFpSZ^s4&XNQwKDi+ zGga)2??W*vzGG$NU6#+$AIK2t9~*oW9-lwzqEVB$MvR{Zgp zOCl?H_n*7b6tB78zqaM=6>PDfofdhBG=_ILy+XtI3NdS-`?}M5u&6u*_ci<9gP;=| zVM59;*f=K!A_+4z`r5}chE;9XEt;QJ%2xa_00&>fF7n=JGpX_8KUOFHYKjvAuDoyI z@~P?SKjc4Lj{%xTrPQrWcm9h=oo;NyvIacUYdJXyYUPKHa^4{h7hZg)x{1aT9 z*1rom92X`CRh z{x?~M*Ru=x5hhHT1KsO;2#+rRTn@(c{JNF(Es)2H__uQa7_s0{TXLpqg5TMBikF(> zTQtbM-{~=nqVXfPZ%s9uL*ZD##`)Anc+9TOjlP~&r5xNRILDpm;TCmdLOiJ3XKEYY zHlFwY3|hE=W4GCs&;rd@55!MK?334_1CrfX(L7ki+!O+)9ipbEe>zIN~#~<}kduk0dwcAa*45=shF8YtK z%}qC9WLzBSpNrbrS>2(tPb>lOk$(jRxTb)hfa2BXvqp={i5lh|t@St{nH%%*Q4=_>^f`@I0uOQx)Jny!`UT8&bJ)~o}%e;33oI^Ety2V zGu3yh*LfhJdl0ibEKlb$GK^Q>C8HwvWw$SXPohRRH<E*~4Nkup9 zxfIm7H8V+!Mq%!iY?g#NS8Dgek8+SnrBb}?4#Vv{QNHwGL627pR_5=HllpaFDX6Jq zEn9AdkYKPGb-Q4ztcYo9bTO8XV`I^sfbHdMsh8cX0pR1-?}k6V|4>?Nb$v9kk<~w8 z3wsqKCK?}|iVHqzds7K%_gFQeuf$sV*gm=81WR{Mx9x)GZ1kvm6r`l^k7$Vdd)S{t zO)l3KS#>$#c6!VKMD_+qQL)su4j2^1h55S3yefwTXHCFX}=;N;|(J>#yO_B3uxD>15AkQIi&1 zN(-CtH&@Vr$@%FEb%AO1d??(^Os)%*ZL&s9p?POkzcNl`?#MOW>=6ZNV{ne^-f!$> z>3@1qKL>->P5T!rL@U`ZV~64obBj5dAKyeZ zI-Y*+?+1Unlw*7t`|9!r2vI4Y4Ka=;PSgGpj)({3?t>Xm7x|y}>a%^)FrIGI);^=q zg%CHwf9h{{kf}dmqe)9`G)tYDQkZ3WcMZQm7VwfPP+W7q5L*C?l>FJG}=>eZlmzEhdM-wFv{?sH8a~=bqhWiVI!tN$*OIhQeLsKAO zR7wLu0noIBkOj?2F^F6}?c#+q~BOCJ4_J`j^hVg}LIeeY!42 zqT?rEazu~VFRLL+jwn#k3t}4)y3zX48;L@grASPi)RJ>g0%m@siGY*lmost>L@7j@l;5$#T?jPqwuMH}G1t(JHIa`Cy`j%S^x-aGy^u~-B?$j0T z#_g@f?`#QP_J;+-s85WK`)#(G^^Qx|&Bn^;=%bg|j9wLw{cnO~)KUNFx8{+HmM1Cd zd_Upn5qMXGp3kh;FT3 z{Oo+Yz`7v?%l4W=sfhj_gxILOdC!a6%m2B2s zXI9gtnXrAmUh!xp;=j8(q|)mNjrv!1 zp_4Dc%EDKH0#)io^VIMXVAwJKPk>>gCyep3RI9axTD4LjPddXQTwTxxcoKiq4H5vK zk8)cU`@cjqe$5~=3VmSE53E?!4~WI9g3)f?Q0}ojuIER3s25`9xTfx#gTih>JLbmS zci4juk@Wcg4F3PKY2+4qBtYRmi3H(&+>sI)x{^34j#Gi#-Cq%W2-2bxW6(H*;ySGL z^$zFT!$d`RngBlmNX`3@qQ442{-$rtva^#$bgb`w&(eG4|l_HlIk$%R4ErtTXCtvVem!T#}S2>w5-;eeeT z=%kiW;jRNfqv9y#^I+kMl}b+VtzlbZs)@%tI7V4E-t8fMGoF%s9T-njw`89Jke0}! z{}o~gqs!7&tlHq03lKolFqw!B=y>$o+Sj;@LkA|5hP1r&*s zG1@6#^!$nDcJIRMPGT5r=iMDQJ#w*?b}mv+9L~+&28-*@rbawOCzbyXD0+cwF`xh# zh#R;3UlX*KbN&?GKNJ9LIy&V2v5G}Xa9F+3Ct2c5@S>T(Tyf4(GyC!#Ts8#0KNm2S zq_CpX&TZizspkydLdOSfqWit@4@PWMvXs%HIZ|7#;5jpXzWTf)&XU2qPmmu;fA@UQ z4Cjg74Ss&6K7bw^w)VmGhW{P7>gEaCLlu1I-6Dkcb-eYiP{2d;R77OTQNZUTokFZ5 z%JByf2D&tqtb#zBkGu{mR=g?Lz~N~$-~PvL+Tqb&l7ZEi%Uya-4Gn3|%R|U=;oZzR zx-(HFutUQh)5V${x!T;73Ts0#g?G;oWz2Y*WNhz*``QXh?A!3nFSCBiIknb}n2pow z)B}r&hDn4V>G)`_2E_SYfx>8L{DMI9Xn({ae1_4zv<;bC-yb?)wq3nmcQ!{eP@QPA zPV65i1H@?(hTNIDr0&rMzeecmrh+b%*krkc$-^z>89Y%eBse^B>DmY`<%@4yryf?E z&jR)&Xglxk^+=-(ApzDze>m@VRVHAyM>O4R3DDZPKl_={&Rk_5GotxZcNn_Z3Y?6ztG#4?`P7w_oK`EmcMypNVSim^ zJU{dnKY0H3a`deClxYCY@sg4%s@|C))8wh-coY7Q4upIs7eqtY7M;kP7m-#lwJz^u zm4JquUl;}@j%DJyt{YZ?Z!T|+w$0e*kFeArYfZOH#a~50h|r%fyOZyO#FnIafDeaq?15^Q{X`DN;;X_qopDSgF#!D6qYe191HDA zo{SGKTVyol>2q4_5MOZmQ$lPJg2U&2s8Izrv_1Dx9~UFi)gae$G*A`sY`4ZP%1>Ap zDBRabuL3;z+lW^G{3GQ3>!Dm*$W*`Yu2zF}ZcrrOCK5YR0MNhRzCUjLw&4PDEAD-# zADY%nSf;W2eE~B35(45h!)fzY&n_Sg!J?C*?&#*~%K+3nq`_(}t_?|AM6DRxe1tWG zmcQS)z;CcZCFw(Jz~KJ$Od`*c3e*@Ie66t^mB`+Pd0{{-dIXj9`zLUnQ=$Ya4){;Z z<~VKqB8l&YzcrWBtPluRdL*(UuB}lS&@;8|cC(Al%sRqSknI=PALW59@}r&-y|&#V zxYNBgXOE(agYw~b;U(W87*BZP+kJ(J%OSMaMXu8`<0wKxrvbwFq#*Ld+n>khmRmiE zaDvO)WH;Tbp80%`htCr!kR(QC7oiJ?2I}-0P>o79@L6OJTvdaj?1J0(tx$~;VD0L* z-oG9dLoaTKchkB_lytwK5_{}Rpa$JGK?uUG>q=xq)a5>uJ+C|7`>IgOso(7~-rCa~ zWzY~dAYFk7Xmbp#0nGqYYuzpvYYLJ3R8xr@JCPHh8=6!u>)*cYj!ug)&!$k?een!@ z>$CWs&Jb6rxZmmU*<`vfE)^g&_8h)2ZX|ros%{B!Zul+vD=QkIDsXPV;fPe4DqudC zJpf2m2S!_&!Px<#WEfmZgi(Zsuv$I%{L1Q9e&BDrTM1gsL{loPXz_-ZP;LvH{s-{5 zrN8v}jm)|N-}hYJ0@=~H?Ck6=v3Np_E6_9?rBHsbB9krB=|wwYiXt5%j*yF{Dju4z zs%8YPv*{>6Sk*)*)H$YJ>ag^Z2q$rs2INiSU0 zoSTzFQbCSk2W#nnFGGL$=fLfV||d zDrO!FUkG3;&itWEr|hoQswlICUi>QdBPaN^MD3RNRQXAbj1d&y{gn zN01%DifcLLctJ6elC#K9NmHD7YOx6nS(i#qsic;-uI00`6i!}#!=iE(zJ5oc`@CvVX8&QUMYw{hfJ6N$L2%~Xr z*;`tF(BHcM$M2n5B?`(kQUftLMMzT-AAfV4vSHH6&w{CqQ|nH|Z%~?s%B6D%K(bFb zlGGX~H7(e^h7TLDqo9#XF{Sfdfk;t*X7sQJDw3fvMDlkP8eB@KBu~eT zd`qLhBJn<9JMBV~#CCjkWEYXWc+Y@@YdDms!#9VB%^+~pa&Pe~Wh8;16h%N2u5-vm zgRQp#@^xBh@L9IMYC6M>Y^kh4>7$8oY3{tT+B!KN`$XeRbJdTI8no7`A4)xGR{wm0 z;l92kcom4T{qDoD+A$t+B1#aH&O7Pf)%rxva60pF$S_22csoEmY#k5qTLFiA2w$Mv zeOAXiQ{i>MC#$y-z0duxZ#SoVK`LRB zf`Qb!mrkukqoI!SixvFZ?8GPw+4o9jX;iljFnLprHMT8MEwr)QG*U{apVU4wLny<} zkP@Q9Ec1q%Ox0bVD_p#OD0_p6#3IF?!w$V5{bY+-rP6gmRu1_ zZm_Z97ccW@T;4^A@ohnF!N5~p`I|UgCjYedm7_`VZA8!xDe}6qrebhk)SYf?Lr9+% z+X7?KEN3vy)GLsbA~GpQ+_qB((=FIrah13E#7mi726paTMnF6gMHG9HGF8psI?(MO+jIJ6{O%Kr}j2JphZ({S`hW zBXg3c*AE$=Qfc=FCC}{UWR1*r!P4VittuGW`z_IwDj*j`wCen($|D3y^wJkTH8nAk5)H7Q7;4aDXPT^Zz9-r&mB4S zCi>$3I>h?s0^9l~pAm7-C%k6dXku>cB6~Y^aBoz3i~ZP)ror()ZgB<|9Rrc zNTZZPA*a9C21ww1VeTO)J;eYWkW4NR(djL?!!;<)VKs4|P_WOGQyVtHU#e_2Ldyvg zep@3@s3TRhQU=qx+%M8JK60H+gpebw{8dx4H`ZuBd4|~)$ys_pVz&E`7JWho2>Se! zD`SKYExnF~&)uAxj|yyPFG_2FMof%6O?7Hrs6sE>;C>g83_C!PZ56k!a={@zR9h@w zruevSH0>nUkbCDNlfAY)zj;tFyEkOQ2PzhP&@WbxFMCX>Hs=ycw!uE1Wy%`f9YV50S-Jy3fRK3>f(w zYq?;&0HD$@u<9+TE&CSJU38M880eecqCd&6BC zlQOKkb(fw6n>ojxJ=nas#mw&JDuwo`zhidzrK5K7P0bx^LILH^vbQ(r1 z@{#1{cA}=0J!-0<+)Lpulz1SwEKRUgt;r~+aB`iHASJktf2HwVL{sX`S7F3pllu)3 z4G96Y&eI`9w0`Zx}_DwG;k5n}El| z2Ep(TR9FJO<_cvLytquJSb(4j-o`V6AM*hAJIRqbPCPjPB3}>&YtA_9nx0>|J+NoT zl+I>bz8foxd_@Y6rgS@2(X?OMBmo1<4jzj_!oNRB1vpLCrAI}b>0GuwbJSK3H>X0R zvwRPWfQoDT?nRPdxROpkbWli)PBPn0ad~UhauhKoY-K#*K@ju3fcg37D7XF^_w$~O z-KBgIpfeKx*t*C*_-v`p>1>p1^=&Ele;jfDy6v>{dH=|U;tTLAfxW+C2se8E2>W4# z5vez(Cz?jf?K1B4?MDFJnKY~2;@_RRt5ZR>w$k6J-Umc{bJXfA$b#YSd zI`7c1M69Zw1E1Lhii~ek)q6z|JugZd<`uVVjW7zb$ZxI(PaNtIG66owTq-ZpQ+Zkh zr*XLVzWHcTvMAf162)uBWhrp1`|{9eM}(}tU{BoS|6ryHdv_!k(1s*P*yK0TLHS}F za4_zVg8%gPSHiAWoL^WN$ylkc*||SsNOaVRp5s~ZxT^7}x@%2w@zHYe-oSJY>85-O zIqz6xLsI(W^ROm!Y>B$`*XF5L>o9y&s>!|QrPH?Dr&JO9-%VO{hhtJpnC-5sw!lZk z5s;k6rbgii%ssULLbV6T2Ir1)`nRQH7K|Bpr-KHGflp;;N^+TT(iQ=$?T>3*W+}bo zz09Xg9=n4PI}7#(hvC_a*Ey&!%9e&(L(=Sx*FoO166hvb7OnhOhoqe_2gHZTd!HjD z4HzDa8sth#n_0n#x%%P~@#e*`$I9^o52S+F&#%ID5xNld;ixzdf-x<*K$g2qj_f4N zOU|=%;W3;U2?<6VmSahaPO#!tx4+ysk|kJJL7`@>D1vi70dnYBNGJ;RIxf(&W>Cn4 zX8x}ERXZ4mw(ovBC182|9H_9bLFHM*)huX$XV+WHmK?WqoL}* zT{5dXUVrGr_=cnrl-KBnj_@gpB<|_2Iw4U!~J>Ec-hVK!cXg!UH!F+Zo`ELjHpRi`! zz`v}@cs%=~7+m*EzBIvx=mo%2n-M81BgZ|J*E$ZZ8U#wqxi7GxG7*)d)8eZOgbDF@ zdO~Tj-ruCM{H?_?FO{O8#hzFSygXOL0-60vHoN$*V&hC_`a>-f27I^HvH91J z{I}~NGo846C;i~MMKZ8YH%g6H~o7-fJYUMjmazBEew0JLU@kR(=u@q0q?r zkiOUaAVm&!lewC2%0MsEqy)Or=`SKAguC`yERW&eg-R{}!*i*m0Jbi-Z&@)Q2i+^#D1{tAM8m@N*MV-uhJU<$2 z21d!rRchKV)db+$j(HiPwZ8Q5-y-2s*z2`KM@N55Ifuq*+jImZh&kEW9j@M`3q(6_ z=og3#W_$VUv+W<{JA~_7fW7aDuCKL6$9U}au}0}mJ=J@^snQurI)SnI4-w@iuwkBa z_IA_FddTqGY%1l_&}338jWqHUi@ z$i$T%JqBi}6ExT!g~bN*`hziMQ(~FTOB2GjGWzmr%EaBzJ9Et^_lKPBN4G1l^|yj? zNU6#~s?FUiHn%UvgQjaL?a!gW^;e26s?GPy)h02i)&rDspKujQbr~vi``}4M4C8MX zhUFdg<)ivd>kgYc(oZLugQ=x#&Tl$n+xpEj8*NvT^-m`oc(n*g@31p(8!6s=&0=9y zJ~<}(aZX$^x%2AsY*01j?BT*`)NgvpC5?Rq*Q*iak@OwoB$N}F=r90bhKBWED*e7l zKsvkv@IiA_C-W-ZHyFa!8YSQn)Zn`$>nxUXgd_0%p}yByq`6*$w9lW+N4uON_m zhO-~Bumb+%+yY1!BVaLaiEKJ*6bA;&+6Nsisj0Y58)4rjDj7FI-p~)Ktc;{vk^h+G zCY^E;Gkk}`ERiRXqzhUp&#kUjXn8mj2fx>UXF#W3FLSaiuFT-nGq-pSBuie#j>5F1 z-4;##M-I!RC2t9cR9V4tE%qhUEr-lW4)-h7rs!I8@3U#p*!icXKNrxN6(FS56y^*b z{PRwq!hRi!1#C&v2#D))8320$c1oDR4z^CRpC`+-xOE~j4-U*2EuMf83o6)qBALmA z7?6;bGtGAU#>K@Mz8gyKPYV3x&cHjoRp8GfJ5F)lWYcS8AlOeMSGzV@ctIT>7qYo? zK&9GnNTO7$?m_uJnlMmcppG`uKoIL^l5k~?c}R%<;EbjI?SLssldSaK*qIVo;jX4j z49mH+h&#aS>=NKd%`zFgxn>*5|G|%Q0TplDlG0Aa)0eL{s!Hg&(8MjHb?hP@JWT%$US~KUGHab_phh zm10R6%So;#h|sMTRDPTZVz+8SDk_y693K9PBR#*ed|Y1JWykBpdQ9&4qz;hp+(mobgMYjx|&Zjy)Bk2c#g9z1$l8BwC-<7@V$iCa{&tBp{wG z3g%qDha}FSfu|qvX#abU75DYCIjq@SIqwbOA)qulhaB&o$tebMdNtY~eg^RLsJL%G zlzoR2-=EBnz)q>m#I_Fn%`_NKgMeH1JCe{%lofda0e zal+Ps&8-XhiJdaGlm^9Gi)5-41g=<2=+?;fiHcWjkUf(^rNU5-{1Q^!d{f?WjbMM$CE(_wDJL6UhPhb?o_a>O*1ajJioNj77W#~$&7&?Hsd z``sl){VXZ%M<-RqW%{&*>RApp<<17YU;1)rX=!N%F(wfuC`lSEhmF6O4&T*$)0)(x zdhbMWR120kY{)F>BZB_!QL~d_Dxj%aN25h1oHWBZ6yL_oG68e78LT43im=i?BW z_RRs^q)g`wU-2NqjJC3MI|HP-Y2Qt*=;V9!3xt&XEIDkGzs>7i&Q;*oCu5F$c(GhM z>(au*PjFOL&sDOl8bMX6v!!Tq^_u|6E=y7 z7iBH`%v=D)ks%(a; z1AtiDkjf~eaXTOWRtfwLxWl+w+x?QdWah^Eqo%>%vcI5~`l&)+#^BgY#*ys@Kz$Mk zwE4Nc2o4!Vj9$7bw+%q8Mpl}SCf--C0JRNAo=RwxYURI2-+DD~Y7L;r5;^8>LbM+E zV1=)5cQRg9qK6%T`^PB2?XC|2`ySAD;#hK+3BN_gHv>6g=Xeg9Lz>YHH)dxw$WvM~ z=t2lQBxdXN`^Vz$O2>|SnywV+mbKT(D&G@mHS_^|i{U}PO01xRYt@czV|G=I6z*%gkhZ%@$_b`t z78;W8HiP|#6k8bFyS_)v*_qX@>6=Unx_U;jlRF_2vq3+p*gWWF?I++|x;OH2iEW|- zGp?(b9jJ7wtcyg{Xs`N??39iNJlZ$sc}b1!b2LBvW^8cyT}mo;HKgPQXg|H9 zQI$Gu=S?44j3`fSiy+NEDnvff?1Sv5%OL4gXPNPD_P+4J1@mtlTnajCaC71pk+h*p zHVO0>D$yg>L`v-A8zO8D5{Ze)1R&Rx`flMQCd~H<#})UF->K(A$yE7^#Zw#|^!ny} zwlrppI%UIVQR41}SUC(wKof_wZZ&>y2yR|AqvXZtA>AD2bvzuq5@={!wlJn7G) zk_3jqq!!ZPS#`blcg!ilbGsO?O!24g95eu0F?qjS?horyg6!#dP~Jv{`EurPI{U4- zvsJ*!!AWPCmNp0x^3IlN(E%G57V8Zs5K%5eMRarg?4XlE$4J5hI@?}6hYx&z&=G?m zkqV<9NG8hMFR3=7u1`9VQ#joMo6VrJ4FJ}pKOxZtPQ9v(ZAOv?1Vgg@)1v%ZH*aIHK)4 zNJ!vC=LL@SI?W@G^=ex6H(EmA7K7k-$Df|E@jQ8M9Q|R!?HEh`NSU5QH^I^PeMXK#p1uEs*pG7Pu6_fgYuufrMRwxsw*y-S@%r6#=o z{56%qWV~1b#R7@b{0@b2?5F*icoD&3zVB23eBh|2+jP@UbW)$cxZ(l=W-LcvbfN>D z@J-6H0_@?T&8@^QdJ-_N=`y6bZUqPfRMI+vk*>sf6G1(b0VkGp%@@)A!XG}f2`S+_ zZN86rtK(Y^A+5a8*`Bx4zd^;C`DHK3p?4KUtWKm!=NQh*CS)QGFt~rO@}5JA%90Cf z%omhY47$45h~TxHwlFq0V}IA-^C+QauoxS}Wk>;46DiYYCvxd@GqZ3$T^Z)hccX7c zQSe+jmQgNN*P!6K3yae4B<3Cact=0no?Pf)V0B^Uz;7nGy%p^Zfs>#>dzC;jah}&I zMowB4nVtVkx^;&S8&mG^N}tXbEr*_2m#X7)6@5RGRw7L;$aaNO*7p0-vgKRlcj3R; zm%|;)1*0g;m!+liZXawLZ2s4a2lyio{cHPEY@$2WRcLTWeS3Jr2N>Xg28?Y}zL2|+ zszqS;eduX~bvk?vUY*F!Er)sESOJIp;wS)&a6x_!b1=76X5dO+IgslIAD3%--9>}~ zXeWNWIqvxGrbtm86MG`!5Ei294-R?=P7G0>KhngczT66*Uss5MW$u!hEL~%}Dt}6Y z$+j@WbBw16P|0=GLUK83h454hW1tZ2S~G*i(h{FpVRdRE{n+->OCR{Ps9Vj+t#hNb ze;U*6Za#cs(?QayXoiM~kY(!^Osx2dkJ>MeW!Vjj5$z#USs5kH*KU!?-LX zV@)u;2nv!{?kNfwOs+mV9hR6(h<9opjswJxqeOBI1cV&L7+2B^91+!9Ov>^8t=oC* z8|^zRhH_%HJ4g+IJJZ*4CN?NXRt_Ln4YXop_@TmZStopz;0L;(SpUp4Ml^@RX_M`~ zZz6z;St@4EdQ+j%RkMGgQw(#MOs|i%i_dsKDHw43GGEUcn=lc#hsOA04ntB?`QFyE z;&8GkW`q{cmSak#-xgSTg4e-l1x{lkg(Xtz2Tr9bss_z`KZlJ-6*~RLm)D9DI|jFa zD*~B%As!}uuWuGhlTh-qdF;OBKDRTeHNK|tqj~$y>E3SgECqq&Tsducm7A4?9U*Gmo}72b>AFf1SVA| z-5i$F-bAh;?*u zd=qXV3OtYG1!C5MK+r%-qb-F97eQEH%P>? zPaN#vZ%mf~S^)c}9EzTCB~nXKl3?~K}3J3}9)+WV{4 zQ!%zJr}JDNpqRCRhA=L@sGoVmVcjq|5`vH_tR0m5!GzR#qF9D|6asnyG&k4^Mee9fEF5 zQSa{JTLv=HO_q9<_CcuxO)NwTFkALe&4lOz%`(HdR>>-~(yMPPj?Cq#esQ!5g6d7B za2rx%rB-3RdeE;?bfG6sfUcmFM6$4n_;T<9hJyXsQcC>Z7d)G*ycaCxCIE$I`jUM5 zbZVdcYnMzP7w_nWEksuj2$o+`y5_c%L`G%F`l9YVDl|fP%`S+fJEGSPrOs+K<7XZU z6Xusj8KT#wrEx;@4VDF0flvn?8xre}bSafvq z_5qoXw-Ul1dSzM=JC75vbO&g8I)4Q;e83c5Z$D28zl?@H>o1DbB{`~nOoHh(b{*Pl;bGC@A2B<#in#v#>e9j-{1$Csd$M^1ej z5TYlyc2qXeJmr(IR-A8ZPUh7k!p$QBX!f4LCrMCyHC2WPc4m<&9~k-8#fF zXEa=WWKG%J>7UNOh%qYT7ENz-)M{8O_L`{feX9KIVVTsD=fiPj@X5(l>CIk?3jwQ5 zSH;;)#%Pr32nQd3dppYA;K^F&Fg}sv9m;u}n<$|hh)VgVR^A5RZr-amZxh22@CdHX zSavgg0KgB~c@p(?slFEv@e0@|!+eCijQ+HaVzbzE_UnpITMb&ZX&`rBIIqv!q>+kC z=PW(!T$|}f*g1&&Cpw%dpsiX7qcx5G0-*w|l9Bu*_5_rb> zb6tGmc!h3YZPKW3En|c!2l;jel1UPL1MYwP^niPe+QALT30l97vS%T6kSKBm>gpPA z2#T-_=$}?~Yoc8o6@*C@E!-nE2;yv}>zY=k@owe*I8WL>pWhDuM^^~R%NSP08)g)v z=?{cx-(BQ`BK{AId|_x9-moyR`UoT;czV`Dc9F!JHTd+tn*8|=rCntlrj)V-fgJ7X z;{T2Tgi;mryk0Z$MVvdt069u=-Rf77M_qGtp*>{f%*}^bARO^YGH5rE5|J)#jaNHmwO4D^yfMS4-_$`MYWVv&P%ok ztN^XhN|gp`t^02#3$C$D`z&(GughZA!;ru$vm=|$-Lr~u!o2W+0pk2-*Db3hk7sH@%ag6L`hV|N!!qmpTiX8 zN*lf-*)}D@EjF687_@2H+}c~<0DCW}k$ugq1}E6jFg#J~9WcC+d?noM;b1#`pVdk* zb@fecvIJ_Wb*Dx{y|@u+mifK_)mYP}6QVsiCXF8=9(!+LE_Sya@mWBOEv9~dCZTVJ zCMC2=E|*K2-(fcFCb>2N3yV3XCZ#e*byWl2DG>=)4ERMwu-EqRc?StY}JUIcMqOUBRrUn&QQ3jsjkX+ zJLaP`WV`KbX>SQ_t&i(Bkg&i0h#eyfvN^zj6@EHWeNh#_8QlOWL{cS_sO{1~#vo6QJ>7 z!2RiLFI!hB7LEPIu5ajcMe>2-_8`RuXgDX`x4V2+^)_2;%yf`8wUoHI5A-4ijqd8Gzux zw#;nzfl0YV>3K`N$d4SN4|gev-vf)oY!>-~s+6UV%sdm(dkE|e!@IT~flvS13Vb7o zuexgHu zj}fCFrqX-e?drJ|izd=s3(^2RF`@2iMij>q=gY3xxq_%t{P$nDmhb;B-rh1S%WZAL zogfH^NJ)3Rbf>g*hk$fQOLuomcXxMp>kHD|At2J-UHf@mbIoslYp!qq*~hW}DTjdY zjAx8--}iN$S2p@Gvo@@CJt zC8QfUr&eJJxO+5?I^D|!zj{g(%#F!`yhnHfoFz*iRxYYNa8+`8+rFnIts818cDS=l zq-YtRS?|fxtic8$J9aP(?wQ&4g^f7-?iPZeB zKRhp6`tUceswJFauVZ#yeRGMw*kHR*({6>Ibgl#y^Im=}$vFlRQ9~RL7ukE~yRC-< z86tvE-&tKunOagH;T>8ZJF976Gw2Wq_TgGmZ1H-%C7`^-_O00S{SXxxQTlumEI>|o zVI`IL4#(kZuGujmhu|D2OH~|See|AGgk>U(yUSx%I@n=wYdxWXq_G@PQaD)GHC-1y zx{AXQxlS}A1sc?!`1SQ^K=qm5S>f+TA2-CK)P0J{#i`rc2P}eEIo{*;F^V#3#gHe^tSODwZsWimeL`$T!-|N|k5V}q5P8|P ztkB!sg`C(Qqq%R778?YlaO%yyMVCf@ zO=ccAb<#HshV|X^3^LQ^z(+Lj1yoy3A7t>J+b3) zbu3L8^P&&L9i&%p+@baMjTMkHBWBXNM&>%{CnGPbB|QzEP~Z*v@u4#q|1Rn=Px7Wg zs>A879_Q>BD^Ff)j_RF1dcdH*cbvxYew^TffGOr&rMKcullajsIyC_*?y0Tm=zXd~fBb@0^qSLiB|K z+;J!aewrLMXoN;*M&eG-b+QtWLuc18KC=xHA zbY1~O^vDI}G>ZT?U@me$IsTKu5zq{SQLvdl`Mf6JX0sg#$6*~7^9eyGfveDHh=uFr zr*c1BmJd~}0 zn6oqNYSZyxQ7P-{b>|go&y)Mc%aT+|n{@{iOW2%nq0YQD!phx5=fyV~zz{ z)LmWS_{@q@QnN#qZCBC$Z@3j4fc%2J;ygnDizGBp%|yTbtIw_33(pz7BG!X#`)TwY_p zE97OJ#Dkr)+{NYh*Kwm`W7paq_=&yg820ihuflR1Da|Q?)j~4f`KVnhz0G-XZv1c_ zWVq=()xm_DvTC`s5>1sYo3>wI8Id#PymzXdoy4cVm+v<=^oWadY z0!tq$yHgwmXMx=taAxgwp0>BON#o0^$VqktF4yxh+`lDD3$5QGW;kHwkmXF(!Eawn z?xu8k+tlB!or)&i!p^(FDv%eggbNdrL6 z^$5`jaCsO2%BxL4QV|Up8$NVg93^$bA<4NM!6w-MufM?Hz3rIpPFrTZOIV(8t zW}Qlx;{X(BpYWhXMe~NEMhUVKFhETb3qkT_XbCrhXZBYKe4Co~~Q{$(MN_z_GItideUw234hS zX;1)BQ+mxOp|NNH)M&tZtuMnR(IXMBXss;w3Z*``-lpwTsY%5eCjlus!5Lol7O@Sp>1ll^KuWBe!@<#xuF-$O2Z=VWy5*Vi% z^n&REg78UGM*zA$bYGuJ(JJQ@5oD?w`gN;zcd~u}Rk3!f#K*2wK9#Nin8cOqG5+GV zY}C&xp(1T-aLW(5K=d19ezSvxPo$e$YH7lrz(9{Yazw35jTQE+IG^zrC{UG0Nw{iF z*54zWvETONLG$ghG|>X<(wEl?W0@nR*9MobaGMnKL*#IuMOAg0CN89m@Z6j+)Qi!L zy>*OhC6h->X&;{X{!&WNrIo}vLzEJS2nqJ_g&8?lTjqD>AX4Ot(#a`6De+!4he$E| zQq}#bMQkI%@ySSpFU@qh+oGkduAlTf0&m^S7H7j7WcQOuv$Sh&p*`(+9YZ3|9P3H< zi{{zKk27HbYdEtMrFQqvc!`X~?F?>R;6-T#emeM8%eUc%vDD)NZnLc4(gqBOoMEfe``>bg zmCRCEKtA}VoFS6K79wY8Z2U{k0Hjx!&Wik7(ZB_<$n^v5^DK#?4A|X+)vhiQX6I$c zxt>c08U&oA&@YCYlQ<^ndfe4j9apJ9G#noVGRLn+v3IRN9)ngdP;quD0ZaSEL7KRZ z#7tG!lg1H%gg4;nMIz)?ljGWa%li3`q$R61`ho}oFWdT!vj?k+Xd+79lwyNU$UQhY0+-QEx|h zS+eF$FFn#0CPHHinYXWdE5q^3@x+dR2{9)(RTLMfcWlSV z`Wi@LQ1~KbfW>}PH^y)L)IW-(C%)S0$v!z8RqJuvDEctptgszKciUi_H@emfqKoLq zohp*&G|+};#k@To(V5u|9ev^gv`O^P`$L5iQxEI)(Vs`?DVy@*RUfj^zb$?7n>=%k zonoqsW^vSPgI@$Zy)4Uhzi?VF7v;DmD_wrnD;vt8ot|gT-dlkzRQC3E8?_E%R%z8m zB$~}H^t!aGOtZ;fjEV^Cy*?o*H4ll!uKT!(Et{4d5S_r3afRD;tS-g7TU>U&rNTX#p1>h0PneC3 z(D6oLUrMdh*R`QWal@>mY5A+@{!_uq^(rlt3U-w{N@mHTNVI6l8EisQX$Q<^cVzbc zdkvJ?l0h=Y*oSiE4B2~K3T%0TQ-R6sT&>!8UWW03?LygChL@=gwRW;KblQzVnDjd2 zn^ITxmNP<_44U-I?#FrPBj0qa&$q{te*Myi5Gf#0!dMiL3k0EpiRuxthqhXM#TX8XgsE%%bhMM^+`;QRPnfB*vs z5XRCzQGE6X%)8p0|jFmL*xAtQaNp)i}eAX?Zb_r?-amr41 zL@sde&xGW(0OmJzNt_h;%r?r!nFQri2Dc;2HFpBN-f^^y@MvH zvp)%WG4G^egKJl7(V%!M`ah45Pun6su4ujP;ld&LekO?dh+uu*{&?5(tpNzU9zN^r zCbJ^?Jy04{hO~=pAi(D?zS@`s5*YNiEYyDX5*~%SCvShFXv4<-tvjfbZV?&w>DLS2 z!N@gZ3eu+=Ra?NTrKdtd3#kz>L|wP+-L}y5@l27ZPq~p{YvX<6`wqGw&3OMq+LgZCMT_Q)qQ>kdaPG8}AlOSJ=nONb^?*y?fX$Y5hKrH*y`(J6p z;s;VnVB~B^s<~=EAY>*YC5Q}s0Dlhaw;Uu#2S+44>y}WCM%_`*KIob~S@Yd?JGc$W z@jt0a=BM1{Xi0>xa6@fxik^t2=7wLYf#l$s=Tvn?@`(yp8L53Q6TN!PA5Y{KH9O4A zAl}nZ`t@~3p9iSE<(Vs)f(A5RPmH|nSrylJ%VmU$Un>6fDkLPPJ{JyRDeIJ8p~WL3 z`W*>l1Mfl|yR?_e!7W=?oAW-!e!=@f&n`s{%aZueTG`kl&tN|>u39V05ak}4{021s z2PhrQb}^k~Hd&4yT#938o*&wvq z-=*Ux)Js?F0$JX>ojgCd5eQr+75e_BdM`M{Q3$CMs9<{C`s|&=Ep05+)=k-+k2wMYN z0_|#?1)4n7hejV1X}`xyL6>~4SH!wa^naOFC%}7AeQ~p%WtcQn6)lEa$}9su2&gxz zBI(z*=urvysH+!i+R3D8;kw%?oV!!s+OhiD3AsdrZ&jG;&mWNxzDF$B%Ex<1Z800kD zU2OGph^_>mewrG>G7|MZY&3!2;VB}X1#zS728wq5Ruim|g^2-bg6;p0Y61x!kY#kB zRDM3~^ElR!^-_pd2{|_YBBB%%Z*C7PdeV}njUrCL3XO1DX!&WhgifoAd3rzDEgUHA z)n&q@9OHPkhFxRjA6o`SoR@$2kq;`fl(@guv><3E7)p3&UeTjbsE9q*_RhWjqbST7rzEwS4SHwBG5;Q5gkxf2fM7BX>&Q>;#U6( z!Fj&9o^QQbY!$iP3u@X`QV=&apDxd9BAI8Qmnw$Rx*b>DQH3uxdWf9b$p~G3LIISx zViG^GNPJTv7FZ*FeF84P0|$)!`g)kr@4qbf1xVVdM2w>rquUfJV21QgA zN`_-zqH>2?9g^n3I+hZ>?$5`{DokseXHWOXaEX>fFUF`Q1v`Sv>y^MNwPh9ymt)?E zV_uKuY4Nj71j_UGpWq0}#p*;dvcQm4B*kxHTe9yAe~qR0GcHoZ^xW%?Wn{Z(2(YRA z6Agh3O&4oe{zW_tqnGudsNG>#vCapLrC(l+Zs?Z~JSsd|+iJ;bLrNwsHMQiyfInX_ zfm8D7gI+k;rJskwDEtuQNi}tJJPiKC8;=4mQxe$n{6f5*WZd1t>}8nV_e-BQvVGkp zkM5U14{t9%au zu^9h{M&RHy&Pd8arFi6U+Uo=9>!(=HB={w)Ad^B%QaW8VSDmG}CFxQR!Rxi)cUC1w z?sl#^nvGMZKE5vu7YDU=Cp+qtx7g!2O)v)biK?PGM(p!Vf(s|hEDflq-Q(uoOYYKR zD9)Q=QI9LwJYK=;){F&kc>mZ9uU^bR(Q@Eu2ea$c*cwyHrGEy1U#S_z4PCdrWPTW+ zaN6D(0y?zxn|-w-T+|AhKGw7?X7#p#nO+rogewMJo|L;OP8R{fM* z9J)O3M2}4Gecw_@L~dHNt%DB`<&pjkd9h^3J) zb*Msm?G;_ku~Wo2=*~3dp>q!y>HiHP`RjgLZBP7l-tUykK$2(n`+8PC4*a?>I0uAi zz$oN{PWVRXrC$KE2{*Vo|_c9NGRIU2l*7HgJ(Gi}{x_fE|(iw=&@I zFJ*ut0iX;RHzbX`8GvC=qO^q3*G%kBtgr_OT)$sXz--SY{H`gzEv^1t$aLkGp}H!s zAnulPoS19yP%XP^p|(}WutNUEA30-k3X{fxu`k4TnpENY;)}AjT3KAfV&MrKNnP{2 zgsIYt2;UtSCygmylGbF>*aCqC#M4WtQpt{U?Ode>1&JTaZ~Yu9r`QZLw#*A%ou*Ww zNtTj{b%<2cD>m!IM<#4}()LcbTsi@z0GlSC`kR?YG`maQzCVQmhd0UyWI|&|lNVTJ zKbT$&)%A=+Ed9#+w;7quHX$uq${%|HM>C>ha-I{i*@w*%wy|@T%esO=Qt#oDlUuEQ zul8Bzl&2cpV#jayyR@)I!tlTqH-Mh?mWNI^{(gH-2YkjpeFxj{jp};2ig$6_DUssV z)$Wq|QABP*Ts3-!jMY8x$Jru{dP9wns!IIebS{gjQnjcRZcFgNOkG|$FSWEYaoB(| zk}L*!cQ7(%+TERF&7=y zk42gJsZ;3@-H>u^+lBJ|XCI<5`W!fLfIDVl)V+v|Ia2_YOd_#nGs6ynB3{P8<~g2S z7)L)}!O;)eDj1-C7%@p5(`@8%`Y(GeC3ND?Z}za0{>bDA6BP5R!KZ($tK}-7e1Otp z2s|aBrWD@EeMwklalWVfe`wB1ih@Mbh`^zLu7P0h2z*tQv5~2F?%y}?hz{*D!gjjlb`Ku4F%+VW%Hvq#D zoA|M+g6j`~o~n*vxmCC zWGwFT6l>A2=Py|u;!1C8a}V4lwn?r#@fGx0@omfnJ4QcpZx3N=H^g8}?bFa2j{dL) z@)TE3K8^rOcLDw|$V3D%dyIGq9Ha>qLFX8-eERx_p8p--!5nNyOl~)3@AtYtG@-DM z0{nYZ)F~X6sVOifWbP`F={8^l3NKpsB;Najx=xOK$4N-{rl0$Bmf&?jf)vCJXXBkh zZjvVs_L*?n%@1lo_(A{=%^l$oP9iwp94kw2YEVzVh&b8mXud8g(svRo%@6}2VY^@p zLQlx;Qh5Q;)F8UEsex5mcq#729)+OKkk+s)B`!3nOf`D!7rXhM*zrI}eM#WRy_%ng z_*nky7Z=G9GulPDf-zu5+(_zrC_vy5?@KcYjC4d{(DSx$`dr*Hf?|AE@}pg{Rr~5$ z=63b@)QyBb0cHUK0d4_&{@MwGY~QSCT9GOkb(r-(J0;9s`SemRSR9p5|A!`de`2+> z1YkO$0sjmYM8u0D1o~4zPQhYp18aGEHAj|dCo#|Rf9q#H5q|}XY&p&EW1j__zRlzt zu47VJk?uZXebQqlKwLrTC){7|25a_XjMgMa+p^fR3{Nm6e#!ym%)qlp!X${}uCNYo zvZyo~*=91+1Jn;@YD7CM1}%wLR7_iaxb5~^36Uag7s=)F_xL%8$e=+(NJN32>2wS; zpN_yVE9)-%>{7U##6JEC#{}HF){2F@9~j08 zO@&Wn=_BRm>P z+Op`!oT5zMwUu(i&X54|NlJm?>W+w6Uw4bDx>Y?F<3$SaSzv;R<(WC7-mM}8P;aU3 zai6d)dyNlZ7iN|SnfP!yt+Zri_~ro9=y5YWhxry|&6q%1;*E#Eecb$VZm_ob!9 zXggQUtv$bVzU?v=Yti>7rzLu7$9<`=WG6NK#R>R!6=C=94lMOzt#Yw_H$X5#EEF^7 zhrf$K6M-BF)-^wQ4sHQid2^bPhd?8rr!+n841jfe9GDHmYa&;% zk+oZ~nQhYdil!E2a5;XPA?tzY9B#izb}3nz<=5%&VD^}yHI=IRAE_%$K<7kHC&2*- zE3yMTq#(Z06@%Z@t^Y}-bw{f{mnAh=d&Vc-p}Ip6LT_r0a?z{2Xls@JCNNa`OM z4JAMq4R@Db!Ox-w=V8z1miEOSN>Gt*+B+qjJJwP)H_iN8ogzX-GA!kcvmq14H?(6q z>#D)k9wm^TLHw?2W&>z9En=l(40{E!MC3Vlk1dJwWiEFb&^I_Jzof`rQ*r!CL&7D^ zH&DKF7@E?V#kcl666&n75*)HTMtX}|m1bjyORKmi@=)z`$ck?!FbuYbDg3n=d+_q8 z<6XI|OVv4HU2I5#+>)@GWPnL58Xq`Q#HH$|;uC=NqpZEl=AN&%qJoCo$Jzdfe^HJy zmLl(oD!`KvEO*OrQYS!1WSge+-Ri0?(9Um#q+w#$L}1$mdGcjKPl>H$>6zc6bm6TT z_D{Kg)J*`83e7jWMs9vS>Xb3d@Y_^hC=!v0_tJ6p*$LY#G(4fvG`ap3e$P8>>#Xck zwd7HmDX`7yB9_x|x$4&gLp6@1t>4aMAfzDt^9PK?qoq`Dkv-YNdEF9)?o|-ty#W1} z;67=&d&Xxzt!o0V6NzO;@$RMt0h^#-6GRUjeRuMtvIxXN9kN**!cUL-7YN6PP^>xY zHrLAx_jeY^{*yCnizcmKX4>{`D^#P9&?6(>^#vpYE%oD4OBwa!aHp3blpE?*l2&fn z-%5ZQv%-5O5Y4YQf8Ejl8q`Atf0YmTgJef-V;@Tq=@(osHCNA)yrJ2K{z6jfEhEo0 z23KC&{|E?pUO;y{dMEh#QJjq~#f_|xdFjA45*{Jvih@0P{u533x9?WW2YnE+j$uun zbQj-C*m}Tj_}!fH9toACJCt)I^ZdRt?Cv&U-FXyMz^k~~{*5mi%FhCuQLgRek;MVA z;wIonV?;s%;3__l>kXhq?H|t(Mo$99@WK@GndvtKPG7~G{zhCdRVk=L;G&&cU|Js2 zekYS9Z4ZC&8ve6|;dVtG>klfEynj|L|N4#W!9#r^`UMwJN%kmLA)I{$&-S^H-&E7A zswk3;iTV*p!x@s1Vdbj8Ga3SXGK~qd63Am_6^`T4TB1;TEq0fwLrWoa^cXewoApYo z{*>-Jz3_X{oPUQd2>b_hVf!!W!aeoq;TP_kh7c;p5jNeo%Z{G1U+e4uLEXRz=W4o3 zFR?kmrNo1gDVWKap;gFqnMy^6cc z_kyrYC6#HZ(xRe)w=mjG!lUyeIJm-V?50ISD zDUQ6#_n}&!CIgdn+)7B~0S9DiydtLnz%AFWJC>)o$783XiVou9QY>+w0<6vdd00hPT5%{Tsm$1t1ve^p5}p19Sb6&*5JnE2rIu zAg`5h@0IBC2dmZ?F|zYE%OyNC*m_g@4mK;>`rC}v2OIg!(ss*59h1r0vsQ_=#wN+6 zH|Y6F!Ib&g-)j&EE>C@sx|0>2My$P*mfN*JHe!2QecH+FucIyayaC+8EPhX*XK&$d z?`)VqCw^U$%00gfLONNRGEc(IT9Lhf=XJs6`E|A z`z-2hP8GL#3l*B2IQ$;Xz{H>63?M3$CapZ$-DH1580*NkgWwi~x%q&r*%^Qd7|L*7 zq3&^OU@`1RxeYvGtncoDjDcB$1^lm4T=g2pAQ{B~I6kh?a)}~7Au(|-$1gnR z003u`56W#5$C(6n)wCTpYI1)kuROfGmp1DE1)AKS975a9#W&P9dE1eqrG(Wu9XKkp zw*^S;|MtrMYe*#^0E3-XsnfBj9PXOjng~3=_`8e!IeEEvYcP8JyFdMrD-%If6+kwJ z@wc>5#URRsDfl0k|V)`)`O+dnH zv@CWM!QKQn63=qnURNNjeg(TWXe(F ziuq`%_Ki1Rg}>tJ~S(D~ znE=f0*s`)`JYy4S6@QBb=buk!Kxp;gbQJ&ViwC{UehgJDlD{wtTo8-`!ZsGb)fnam za@ix`Ch)A=*UXJ06GQqG=~U|Ueo6-16x`CR=Mx!Mda8WGn0_gVBsG-nse*y|WNl?t zxd1Wx>pR zJ6^PC$Tx<*4) z&KDkzmi{RqnXmafF#!xPI7(=GGT%pY{K-oo()^Q`fLMouYxm_d$8TN&ktT$f;Qilt z38UEmzv%!nl#hNB6OtcYQwAvXxXUU}p0Bl#ww=ObC1o9IEehpU_!@-_8!44XKcqSZ zEv8lNRwaDltuK|5XKmPq)(-TN@SW8?yxqo)QJuX_LcO$rRG6Dq%gL58zhE*K%+Av^ zf`j>7#2ZH32}n4wg46!Z0=Q(bV62Vm9OvBSbJN;?D%}$WpS(6l|Jd^Qj5GucZLm6Y z^$tN)M3^wk!TKFzf@(!H$Q?lI!_-u&5skY$64&_YD<%d;O31=Nkb14%rrG5mW_0T6 zNcqj>LMoM$SBu0H8II?j5YPcb{o&F+-$z$q9&ZT1^tWz0u2)z^kaPq%S6uQ^=uFUV z03cy}AR^Xa!<>$JrTIc_0ZJhRc7Upq%?s@7XTZcWt=@LMcK})7;gbXnh2k#C4?tKz zLBA;gMD{G2j9e=cG>ieM8HhQru9uy4?N2`*5=4)KG{`7lbo7n)`j4^)? zP*~9eBl@Hof^`6LKnIEH#bE<52TjX~lf7@c-f7smdNrx^N1o{ zwOlO+(W#3mMhhF6m4o8dO9R#3hz2AwXCRvjk08D;9c|s@qPFSY7mkohB&Q|}_jl(_@L$%R;NzUO4e#vH8Qc%{p45m|?{3R8d+DLjX?L!;Nv( zo`a~#Go;qQ^u07v$~S$o)sU#)N{n0jFi7h)9`e&tM9}n>cfd4RT_UBmLL26=Xbdam zNd5HnvYRDk2DvJ)l?Dw9Wyp=I{Jv)s8VBu6K@mNc`^L2tCMwzyuIIeP9UP3io_5kQ`FIW?4F1w-&DF%h8e~-SvxANztgrI%3HiCqr=D| zB>bHK*QAPZa{q4kR1Nf@@be-_N~K})p&0Uf)4MiV!E@pKe7T@vGlAzj9h^Bqsi1$ z5<;(%_(K$1F;x;BC<@Wxti?1ATD`-#qFhS}$d;~%77fFaY>DNw)C8=yLn_kOEB8Ah z_SJJs54Yz9&CO~IT@Q8;a#G-cJP);8UqH2WGt(5;r`*MTdO<>NRuq0nCk({&l7@CB@?`tY0ofrZ`r(QbwB8@4%|H$!y6{4p7|;1sh-|c!%PVdB5q9X5 z7OzJ)E`SS#ez;Udd7J!Es$TmG1w0O~1j-c_X*_AbA37o~eI&rkB9X8AxTo4%cLb!s zbGYB3ATGQT845r^e=Uf{exbP?RyL^f^n-PB^T6J4Ow%ex3#0-D4hw)r02ST-A2b5( zzi0$T((^pz|Dq8bQRf5Z3ch^9J1P6&Pw;=!%ETP?{Fl>&3-wcGz$xLn0%^(5-!uYj zodm6g~Io6Ie+7&klzJB(H3D#!v+%3nnP09n1qCGC`J3G>tc1tVy3gLNUNY)f-E zx)AymSCB7n<L^n55EN zE+)ibkyC*PyBRjvt5dxhWB}2O>#>=j8h#z!l<}V13-|}Vv>6W$xlwYU9H{{w*E2%{ z`?qNRwIn8==Xenvnh~nLWWym-N>Hf?UQU}JGA34=1bVpKRhPcfM^uD88wPq+>)&B0 z;zL}nIaw%UpG*rF&gkKgI83}G8-~&8`Q=6j@dgOl`0+(=5ZSxW1 zPjpoN7JBLWeJM(_cM8 z0tfyN-=_Ls>F|f&4p;};a^z!fAUP6GB8GX9#DOOdz~Yz@LZ6vO`!LT8Nk6h_P;hAz z2ESK^(NCz&xnMV4xuFU%TFb1J9i&Z^m?#w>m+NW7lyZY{+$4Son~^XYbm^I_~DF7n3&X z?)4)v9Xwl-b6{xG8FF38jhBilouX0>if)}w*!;;)fv?N^6Fc1wgNbC3>c0paxQPD; zIlvGg2lR0Nn;Z~gKyiiI7KISEQI4BKra<{?ar?@fG4KZ!I9fZ~eW;rAEx|lccr647 zNf*fi)PT1Js@Q8#u_5zLTQic|ac@{Njmzn{fg4lc$rlJxs02By9 z3{Xq_9{8IW(53WWhymDvr_s(bgV;;Ii22{B+WY( zE`jFl`s>4h%VWX?Zk~?d>_ad zsx#Q4CMntvzgB*(jXPecmxqP3U-{oFwZze~ba)&@OfV z1Mz>u;yyzzeO3qFqu<+EHmBl}Z|$X?*PY^}8WdqDR;l=<^mkJr;Mw}{%**O6~+4xNiAUuaX}Xi{xx;AH#v zf!azJKepjx3JBWY$h=af?T1@Rj>G_zP*z5E{>Up3T;#ZQ6-p$vyugT~(HSNPm3vR* zOzbg8;hj96&B_PHtb#pJeZG&sc*8yj7B`W_GwoNp+AH@-(U++K;6SY+(|+!eIhpf= z!YUY{MrOfw{>pcnLncwFdFxIbGwUO#9{w|UpBvq3@k{SP8+)#iYkS7jP!7e5rzL1& z0hxIDyNoI=dM^`ALUPD1zk8L;p7Ze8uA$kt6qknYXAWZSF^L}%PUjB($qF}^7=!|x>j;|0XU=M!RM%rbm9=SJe2Hl04`9l5UF*jV z>15B&H};h7LeguaDv2k=jjvv{Y&LwXH8TjTET>8+5jC1D{1oKt#4_5Z=l_M04A&;1wKA4 z&k3eJnLRCvL_i4H5p71`Mgr+oYklWqrs*7i_p0<#ji=C0ZKnkz7Sl=cDV_2;L;^nU z20+cQ+R>XABuDJNo9*wkfdQ_A%>Si%Kf%A{t}FS{^Wx3BujTJ@l_dL?TI<{$;Rw)E zBO1+B1yE;^^xuBN{KbNfi<;waHRYFy0q#D1Ef~8t8A;2F{mU#Xgs%A#Q~m6xS{!?m z5$$dj304fF9Pc-eqC3idIq=x2oX_|l0-2gZ$N>n~bU=w0l05qf{zEdIV55!pI8QzY zcihl6=LV)_w7b-Di>eL+4Pb{DW6Nr?aQ<&FQh6AY-iq&WVo_kw{8D$^X?T*WN4UzlU@!VG92?ABUg?OdwiyBv)A?C9_6Q&V@T96 z^#maU!JJRXvFR%sX8HJmP^He=dtpY_YiI#Mcoax`PY6bGu?OwmLr+@Ig6c_r-B$@% zKl@|IBFstVZhj*5uz)+oeD-6Ih}lP>xon`?om?6BU=P$#@Zpah!J^3kE$*zfY zH~0kk+AP3Mej*r5hbCQXO8(|SGDv_ldqeQLx?>FW8f_tzx6c=ZGa+F}^(Fjcu?5f? z<4y`naaed|9g%6{n|)-junN7S6=`t!3BMzhLCBLl&CCa8kn)?q-L|m6KuH+t=pmKM zi;frfy1{($)(THB^R@_+5;igYss0A;QZH{i_Llq_l~RRFyrY?iCK6Mqgi9jMLK^w{ z6W6LE0u~u3It_fbHLJa8`e|r&8t2)%IHFIu5a-9q%IJB$n}f!m-&4dGo_ELXQ^8&$ zXcoESO(!M(^0=MpI~PCQV?^|6(`{k@aQ6vi*6Xsy`z5^h!~NyH$;%cTDbsI$utM@7 z#5lZ$(e-ATf$j>vZ^`mCi^@|cb3fWID0Rh65AP8layP`SrcWwIn#VBQGnHjNNH0-tdMnPPcDmm5dry=Y?at(&&pU~$hve2XwpD86tXS+q| za9V8!v{08aG=Bt%4S`{w6}c_8wvS2N7x){%H| zizoT;3H1t~HhX%&QhJVRTBVQ)qwsQy4Io*POZ~|K*Tsm{(^A_&6T6MiGrt~C;x<{W)wCqI@gyT2-4385kC2n4_ek=%468?m zOq){b)LN=Y;D5m4i&vvubw0c|SHNBBcXo4ov`J4{A4z1YZ8caxV|^vAf0>7^Jjx%f zh3o|SD7Q3j!bBXw-0ts(_|8c)&UV>|V!Tl~en_Y#-e*72%^WuW{?a*d$Y!DE*ikyN z#;R$>O`_~j32TU%FY_+B32N}GbgfLwS^e@*qUur*$|%DRiY|_u1^uOsKpac!0FD#< zB@ZXO0Fzu#(qb(I&smD9Cp2Y-Mb{JX;_UYdRnJj}zNZMFS9t>6y#jd42KD#)<=csH zuit-O^Q#hG9nK=SeQ3VfACD~v6Sl{!fT!2)WUIH^eUQLC_iX#*v_pja1M4HFu|~kj zS2VDHkhM<0)hj)wdKvDQ1HupQ?qT|UP`*IL5wX6PD&RT?wO2jX(G><)dyDZfCfB?L z%VeM!!zMJm@SG`=7nvI9gcIsf(ygyOa^RH+LJ6vf_XJ3JiaQ~cnP1cemVVASukjQ8 z3U)+;t1<^4YkYGPASrA)d@Sj7UkJf5+S2*zF2w5uoA5$VWs7ia_OKDO$VZNvam2m6 zx?p@In0cSVY7zuwqT+oc*6{R#tQo7Ni?%-d;C)U-X2tM&L8%93 zJJ&GA--|~J?R!g5gf9QJEnt^ldnA32HX-e(vx!YO_-fyY=d&RX5Vg|Qkh5{%d_7yn zzqPQNSI29KTnihs;_-b9Q}b&X5}S9LJf7dl#U6T+Z%!B6@Z=KBh!ZDoeo)mbqB9y3 zR=63HbC(8^Vy*gflmPbqi_)>hUyF7*&fDx_$AO>ybd_J*a~TkW7c9JM$SDt*QIQbW zd=k1h$X#O9zwN$CF%fY8>OO>XobF0Q?!<@aOjwjw%@e_@NF&l-J^O_7x&h1h8^}Xx zck`Fi3uogud+4Jcqn@LNzhsJpNzx@n#>i3l!0aJA9YRTGz#^tKzVx!J)>|(A@=k5+ zU6Fp5dEjaKZ3*2kBTAQ@;%jKed;dXZEBpHS5uL_r!Y+3SR|mI+F@tvGT4cW->NpVa z1Rp^Yc1EwqU;HB_Q-K=w#C;CI?Rx#5QNSvvC<+6Qf7Gi%ok$@5lM4EO|FiKidVn+=s|Sv}jaH zkDM~{Tw&M?juijnlm7RY`0H;(&{}wX)T0s%3DgnhJ9ZT$v~oNxD=1`!YPN+720BZj zpR-i?bo%0NPK89HsG)6%zovnKyTpk8AziP6HN?G)kIPmD{3l^Z#({GUhu6i1eCS7u zy-{yCPRwzFC7i7(`dPe0y_S*rb55hUE!XsyQvQEz*$Oakp@0wGw2%Rm^i%(%n-)&x zCDlOG;zX#d`p1MuDExRYz)3&yl?|?_MximBNZ={EgT0z3mt@f=S}V;HY*#=!=BQ4J zcP@J1#-(o4>z+e^Ddr=*!h$dG_E$ljW@vw)nn0nvY>b>KDdKvuxpE=#c%gS&y9I;1 zrw)J+FbK{=((xosY^2a&z+A@2=-bwSG?c>$csT4Lm>c(}OO$9AQv)tI&1YqF0Qx31 z4Na_?iFb9b$Y2B^-(tD1VrGL*^}C#h%PMbn^Kq9JR!SH!30*uqLvZz7mP*3hW%hMu zC>N!UnyQR!>BoPB)W7v5d+9qoECWyo!%lN^`odT5-UYvk?$%z-scp->VEa7&K9*Lh zWslDe<@2PWUp|Gip#R7q&U}#J^40ST-8xYXc5mr30&kHZzB`1Ns|{vs!`Z zi`F8kb?&A_XtPycd84J-HV}110b3=HhN4od-eH8rzzV16m2sCUjkVDo7`B@MaBkLy zsTM)Ut*Je@(89;U!eJ588u3(t1sz3<4T-es0{6p5dcm{ik5~Z7u-l6Z7DkQ+voUL} zMxi}vR4e)e*Ug1kOX4S_!h_n>Tx&S+7W?NZPX5EQNrlY2_6x2k*oNhls`h(~g_q`O zs#(>hf=ez#1PlNQK=ATbixy%{rBkbZU)lBK24v7@&%ABsyN!RUz3U?~T<=Bd#fw6G z1X6Eh&E_iUO-)TF+g#8F@ z{w<*YTqK#s%^uu$8+VugR0S_%zwvKQ?at zpoFvA4s;~9#jTKVlVX&V8i^hErN0rg>qiqIg$*n-oZ9*@ycJDus&!1WpnKC{vEWok zKDR?GyT8ildMiyH-?bQ1b#`DEQ-#cblU*(d+@)jAJ%Ej`@+*Eko^-oMz~)y_Ld?@i zG`CN!PES3 z=uy6;w)9?0O3oW+M$lz)RD`@ZUDtDh>6}*l9yp1l5lhFmHAqOl$#;1@>Qi_r#>Q6Rt8^7GBq`%N_E{O~N%solc2xE%(Ty_-yj9 z2!IuBT}y=ph3Rq;j`jC5i4d7!#mu_{gi_acr7jEKg#2G3vGjBjouUUc9CDt10_n{8 zuegJ^$Bv)C%3bv^M|^987gc|Ms{ek}?@8e|wzggg#Fv+sizxSe+_IU&3bS4He(p#a zK1kr4U}OA$D0|DWD%);tTco?YVbYz_jZV6h?(UNA?wpi#hl0{2-6<_8-6h@OcX6-h ze%G_sb8qkVe!tw%9~1Dp&U1`$jAK9a{045LDhd$Z)4zT|8%-3bS4zstX1kkF%it(W zT~VA;^$NIb#)sYwRsGZ`fBo}b;^3!%q}#UA<)n%Xdac#n?Ix|?BIWRgB{RV+i=JsS zQ|qD`LfcyhqT$^pam7W1N__o?LTyCpOupJ6`bwRZVb1J%v*Ts&d zhDiR$>YLCJxP76rwxbnXgk4@7Q&l?;OqR}9Hy?GR@$gAT-36vtL)-Sj{-W=;J|C~w zq0DoAsRplVTZ$gt-6$ryyOT4g4eA#FrN(w5o>3)@mk>?Q4*FzRDX9mYF`eYWQ+4EE zHIL(pQ(VrwrPW}doz4;WrP5t|a8Ty&xYyqBfxW`-aIWh-5jyJjyzUXu_MJI&|H{sy zIJA_>gpW?niVC!1{?8Y=&Vbi_hSkB*?-Zbm4Kl4-UDE32GM^4XdW!Brivovb48XD0>Fq%G`@VIzM|kUckn*2okR6PldJL-QXJ0*Dv1o&u@{4U@JHpP;3)vi z&N!gOrJ?7VQs=NLGF7CI$(xShBiQrB z!#M7eZ4lV9y;7hF4YjIO8S7(ueEHVNppRcAC7Rals{0&|7v_7i_HKsDAS)c-;v1~OZ z;h%ekzC)knM>$9iiW{eZN}HbC|FL>LvI9WRRHRGEdo$D(0=P)?jk8o1sHEG?x_xES zfxX-)D&=Z~BYt+ZgQ7pLQ+j8YRaG-(#3#K_V8}s8=g6sYvaT4!A5>Vi7&zgJB5|j_ zHgXS8@SiqW6b`E5pItC4J|Q4v+Bxo9jgF!|1be79954yZ#oizIUZtU;)`o6hAvK{?{Z~LGT=$m z0S!V%GPBy|-56Zna8F0i(=K>a4ii`DB@tlZt>hWlc$!Q$ zXtdV@)zK`ydmSPX6r6u~>nMe2?X9eh!Rogif!@}z$KV3MBu0R_gLOn3)3+w7FBTB; zS`>DImV)zoks6i=EG#T-EC{2pf&Cn-Fe`zA zu`)r|xQ-w$pS?(A&T)53BMx?gHtvTq9~|&wZlbC4sU%Ci2X4~i{J~Tc>Z};~nkcC> zH7tdX2N4bAOJE;vmW*!*Fcb;7jRc>EAgYH1w(GcS3CUZ$Qw4X#iAq@D+!^aEfFjdL zDV;C$Szp*EksnPXR__$CxVwg@&urkzMgMI?y%MJS!77bDX9JH7d)3HQfSYHp^Ad7U zsYq)mQ9zaEcY`e`_92w@{)P2uN9bPx72^h27+NnA2n zMg5Q!FxR>(H|7?)>Se*+|GB}RG6;>+N%)^F%+eKa?`NiRG5+uNb6!CyAf zd4%5_g>NB;7Igw@+rHpPNbeAU!(w|0R~uSw;y~!4-?n5DR%}Ap0AQHZmz)dr$TzWA z`5Ks4Z9w+g4uY#QDK!KcMW%mKN%$(6vhG&wCjdH`9(!?)gzXjFC|7jfTcKMtv1gH< z)z#`0?4Q~Qj@%W~1ksIC4zY+lp6LSn#yx9u071o)HlxAQ=!eJ|kd`tsoWk^@Ih)HO zPHP|%DY&WuSL9-pYey)7z2MY{%{+1!sJY^A{yGeMee8J(|KU=82Vmu==p`SxK%zr< z?fy&ESYf-X07mB#NOIoM%UTifzA=8pl;G@-i>*4!N1|>7oV{NH8A<9KrVwrPxuc#EMY1=!^Mk6AY*Pwkv*WQZ{9dX*eNn-HFBAGRmZfu(-G;hR?ecS`>El<>E!XOn-V~ zx81d<{9zH_q8+1Rpo|AJt*y=G;|Y9@=~}y76QB|#1woQnS(z!%KeYnZELwX8twCkd zP_}^>f4>Ts*R=`|dKHy9Vb706_OcFKF1@D|Jj-lcMe6MVM=7R_j-NXY|M zJa$R z_h-`kZtXY1N~yvr{>nGr7p-o9@v!ss7u0zv84{{Zz=Y;iH5!JHTx!Z*QI5wY?9V0A z>+F<34ChnE3^?=(?6o0h{ITe?gb`~fwvsyvA-B{j*y!H-RE}ewlR+BN{a>^E6y&iXWncpR`77`9F%VZcU#NjI=Vl5 z@2jC#V@-wxtlt*699D#C*ML+cDeUK_kFj|@lrFC32%HdD5Epod{TnkPvuL#q?qpIV znSwf4OtT=!elRjB+h~)O9NFzRXZXFK#a1hj26bL;@Fe@fEeghN^Up#z^>ghHs&9!E z*K9dlUwoIv((9)|S#Xn6g>N?jy!=f!?+2Sd{L}yZ<0uA2b+@}mm~G>VV9b}Up$42d zVN+8>`6-&Ly^%pClw(DSuoV%I*#>5NZtZiPYk-V=;#&ShVYmq+%FS$nG;+GAzhC+R zSOj*zb{~{v>-`n-^3LbkU8;P`o60Cb=#3Dw6rByG*QMS6O!t;W4JI+%tNkdx=n@bIQm%MI-qHNn{nx#;aPUPg7AXL2a1nD3x;Jv|S_O zvFO&9&Bn_9MgGbmZ+1|W1oWOUjx9_Bx%qo2MZkU6+Kyp}b;08Vq=s4Y>uMJ1-!2LAfK-B~GY_}n%F&v%_qk4JTzkVVTH(qXTG*#ud};g}@;Q=SqX2$a0)5cqua+tv_trj~NE_&4fpx|N0wPC_v?zVe z{qbqpJ^>UQu2V8}={){mqUub zf;z+M9#T|WJ1#>Lf*l=VRpTT?!tuj3VrrBFZHJ!2feFrA&J5H6 z+vDXLo0DnPI)~6ksFXEJBq0<6-l)+_K={YA#f1M%2E%JoBSq$b?2yf#Ny^~DZ@!uP z+pu2AI-JL)f%^TteF7`NV;g=E0wU5mr|!FOeM$9_u8|UaT@6|ZHlt_wgY-D0Cztj( zWvAq33MR{eLc39oX1}k4ji;LV&!T4C>hs*lMPq0)Jg?|(ac9&5khFt-c_6tahGayw z8i=89KgUuv8amzvs;{4n;0f#s#V_FmWQpo2ozj^_=H zxLx)=A*r6lN)h)Kl2v~1;NWBZ=q>)5ltmVL=7jj__L00m=un$-A;3&~4rCyX0P`2i z2z0cK3(BC7@f>0Am_~GRl7d9Wie5lt{Xp67XqbERiV4`Zhxk7o8k_^rofO8NQ24uQ z;InKH&ej8r_QCfPykTBCHKvga2X#GPf#|H0nSPZ&%?AY;qI7*}cG&h({N^%&F-<^?Yd;yF3B0@w#dOZhbn+zcNQ?K}3DAZmj zv^+O;90DEWi2>{{?+p<)ZL$BHH9|?}p}Ypkf<1ts(a|C&VSNar?ot{IO8iIN;2iE$ zTdg|FL?96yfW{Ke<-tz?MOzG+qMIbmoj1wS-AomiVJk{hKpfz8)tNw zD3&WbYIVa#X#Fa6oMT*Du4W~*?Q*|s#_gZY478#u!|6lJhEqz9^&46jc|#oC)`4fK zYaDNhmTlD1o4PJ=cYddKi9!hb7+ZWh=%AL@^C~URd2%cmf3|_uzU|;+iQu*PEJs*!yoz;k+}DxnZ*%cyfdXA0K`MO+z49y26)x$2Bs~*wy;f+u zk^1w)=}#E{+4R%Ylgyd2(0}MyFMq)&jna3Nady@yY!5+Gd)590B0-9_cHGz4f6TR!vV^1`g||D4YwuAmj9uU_L-_PYYJLC@}=Ax{SeCXeNVaJO1j@hM_|o18YE}=<=^Ygzi*CCO0iCd z(M|^zBlV#(WVRC$i!wj2wWc;l&Dlzku+AH7!0(#A(qN~9-CzgsV2fJ=vOEDOZtIYP zJjEopMcd>xrs&WpI6hTr8uu^#S#IlEcN{a(pUji5LAL{{bqBjEm<`Sg^Nbsd;paU7CP;%1`157!}Zwttxo z4G#3!Nj4u$dn+}sO?UtOt^beu`2#dmn3KsoA92I76;C8NmIBI+dI}7gC_+h5uhItr z3+oqw2T-9pjU@8C>P?3R%V>@>c~bqBGt--Drgtinacy{eB=_6hjm^ZmZA@ePY>tf_Kbcl*{1>f`$N2Q%~-FP`!rHSmL zM}ENoP9Pt~nu5{RV4}GZE1pSNBj6My%v*-W}(?0BDb z8o$-qLKvX$bLf5Jj;VgAUC_Aq+(0_$-Z>*DZPk*y4dO@74P~$h7Gu`)^j++Fnb+(6{oVeL-%(VjQuwP#@_zL1o26XhHtH}16v`~nB32KDruX|0{-#I3 zkeC5wj>c)?8bO1xhbV+rh?`t@{R0( z*U(g{uZh*q*2pbf^|WmM=A1KAXr}U@NR<4WVWvv3ib`UyKnu;JxxV*tJ#E|x&_Q8; zz|?4-EPRK&i9}a(t#YNkhtI&Es;i8lk&PQvI(~(bW+YlV`e85U>SK`JQo(;Jj$-EK zB>)Gz&tr1Jq~GDkFUioE^>1wIE#)7|Y4`W{&1M9a*nm)8Qg7jh|}D|B|y{9V{d9@ha4nV^>yl z(^8mhgf_Y$s@o^db@Q~Me~}Zj27(rUkCr|}QY$C!vZ<8&e5-yjR{_!A6u_jp4TXe{ zo%Z-$qYdq!H!wF@-LDs*u?(MXqD`0Xc_0f7N!+%}T14(b>Ib}bO#Fu$;8+$L5Vf!l zQR$l$ck7=C3#-i6Url~BIZc&ouqCwvGxIyZ@VX5Q(Rt2He33tNhde&|+yRF6Eyn~7 z$uJa5)-15nSC7zLL0GBLI;&}8RskUBO{>GNjamQ5pX@2wwRK0^3bw%dr|`{S)EWAn z8PmsBiPOtwvf=lBMI>bsHx<;|$(DkDolyTih+Z2M3pi;NbUVU$D`yS(F+(f;y_*iB zx0$c^CMBERDmy#Z5X#-1kFfplfQ`q-e*2!G6EyXbSl%D;qTao@UD=5goSdo&@z;s^ zBO|axBlAkkWGSmWF9gd^4BrKi-qWK^wFL(puy-e6E-27!z6 z98+u;g`yTnAfVVoJ(>sPjx?4f`_wHW(ju#;2O2j2$dGdl!SfF}daUPH030`&$5Lr1 zXv0%vMZ{0T5N$Y?Mqi}cc?~iQ_V)mjifOTJ49M*+Zm=&Tp+4-TZ_!=Ko^QGzF@%2M z+-TsMy&tA`CUHEhllfZ)^K8(+l`0=+Tb9GOgU*Fl?K^22=V>tRLt1|!fpcJsdfve{o#4o5TYsOCE2s4S_;J*tXXCEM;?Qfz zdg+7p8!H;=uT^{G5i_vBT;1(j@pGa!6M|kX??kKT#TEcN=c(0u8%sp9+~N)(U$)uI zhMnHRldET~&+s9Kh+gY=D;t6MJ*bT{ZO|5Y4T;-ZVZ>l$wjimWB&1etn{pvG6w72|feT1N;LF8@P!Ga?0)g!d+i z?F1O(;lh7b7|wtHuDRdjhgdEJ+j$ZD8F1u z{0#c|eDakR@vXAOE;~bhQQgO}2i8j_q|uAketBab|LXuLK4py8Lp4gO5H(P(EB@!k zH#%k*b}1(vf5gA=*fM-h|7vzK_O+&R?G?DdbjaQN-t&En_l^CgfB5C{c{BXWk7@xsu(jUWd{7tL2Uh z9B3Fw^@iQ#aT#H0B&Zh$X5lZ2UV&*k>ie6htF*i5IAMzbm zGCdf#b!ifUe9i6vBmuLMl&mzi9CN4_Vt~S?w5}P!GIByf4RmwGW+SPb7Goqy@tfFL zt;5HFDr%ehsWB`5^IsacH`-{jz5S4AF&tFVNS_NI&{Sxrx5+HrL4qo0z)|@=!z*ZOs2@nkSdau_nkH z;A^w(v|Q!e(}u6H;JA!^w}_K~r5@z6D5Sp>N|EzBe1&*6YDP>?!#ri|C+~+XnHkBc zO)3nWr|zSA-B2IlDp5>Or+kqfYRwm@^e|`ei_RUoC1;R;W!r;q$<%XxB@=DGRZC%` zI+3Fz{>tnYq&1BfX{#h`W6m2j?X2iJGyhHRkj%bC<_sKCt9jOeHXyW$PSP|o) zuDtv14B(-_vU}o*$Q?iJ;etBOpVPR4d~~6{w;;~Z$?7Y2M+!ZnUqnQ zZ^$+2#jSv>wPfHJmIoX1*hM`gNZw`_;YQ-kdhVoK$N~kad<*#1WbO7&rbDWd=+pb_ zqlK5y2I=<_p$B(slB(fS*pgOLN#WgBp#&l)K-0yeF9k}a_YngnzSVFRDN026=!FMs z)PD||2bl532)to_Cd36Y(Ua@n8(XiV>RNq@NL*KaAxQ2=mJo!TNtd@%ZQtPAETq>M z>ea?MWvBmU0rZ_{6)zST{L{4~fsk;y&IdPur2bXgjJ6x%lJ96tHt-068@R zb0f**{Q>ggXwR|QEWj8k8erCX>z~6h`HtPJfKmz4i({Lhg{a{`*g@fZV@9DW3gC0c z$9{$wpKio561H^@IAEqN(H-F>WBLR@=V>Dc2tq?lF$bjh!nzx#+oeDaiMLYRHiXi7 zFoBP)ee-gu$=U;kI-Gp^kW|R8x7ytP)aNp<_mi~0V#2A4IC)52b4`<^XSpQOu zERz3Tv!SHvs3`|cm{;US@Ea6_SEUtQ6wGtbi?AC5EZ!X5y5!LNsO-w^I636#0VswD zZ_tF;x=|gWMOu`f1(OZd>B)r9 z+Dplc!sUHpcG5IJ&VmNJ@ox^yf#U!!BQKU34YI$3I7R+E{#tR4?l zTZlcc+MJTsDPUkAjHREE8u9~UuZ1Tn$Xd>iDj2w#hp)Or^t; z%iyl*9CpnYs^FJqyi}`1$#dweR6n&yHay*aJz5HV4thvOlvJ0nD4+x5@Df(SixmNw`;*~g)LH*%c5(QJhJvdc1_P{ zJ)bA1!;QxFcprhIlF;3-r61$)i&|-%qmlHig2N}h7Du)qnBqim{tv%eOR(W2)9wog z;yXZevqhj1sbWW=9T@|T`WXeK63vhhyd`$OM~_V;-x-y6>BcjXN;DAVESl+j$#C$I z&bQn;=WSUNI*UOJY$m@?3oc$9lYX-@M#MoyP`@ed)R$#B>;iv4n5UIMqIRFZafE;_ z^X>rj%#R1*Hs6W?lpzAaxUL$v0vpfF7ok-0(sALH;-DEf?l3UzqE2$OA|J{H7F(ph zEY+Ar6B-zKGLaxtewIEDvs=sPz9gYaHx2#%g16yP?B-QD3ei`b*yh6;K(j=?qM>4S z50ZLsNhnQ$^g7>o=!d_-!K{#;gv@&Sc7v4oq`*S!6YT?T; zG+LyAN_WWI5T8#O(fHjCHE2KC)4_i1ta z@D-J!FIVv&7JFUIf72L?JL?(M%U$)7<;`8@4IM~Hskj&)cnqszIsWtPp8h9ub-pO1 zq2X9j2wQcFwdjWV4b^0a#)h}L%O+3wKMc%L(4iE;+Yds*+7*t@bOVaK_P;D?%cm~- z<@P9h5c5D%-~`8*9c4L1MMYBuvc(?9r_0CnPsouJyG>B5OW8%}cGZV0A0-%tlj%_c z$FCO<>tk-a(U-cfKCc2b*Pwt1sFshlv$f*QWHl6xAIjx@X#wWo!b4=;Xcqk)>_<5b z+-?dRaVv?^(@t}l&&#hdldXd%)@IE3c>hDTb;5HKx+!(NWQV#_Lk|rzQoo}y%oT;d ztE!Nq!;E}qfDyE8L@t>a8>}(O{VPH#0Dn-Ak4;~KwH!7ge|B=ip@;o(9Yd(-y+%$VXnhTR_KaR=41)IueCu z9*wWX40~{aYAvUaI}6&mN>?!I&II!z*t_iBhQ!u!4^cU;OD9dRcJB09c;u(1BizS^p-+fLMvPI7-Q7%i$& z*6#30xo~upQ8|geX8Sl63v!AQ#cXS#z#}I)(1dTsig=cpghbdDhRILoYkB32pV;H6 z=tjCp5g~J)yV({8c{lMFPz-p!DDU2p?2KjGNX(;>@F|mdZebQ?q8sQpK`5MMG0ew& z&W=jCh4|D;rDdAf{VW^{FLr?U_N|>Ui zMx>vhiV|Z+2)Q;{j_NjpIYnMH|r5BRDCEMA|uS^oLc!*3mV zJ<+pEoYhfVtvEEv2SjH}152-uW8^ZmIkze%xleW_KeBImQ$=!{t~qItk)q8GbDj|7 z3DIu)BcJM63%;x#t4a`?vmUHMW=L8qm_AC z2fYw8YBRe%#Z2zMC&-DF!Y2Q+C*0%fwI+rAorUX~gWbIj?{mc9~ z47umK6C6e;x{}>eL(0Xz6*Q%v`X1VtGfMPai~C8$QX{!}gKjIpJ6)x#jTasB!Ru6K zTFr6)wtTfr8&z7_ZHeeL&iFw5ZuHBoFw>&doXbC08T_mP@2ccDuAod32oVweTX_$= zIg3sMccO&tAQyy%gsplL?U#iJgo@cl7}%L#rn!x`iESCwIv z!SKZC2tKW7NO1n^1;`V+m9Be?DK~Lk*U!K8W$B{#W=reRe88?ykGyxN0izk)z&QE; zke7!(^24bq4H&kjBrO`DW*<}K8g|yA!R?*EGDrjkO*oN~dZB8i!?9)R6L{nzw1wP} zmstofUQXW$Nc5@f80Y1>2`%>fq#Vu}KjC|LEC0!54-kdp{jnI0>G1CYGr@ld%oq>< zEiiu;`j_r0r*Dh_TNhyVI4yJm2_w#+DhfU2BUpUj^w9gsk%+<4TAq zHSazm9ryQCZCE$Yr^hPu?<@nZ$-l7P=L%L{Jb*kFo!ZLEo$Qmu={d8OBrb8i=7r+x zmYkmZ>-+CIZ|Elf;88C}t=@T)J{fbnWJiezqQhiN|H|W?=RU9?jFw~GU0&Q;Jb#x0 z{pWYzmF|M9?{*Aix0LPGokz^2y_V9H>X$AO0vw4yn`CbLXKEeht}>tXnLXhlriafs z3yHyw(NNvtXrj#k%kziiD?}6#sh7o^2vc96?b>(95!NIpfsW5?>LnGi`M6;zL`S5~kwa=MYaIns1Y;SXESmEoA_99To7&<&%4c$g zQ$Rw;uV0(;u(w>mK;#Yx?xcBf#xg43oVc7f??anq<7&*fN8V`w8c2o)+FzOU4_`foB$3Xk0UvI_c# zlHvma6c!E0g|9lL+tvGyZ2Zaw+ZGLck=qHF#EC{b)UYP$BQpY6Oz~7VJ zr<$-nus2kTT{sa}eXA{s9Lv>Tg_z8%v!Gu-i?a2_w^VhiF&+cWF?ZJJPg<`R_Tv} z#nht$sqFD!N6!Q=opkDxnrWV>Ls9~@$UNaq#|q3;U(Kx~Zyi8tMM0U@cq8#k=VsgA ze<1#OHDHV!NerRM+BdSO;~SZL2t!95gU8CWZ?icHP7=;ZO*I#CD4Tvd#Yi?E8Tejo z!`U-5IWjmaSrBnOA<2MeL=0A*L|p>Hxk@IWBY2a1c6)Nv_Kthu>2J;`Mzgl#o1Ub8 zQ+kb`|E5L!RojZYukuf)G8sa`5yiUk0K9GaZQNMo7m(R635n2?N048ZgaaXxs}mNJ zs6FREr3kQ-jFPDacs#9X`=;c=H^}_+S$EbmM)Wt?^aZ~9l{iUM(YPHsbu=toQs~;| z1n_7Jh+U5+QTSxFs}>l_joYw=i9gkfcB?A@!G zm#TDlPv%+RuhaaLBi`L4CNu)Ga*6I2VcC2KRoDZFoTb91Q%nXTGta;QVoL<*uipWe z6j>SfKbl!O^Cdg6L;=B=p)fgr{>2z66bbP7G-%su$dorqYjOzdtu%l;uL`ZtZ8I@0Qak17no0dw*ZZ_`n?THD_ZHDtG#{f>&QakW4b z@_&?UOi}R)A>aGeT&qz=9|MIgv{J1O4c6G4sm;iU)W+wdRC zU%~&h^zzpxOrUI3r^-90wwvJY2xfIgHEm$NS!^|3GRs~2HvXxb$DN?BH>wu(`qNwk z)#C1l_&E+5zgFc*lPEC~gwqms=PfX{9Hfun;27wd<0XU5KfvGwMv3JCJ7=68r)v=h z%^!C&U}31?U%L?jYUZDe+aeNQdGv*PlT4rc{n8iA>qrzXQ^=u5^JYI>*%U~6%_9EQ zT)ry$x7;VISvbc3W%c|^ihfTC1Ab&jn;`u#XwGv5mdw+JQ_ug>YLvp)G+NKd*Ex!g z&qWSM&Rw;=p}*g8OA%ve+W=DxmT&Le^il`=eG-o#G>w0Y&fEV(bng6j(HSQu;GuD3 zWaoTlw^%8jR3C@qfTU$qoz@p!f3|+P* z-kIRj<#e%9u81-I&dAUZLoHvf0W1lL10XKp09egNEW4f;awb@#W!}8N%b4Yj*QI4`z+Qvhk}(U` zkOpaVlDd*i;zm%=Ajh)Ge_CYLz!DT(T+dJZSSaPk;)wS&d4U%kNr0vEGT5%pVCMOT7MT>XZ&!c5_qU*4i{? z(KvgBJ9a!u$d4aWn+%kyIrjxWab~V9&3J@)VHnRJ`^?1`bb)06fZ|~OO~&h>mkF5g zW|_`>riKlD!0d5N0-QgYprAR|EZVI1k0XgTqr((5 znal*|L{$}2xzJAWvK0P@kbxWjY84-#Xd5wZc*shR0DA(d*O!2fJSKdjyWTVb3`BIl zL>OoT!bT`|{J6-2?Mpda1e}1y!+jTh98)Ap96!ntNTdueb~twAqRlIZXR{u*#D2Fr zQbmx<{+o#sj;hz^6BU7`9!(~1Jd%IpqXMM=>Ko)Kbaf=v?v0#tEZS7v^AjYkLrUVV zoVuBD8V&z$Bfy?;0Xm*kmTVB0?X2bXM`e5FvvbVU;93R3eRTBC(DZ`Tjq(tWeO(=9 zPBVO=SVMB*q{k3_8F(M8+5x!sOyoMQN@DTxT+U%+CiyLy-3@T3%QD z`t48Gf_DcaY(B#Ne4AUql0X`uzK;O5JM#6I=<;vhWj%a=_o2T+JQxmf4kH6dIw?(O zC`%%f(_gRKlaV@;y?EarEb=L3=;SB<(^xReQQGg~XtRPVL{Z;{_PnnK3yi-igEMTV zRn^pfbSGW6d<0B=pT;YhV6_PxngII=zbdU|I9I{# z8B+5gp-QXbGS)HCrmK|gt)-UM!Uv z8%y&!U=WT_VQI@QD!zQR|B>x27jiMXL;6wqRp?B)YuM3n?p|Rbgv@*zIN%i?>m-x= zCN6-!t}%d_8H>yA2bcrlK)_sk-a61BA?yu!M0()m&6-8Cd<0`fZ*#P z7B;AQa~3aRQfExE6{x1DATy(|6K(BGFOMDKu^~3hKY1BMcc!m=6C$4pbs< zGNER-4rFTVme4$2{Y)+@vaUH4OSCQYcCJsT3urkHgnoFf=a9(_6_)_mK*4ydx|Y|? z<`|JgUQfRO8nXFBW^glG>uFGhew(&^^EO^mrQg$ix1$b5%v3I)o~s6E$Z^{n_df&B+2!`&ymYCty=S;-bn0NLEpOeOCU^O z`%0TSMoP8GzzxP4)>e6Z^h6Y7__;!Ik`Bg%7~VZF;^nWKImcB=tS6 z_nN7uz-=yP3b<4I-H6@**+tEOr}T#$l9T6JAbi2%{ACOmP0F0FQKIdqbBUr@aly$W zQ?T#~^6i8Y3h@LO4Hk;-wd1&)$h@~umK1P1 zqDQ%y`=%$T)#_2*EV}A-__YRDHhO8UC+}M|mI_mlYt;U8vx7ne1{`wwlyZ{qGrs;Q z@}ey%3FeMW89BcW*xQJTY+TKVVD=H+Zec7`J=KGpIBf$M=7Z0rM5Gn*CXzn6#rofl z{Wc=$GF)?yK|MeLJsShPz(KY++2-vo6|+F0(?F8dt3<3}5;FiX0K8o&jZ#5V1pCHg zS^PW8Ng;%E%T6OarJE_YB~<<8dKXNu#SF&-(^cu7@aLnywiFlfCIuB-5lje!aklW8 z(~=J;c)tMA#j2)gaNS=GANB36;-I|OoW|cK7E`>=I{%ktHIyngeSvZ=fY}(_l<8D0 z8X@$PT`bUng%=G-Q_$b1RLyBn8lWF3!633@TWJJWSO=VMCrT}mnIaFRwNuc{hp|nlhixAw z;Vlnah|d<~2{#748ZJYtfob|EOe=nOj@6inq<+r8`hiq?jF#5Z-^2+(mAq37t;gdaTj&)kAd6fzh22Dfxk63er1m{ z>5gP;X;=u%R5Ko2;^E9U8bLSn5unaD-7NW9V}4A;@M>YYNnBW~%__X^*ozr1hWLIK z;HS$j)Z3)#0<`e+aM4F`z6dM$#yhc|DhC(e9Z6XDtZaRB=ALkh|I(WYZ|iGK38>S6 zHT*UGD#Jismr3a`igTXc?6uJJYnwN+>n9(I3_d=Qim?9y$|^43NFd}Hp5!ZAw%ejr zn1`u>AmAHRh~V;Se#wT|5r2t7=Y~F33;+`(*!}{%w^gD7p{s2!Q?wA-OPud_JDH)N zJz&VV6~*)|LKk0A__yy^lyNTIq?AM$83^YaTQ5co2xd=Os!~9<&L##l^_NKJoucy*6JAW_ARt&?|k>Kp)R?C;3Ly zvW6`iYGxBP@{I=W=YI8>6As(?^e2rmrNM zoZ$XYd2HBrTHp`+ho3W%YzMo1KXmlVg~hUh5~)ao5z|14;P=GzqW%E`ycE5NlB2Ez zu@U>FhH}mu)IMP63Bi4W?*Yk@f60FGtX9xNqA#ak9wZAfL<9oS!jOEeXG>y*KvoVtf)R%kqD|Ge32H zygl223ep!b@PDdLAlW#=8QWX)V!%PupPmk1UtRHguv3dOD!Vl_-A=d2;jjY5&}Us? z)p{6}JPDZJQV}w#bd+dll2$16Ifh<(S+Y8_*&>oT_Ev zWQw4CzQZrjKxa}7qi4eWPKkVMgQ{9uHFPVz4K7`gjn=qZfMM(^b`)wZ7tV30a@9Y8 z|NHaj(&FV~|0MM}JvJVU4GwV75olqgJ`9XB{dQ4yow=4mrcO73rJ>#9M7SCViq6&n zm~h(>cBPHf!t=(7n)j_~IZ`74lGo%vmbcg91G0VkF`ly4q-#P?P_MJ2PoVy#dJEG1 zOZ9F)RMnhSOt`9KX-F=XOCu@>|H~nar_z3xs)v$nsUwxt!} z4G}j4V(RdDrkScLXJkhV%4C7+ZdVj^G-)vrr!6CsXE>w8DiA`VXx{ws{j780f=CmX z>QF)z}0pU1=TFwfRCoqf5$k^ZcI4(}09$iBe{QGQJ)XC%yXxQ2dy-Khhc##C*){ceL;#r-4fUZPoYTv)!4G-Ydqy?MQ;$#1(ndJI|O7g zV=Dtbi~Ww)i^l%=ehWc4^4-Q!$aN*lel7^_4Ro4)6Rjxgz+Ej*^O9k__j-4o)L6?; z!rg1l1jcE6hqN2f3>H$%>o0PfIQ%7A8Fgb7G^p04!9L$;;gPQ8(BRTEHpDkC_*Z3f z3rzRpS`B>eQXK4jDDu*JfK4MdfE-mt8T0~3m0-HV-FU^n4J_32l4r(4car3l&R>D~ z=6@{E`ULQS3Z4Y~2$;k>%n52#GM3N-Qn7E`BpLS#tuQgnx%TB>DWH|`BFwr z9~le`HnfC86GpI zS^C;iNg_5tkJ&H4I5!|PS#Y45lJRSPBkg;vMv$CTLHi&Yv)vkQ!+f*4;_EeXWcPWh z#txZ5aU=m9-#!)H+%DyC-9n1MtprRltj+J1xCPg&J3h*d!;ML;%cn_FEmn?nYr@|1=xy#- zOT13#MUv?O_-A~amSl1U&b+_I@@j%l#_FVQ?hME3t^BUa4TfG6a%1l4hk?5Zl4$#| zm`&�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�YF~$!XS~PAN@--%wf~p1_<+=5>7eIdWBpVcvqA@fSU5NWu z8M#po<86SQ=i$UeOoRKg+h_L@E`vQzFEE+Yy7+>K$I-kbpkeiS{P2?pk|B{?Gc~T# zipcnJ;kRVukuia71&~yM)+zenNoMxFjAPPgq3w=4H)X|v`CfJ_oCC`HQ?ex-&Yi_( zxTg6hO{3Jg+4{+?ISkb%?n(bZ=;JWFYgDgKRxP;pKkh10_p8l&k8{uFD!J&(tL^uI z;`Cti&f{#&@#gwO?ve$TR+Ci40H|e&)zo$8lvVAxnRt)Kdd0)lsE9cD)wN~yD7VPG z>cMVo3HHzE^x4H3ZJ*6&T zF^8C>fJqQx{}w)c2jU>sQ>oDIX6%nQtKMSIOQyU)vW#kMrzNO zN=WnPfAU7Y%y2xif0o`Ce;kP2txA@%+P>_WB$B~mP8l4I?JuUOV&UfAU}=ox-kRUq zylcIyWT39KhWYP57q&1nlms~2to#^BsDt~Ql1?~0pENTboKoWKONf(cJ*!sea1~f z)D`(sDVX-L1q%BXn7r>R8?k#U)o0?({ij&hX|N{geu<3>q;R{|AL)EkC


    @}XUA zAL&GY%DTK`eJVQs&TpzvS|AboKFcnqv^q%(Qo^^{Cx1hcIadDAq+#2a{r+Ze*SK7< z#Da$db7x&BziF(wFfitOts8m5ZWi{BZ3P?x;DAX3aW{IR}(+~YgS?%{1ieHM2^M3lh?iTP>*j%%>xR<9uD`^g#4~$nxwQ+0d z&$&wF>c=~`IKMl@nNWVk zJ$dq>jxbu*TeC_U;az|B#Cw~_xHw-Z%Dk4#PNT$)bU^_UPWtPL_8)uVqy=fF;mfs) zme}b5t#=IYkv}3OCEW|_dYpK4VnmV3)bq_}d2jxjmlK%DkRw zA4+YWciI1K75$I*^x!2fqf;$G3&-W!6BZWUn-&ldu(Yum<#Ky4U*lPP_3E{vm>{2G zt!6ja|NJ+9rnrj5{-rh zAOdgU?CfmiG8LELKezGU&I1!*oxg@pPRf!J6K$`L77O2({m);=r%eF9SQ`KIzc)%X9sTBI)~&(i+Omo6{r@;(z&m??iumv#DJ@+Oj1)Ea!{NbN9`9oO e&t1h!bpqEYUh(Rv0bS}9@DLM{7A)b{_WFO3k)V+P diff --git a/docs/assets/images/sections/caching/caching_pipe_3.png b/docs/assets/images/sections/caching/caching_pipe_3.png deleted file mode 100644 index b41a3a6c8b164dacaf1caacb938f9c8956fa3648..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 129190 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
    fu>qUZ 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%0D%-YvFf%0wgt8EWI*>Ou@W*);0~5{Ni+TUAKo^- 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 diff --git a/docs/assets/images/sections/caching/caching_pipe_4.png b/docs/assets/images/sections/caching/caching_pipe_4.png deleted file mode 100644 index 12ea2c7f2c44326e3ede1d43d87a3f1603ad04f1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24695 zcmafb1ymf(wlxGB+=9D>1QG@vd~gWCodgf=4ud-c3nVxMcXxN!;O_43&Of>O-haQl z-mKMh(bd(}Q+oD3`%JLBtOPm=F$xR}47%h;QAHRSL{Dg15P%5%%f5|_4+8`H#Z*K@ zUQ$GaQr^zm$kf6R2IlP#$H-=>PWji}a%NT32zaC_A$0%+v*I-U1)%^G7KS{H#Gb10 zh#m6~K(YhA78&X57hhS1w+)TRzN_Ps&gp=ft_Pgz;`M;RCtI!)yA@dXB_93HM1oA# z_LfZeni?yBdmXCIAnmX;DbA4h1qH|zt8O)FN7z5(F*m->oN0T2?CW$&R51#2Kbanu zwidf88jYoW&cz@KTky`pD&z&>YQ7;~b$EUDLw9h)zFjf`s5ugXPv1g+%r@>Ca+{zo zS+lE(x>G^7ae!#+(Kb}d6v{v8C(jZA?+c*umF!~n%l3N2o}rL{MQdE`YZE30HHZJ~ zhzyWy^5#R67PNH^?9%>maq^frKaI+)MO9~IHcf3KNpy+HSv|&fKY(tN%aVK`kpGFJ zzK$F3nybxc*tyq*`ln_e` zD|;RYKlR`4;DM(9Tn188{_PeAbAD=d8F@+(Ydb?q5EC;KGc_26l9H0o&cKLAQB?dN z#i9T4Q=2$A*zf>>&d$zE&TLH9cE&&!Zf^V*2lztnB~swV+=R_$LR*!o&>x z*R!ET`TktxkvD}HT6`8YwS+bgv<@(co$qhA|3}8ZEBqe?)&E_P1q5RKpGE&i*8g8r z#oo|P#M%;Cr33iiJ@b#k|1yR28H~aQ{8yiWQG7opOG9TZ zjHIZLG6eR>13CV+*tip#J#!~_Tbm7AXFheFH_AG!FLg!9&Dq+$+e2Y%?=hIXC}c1Q zMZ2iCmOSt6L2=`~OK~1*2qgmp%Bv9HUv@N{Lv5r_y-7=RN?K6~DobpBY=mcgODZu5 z*hF_ms*aB0;s(UnVz(mUyiQr?KXK-(ojx#$D9LJ*Z*A@s4=nZc%5bOm+Fk)Nt{Z^`f7L$`6sD+DbF{f0&z{!J563f z0~xNHj=32X)L-heU$FrgO{Rm#53(mhelzB?D~>ruuP8PJfDRWwqO+37TlwWw8EM$* zQxX|!T{)Uf|G17l$*x|Qrla|($?6ARZ87=t+%9qA`~zcNUKLk*9eQOf`o-H;J#g_# zvk!>XN^dv*`pBf`6n9;0=5Su-NZXymGdU?)CUlrDhWy1A`ROLkK7*5PC+AFJDedfM zj9de|wXAuKweb_HSE>1W*1MJ5 zG|NZQJe^jsp^?$}a58VPiXmrTR-qNY&pn2jl#=#CqLamv(4D1yo{%h0u}-U>6JG9E zdo!x3U@F|+1H#~L^Sg_@yry{2)9Z9PRFL`>qG6TkM06tiaF*d@yX9nbAshyP&S9i>{dgR4VgP%;7_EoCB&ME%h<^!3l3U<5Q zwk$R$7#b)(9ln|5c>$PBAL`zl?hPtyR{14vJ7p<(-emv0cnrP!RTE=pzc7))-FlJKiURF-lJjxr>8fBr~Q`mK28_bJI7i{n)}2T=eN!Ib#+{8M7zW@C4Bk_D$+xJA2WxOHBK94 z6ta!ww6#SBtfwMD7K_~@Ij8X|3Uqn|@bIugf>>DpaYA~f)##LsZjpMX81G_e?ZRD3 z1^+yLf=KMV2}x`+!O`M78XztZ>w5kIc+EbT{lf76n_8=1IJ?)2lh7kEz0NNul;$c6 z1b*g|1^>(a7Q)$*4`fLz3`|b#)0UUls%nZ70URi^qZPeX`E zgz6+51#%xWwiGuI(M{WZIWCVD9hDWo$nt`Y(hXNEv-!nT*gB>Iuhmq7JfG&rqvXgX zXe4B_Pg*gJe-)V}_n+=;8jP+`5Q!el;EjE#9r_elEgN?dkW;(DU5;P-Ask)SrOIAyUqAo2rWxNkH17=n@XFVllJY-KFf^2bsbBce zx=xisc_ow|rm*@ff5=$!u%~xe)la@J1s^ELb~j3yK73dF(r89d{JB24IfFH?sVU6K zYN<0YU9>b;N5(c^9jlCTH1mObvgSyBV8CpET&T5drc15rGI!?Ay)Z!kMe_MWt;frr zm}&4|y~Uqbn*_l_Ut+~GRm`4XGZT4^*H7u2DGio3Uw>JyBThohZHW`KG?P#MGM}}I9n*jI*8ksySl)sc+rMLEx;2W(nHrW@yj{U;(L9>76G>{BJG~TM zeCQ;gB1N*!Ak5t3rAD<+rTy?pOe*=61+0yZ2jXyuXlZfUuJ203ZEaaR9*%0OjD9CV zzQjir?BnEoFEi9^$eM3qc1${T*uxRe$(NStE0WZdwzSDs$z@uowN?`!YVmj&uir^I zC{R8BKU)tAUYI%!O(2^jiOS%(t}?+8?Jwz5<^7vvhJb5(`(#rD<>+6A=+n*271NX2;tr%^C|L{_9!&%j*Sew@?)xxoY(1 zdj{lK_?6R7N~jystY}W$eakZ?0<7iW=j!hp%-g4#_9^XoVFvq z0|MpN1R$rncm)TU5#Wq+{oe7b8J$wTdSa+uW4|+;-5*6?sE;*Wa;;Lm@&rNLTkq(e z_7Zv`M;f#F+87}}84wyeGKJ`eaz?B1zznu*_~pFp!v3`jF!1|#_EMuWE*eh|xochn zXDVbJnFA~{f~>ViWc*7gPRofX+5NnqN0Lm*(hr?r#(wYl;Y2P^gX#Yq%6X`O`0HDR z61E>dS?~8=JcWOZ+nKpblbCt63#HFb$$nxxEwME(rH7we6q7Rh8xukkrC$PS1Cq!u2%Snp%*(>#W zDl~&bBb#-j|1)Qh!#&z7jXDF-**)$rA*{oC$_q7?NoMAJesUI;nB(2k;3@=hHi&Z@ z1PL>fjMr&&D^8~b&6N35I@ow?IPtp;>~TK_+v?JS zr{i9>j|3!4CPBq7z6QQC;x!L|Ev>q`TTRQdJs$^+he+cI1Z_R$&5l052RL#bX!B&^ z%5uS4n7JpW;4(~{#h8&;)YYX(VQ|n&{l)hEzvh|`Pk%?yr>6+g9^m0aJmkk3VCXw1|O09_CH{ zr*fx{V^nF-g(Uk;bomWtm?-n{F5dm=`%!qmuIP74uYu!jJJfMZ%wO8RT|vhMBY%qf zx#WDKb15sl&>KGQ)h$>c5~QY0-*hncgU?arnJNE5wYda+3WS>f9|1jzs?{#T@~J~=Uj1*+8#+QJ(tH{mSdKu zLZ9x*kx4>a|7uQwhODyZjGL0b?u$5oKSHMji+gI=5YhIvQ!JG23?W?Ww>i)Jd{70G z7Cw_^-Z3trz>4d^2M+PHpf^z|JR=T8pGYW2lv5At4aSAW|L<`f3g|)?koTNf)KMnc z-6K;g^KdHh$&(<`~2bF2I;Wtn|;(y=et(uWy9!jV1Mn`j; zo!Npe%4&g>5Mgv;_C5{1?2CeHw+cvujS!sgJuq9GHR@R4#p5;YNEg8*V%$(c4Gv4wAaDFjRjG7euVDj6Ji@AOTD_gXlw=JL|PH?IHw>Y!N+XL!xWH_|3I z*(njl8Brf+|AP1n4&l$bvOQl9e?&CGG~tQ%B-7o|bh8yI!n)_j9f66-e5I*p&?tpJ zvs$7ws~)@ES-K^l`l=m?!i43FrlV+Ub2Bf}nb-L^i%F~D?Fgl%NhksPuXqR8>6&XH z_*1bY>=evE%Bs(}O?A2sok;1ElobA8B%zUx4q=|B%ZVwDfjzz?rjqOI`4c9-^mrNQ zI+uUlXs*fVeLLtxQEvlV8jN`pQjFdJo?yxI0BK2AJet1YcT6bOs4d{n#1MG2F+BAz z2TGF!qLbuY-wEHLGSvx&DoJQ~2#sXJQ`u4dUpAf~nW!L<=Xua;`NBaA`F&vHJ}$#@ z^ltx}nVORmD4aq-JAm5_I*le%m>c*yV%jO$-L5V0aHMlLZlPxAC{++?2A|Jr|3z)< zlgttJtInXD@o~it3Eb-=LN+6`CC3@9fd%-7W8GGw$WXb9h3_D(p6_H*nGz9iNc9Xl zwohl>;Fq~hUkjAF+`bYECDTx`mCHI;m*`IuAh5qkt=bK>GNf;Zo#D@ogAAaN%Vdy-fYF6kD`|;)ZiE(Ssl}kBseP%{T0@NnT<*!D|#x}vg_A&6oN)D=< zu;}U8#Z;+|z<7h>0pR3G>7860M?d>nvdebDk9SHrga>041+MISuM(cGua1{^JE-Py z=DQJd`t&3CjVyE3I5A4`+L2667<+;^DRw|wk6V|H6;CN>DbQq{iI((J?#1O8BkqP@ z=T9Pmw-gQ(rU}pbgkUccUk4#Fz4@J)Zbve4xl=eceygVQN6XXgavxwvPl5j#p0ZB- z&d*v5Zp@b?N_AvaRzw4|KEg;AN`1i|?=V;*xWBjn9ZS3O)rA!!ZQ_{%0~^M}<)!vI zS+W5B?OcUS@%&K}3XjVlU&c;GW@cQ0m>y&fT_`Wi>6N~l@#mHeZRXvgr*`VKR>|)e zQ;dF^u6Jq++iwpkP?f9?O}T#^M^L$4K#~i|{LT|HJDPb-D{R6EvKdRVZXxluAPc!%4Xnjk_bn#KhR$Z%=Ql_rM{> zJhGbZ&mjLXI_U?}ef3IBSKt271hsFi=*vwyo_F1D2{u**z(f0YLXn#P4LAP_iOC0H zebm<~{{p5Hep~hjYu;R)p$HK&6PjREKBB0B@%?8;wGsl@D79VBfs0#8;l)^EyFG4 zFRGskRZEl$vAGT@em}W>XCUeCOT$AY7|$>qe3d0t^WX6L-&*E$LAcG}Rv~@)HCjMG zlal+>TWS(cyG1%TKz6jQ`Q*2^Y`C`FieEWdr~IY$#U4z*5-&Vq>?(}a;V}j~os`tY zR(=AUrm#Bn)5YcWn;0C=miv)=T#kL(g3ig?50s;|);f6OO0`#or7anz6FGZQ8|771 z(F22n@A&z($QqqbWN&R5HmZ(DjqH{=b(B7TCL$Yuz)BRbX2K&NP?}$Eo~%gIZDoBt zZ83b9^t02MF+OyceElC3(Z4N#S$aU0{Re$zC>WRIOOjDm{8SZ6|MGh9f02_gzw=ven=dS7>T{}8EptQNZPag6rBp*r z{gJ%0jo#i9Btpls*4WSnhdp5^{@zzgO-<&<;PNKsuqtZuaJo1dessBr7RplE5#D-& z`}|g?%ABD1OZP~gPVgU?#<}DWH9unswO>@mGoZ}J<$hr)1Kf-+0%`&6L8J&qdgTZ> z={wgtfakOg&Xjy(CwISS1i0W7?y(NtrTB>XAk2=(i*tE#;kBS#9k-+Es?gqb@%NF~ zIRB-qd>ClF?^Lj!txrn*`#9(LFazt^1nSQp4J~J&#CEB_PCzea9drKm#YphuX#tVV zYwVyUg&s2PF&RcR<>)mmK8JHaMgrS6izq#G5*)nqtm~lim{m{$v-~A@Se_CUb{LMB zh4=lVJ@o|Qv37#qZO88Cv4W6FkmWZ>h0fmToH5=WS7(}kt}>!rPN|{abZ*OI*Z7!S z5G&?9fu0D_H8_z!i3>(&@)oD&$g{o9d~sbb<4{EcApkn7>J zaFXM!s(Z3Ll?n5Aj2D#TMjEE(W1&^%2aU*v<`W2S@<#YBq&vX;$kE;Lzb3*SqOI4h zuiqGS18ru|O{Li_8OJK(Z2{LcbB{I}30Z4LgrHS}GY>XPV@>zRE?Rr~nc8FlkAzY< zjWliP4!9G@vSG(~LY?i#?aqV!{a$XW@>>Csa-Ia}(r@D_bDw~3 zw<|>T@5%nZ$(uZ$S5Uhk(~=ay$SX@aWv3%!$=CN4$B0VDwfCF_rh2O^2vBXtRBGmKSzmo<6tjG7+b|+^VgK^RGXDPk1~DR* z`*-mDZWg-0*r>iJuM0=-adP9Wu~8|?jniaXrV3#l>+(geKfgR<3i(;=XE{52`(l^A zHWU>91U3Z>fzHqn-K9e5Q?5?|Rplr>jKFsNT_`7W{f6$>!mT_2h@eiEVaT-M5kc;D z$a2+TGiMk_*EdEWbd^pz{|$T^)e81%*=vx9AZHL=(RsO_8R)Uagkug;_*9GVxduG2 z+Ks-;>uzDlmw_wmLqD$vZT%H3_q;S77Abl4|4Jk2whF@7bJ>tWpp5HOlW42w)9$DU zX7^nf9%LH))7bq zyjlf)&>60bWsyT|Wh5@e$n|rz;{sm8e??89mVS2Wyf4xr;P^$wCQTyEn)x$W`pAAN z_s(opz3K+lY|(_-a^OT-bRV{1tNJOskw8JA^KwpolHEO1ho6i#ndj-d($tswZpEfUhQNCf(3V+zvLnvA!bBHT8WUeS7Ls zo3`FzLe8G(@B`dI((gN^8eIPRcZn1JSM>#WV}jSmj7A>M@G!t^7e z_b+fIC;5b+lI!|g#VNKtu>X!#sEzEGobj4P;@C?a@= z@W7df1j{)8ncJsV34BoD{cwuyIsD#GL`Qyw0X7!6P*?LBGpeb!?%ULd+;`T^LZ7eL zT+RjVFMhVCgv8qfscEqYf1j4xac{9M5Ck{|Vri-C`A|&$lbEGDyFVz+g&JQXl%nr_{;+U&fb^QL-65mI(uwSnE&-%NYajKp;&~hy_@P{R$V&)gN!rGDUfE=? z^M3jPlT3uh+e8lYoOx!ORYAk|F4uF$dr8BI-m0RqBp?NWO*+OsT9hw2qq*d5*B zge<8QYuyRA^dt7G2EK#=0FKk!+sdBx)8ddl(+&xpIExRwrWeox zT{+y8deF}O|C)mGS>btg8xeko{Qj|vTA$1m&Ym?sRfsmW+I*(qJXUEe zhX;#*h%c(zzKGFecDh2Y;pA{QtDr@5K43Srg)~~;OD+`|QFb+oU=N6wYil`)-?=^Q z#N0p)ViEB1DA<8AY+1q*yOoKx4L%Z>fD0-fiWw6TH!d6I`g3L25rWHhd_X)2vJ{`& z0i|keT$PaD9Oc>Iu-0M^9!Ka&ow0 zph13ybJngR&!sZ#&~&dLX3ZHORzr8uNFA9f{SybXU;Mt=^+9nZOl*Z8Vxplg#V`~R zA=`e;=!D_G@P>~xaTVp!^otzjmVs7VZkfm%wll;TCmw;RM2p8kGw1w@18z*|WkJt< zJsJ4FoCX;(9_rD8x~Nk+bEo)l1$+#PzjpQs$KJr~H1{D=$uK1}gza&qo?N-9AOB`_ zN&QfAfC4&q&5uk)lC$BTxNYG|YG(Xyv;ntE%;XG6l{4rZau{y>-s@$*U5e8FjWu$K zNMYsuG%D5Q{aG`dUG+$JTtSM^75dgryTS8VLSJgsT~lN#dYbY66X|H(^n2E}uOneTyUerQ^Dn{b=9Qx+M*@&B9pA!ihOPt%VTalQB(9 zOUNX$ucfxc4)pn5ci0;fyUkM{YGXaEVS1%OJuf=-@&Z?rN(|E<*7tlZp1$)DP@L-t zx}S9u#K5AaujuZ+%Jxn88V&qw!)BH9I}G0!<`tip17?^#?g;cIF_exZs&kw$UZhj) zu|#={9$d)CP&|v@6PptQ4d%ICw2vNOkD0oC0(?e7R5MLoy7D*VO`o4KLMAB_8fe3$ zVES9*d?jHvU1}DhPeG?07r--1Te;odjzGAjY<+Ng)THx>6?JLo3$XALJex}-8G zeqA8#X$5M33c@7mck{|7;cP`!dZ+!GndN1GvGgq*QWzIy6O6a6sJ8?P8jHHm7?gQl z9xr+4xH~FDWR&9d>~eq8`XYl%FRQ%O>B$eJP6xsXw3#R$%lyg|H=({6l|l(+-;=?v zAV>)_>6#c~NxK~QF6%tYhwAM&-+ez8qk-smm&c7K3<^+jk|JW6^1jGh4*w)L)mb;d z&h09xzX~VdC@|6$;CKTy;24Jd?g79rCY-8cO;^(zpT2YU6ysRk`sQM)Zl#UrE&g$X zO)h_WYj)tgS$`WDG3wfe%Hvbun}9>9^G`zVnaS!1+x&HS&S>-Acb?eO-8|z**J} zO0ttyj#7r@LENDv^>>i^)2`%mc!pi+%d43&W)ncS(g0f}bBGC8^%GG5A57PUG;1{j z^YJA`n|O9AqcD`8j*?C`UiSkFxtcwfNz^b^aR1mH1O{VA*f}2KTzS3{PgCsaW1Gzg zF1IEeJlz}MYf43Ez7t0<$^n@eMGZ0kG4Kr4c32c;c|&vMClKisY0;10;VCQDgiKrB zOYu=DHUxa4PMZ5cH})}d_yHj%=EC6RSrAZ<@MReHynp=Vv~xqT<;ChN4WWfe>4pEx z1YeIXe7@D!q*q!iAtomJ%aw!rqUVW0Qt9xx6<$XAO5`hrcbYKTbOaZ0L=-Yhb6hAi z3MwCGkanjp_@}7(f3f4Pw2b2F9kWk~dbpHdmxhJY5yH-}Fd>~ivH6uBca1bABvm8g z^)h>CC^U$V4BX8H@A~^Sa;D=KFPy4h>u#4O@Gq(|Ey-HPau3|PxX;{6Xa$)~0MTrk zyUnm6_if<>K)~J}-iB1^>Zm)o+$YXefQT@<>>jRAq$4s|&J8iy?YQ1Q;tgSyZdE?Y z-u)_KasHbF5i;iZGzy12o}v_3)1qdN&80=`LWm&OzI}}DZ3haO1qL^ixcpSFzNUZR zOpuD7#d6`AKE-a-V8xB{*Xo-G-dFLcf*(Dx@W;RYU_xV7$3oE$&2hQd#_l-8BxI%} zw?h{vKimoZMsa#R7HYIs34G(W)`lYBun9-<2ze8 zST%9Q2U_>$%x&?FMbtUjB>NuV^^QnU#!0`1r_gAHxNwfC>ClyZF>JW?y_7DZTEv_G zJXH9gpIj0TSyiD%>zWl+$>yVN^FzW7w}{!?S~;#UhxJV8uNu)wjr|PQd(;Tdz;MCM zuj`r#`oS6!O-t82!eNc^b&DID_HR_vWj$J!v;**fKI^`N;q9gj^Hfi5U;?$Ukn zPCZ72Z@Q=Kj6N=#qGeWX@18MQFz4dkkZwYQ zbqCEx!~;w3K9_MD1^7x#Xe2RyI?3m9OerVa?;gQC+^OtmZI9St>{zutuUdG=9=o?y zSdgN(e-VqUq!VA`W>~VdgBC`47e>IMeT&`&mIL4dOl%Ir_$cTmYQfWqbzNB=8;9&> zV{K2k{tcXlIS8`njSgKA4Eqzf7pL3po-FEgJ`^t--0$b0EnjpU)*f*K^~$Ap?M>0z zOcbM}gPYWK10-2Dw1`Qv#dZM#X@Gji2!>jrQfmtEaF_sc{>aeoj@ewK^C$SuXsKnW zcVe-NS7KO zzu$hSSMMav5l7xK+?}Afd$gjldV`J7IVhDpHYt)`x4IkmZ#L zkZO#+`PCl5tQufqjZ?Jvc5~+pN|u#~3Sp>vuOh4uv5hDr{>=xchP(gm$?nw74r*)` z-d~1bKI(Q7`o!Tu;#L>UHCfNn(PxtYjOwdfa3|>(!@P3+Qv+@K$c1`;JfGD+D77gC zg|19JPgtZqme2J@&J^J-&U`q(zfq&uW6dYU?l>vy#8A>!#7-Xd2p7(3^D67SEmZhuc_t(o*5~{i-GFB~XzA|Dr_$BjZ{jmO)ftJoW@CmoUDU{f- zt}@?4V)Co3{ax(5V9Q6|_+KB|&RE50)gvn{E^D zjP{zIEwiU6twy9oGN)w3!C}gnfr7Qz@gV3e2AZ!pc*nr_DZb#J3#_ZcynMi4! zV4hRr@k_7(laOo@rhW}X`tGUIN?gYJ)oHp|18D zdhbGu^kdWkaTnPm!;De~p2I536YUHF&7_8=6aFA9R9`x(;X-|*-6A0oihkBrhv*g}8^vy5V&YuWVJ7$gOl{>Tc zn@&BhuQn>8kh*dCRPkd)IpVuqW^wmEfUUfQOii>tXq>0V`DANvqWQV7#nTeI>Yfd}@5EEAyvNl>z z1jUHPx*@~OlAMbU1s0kR#K|O)-4uua`F7*-d=OYjOR_Rsu5Ws|JLXTr>>moet{+PR z45#pG)Z?x=uXubK4(D9Xo8KtF90AJAQesoc2wm;j<w-O^Tui{(gDt1O!%AQ;J7m7pwSdKcmkRKI{;7 zyNTWOXXAWs%)^<&a0YciDArSaW!uYrGOx3ukdV*$rjHNeg<`z1uL6R*YdL+BcbPYS z4_RNY?`Qwmm1f?CJW^9;c$Xbv_BLj|WV?ZP{%>h#oz*4o1fQX@#HnA1*VO04KO>RI zPTLp1zZo*rO+U`ht>E{T9HH}@i3zG$^=miVpUkh54rW#%O2-Wn+pXK6o~+gJKi80$ z$WZDZy9h+533K0auqN_Y&XMu=e0cyQtuAhv3+GJh>5$pm9Uh6MNJfN(0g`BbyX2fN zvnNSg6TQ?IgD>S)TE<4^c0}Knikb1;9TW~YiZdNx+5J+LZjYuOv!0@fOX^mxY z_2E4*pvI6z2Yg}3F!8+}+-JGlS6fZsF5}L#)?oS`-SJ^-+fJKCN^4cbll3ay!hTy% zt`EM&3tyb;QnSia!z0JF<21V_b;DaE-VPwn*#Yxag#uMa z4?0~CI%c{{=c(;%9RLjChXhDBeW+pW&qA7t3oA;iC;_9aJ8oU?~d0sIJXX#iQi>g*M=idSI4QAI6?F$A#oNHh9&~Q#T?Nux;j$x=L&h z4keS#ah0RgCKV9pjN#=1>bd7h=)0g(o>?GVFqMV_{-5VvkKx?o%8H+I< zZyziiaXFz3fGZnKMTww*>`-R(zJ`X!M^3D!rKX!ssAm){8z*XfeBxb@ z8n`VgH(Jf0OHG0@EH`J?<=qKUPz-^(Ub_=ra?&{VqdL)7>Skhi7j?_EtM@`r{l_Dj z2WiHpk1Z( z^z*;O0ZfX)H(ju3KVg3s#ohj~ZZG-koBiR0J`J$66;1eYq3^X%PGn~lnZu}1mHXW} zJdm;!ElGL^n9vm3b}+!Uw^lsjsI7PVxxPb%slKUBh>9Czyq6fcS9Ho5i7Y+$LFO&r ztGaTia_IOJO_9|R9!uNG)uB16+kl)4fyoX>P|ni+306;SY3 zB`7>5hbg&z+dRWyP8vm+H%-=`ASTd2qWf_ODZ5FeU8l@Wq%~=7wD|MdtBwdIB29bl z%T4>2_#)623G1b(>q9Z;WR7#w4Qfmo(&$hGm~|jp^zYcg=Lr>-??wsvEP-L=IY4{K z@^{Ojz@uJoPfN)t1&G#McqB2>bfT5PvbI0k;eWvm9~}bNs5Saz72=D70=jC{SLE>f z9C{R0{3-tPjv>Lv&}afl87KV?s4!#svJDPX`E5dHEpzXBOc@Yo~meggxrfitF9>rHZ@Vya{c=kH0d zwI}P2-euA7y=FteSa)}-h4vpW;r$=Q*xywQU9=tt#kIeP9rnfpYNG}fQ}3H*z(1`YRoQ#B$vkD!Lg>YaZ3j_=`{|B@xS zxVSjLjG(u7BBCmr@hrZylmIO-cQ!ZGZ(vYpGNDvL%r$2TL7>6_;FUJ~ms-znJT+da z0u)`~6QwUsu-3yrGD|e78)&qf3Ulb0^lG3BCb4GR~PwZA-JM6c{??~iVa znaymwn8mxdrd!%nmBonVKmXIxAb9{gpMR(w{<;+iQV z=31uPVa$Jn#iEU9{4LcxPraY@QQ-;;cN5{^;M}dA&_EvU6i2HhvI3JPd6oT8)bd~P z^TzLp<4MIwpaPL>c|tAC$Nuer{1MiYm#b2*{D)O2pq8#tHCzlXi<9Hmo=R4Y5O6Pw z(~e-BMSbfh(wLn)1tq5CD2ndITSA%YR$q0Hb90FOT}mKds9OB9fSL&0cQTqTqImAO z26f)2{1IjXMaa(34wjqU@+w-Zg_-yDzoKcU*u?aBDnFo3IkeKVZJVPgNtZ#TWIs_* zP#9FGZa&d{G}99PN7zhs5jLMnOTnwi!+1GEw48#C1?yHBit^g`U|`efD=p8vErw__ zLYD_cx!uarhm-u=l)nXkJU`#G;^Jp}pOO@vx$Y!8??P3pS~YVF2BK}d(p%UVI>w1<{XykPVc=cvXo7KyBy>mpq}xtSwTTWTm8kfK0*tr0rETBQ0Lo|v8xHW$juxJyKo+e@ z*T=tb0|L+m(hi5f(XladKbjBjP@<~waBJwY2ThI|3gw4HOeYy`i075=X^K}MsD_&k z*`VLY=}X-!3R#f)T+JhHG4*dyXJ2WBFz>8BQq{uxk$&Sw4ciK2)sX5mhWPxuK4yON z5U-{2mUPrm%$6w7N>7ev8Z$)_kdf4%KNUwNXw-RB1M2_}C^7bAZ#o7-GN>!p@W>@V!gV|lo&;f> z`df?hNDTfnA5DR|OHx(?z|mY~(6ZNyhd^ip;_~Pe{y1X8{JHciy99TSOpI?#D3u$kI> z&XmkS#ymyP+m1VGcDhPMWh0>2T)1!NC~0=R8`08V4n#Gm+ppZqI{;8}WnQwHFt4SZzAAFptH)ZE5MPw)LBHAv47o+9J8J?OmLj{8>eb?k|0m4J7Nqflut!9&%Eb~+ z@VXTJ>1N4!W$dVSMHVV&R$XptAc9zdA5S~0JfBTzHmrPVx*{%31P27+P^2N;->98diNfdE8+bSm?^7A2LMgSe2q1J0oj-$=o0EQ z#nHX12sf8HWA1kg4OL&VOYC^KHJBRN?1!kPUXAqiDKGP%7BFsOHp1F^{!B3fBb4#H zN>9Y#-o)Yvep$3189OL#E%~J7^>j6$gh>7h8il2zEyCr?-0t%ThshVITBM#neo4S5 zNNEoZSsM^MJ**rVH!p3@snBvyTyc7QI`NY8y6vQxq&ux&A$RG7V0X;(!yXnt@TD4MbvkdgBKQV0uV2y`1=UKg= zF^(BcT%8}`>`m}guV06G)OZT=ju~BYc2twuc43>*-9Vu=RmaD+gLwV#3Muk!xQxwu zSn%I7W?*1Qzy0|wfQQG6k(d;wgc@6X{{)2}>KTPEn(_6wl=IUkCALT76CB%?b%H5{ z;6-NueK3I*;o&IKA19dAOG!C=J@ajJOicb0uMIj%gIY%R;Rp|AVJk*$JWnBW zU5~+5r$aE<_Qb4F3-!|;c_&3M6u~80)o;b#R$4$2|G=!r%hOF`fzk--f`@^*$Y^}6 z9->%D-8u@-aot9=Y^wV?vJ3j_5>L69kt=swqG0H<@%Kq??VA*nYyoXU!7i?c7)p_=6X^ z{=}e71_fBfYxro2qP9Q6Qm^j@e2Zsne^J~;^(E(byH;N7EAIpNpIWHX`nr%ls`SG?DLzt-F$q(#p9VaQ;KT#c z)9N;n2BeKU#kI12NCmg7B0I}4moCqL4~Gfs)iVFqazo^4WYb7t zTavCfmDm>4Ub5R4!l284sFTRM_4EH*RUPS?BD1_Wu8WbO)^#!M(G~Np7CKakfDx5k4vqlEa%&vXC5La=4)UjAyu>Gq5ubteMEz zP#yUyan|_j7DQly-87td^kN)0rfy`ySp$HQGyaSBKrl#rT@foh}CrI83h99kL8^AJ7#rFf)bv zt&EkfC%BFvOD`UPlyQU4F5U&Yzbz(F2=cp*jFpTN5sv4j^L5J(KvVa0P>@e+ftOBy zU-HJ{#r`uSqb@SSDQ!wjB!+4=P@LjA>In9QDDMq&E=VZAmVEA9>?L^TkrLoswz%pB zRP~r%l>HX33&X+%oQPP!&GnGi879tkf*1o?&paDD^Nw#1Io8B$BM2!4|ObBz-;LtV}`3Sbc!P=WG@Bw zzWVb3PvyM4tqkhtL|~)d`l`1&zW6%&f$nNm*>EwRGu5R{J1b{GoHoX{S>k_&bo=G& zsN#O5Re7Xc8_PnX?}=IA5)U@K=$V~PUkwLpobe&O>qLprCIlsPg0&&FF~<|U$?JD& z^UC_*0%(3;-9J4)eUOOLHa2r;lv=)xzGIu!sv-xAYMstAuSd)sW!7dv{sw=_RSs+P zTPZVxiS((8mHtv@R4B3e1To7q89*aUafe_)dvR~#=*WlS9jj}+j)EJZAOS;n73y@j z=>uSI#@HIWEDjdB;K62R5FV`hWJ=o-cMW8vvprQFTD}Nq{H#pHk=SM1h;+z84A1ch z&amj?F#1u7NydJpjyh60|7LP!e@C=R>o>55D+wiiT$q4M@0X?VUy%)11(n^ay<##>Sq(6m#rz@514kSy=90>{=ex?x2IP#c z%OtCJjQHqgO&eS}%RAg%rj+VTH-yeSLNwjlIkF}%;OfRGP&79D@`a-W8q8GvkcZi} zP7SuiRBLsjCUA6V=!5<{H6A5MjZlM43u-Be*#|5do&Jyd~MU$1iQ2b8 zB<=OlHFN(<^n1i<{%ycEY)liHUMbK-;dl#ZnqfRNgYxj|E{-?(+$YnQt{Po1uZ)+J zjzYl>U)dXo=#zi6?MYKFw;4w6oYm59+hZVQR$J&os(czul8iPi$|kVl%xKP-)Svh~_A=dh}0)2O>)89_?dCkrFE6 zn>Rf&&2mC`cfjA*kwcI-woIc@MXf$3!SbSAj;y-0e&C*lK}5rl3HRpQhp`rExo2=j z%g%SF+qe5!I~utmypCqC#6FjK>eWs+C-78`rg&`)s`+5sCZ8L|eOVreVu8Gl9`7j} zU;HF}?G(gA!irXnNMpQCBO|Ud$Aig~(DF+tx1pg69qHDE`8#$q!}KYUg2?031z;vI zcR|?tqE}_Xlp~1y3sSxHF(+n$K=J9u-Z=iMJx9U2s1{wxA$ha=DSLQ?h>9&`c=M5s zon;^a36XRGjE{A9*26r1XVzz^0ymwiFNMM_2N^|9>{><73@w@}?AqJmT$5KHu3f?D zW{IgfC{FtL{BgU>t-Q7!UjNlLDN)TPziUUfLlNe*hSi5HZsWDVm<6vQ)aOT)1+kL&LvK8y2M6`Qh%g#` zmbnn~HPs^}V`*c|OllCHq>u)%U`!RM_mr4{g^jMep#N4N;h-$j;^<)1v(ZN4D^IahF9 z=Y@K)&zfS7+i=e8Rr2F?uXiV^-)d>6J5lSkRvRDHUUtyxMlgI%3QW|YR@XchM|A!v zFPX(hi73=`h%3duJ1fTR_4LVnS-tr8XqY`Wp?o0-l_$`hBl^&@1Y1^0Hpbl%}kM>D%+WouvLW27;<_vqcyjx^a+ zLrM-8i|bTF>8)9(f3Q5X)$6e1c3j>GhdM3aq8lvNfFsvatYg_dc#s25*!I-Q&i3No zNCmqW;5W^?OBafBtZ$<^$-kL3&OAxJqMOQCaQpmu0HGvS{kNv11q;9;~Dd|&Sdy;baQQ%7SYhOC=ow2wN?_tpVDb8J%fF7TD9g2h0 zg=GDr%14lm%dzXn$y`4kwlg|bvlC=UXTI{&m}&Zqr8p8N8xcgIvTGs6zo$MHS<)Quv-c22o;YYF4*$}t=Iy?? zBZ`VH?Z^9ZJCMm4y%w~}bC6zM*M#09CLOZR_hvoon6lf^ehw3daeMg;+t<*|mx1p4 zorJUhEIq%IIFViU+&dKM{YR5zJ|DLXwHu-EYjf_+0w!<&iMx z9laSR1J+l4XTQCx24ZL9Z!8Mry0(amTcd4I_vLd1VCmQM{UP)WCcFxIdN^4AB+?xy zQ}}AdT*72odUcDsq%mXr8z{qXU|?nyz2(i7arsqjJj{=PIh{hff(_TGqI|*NeOU{N zODhFc^o(ZV{J180uhR9?Ub zKU%|*CKSgUF!7+XG#$B;P&^AA_W`Hee{YT@@AfioG8{$-_EBy-W)~j5YNa0Ws-3in z-c~G8@wiP>K)!yu4Aj|8Qet^UY=Qj8y;d*dl?ojQw3O2zH}>^0t_OJHoIgMs5+8Pi zNP;6-dG_AWSn=jJG`cu3x)T5t;Joatu4L289s-4RMA9eDXZW##x+rLP-}{wqws)OVG-@W_vzMThc*&_|ql@lAjG&P5+TaK%@DsJ)|r zHvd?2n>6it-Ln5=DMa&>ShKa$PaKNoswoKP+MWbV*_-pGao z8oj6O9ouX-+a_F>NNWmN0F#tM5_AcjOh0Hg0t0cDP;>njPpfdR0!z_}Bz^*cd?sBr zm+hFxgVEa(AfJA|seqWBwH4k^(d+qK->bS!a{K-k#XyZXeC#y2zmr=|J0Na;OgIa0 zDS%3X4<2MGOB{wglPCeyW$tL@P62N+Dzo)n7IyYqtiD?aLQ}x$xA~y9j@<7^J%9=& zo5iLEJIl0r5$V1u)USa?O>Ouef))oDjyHM;7%Nn)c6~e+JZIvxOIdWGosp1e4LfR1 z8>MI|Zhh2b^@4*Gti2M_#|DF~gAV3*vuBfn_X~C>TQ4hd_+Dopzry10kS}nnMMT*& zqjU?pq?h)sUTQtNKXSHeGmFqiW5IhL$s3%L~tNY zBHnYM!RerGS8p?dsB3C)E68@x66m?Dra3Q(!0}I|aOfY;@bd~;_xW7agIG|vg6>qV zU2z#MdHg)I9~sv7Dk(ZCm{&wZJ3hv9A)~7@`*)FjhEoS_QQFd! zf39gHxwEn-?Or2gcz-|aNnXp%`SdL29~57G0o;Of+1K{Y`+Z#2L;)gR`oSc?u9#zn z$jyu-+kXe$Qofu);&U`D9r+S0_7Ss5pknOx=1Zp+`8L$w5teWVUH5;04?G6t(`3h+ zo<4q+hw|*Msh#|g;u7S>nMnQ?%-C<6zJG6H%O3I+|H)KkdE?YPn=nq)&bU> zFjjjYXe?MvSz$P8y}m!nRR5)UEzL!v7}hVropZ@5>>kr(aQBz_K~X#=?`w$3++P91 zCRsrJXTZpw*Sho4x^YKUBkpABT*jD~vqSt*p0=xhVn$4%49F2mAH9>_IwW!XSkQ{{ zj6BcIlx&t};Z>!1w=p%UHE@SnzS=1@8b9P{E!@bfs^+7?4LL9)S?#iJ-I`L7igLi}aVOm;ND z=^ECIOzfx8)0YkD(;5u6PCVHPw3Pv~Itu~CTYdhw6E6@aB zihiYWwmv?I43w~G&Z-`vS9-x&<}}kD`%-glIk@84N$?ST5*=hIHeo^5o1{0l*jBf@ zRP{1ZCv{$z|2r(VaWGg#?VIth%<~UldR_-th$FkO(JPDBFO01S8gv8jX&qU|zN{NN zm$O@=peMi`cawNg7^kj=pY%)t#y%4HzPG~7sTogV@yz4m9v&V+@yoGlHi@^#lI5o~ zdF8AXGB|tKhLWj9M{JFy<7VqBXQ(A9;s$1O?PTr`r24JNf@>VLs@zpsMv~^9Bi3*3 z#qx=?T$Xs*6sA_jENCe(V}r;Q;_o|edV7yMSIK6{&*KISzjDaE^6R?y6P{J#$_%Fo z>{nq`+EHOdQ2qn1)s+!Jsr`Mv65Z@y`M^KTH~;+!u0^*;K!T0zW${Hy>@BZ^2~Lw3 ze-epQc3recDceD@XiTL_LTVp5knH^reagRrcl~8KU4k7E!x=OwP6!xT{bQd06*BhM zpsnEmO;icqRr;&l{x1ls|9E-dQ}j~%!*f&IlmB%}{>d5m=Mz~_Fbc}S>vjI$k)Zzi zA2lm&HC9#MhHb5fzuxkH`+}dpDKWjy8pLJL8UntDGr?;%#HoYnb)~`Q>#(~9`+ws Cxj|C^ diff --git a/docs/sections/how_to_guides/advanced/caching.md b/docs/sections/how_to_guides/advanced/caching.md index 1fc9414940..d4a03c09fd 100644 --- a/docs/sections/how_to_guides/advanced/caching.md +++ b/docs/sections/how_to_guides/advanced/caching.md @@ -1,135 +1,60 @@ -# Cache and recover pipeline executions +# Pipeline cache -Distilabel `Pipelines` automatically save all the intermediate steps to avoid losing any data in case of error. +`distilabel` will automatically save all the intermediate outputs generated by each [`Step`][distilabel.steps.base.Step] of a [`Pipeline`][distilabel.pipeline.local.Pipeline], so these outputs can be reused to recover the state of a pipeline execution that was stopped before finishing or to not have to re-execute steps from a pipeline after adding a new downstream step. -## Cache directory +## How to enable/disable the cache -Out of the box, the `Pipeline` will use the `~/.cache/distilabel/pipelines` directory to store the different pipelines[^1]: +The use of the cache can be toggled using the `use_cache` parameter of the [`Pipeline.use_cache`][distilabel.pipeline.base.BasePipeline.run] method. If `True`, then `distilabel ` will use the reuse the outputs of previous executions for the new execution. If `False`, then `distilabel` will re-execute all the steps of the pipeline to generate new outputs for all the steps. ```python -from distilabel.pipeline.local import Pipeline - -with Pipeline(name="cache_testing") as pipeline: +with Pipeline(name="my-pipeline") as pipeline: ... -``` -This directory can be modified by setting the `DISTILABEL_CACHE_DIR` environment variable (`export DISTILABEL_CACHE_DIR=my_cache_dir`) or by explicitly passing the `cache_dir` variable to the `Pipeline` constructor like so: - -```python -with Pipeline(name="cache_testing", cache_dir="~/my_cache_dir") as pipeline: - ... +if __name__ == "__main__": + distiset = pipeline.run(use_cache=False) # (1) ``` -[^1]: - - The pipelines will be organized according to the pipeline's name attribute, and then by the hash, in case you want to look for something manually, like the following example: - - ```bash - $ tree ~/.cache/distilabel/pipelines/ - ├── cache_testing - │   └── 13da04d2cc255b2180d6bebb50fb5be91124f70d - │   ├── batch_manager.json - │   ├── batch_manager_steps - │   │   └── succeed_always_0.json - │   ├── data - │   │   └── succeed_always_0 - │   │   └── 00001.parquet - │   ├── pipeline.log - │   └── pipeline.yaml - └── test-pipe - └── f23b95d7ad4e9301a70b2a54c953f8375ebfcd5c - ├── batch_manager.json - ├── batch_manager_steps - │   └── text_generation_0.json - ├── data - │   └── text_generation_0 - │   └── 00001.parquet - ├── pipeline.log - └── pipeline.yaml - ``` - -## How does it work? - -Let's take a look at the logging messages from a sample pipeline. - -When we run a `Pipeline` for the first time - -![Pipeline 1](../../../assets/images/sections/caching/caching_pipe_1.png) - -If we decide to stop the pipeline (say we kill the run altogether via `CTRL + C` or `CMD + C` in *macOS*), we will see the signal sent to the different workers: - -![Pipeline 2](../../../assets/images/sections/caching/caching_pipe_2.png) - -After this step, when we run again the pipeline, the first log message we see corresponds to "Load pipeline from cache", which will restart processing from where it stopped: - -![Pipeline 3](../../../assets/images/sections/caching/caching_pipe_3.png) +1. Pipeline cache is disabled -Finally, if we decide to run the same `Pipeline` after it has finished completely, it won't start again but resume the process, as we already have all the data processed: +In addition, the cache can be enabled/disabled at [`Step`][distilabel.steps.base.Step] level using its `use_cache` attribute. If `True`, then the outputs of the step will be reused in the new pipeline execution. If `False`, then the step will be re-executed to generate new outputs. If the cache of one step is disabled and the outputs have to be regenerated, then the outputs of the steps that depend on this step will also be regenerated. -![Pipeline 4](../../../assets/images/sections/caching/caching_pipe_4.png) - -### Serialization - -Let's see what gets serialized by looking at a sample `Pipeline`'s cached folder: - -```bash -$ tree ~/.cache/distilabel/pipelines/73ca3f6b7a613fb9694db7631cc038d379f1f533 -├── batch_manager.json -├── batch_manager_steps -│   ├── generate_response.json -│   └── rename_columns.json -├── data -│   └── generate_response -│   ├── 00001.parquet -│   └── 00002.parquet -└── pipeline.yaml +```python +with Pipeline(name="writting-assistant") as pipeline: + load_data = LoadDataFromDicts( + data=[ + { + "instruction": "How much is 2+2?" + } + ] + ) + + generation = TextGeneration( + llm=InferenceEndpointsLLM( + model_id="Qwen/Qwen2.5-72B-Instruct", + generation_kwargs={ + "temperature": 0.8, + "max_new_tokens": 512, + }, + ), + use_cache=False # (1) + ) + + load_data >> generation + +if __name__ == "__main__": + distiset = pipeline.run() ``` -The `Pipeline` will have a signature created from the arguments that define it so we can find it afterwards, and the contents are the following: - -- `batch_manager.json` - - Folder that stores the content of the internal batch manager to keep track of the data. Along with the `batch_manager_steps/` they store the information to restart the `Pipeline`. One shouldn't need to know about it. - -- `pipeline.yaml` - - This file contains a representation of the `Pipeline` in *YAML* format. If we push a `Distiset` to the Hugging Face Hub as obtained from calling `Pipeline.run`, this file will be stored at our datasets' repository, allowing to reproduce the `Pipeline` using the `CLI`: +1. Step cache is disabled and every time the pipeline is executed, this step will be re-executed - ```bash - distilabel pipeline run --config "path/to/pipeline.yaml" - ``` +## How a cache hit is triggered -- `data/` +`distilabel` groups information and data generated by a `Pipeline` using the name of the pipeline, so the first factor that triggers a cache hit is the name of the pipeline. The second factor, is the [`Pipeline.signature`][distilabel.pipeline.local.Pipeline.signature] property. This property returns a hash that is generated using the names of the steps used in the pipeline and their connections. The third factor, is the [`Pipeline.aggregated_steps_signature`][distilabel.pipeline.local.Pipeline.aggregated_steps_signature] property which is used to determine if the new pipeline execution is exactly the same as one of the previous i.e. the pipeline contains exactly the same steps, with exactly the same connections and the steps are using exactly the same parameters. If these three factors are met, then the cache hit is triggered and the pipeline won't get re-executed and instead the function [`create_distiset`][distilabel.distiset.create_distiset] will be used to create the resulting [`Distiset`][distilabel.distiset.Distiset] using the outputs of the previous execution, as it can be seen in the following image: - Folder that stores the data generated, with a special folder to keep track of each `leaf_step` separately. We can recreate a `Distiset` from the contents of this folder (*Parquet* files), as we will see next. - -- `pipeline.log` - - This file stores the logs that the `Pipeline` generated while processing. Just as with the `pipeline.yaml` file, it will be pushed to the Hugging Face Hub datasets` repository to keep track of the information. - -## create_distiset - -In case we wanted to regenerate the dataset from the `cache`, we can do it using the [`create_distiset`][distilabel.distiset.create_distiset] function and passing the path to the `/data` folder inside our `Pipeline`: - -```python -from pathlib import Path -from distilabel.distiset import create_distiset - -path = Path("~/.cache/distilabel/pipelines/73ca3f6b7a613fb9694db7631cc038d379f1f533/data") -ds = create_distiset(path) -ds -# Distiset({ -# generate_response: DatasetDict({ -# train: Dataset({ -# features: ['instruction', 'response'], -# num_rows: 80 -# }) -# }) -# }) -``` +![Complete cache hit](../../../assets/images/sections/caching/caching_1.png) -!!! Note +If the new pipeline execution have a different `Pipeline.aggregated_steps_signature` i.e. at least one step has changed its parameters, `distilabel` will reuse the outputs of the steps that have not changed and re-execute the steps that have changed, as it can be seen in the following image: - Internally, the function will try to inject the `pipeline_path` variable if it's not passed via argument, assuming it's in the parent directory of the current one, called `pipeline.yaml`. If the file doesn't exist, it won't raise any error, but take into account that if the `Distiset` is pushed to the Hugging Face Hub, the `pipeline.yaml` won't be generated. The same happens with the `pipeline.log` file, it can be passed via `log_filename_path`, but it will try to locate it automatically. +![Partial cache hit](../../../assets/images/sections/caching/caching_2.png) - Lastly, there is the option of including the `distilabel_metadata` column in the final dataset. This column can contain custom metadata generated automatically by the pipeline, like the raw output from an `LLM` without formatting in case of failure, and we can decide whether to include it using the `enable_metadata` argument. +The same pipeline from above gets executed a third time, but this time the last step `text_generation_1` changed, so it's needed to re-execute it. The other steps, as they have not been changed, doesn't need to be re-executed and their outputs are reused. diff --git a/docs/sections/pipeline_samples/papers/apigen.md b/docs/sections/pipeline_samples/papers/apigen.md index 8cb034c182..5d3522c1b7 100644 --- a/docs/sections/pipeline_samples/papers/apigen.md +++ b/docs/sections/pipeline_samples/papers/apigen.md @@ -59,7 +59,7 @@ data = [ ] ``` -The original paper refers to both python functions and APIs, but we will make use of python functions exclusively for simplicity. In order to execute and check this functions/APIs, we need access to the code, which we have moved to a python file: [lib_apigen.py](../../../../examples/lib_apigen.py). All this functions are executable, but we also need access to their *tool* representation. For this, we will make use of transformers' *get_json_schema* function[^1]. +The original paper refers to both python functions and APIs, but we will make use of python functions exclusively for simplicity. In order to execute and check this functions/APIs, we need access to the code, which we have moved to a Python file: [lib_apigen.py](https://github.com/argilla-io/distilabel/blob/main/examples/lib_apigen.py). All this functions are executable, but we also need access to their *tool* representation. For this, we will make use of transformers' *get_json_schema* function[^1]. [^1]: Read this nice blog post for more information on tools and the reasoning behind `get_json_schema`: [Tool Use, Unified](https://huggingface.co/blog/unified-tool-use). diff --git a/mkdocs.yml b/mkdocs.yml index c43332994d..69aaeed275 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -162,6 +162,7 @@ plugins: - social - mknotebooks - material-plausible + - glightbox - distilabel/components-gallery: add_after_page: How-to guides @@ -185,7 +186,7 @@ nav: - Execute Steps and Tasks in a Pipeline: "sections/how_to_guides/basic/pipeline/index.md" - Advanced: - The Distiset dataset object: "sections/how_to_guides/advanced/distiset.md" - - Cachinc and recovering pipelines: "sections/how_to_guides/advanced/caching.md" + - Pipeline cache: "sections/how_to_guides/advanced/caching.md" - Exporting data to Argilla: "sections/how_to_guides/advanced/argilla.md" - Structured data generation: "sections/how_to_guides/advanced/structured_generation.md" - Offline Batch Generation: "sections/how_to_guides/advanced/offline_batch_generation.md" diff --git a/pyproject.toml b/pyproject.toml index adab8d4fad..bf9550c9f0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -55,6 +55,7 @@ docs = [ "mkdocs-literate-nav >= 0.6.1", "mkdocs-section-index >= 0.3.8", "mkdocs-gen-files >= 0.5.0", + "mkdocs-glightbox >= 0.4.0", "material-plausible-plugin>=0.2.0", "mike >= 2.0.0", "Pillow >= 9.5.0", From 6ef15f449ee8fbf9cccc7f334d0a99b4a5f25178 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Mart=C3=ADn=20Bl=C3=A1zquez?= Date: Tue, 8 Oct 2024 16:16:09 +0200 Subject: [PATCH 82/82] Fix cross-reference --- docs/api/exceptions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api/exceptions.md b/docs/api/exceptions.md index 7f8cb05799..5826756a22 100644 --- a/docs/api/exceptions.md +++ b/docs/api/exceptions.md @@ -1,6 +1,6 @@ # Exceptions -This section contains the `distilabel` custom exceptions. Unlike [errors][../errors.md], exceptions in `distilabel` are used to handle specific situations that can be anticipated and that can be handled in a controlled way internally by the library. +This section contains the `distilabel` custom exceptions. Unlike [errors](errors.md), exceptions in `distilabel` are used to handle specific situations that can be anticipated and that can be handled in a controlled way internally by the library. :::distilabel.exceptions.DistilabelException :::distilabel.exceptions.DistilabelGenerationException

    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