diff --git a/pyproject.toml b/pyproject.toml index f79b279f..3c246a62 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,6 +48,9 @@ workflow_payload_offloading = [ workflow_payload_encryption = [ "cryptography>=41.0.0,<47.0.0", ] +workflow_payload_compression = [ + "zstandard>=0.25.0,<0.26", +] [project.urls] @@ -69,6 +72,7 @@ dev = [ "griffe>=1.7.3,<2", "authlib>=1.5.2,<2", "websockets >=13.0", + "zstandard>=0.25.0,<0.26", ] lint = [ "ruff>=0.11.10,<0.12", diff --git a/src/mistralai/extra/exceptions.py b/src/mistralai/extra/exceptions.py index a6b9ff11..a51d5dc3 100644 --- a/src/mistralai/extra/exceptions.py +++ b/src/mistralai/extra/exceptions.py @@ -28,6 +28,10 @@ class WorkflowPayloadEncryptionException(MistralClientException): """Workflow payload encryption exception""" +class WorkflowPayloadCompressionException(MistralClientException): + """Workflow payload compression exception""" + + class RunException(MistralClientException): """Conversation run errors.""" diff --git a/src/mistralai/extra/tests/test_workflow_encoding.py b/src/mistralai/extra/tests/test_workflow_encoding.py index 3703012b..15063adf 100644 --- a/src/mistralai/extra/tests/test_workflow_encoding.py +++ b/src/mistralai/extra/tests/test_workflow_encoding.py @@ -1,5 +1,6 @@ """Tests for workflow encoding configuration lifecycle.""" +import asyncio import gc import pytest @@ -29,7 +30,9 @@ def encryption_config() -> WorkflowEncodingConfig: ) -def test_payload_encoder_cleanup_on_client_gc(encryption_config: WorkflowEncodingConfig): +def test_payload_encoder_cleanup_on_client_gc( + encryption_config: WorkflowEncodingConfig, +): """Test that PayloadEncoder is cleaned up when client is garbage collected.""" initial_config_count = len(_workflow_configs) @@ -56,7 +59,9 @@ def test_payload_encoder_cleanup_on_client_gc(encryption_config: WorkflowEncodin assert len(_workflow_configs) == initial_config_count -def test_multiple_clients_independent_configs(encryption_config: WorkflowEncodingConfig): +def test_multiple_clients_independent_configs( + encryption_config: WorkflowEncodingConfig, +): """Test that multiple clients have independent configs.""" initial_config_count = len(_workflow_configs) @@ -132,3 +137,39 @@ def test_reconfigure_same_client(encryption_config: WorkflowEncodingConfig): del client gc.collect() assert config_id not in _workflow_configs + + +def test_payload_encoder_compresses_network_inputs(): + from mistralai.extra.workflows import ( + PayloadCompressionConfig, + ZstdCompressionConfig, + ) + from mistralai.extra.workflows.encoding.models import ( + EncodedPayloadOptions, + WorkflowContext, + ) + from mistralai.extra.workflows.encoding.payload_encoder import PayloadEncoder + + config = WorkflowEncodingConfig( + payload_compression=PayloadCompressionConfig( + min_size_bytes=1, algorithm_config=ZstdCompressionConfig(level=3) + ) + ) + encoder = PayloadEncoder(encoding_config=config) + payload = {"data": "x" * 20_000} + + encoded = asyncio.run( + encoder.encode_network_input( + payload, WorkflowContext(namespace="test", execution_id="exec") + ) + ) + + assert EncodedPayloadOptions.COMPRESSED in encoded.encoding_options + assert encoded.encoding_metadata == { + "compression_config": '{"algorithm":"zstd","level":3}' + } + + decoded = asyncio.run( + encoder.decode_network_result(encoded.model_dump(mode="json")) + ) + assert decoded == payload diff --git a/src/mistralai/extra/workflows/__init__.py b/src/mistralai/extra/workflows/__init__.py index f26edf4e..cc806268 100644 --- a/src/mistralai/extra/workflows/__init__.py +++ b/src/mistralai/extra/workflows/__init__.py @@ -9,13 +9,16 @@ WorkflowExtensions, ) from .encoding import ( - WorkflowEncodingConfig, - PayloadOffloadingConfig, + AlgorithmConfig, + BlobStorageConfig, + EncryptedStrField, + PayloadCompressionConfig, PayloadEncryptionConfig, PayloadEncryptionMode, - BlobStorageConfig, + PayloadOffloadingConfig, StorageProvider, - EncryptedStrField, + WorkflowEncodingConfig, + ZstdCompressionConfig, configure_workflow_encoding, generate_two_part_id, ) @@ -27,10 +30,13 @@ "ConnectorSlot", "WorkflowExtensions", "execute_with_connector_auth_async", + "AlgorithmConfig", "WorkflowEncodingConfig", "PayloadOffloadingConfig", "PayloadEncryptionConfig", "PayloadEncryptionMode", + "PayloadCompressionConfig", + "ZstdCompressionConfig", "BlobStorageConfig", "StorageProvider", "EncryptedStrField", diff --git a/src/mistralai/extra/workflows/encoding/__init__.py b/src/mistralai/extra/workflows/encoding/__init__.py index c1881636..263b629b 100644 --- a/src/mistralai/extra/workflows/encoding/__init__.py +++ b/src/mistralai/extra/workflows/encoding/__init__.py @@ -1,20 +1,26 @@ from .config import ( - WorkflowEncodingConfig, - PayloadOffloadingConfig, + AlgorithmConfig, + BlobStorageConfig, + PayloadCompressionConfig, PayloadEncryptionConfig, PayloadEncryptionMode, - BlobStorageConfig, + PayloadOffloadingConfig, StorageProvider, + WorkflowEncodingConfig, + ZstdCompressionConfig, ) from .models import EncryptedStrField from .payload_encoder import PayloadEncoder from .helpers import configure_workflow_encoding, generate_two_part_id __all__ = [ + "AlgorithmConfig", "WorkflowEncodingConfig", "PayloadOffloadingConfig", "PayloadEncryptionConfig", "PayloadEncryptionMode", + "PayloadCompressionConfig", + "ZstdCompressionConfig", "BlobStorageConfig", "StorageProvider", "EncryptedStrField", diff --git a/src/mistralai/extra/workflows/encoding/config.py b/src/mistralai/extra/workflows/encoding/config.py index ff849fd7..a5c3116d 100644 --- a/src/mistralai/extra/workflows/encoding/config.py +++ b/src/mistralai/extra/workflows/encoding/config.py @@ -1,6 +1,7 @@ from enum import Enum -from pydantic import SecretStr, BaseModel -from typing import Optional +from typing import Annotated, Literal, Optional, Union + +from pydantic import BaseModel, Field, SecretStr class StorageProvider(str, Enum): @@ -47,6 +48,23 @@ class PayloadEncryptionConfig(BaseModel): secondary_key: Optional[SecretStr] = None +class ZstdCompressionConfig(BaseModel): + algorithm: Literal["zstd"] = "zstd" + level: int = Field(default=3, ge=1, le=22) + + +AlgorithmConfig = Annotated[ + Union[ZstdCompressionConfig], Field(discriminator="algorithm") +] + + +class PayloadCompressionConfig(BaseModel): + enabled: bool = True + min_size_bytes: int = 1024 * 1024 # 1MB + algorithm_config: AlgorithmConfig = Field(default_factory=ZstdCompressionConfig) + + class WorkflowEncodingConfig(BaseModel): payload_offloading: PayloadOffloadingConfig | None = None payload_encryption: PayloadEncryptionConfig | None = None + payload_compression: PayloadCompressionConfig | None = None diff --git a/src/mistralai/extra/workflows/encoding/models.py b/src/mistralai/extra/workflows/encoding/models.py index 496be8b1..190eb5dd 100644 --- a/src/mistralai/extra/workflows/encoding/models.py +++ b/src/mistralai/extra/workflows/encoding/models.py @@ -9,6 +9,7 @@ class EncodedPayloadOptions(str, Enum): OFFLOADED = "offloaded" ENCRYPTED = "encrypted" PARTIALLY_ENCRYPTED = "encrypted-partial" + COMPRESSED = "compressed" class EncryptableFieldTypes(str, Enum): @@ -35,6 +36,10 @@ class EncodedPayload(BaseModel): encoding_options: list[EncodedPayloadOptions] = Field( description="The encoding of the payload", default=[] ) + encoding_metadata: dict[str, str] = Field( + description="Additional metadata required to decode the payload", + default_factory=dict, + ) payload: bytes = Field(description="The encoded payload") @@ -43,6 +48,10 @@ class NetworkEncodedBase(BaseModel): encoding_options: list[EncodedPayloadOptions] = Field( description="The encoding of the payload", default=[] ) + encoding_metadata: dict[str, str] = Field( + description="Additional metadata required to decode the payload", + default_factory=dict, + ) def get_payload(self) -> bytes: return base64.b64decode(self.b64payload) @@ -52,11 +61,12 @@ class NetworkEncodedInput(NetworkEncodedBase): empty: bool = Field(description="Whether the payload is empty", default=False) def to_encoded_payload( - self, namespace: str, execution_id: str, execution_token: str | None = None + self, namespace: str, execution_id: str, execution_token: Optional[str] ) -> EncodedPayload: return EncodedPayload( payload=base64.b64decode(self.b64payload), encoding_options=self.encoding_options, + encoding_metadata=self.encoding_metadata, context=WorkflowContext( namespace=namespace, execution_id=execution_id, @@ -69,15 +79,19 @@ def from_encoded_payload(encoded_payload: EncodedPayload) -> "NetworkEncodedInpu return NetworkEncodedInput( b64payload=base64.b64encode(encoded_payload.payload).decode("utf-8"), encoding_options=encoded_payload.encoding_options, + encoding_metadata=encoded_payload.encoding_metadata, ) @staticmethod def from_data( - data: bytes, encoding_options: list[EncodedPayloadOptions] + data: bytes, + encoding_options: list[EncodedPayloadOptions], + encoding_metadata: dict[str, str] | None = None, ) -> "NetworkEncodedInput": return NetworkEncodedInput( b64payload=base64.b64encode(data).decode("utf-8"), encoding_options=encoding_options, + encoding_metadata=encoding_metadata or {}, ) @@ -87,4 +101,5 @@ def from_encoded_payload(encoded_payload: EncodedPayload) -> "NetworkEncodedResu return NetworkEncodedResult( b64payload=base64.b64encode(encoded_payload.payload).decode("utf-8"), encoding_options=encoded_payload.encoding_options, + encoding_metadata=encoded_payload.encoding_metadata, ) diff --git a/src/mistralai/extra/workflows/encoding/payload_compressor.py b/src/mistralai/extra/workflows/encoding/payload_compressor.py new file mode 100644 index 00000000..4267e41e --- /dev/null +++ b/src/mistralai/extra/workflows/encoding/payload_compressor.py @@ -0,0 +1,100 @@ +from __future__ import annotations + +from abc import ABC, abstractmethod +from functools import lru_cache +from importlib import import_module +from types import ModuleType + +from pydantic import TypeAdapter, ValidationError + +from mistralai.extra.exceptions import WorkflowPayloadCompressionException +from mistralai.extra.workflows.encoding.config import ( + AlgorithmConfig, + PayloadCompressionConfig, + ZstdCompressionConfig, +) + +_ALGORITHM_CONFIG_ADAPTER: TypeAdapter[AlgorithmConfig] = TypeAdapter(AlgorithmConfig) +COMPRESSION_CONFIG_METADATA_KEY = "compression_config" + + +class Compressor(ABC): + @property + @abstractmethod + def tag(self) -> str: + """Wire tag stored in encoding options to identify the algorithm.""" + + @abstractmethod + def serialize_config(self) -> str: + """Pydantic-serialised algorithm config, stored with the payload for config-independent decoding.""" + + @abstractmethod + def compress(self, data: bytes) -> bytes: ... + + @abstractmethod + def decompress(self, data: bytes) -> bytes: ... + + +def _require_zstandard() -> ModuleType: + try: + return import_module("zstandard") + except ImportError: + raise WorkflowPayloadCompressionException( + "Payload compression requires installing mistralai[workflow-payload-compression]" + ) from None + + +class ZstdCompressor(Compressor): + tag = "zstd" + + def __init__(self, cfg: ZstdCompressionConfig) -> None: + zstd = _require_zstandard() + self._config = cfg + self._compressor = zstd.ZstdCompressor(level=cfg.level) + self._decompressor = zstd.ZstdDecompressor() + + def serialize_config(self) -> str: + return self._config.model_dump_json() + + def compress(self, data: bytes) -> bytes: + result: bytes = self._compressor.compress(data) + return result + + def decompress(self, data: bytes) -> bytes: + result: bytes = self._decompressor.decompress(data) + return result + + +@lru_cache(maxsize=8) +def _build_compressor_for_config(config_json: str) -> Compressor: + try: + algo_config = _ALGORITHM_CONFIG_ADAPTER.validate_json(config_json) + except ValidationError as exc: + raise WorkflowPayloadCompressionException( + f"Invalid compression config in payload metadata: {exc}" + ) from exc + + if isinstance(algo_config, ZstdCompressionConfig): + return ZstdCompressor(algo_config) + raise WorkflowPayloadCompressionException( + f"Unsupported compression algorithm: {algo_config.algorithm!r}" + ) + + +def decompress_by_metadata(data: bytes, encoding_metadata: dict[str, str]) -> bytes: + config_json = encoding_metadata.get(COMPRESSION_CONFIG_METADATA_KEY) + if config_json is None: + raise WorkflowPayloadCompressionException( + "Payload is marked as compressed but has no compression config in metadata" + ) + return _build_compressor_for_config(config_json).decompress(data) + + +def build_compressor( + compression_config: PayloadCompressionConfig | None, +) -> Compressor | None: + if compression_config is None or not compression_config.enabled: + return None + return _build_compressor_for_config( + compression_config.algorithm_config.model_dump_json() + ) diff --git a/src/mistralai/extra/workflows/encoding/payload_encoder.py b/src/mistralai/extra/workflows/encoding/payload_encoder.py index 611f33fa..b6f9673f 100644 --- a/src/mistralai/extra/workflows/encoding/payload_encoder.py +++ b/src/mistralai/extra/workflows/encoding/payload_encoder.py @@ -7,7 +7,7 @@ import logging import os import urllib.parse -from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union +from typing import TYPE_CHECKING, Any, Dict, List, Literal, Optional, Union, overload from pydantic import BaseModel @@ -30,6 +30,11 @@ PayloadOffloadingConfig, WorkflowEncodingConfig, ) +from mistralai.extra.workflows.encoding.payload_compressor import ( + COMPRESSION_CONFIG_METADATA_KEY, + build_compressor, + decompress_by_metadata, +) from .storage.blob_storage import get_blob_storage, BlobNotFoundError from mistralai.extra.workflows.encoding.models import ( EncodedPayloadOptions, @@ -78,6 +83,8 @@ def __init__( "Blob storage config is not set for workflow payload encoding" ) + self.compression_config = encoding_config.payload_compression + self.compressor = build_compressor(self.compression_config) self.encryption_config = encoding_config.payload_encryption if self.encryption_config is not None: if not _HAS_CRYPTO: @@ -151,7 +158,10 @@ def _decrypt(self, data: bytes) -> bytes: async def _handle_offloading( self, data: bytes, context: Optional[WorkflowContext] ) -> tuple[bytes, bool]: - if self.offloading_config is None or self.offloading_config.storage_config is None: + if ( + self.offloading_config is None + or self.offloading_config.storage_config is None + ): raise WorkflowPayloadOffloadingException( "You must configure payload offloading storage" ) @@ -228,18 +238,80 @@ async def _partially_decrypt_fields(self, data: bytes) -> tuple[bytes, bool]: return json.dumps(obj).encode(), len(encrypted_fields) > 0 + def _compress(self, data: bytes) -> tuple[bytes, bool]: + if ( + self.compression_config is None + or not self.compression_config.enabled + or len(data) < self.compression_config.min_size_bytes + ): + return data, False + assert self.compressor is not None, ( + "Compression enabled in config but no compressor was built" + ) + compressed = self.compressor.compress(data) + if len(compressed) >= len(data): + return data, False + return compressed, True + + def _decompress(self, data: bytes, encoding_metadata: dict[str, str]) -> bytes: + return decompress_by_metadata(data, encoding_metadata) + + def _compression_metadata(self) -> dict[str, str]: + assert self.compressor is not None, ( + "Compression enabled in config but no compressor was built" + ) + return {COMPRESSION_CONFIG_METADATA_KEY: self.compressor.serialize_config()} + + @overload + async def encode_payload_content( + self, + data: Union[bytes, str], + context: Optional[WorkflowContext], + include_encoding_metadata: Literal[False] = False, + ) -> tuple[bytes, list[EncodedPayloadOptions]]: ... + + @overload async def encode_payload_content( - self, data: Union[bytes, str], context: Optional[WorkflowContext] - ) -> tuple[bytes, list[EncodedPayloadOptions]]: + self, + data: Union[bytes, str], + context: Optional[WorkflowContext], + include_encoding_metadata: Literal[True], + ) -> tuple[bytes, list[EncodedPayloadOptions], dict[str, str]]: ... + + async def encode_payload_content( + self, + data: Union[bytes, str], + context: Optional[WorkflowContext], + include_encoding_metadata: bool = False, + ) -> ( + tuple[bytes, list[EncodedPayloadOptions]] + | tuple[bytes, list[EncodedPayloadOptions], dict[str, str]] + ): """Handle payload encoding: - Payload offloading (if context provided) - Encryption + - Compression """ if isinstance(data, str): data = data.encode() encoding_options = [] + encoding_metadata: dict[str, str] = {} + + if ( + self.encryption_config is not None + and self.encryption_config.mode == PayloadEncryptionMode.PARTIAL + ): + data, partially_encrypted = await self._partially_encrypt_fields(data) + if partially_encrypted: + encoding_options.append(EncodedPayloadOptions.PARTIALLY_ENCRYPTED) + + data, compressed = self._compress(data) + if compressed: + encoding_options.append(EncodedPayloadOptions.COMPRESSED) + encoding_metadata.update(self._compression_metadata()) + if self.offloading_config is not None: data, offloaded = await self._handle_offloading(data, context) if offloaded: @@ -251,21 +323,36 @@ async def encode_payload_content( ): data = self._encrypt(data) encoding_options.append(EncodedPayloadOptions.ENCRYPTED) - elif ( - self.encryption_config is not None - and self.encryption_config.mode == PayloadEncryptionMode.PARTIAL - and EncodedPayloadOptions.OFFLOADED not in encoding_options - ): - # Do not partially encrypt offloaded payload (fields not in the payload anymore) - data, partially_encrypted = await self._partially_encrypt_fields(data) - if partially_encrypted: - encoding_options.append(EncodedPayloadOptions.PARTIALLY_ENCRYPTED) + if include_encoding_metadata: + return data, encoding_options, encoding_metadata return data, encoding_options + @overload + async def encode_event_payload_content( + self, + data: Union[bytes, str], + force_full_encryption: bool = False, + include_encoding_metadata: Literal[False] = False, + ) -> tuple[bytes, list[EncodedPayloadOptions]]: ... + + @overload async def encode_event_payload_content( - self, data: Union[bytes, str], force_full_encryption: bool = False - ) -> tuple[bytes, list[EncodedPayloadOptions]]: + self, + data: Union[bytes, str], + force_full_encryption: bool = False, + include_encoding_metadata: Literal[True] = True, + ) -> tuple[bytes, list[EncodedPayloadOptions], dict[str, str]]: ... + + async def encode_event_payload_content( + self, + data: Union[bytes, str], + force_full_encryption: bool = False, + include_encoding_metadata: bool = False, + ) -> ( + tuple[bytes, list[EncodedPayloadOptions]] + | tuple[bytes, list[EncodedPayloadOptions], dict[str, str]] + ): """Encrypt event payload content. Unlike encode_payload_content, this only handles encryption (no offloading). @@ -278,29 +365,49 @@ async def encode_event_payload_content( if isinstance(data, str): data = data.encode() - if self.encryption_config is None: - return data, [] + encoding_options = [] + encoding_metadata: dict[str, str] = {} + + if ( + not force_full_encryption + and self.encryption_config is not None + and self.encryption_config.mode == PayloadEncryptionMode.PARTIAL + ): + data, partially_encrypted = await self._partially_encrypt_fields(data) + if partially_encrypted: + encoding_options.append(EncodedPayloadOptions.PARTIALLY_ENCRYPTED) - if force_full_encryption or self.encryption_config.mode == PayloadEncryptionMode.FULL: - encrypted_data = self._encrypt(data) - return encrypted_data, [EncodedPayloadOptions.ENCRYPTED] + data, compressed = self._compress(data) + if compressed: + encoding_options.append(EncodedPayloadOptions.COMPRESSED) + encoding_metadata.update(self._compression_metadata()) - # Partial encryption mode - data, partially_encrypted = await self._partially_encrypt_fields(data) - if partially_encrypted: - return data, [EncodedPayloadOptions.PARTIALLY_ENCRYPTED] + if self.encryption_config is not None and ( + force_full_encryption + or self.encryption_config.mode == PayloadEncryptionMode.FULL + ): + data = self._encrypt(data) + encoding_options.append(EncodedPayloadOptions.ENCRYPTED) - return data, [] + if include_encoding_metadata: + return data, encoding_options, encoding_metadata + return data, encoding_options async def decode_payload_content( - self, data: bytes, encoding_options: List[EncodedPayloadOptions] + self, + data: bytes, + encoding_options: List[EncodedPayloadOptions], + encoding_metadata: dict[str, str] | None = None, ) -> bytes: # Decode in the reverse order of encoding + encoding_metadata = encoding_metadata or {} for option in reversed(encoding_options): if option == EncodedPayloadOptions.ENCRYPTED: data = self._decrypt(data) elif option == EncodedPayloadOptions.PARTIALLY_ENCRYPTED: data, _ = await self._partially_decrypt_fields(data) + elif option == EncodedPayloadOptions.COMPRESSED: + data = self._decompress(data, encoding_metadata) elif option == EncodedPayloadOptions.OFFLOADED: if ( self.offloading_config is None @@ -340,7 +447,10 @@ async def decode_event_payload( encoding_options = [EncodedPayloadOptions(opt) for opt in encoding_options_strs] encrypted_bytes = base64.b64decode(payload_data["value"]) - decrypted_bytes = await self.decode_payload_content(encrypted_bytes, encoding_options) + encoding_metadata = payload_data.get("encoding_metadata", {}) + decrypted_bytes = await self.decode_payload_content( + encrypted_bytes, encoding_options, encoding_metadata + ) decrypted_value = json.loads(decrypted_bytes) return { @@ -355,10 +465,16 @@ async def encode_network_input( """This method MUST be called to format every payload send to Mistral Workflows control plane to ensure a proper encoding of the payload. """ - encoded_data, encoding_options = await self.encode_payload_content( - to_json(data), context + ( + encoded_data, + encoding_options, + encoding_metadata, + ) = await self.encode_payload_content( + to_json(data), context, include_encoding_metadata=True + ) + network_input = NetworkEncodedInput.from_data( + encoded_data, encoding_options, encoding_metadata ) - network_input = NetworkEncodedInput.from_data(encoded_data, encoding_options) return network_input async def decode_network_result(self, data: Any) -> Any: @@ -374,6 +490,7 @@ async def decode_network_result(self, data: Any) -> Any: byte_results = await self.decode_payload_content( network_encoded_payload.get_payload(), network_encoded_payload.encoding_options, + network_encoded_payload.encoding_metadata, ) try: return from_json(byte_results) diff --git a/uv.lock b/uv.lock index 2f23fc8b..9974adc7 100644 --- a/uv.lock +++ b/uv.lock @@ -1041,6 +1041,9 @@ gcp = [ realtime = [ { name = "websockets" }, ] +workflow-payload-compression = [ + { name = "zstandard" }, +] workflow-payload-encryption = [ { name = "cryptography" }, ] @@ -1075,6 +1078,7 @@ dev = [ { name = "types-python-dateutil" }, { name = "types-pyyaml" }, { name = "websockets" }, + { name = "zstandard" }, ] lint = [ { name = "mypy" }, @@ -1105,8 +1109,9 @@ requires-dist = [ { name = "requests", marker = "extra == 'gcp'", specifier = ">=2.32.3" }, { name = "typing-inspection", specifier = ">=0.4.0" }, { name = "websockets", marker = "extra == 'realtime'", specifier = ">=13.0" }, + { name = "zstandard", marker = "extra == 'workflow-payload-compression'", specifier = ">=0.25.0,<0.26" }, ] -provides-extras = ["gcp", "agents", "realtime", "workflow-payload-offloading-azure", "workflow-payload-offloading-gcs", "workflow-payload-offloading-s3", "workflow-payload-offloading", "workflow-payload-encryption"] +provides-extras = ["gcp", "agents", "realtime", "workflow-payload-offloading-azure", "workflow-payload-offloading-gcs", "workflow-payload-offloading-s3", "workflow-payload-offloading", "workflow-payload-encryption", "workflow-payload-compression"] [package.metadata.requires-dev] dev = [ @@ -1124,6 +1129,7 @@ dev = [ { name = "types-python-dateutil", specifier = ">=2.9.0.20240316,<3" }, { name = "types-pyyaml", specifier = ">=6.0.12.20250516,<7" }, { name = "websockets", specifier = ">=13.0" }, + { name = "zstandard", specifier = ">=0.25.0,<0.26" }, ] lint = [ { name = "mypy", specifier = "==1.15.0" }, @@ -2536,3 +2542,93 @@ sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50e wheels = [ { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" }, ] + +[[package]] +name = "zstandard" +version = "0.25.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fd/aa/3e0508d5a5dd96529cdc5a97011299056e14c6505b678fd58938792794b1/zstandard-0.25.0.tar.gz", hash = "sha256:7713e1179d162cf5c7906da876ec2ccb9c3a9dcbdffef0cc7f70c3667a205f0b", size = 711513, upload-time = "2025-09-14T22:15:54.002Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/56/7a/28efd1d371f1acd037ac64ed1c5e2b41514a6cc937dd6ab6a13ab9f0702f/zstandard-0.25.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e59fdc271772f6686e01e1b3b74537259800f57e24280be3f29c8a0deb1904dd", size = 795256, upload-time = "2025-09-14T22:15:56.415Z" }, + { url = "https://files.pythonhosted.org/packages/96/34/ef34ef77f1ee38fc8e4f9775217a613b452916e633c4f1d98f31db52c4a5/zstandard-0.25.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4d441506e9b372386a5271c64125f72d5df6d2a8e8a2a45a0ae09b03cb781ef7", size = 640565, upload-time = "2025-09-14T22:15:58.177Z" }, + { url = "https://files.pythonhosted.org/packages/9d/1b/4fdb2c12eb58f31f28c4d28e8dc36611dd7205df8452e63f52fb6261d13e/zstandard-0.25.0-cp310-cp310-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:ab85470ab54c2cb96e176f40342d9ed41e58ca5733be6a893b730e7af9c40550", size = 5345306, upload-time = "2025-09-14T22:16:00.165Z" }, + { url = "https://files.pythonhosted.org/packages/73/28/a44bdece01bca027b079f0e00be3b6bd89a4df180071da59a3dd7381665b/zstandard-0.25.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e05ab82ea7753354bb054b92e2f288afb750e6b439ff6ca78af52939ebbc476d", size = 5055561, upload-time = "2025-09-14T22:16:02.22Z" }, + { url = "https://files.pythonhosted.org/packages/e9/74/68341185a4f32b274e0fc3410d5ad0750497e1acc20bd0f5b5f64ce17785/zstandard-0.25.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:78228d8a6a1c177a96b94f7e2e8d012c55f9c760761980da16ae7546a15a8e9b", size = 5402214, upload-time = "2025-09-14T22:16:04.109Z" }, + { url = "https://files.pythonhosted.org/packages/8b/67/f92e64e748fd6aaffe01e2b75a083c0c4fd27abe1c8747fee4555fcee7dd/zstandard-0.25.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:2b6bd67528ee8b5c5f10255735abc21aa106931f0dbaf297c7be0c886353c3d0", size = 5449703, upload-time = "2025-09-14T22:16:06.312Z" }, + { url = "https://files.pythonhosted.org/packages/fd/e5/6d36f92a197c3c17729a2125e29c169f460538a7d939a27eaaa6dcfcba8e/zstandard-0.25.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4b6d83057e713ff235a12e73916b6d356e3084fd3d14ced499d84240f3eecee0", size = 5556583, upload-time = "2025-09-14T22:16:08.457Z" }, + { url = "https://files.pythonhosted.org/packages/d7/83/41939e60d8d7ebfe2b747be022d0806953799140a702b90ffe214d557638/zstandard-0.25.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9174f4ed06f790a6869b41cba05b43eeb9a35f8993c4422ab853b705e8112bbd", size = 5045332, upload-time = "2025-09-14T22:16:10.444Z" }, + { url = "https://files.pythonhosted.org/packages/b3/87/d3ee185e3d1aa0133399893697ae91f221fda79deb61adbe998a7235c43f/zstandard-0.25.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:25f8f3cd45087d089aef5ba3848cd9efe3ad41163d3400862fb42f81a3a46701", size = 5572283, upload-time = "2025-09-14T22:16:12.128Z" }, + { url = "https://files.pythonhosted.org/packages/0a/1d/58635ae6104df96671076ac7d4ae7816838ce7debd94aecf83e30b7121b0/zstandard-0.25.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3756b3e9da9b83da1796f8809dd57cb024f838b9eeafde28f3cb472012797ac1", size = 4959754, upload-time = "2025-09-14T22:16:14.225Z" }, + { url = "https://files.pythonhosted.org/packages/75/d6/57e9cb0a9983e9a229dd8fd2e6e96593ef2aa82a3907188436f22b111ccd/zstandard-0.25.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:81dad8d145d8fd981b2962b686b2241d3a1ea07733e76a2f15435dfb7fb60150", size = 5266477, upload-time = "2025-09-14T22:16:16.343Z" }, + { url = "https://files.pythonhosted.org/packages/d1/a9/ee891e5edf33a6ebce0a028726f0bbd8567effe20fe3d5808c42323e8542/zstandard-0.25.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:a5a419712cf88862a45a23def0ae063686db3d324cec7edbe40509d1a79a0aab", size = 5440914, upload-time = "2025-09-14T22:16:18.453Z" }, + { url = "https://files.pythonhosted.org/packages/58/08/a8522c28c08031a9521f27abc6f78dbdee7312a7463dd2cfc658b813323b/zstandard-0.25.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e7360eae90809efd19b886e59a09dad07da4ca9ba096752e61a2e03c8aca188e", size = 5819847, upload-time = "2025-09-14T22:16:20.559Z" }, + { url = "https://files.pythonhosted.org/packages/6f/11/4c91411805c3f7b6f31c60e78ce347ca48f6f16d552fc659af6ec3b73202/zstandard-0.25.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:75ffc32a569fb049499e63ce68c743155477610532da1eb38e7f24bf7cd29e74", size = 5363131, upload-time = "2025-09-14T22:16:22.206Z" }, + { url = "https://files.pythonhosted.org/packages/ef/d6/8c4bd38a3b24c4c7676a7a3d8de85d6ee7a983602a734b9f9cdefb04a5d6/zstandard-0.25.0-cp310-cp310-win32.whl", hash = "sha256:106281ae350e494f4ac8a80470e66d1fe27e497052c8d9c3b95dc4cf1ade81aa", size = 436469, upload-time = "2025-09-14T22:16:25.002Z" }, + { url = "https://files.pythonhosted.org/packages/93/90/96d50ad417a8ace5f841b3228e93d1bb13e6ad356737f42e2dde30d8bd68/zstandard-0.25.0-cp310-cp310-win_amd64.whl", hash = "sha256:ea9d54cc3d8064260114a0bbf3479fc4a98b21dffc89b3459edd506b69262f6e", size = 506100, upload-time = "2025-09-14T22:16:23.569Z" }, + { url = "https://files.pythonhosted.org/packages/2a/83/c3ca27c363d104980f1c9cee1101cc8ba724ac8c28a033ede6aab89585b1/zstandard-0.25.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:933b65d7680ea337180733cf9e87293cc5500cc0eb3fc8769f4d3c88d724ec5c", size = 795254, upload-time = "2025-09-14T22:16:26.137Z" }, + { url = "https://files.pythonhosted.org/packages/ac/4d/e66465c5411a7cf4866aeadc7d108081d8ceba9bc7abe6b14aa21c671ec3/zstandard-0.25.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a3f79487c687b1fc69f19e487cd949bf3aae653d181dfb5fde3bf6d18894706f", size = 640559, upload-time = "2025-09-14T22:16:27.973Z" }, + { url = "https://files.pythonhosted.org/packages/12/56/354fe655905f290d3b147b33fe946b0f27e791e4b50a5f004c802cb3eb7b/zstandard-0.25.0-cp311-cp311-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:0bbc9a0c65ce0eea3c34a691e3c4b6889f5f3909ba4822ab385fab9057099431", size = 5348020, upload-time = "2025-09-14T22:16:29.523Z" }, + { url = "https://files.pythonhosted.org/packages/3b/13/2b7ed68bd85e69a2069bcc72141d378f22cae5a0f3b353a2c8f50ef30c1b/zstandard-0.25.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:01582723b3ccd6939ab7b3a78622c573799d5d8737b534b86d0e06ac18dbde4a", size = 5058126, upload-time = "2025-09-14T22:16:31.811Z" }, + { url = "https://files.pythonhosted.org/packages/c9/dd/fdaf0674f4b10d92cb120ccff58bbb6626bf8368f00ebfd2a41ba4a0dc99/zstandard-0.25.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5f1ad7bf88535edcf30038f6919abe087f606f62c00a87d7e33e7fc57cb69fcc", size = 5405390, upload-time = "2025-09-14T22:16:33.486Z" }, + { url = "https://files.pythonhosted.org/packages/0f/67/354d1555575bc2490435f90d67ca4dd65238ff2f119f30f72d5cde09c2ad/zstandard-0.25.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:06acb75eebeedb77b69048031282737717a63e71e4ae3f77cc0c3b9508320df6", size = 5452914, upload-time = "2025-09-14T22:16:35.277Z" }, + { url = "https://files.pythonhosted.org/packages/bb/1f/e9cfd801a3f9190bf3e759c422bbfd2247db9d7f3d54a56ecde70137791a/zstandard-0.25.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9300d02ea7c6506f00e627e287e0492a5eb0371ec1670ae852fefffa6164b072", size = 5559635, upload-time = "2025-09-14T22:16:37.141Z" }, + { url = "https://files.pythonhosted.org/packages/21/88/5ba550f797ca953a52d708c8e4f380959e7e3280af029e38fbf47b55916e/zstandard-0.25.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bfd06b1c5584b657a2892a6014c2f4c20e0db0208c159148fa78c65f7e0b0277", size = 5048277, upload-time = "2025-09-14T22:16:38.807Z" }, + { url = "https://files.pythonhosted.org/packages/46/c0/ca3e533b4fa03112facbe7fbe7779cb1ebec215688e5df576fe5429172e0/zstandard-0.25.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f373da2c1757bb7f1acaf09369cdc1d51d84131e50d5fa9863982fd626466313", size = 5574377, upload-time = "2025-09-14T22:16:40.523Z" }, + { url = "https://files.pythonhosted.org/packages/12/9b/3fb626390113f272abd0799fd677ea33d5fc3ec185e62e6be534493c4b60/zstandard-0.25.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6c0e5a65158a7946e7a7affa6418878ef97ab66636f13353b8502d7ea03c8097", size = 4961493, upload-time = "2025-09-14T22:16:43.3Z" }, + { url = "https://files.pythonhosted.org/packages/cb/d3/23094a6b6a4b1343b27ae68249daa17ae0651fcfec9ed4de09d14b940285/zstandard-0.25.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:c8e167d5adf59476fa3e37bee730890e389410c354771a62e3c076c86f9f7778", size = 5269018, upload-time = "2025-09-14T22:16:45.292Z" }, + { url = "https://files.pythonhosted.org/packages/8c/a7/bb5a0c1c0f3f4b5e9d5b55198e39de91e04ba7c205cc46fcb0f95f0383c1/zstandard-0.25.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:98750a309eb2f020da61e727de7d7ba3c57c97cf6213f6f6277bb7fb42a8e065", size = 5443672, upload-time = "2025-09-14T22:16:47.076Z" }, + { url = "https://files.pythonhosted.org/packages/27/22/503347aa08d073993f25109c36c8d9f029c7d5949198050962cb568dfa5e/zstandard-0.25.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:22a086cff1b6ceca18a8dd6096ec631e430e93a8e70a9ca5efa7561a00f826fa", size = 5822753, upload-time = "2025-09-14T22:16:49.316Z" }, + { url = "https://files.pythonhosted.org/packages/e2/be/94267dc6ee64f0f8ba2b2ae7c7a2df934a816baaa7291db9e1aa77394c3c/zstandard-0.25.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:72d35d7aa0bba323965da807a462b0966c91608ef3a48ba761678cb20ce5d8b7", size = 5366047, upload-time = "2025-09-14T22:16:51.328Z" }, + { url = "https://files.pythonhosted.org/packages/7b/a3/732893eab0a3a7aecff8b99052fecf9f605cf0fb5fb6d0290e36beee47a4/zstandard-0.25.0-cp311-cp311-win32.whl", hash = "sha256:f5aeea11ded7320a84dcdd62a3d95b5186834224a9e55b92ccae35d21a8b63d4", size = 436484, upload-time = "2025-09-14T22:16:55.005Z" }, + { url = "https://files.pythonhosted.org/packages/43/a3/c6155f5c1cce691cb80dfd38627046e50af3ee9ddc5d0b45b9b063bfb8c9/zstandard-0.25.0-cp311-cp311-win_amd64.whl", hash = "sha256:daab68faadb847063d0c56f361a289c4f268706b598afbf9ad113cbe5c38b6b2", size = 506183, upload-time = "2025-09-14T22:16:52.753Z" }, + { url = "https://files.pythonhosted.org/packages/8c/3e/8945ab86a0820cc0e0cdbf38086a92868a9172020fdab8a03ac19662b0e5/zstandard-0.25.0-cp311-cp311-win_arm64.whl", hash = "sha256:22a06c5df3751bb7dc67406f5374734ccee8ed37fc5981bf1ad7041831fa1137", size = 462533, upload-time = "2025-09-14T22:16:53.878Z" }, + { url = "https://files.pythonhosted.org/packages/82/fc/f26eb6ef91ae723a03e16eddb198abcfce2bc5a42e224d44cc8b6765e57e/zstandard-0.25.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7b3c3a3ab9daa3eed242d6ecceead93aebbb8f5f84318d82cee643e019c4b73b", size = 795738, upload-time = "2025-09-14T22:16:56.237Z" }, + { url = "https://files.pythonhosted.org/packages/aa/1c/d920d64b22f8dd028a8b90e2d756e431a5d86194caa78e3819c7bf53b4b3/zstandard-0.25.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:913cbd31a400febff93b564a23e17c3ed2d56c064006f54efec210d586171c00", size = 640436, upload-time = "2025-09-14T22:16:57.774Z" }, + { url = "https://files.pythonhosted.org/packages/53/6c/288c3f0bd9fcfe9ca41e2c2fbfd17b2097f6af57b62a81161941f09afa76/zstandard-0.25.0-cp312-cp312-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:011d388c76b11a0c165374ce660ce2c8efa8e5d87f34996aa80f9c0816698b64", size = 5343019, upload-time = "2025-09-14T22:16:59.302Z" }, + { url = "https://files.pythonhosted.org/packages/1e/15/efef5a2f204a64bdb5571e6161d49f7ef0fffdbca953a615efbec045f60f/zstandard-0.25.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6dffecc361d079bb48d7caef5d673c88c8988d3d33fb74ab95b7ee6da42652ea", size = 5063012, upload-time = "2025-09-14T22:17:01.156Z" }, + { url = "https://files.pythonhosted.org/packages/b7/37/a6ce629ffdb43959e92e87ebdaeebb5ac81c944b6a75c9c47e300f85abdf/zstandard-0.25.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:7149623bba7fdf7e7f24312953bcf73cae103db8cae49f8154dd1eadc8a29ecb", size = 5394148, upload-time = "2025-09-14T22:17:03.091Z" }, + { url = "https://files.pythonhosted.org/packages/e3/79/2bf870b3abeb5c070fe2d670a5a8d1057a8270f125ef7676d29ea900f496/zstandard-0.25.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:6a573a35693e03cf1d67799fd01b50ff578515a8aeadd4595d2a7fa9f3ec002a", size = 5451652, upload-time = "2025-09-14T22:17:04.979Z" }, + { url = "https://files.pythonhosted.org/packages/53/60/7be26e610767316c028a2cbedb9a3beabdbe33e2182c373f71a1c0b88f36/zstandard-0.25.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5a56ba0db2d244117ed744dfa8f6f5b366e14148e00de44723413b2f3938a902", size = 5546993, upload-time = "2025-09-14T22:17:06.781Z" }, + { url = "https://files.pythonhosted.org/packages/85/c7/3483ad9ff0662623f3648479b0380d2de5510abf00990468c286c6b04017/zstandard-0.25.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:10ef2a79ab8e2974e2075fb984e5b9806c64134810fac21576f0668e7ea19f8f", size = 5046806, upload-time = "2025-09-14T22:17:08.415Z" }, + { url = "https://files.pythonhosted.org/packages/08/b3/206883dd25b8d1591a1caa44b54c2aad84badccf2f1de9e2d60a446f9a25/zstandard-0.25.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:aaf21ba8fb76d102b696781bddaa0954b782536446083ae3fdaa6f16b25a1c4b", size = 5576659, upload-time = "2025-09-14T22:17:10.164Z" }, + { url = "https://files.pythonhosted.org/packages/9d/31/76c0779101453e6c117b0ff22565865c54f48f8bd807df2b00c2c404b8e0/zstandard-0.25.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1869da9571d5e94a85a5e8d57e4e8807b175c9e4a6294e3b66fa4efb074d90f6", size = 4953933, upload-time = "2025-09-14T22:17:11.857Z" }, + { url = "https://files.pythonhosted.org/packages/18/e1/97680c664a1bf9a247a280a053d98e251424af51f1b196c6d52f117c9720/zstandard-0.25.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:809c5bcb2c67cd0ed81e9229d227d4ca28f82d0f778fc5fea624a9def3963f91", size = 5268008, upload-time = "2025-09-14T22:17:13.627Z" }, + { url = "https://files.pythonhosted.org/packages/1e/73/316e4010de585ac798e154e88fd81bb16afc5c5cb1a72eeb16dd37e8024a/zstandard-0.25.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f27662e4f7dbf9f9c12391cb37b4c4c3cb90ffbd3b1fb9284dadbbb8935fa708", size = 5433517, upload-time = "2025-09-14T22:17:16.103Z" }, + { url = "https://files.pythonhosted.org/packages/5b/60/dd0f8cfa8129c5a0ce3ea6b7f70be5b33d2618013a161e1ff26c2b39787c/zstandard-0.25.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:99c0c846e6e61718715a3c9437ccc625de26593fea60189567f0118dc9db7512", size = 5814292, upload-time = "2025-09-14T22:17:17.827Z" }, + { url = "https://files.pythonhosted.org/packages/fc/5f/75aafd4b9d11b5407b641b8e41a57864097663699f23e9ad4dbb91dc6bfe/zstandard-0.25.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:474d2596a2dbc241a556e965fb76002c1ce655445e4e3bf38e5477d413165ffa", size = 5360237, upload-time = "2025-09-14T22:17:19.954Z" }, + { url = "https://files.pythonhosted.org/packages/ff/8d/0309daffea4fcac7981021dbf21cdb2e3427a9e76bafbcdbdf5392ff99a4/zstandard-0.25.0-cp312-cp312-win32.whl", hash = "sha256:23ebc8f17a03133b4426bcc04aabd68f8236eb78c3760f12783385171b0fd8bd", size = 436922, upload-time = "2025-09-14T22:17:24.398Z" }, + { url = "https://files.pythonhosted.org/packages/79/3b/fa54d9015f945330510cb5d0b0501e8253c127cca7ebe8ba46a965df18c5/zstandard-0.25.0-cp312-cp312-win_amd64.whl", hash = "sha256:ffef5a74088f1e09947aecf91011136665152e0b4b359c42be3373897fb39b01", size = 506276, upload-time = "2025-09-14T22:17:21.429Z" }, + { url = "https://files.pythonhosted.org/packages/ea/6b/8b51697e5319b1f9ac71087b0af9a40d8a6288ff8025c36486e0c12abcc4/zstandard-0.25.0-cp312-cp312-win_arm64.whl", hash = "sha256:181eb40e0b6a29b3cd2849f825e0fa34397f649170673d385f3598ae17cca2e9", size = 462679, upload-time = "2025-09-14T22:17:23.147Z" }, + { url = "https://files.pythonhosted.org/packages/35/0b/8df9c4ad06af91d39e94fa96cc010a24ac4ef1378d3efab9223cc8593d40/zstandard-0.25.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ec996f12524f88e151c339688c3897194821d7f03081ab35d31d1e12ec975e94", size = 795735, upload-time = "2025-09-14T22:17:26.042Z" }, + { url = "https://files.pythonhosted.org/packages/3f/06/9ae96a3e5dcfd119377ba33d4c42a7d89da1efabd5cb3e366b156c45ff4d/zstandard-0.25.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a1a4ae2dec3993a32247995bdfe367fc3266da832d82f8438c8570f989753de1", size = 640440, upload-time = "2025-09-14T22:17:27.366Z" }, + { url = "https://files.pythonhosted.org/packages/d9/14/933d27204c2bd404229c69f445862454dcc101cd69ef8c6068f15aaec12c/zstandard-0.25.0-cp313-cp313-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:e96594a5537722fdfb79951672a2a63aec5ebfb823e7560586f7484819f2a08f", size = 5343070, upload-time = "2025-09-14T22:17:28.896Z" }, + { url = "https://files.pythonhosted.org/packages/6d/db/ddb11011826ed7db9d0e485d13df79b58586bfdec56e5c84a928a9a78c1c/zstandard-0.25.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bfc4e20784722098822e3eee42b8e576b379ed72cca4a7cb856ae733e62192ea", size = 5063001, upload-time = "2025-09-14T22:17:31.044Z" }, + { url = "https://files.pythonhosted.org/packages/db/00/87466ea3f99599d02a5238498b87bf84a6348290c19571051839ca943777/zstandard-0.25.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:457ed498fc58cdc12fc48f7950e02740d4f7ae9493dd4ab2168a47c93c31298e", size = 5394120, upload-time = "2025-09-14T22:17:32.711Z" }, + { url = "https://files.pythonhosted.org/packages/2b/95/fc5531d9c618a679a20ff6c29e2b3ef1d1f4ad66c5e161ae6ff847d102a9/zstandard-0.25.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:fd7a5004eb1980d3cefe26b2685bcb0b17989901a70a1040d1ac86f1d898c551", size = 5451230, upload-time = "2025-09-14T22:17:34.41Z" }, + { url = "https://files.pythonhosted.org/packages/63/4b/e3678b4e776db00f9f7b2fe58e547e8928ef32727d7a1ff01dea010f3f13/zstandard-0.25.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8e735494da3db08694d26480f1493ad2cf86e99bdd53e8e9771b2752a5c0246a", size = 5547173, upload-time = "2025-09-14T22:17:36.084Z" }, + { url = "https://files.pythonhosted.org/packages/4e/d5/ba05ed95c6b8ec30bd468dfeab20589f2cf709b5c940483e31d991f2ca58/zstandard-0.25.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3a39c94ad7866160a4a46d772e43311a743c316942037671beb264e395bdd611", size = 5046736, upload-time = "2025-09-14T22:17:37.891Z" }, + { url = "https://files.pythonhosted.org/packages/50/d5/870aa06b3a76c73eced65c044b92286a3c4e00554005ff51962deef28e28/zstandard-0.25.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:172de1f06947577d3a3005416977cce6168f2261284c02080e7ad0185faeced3", size = 5576368, upload-time = "2025-09-14T22:17:40.206Z" }, + { url = "https://files.pythonhosted.org/packages/5d/35/398dc2ffc89d304d59bc12f0fdd931b4ce455bddf7038a0a67733a25f550/zstandard-0.25.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3c83b0188c852a47cd13ef3bf9209fb0a77fa5374958b8c53aaa699398c6bd7b", size = 4954022, upload-time = "2025-09-14T22:17:41.879Z" }, + { url = "https://files.pythonhosted.org/packages/9a/5c/36ba1e5507d56d2213202ec2b05e8541734af5f2ce378c5d1ceaf4d88dc4/zstandard-0.25.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1673b7199bbe763365b81a4f3252b8e80f44c9e323fc42940dc8843bfeaf9851", size = 5267889, upload-time = "2025-09-14T22:17:43.577Z" }, + { url = "https://files.pythonhosted.org/packages/70/e8/2ec6b6fb7358b2ec0113ae202647ca7c0e9d15b61c005ae5225ad0995df5/zstandard-0.25.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:0be7622c37c183406f3dbf0cba104118eb16a4ea7359eeb5752f0794882fc250", size = 5433952, upload-time = "2025-09-14T22:17:45.271Z" }, + { url = "https://files.pythonhosted.org/packages/7b/01/b5f4d4dbc59ef193e870495c6f1275f5b2928e01ff5a81fecb22a06e22fb/zstandard-0.25.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:5f5e4c2a23ca271c218ac025bd7d635597048b366d6f31f420aaeb715239fc98", size = 5814054, upload-time = "2025-09-14T22:17:47.08Z" }, + { url = "https://files.pythonhosted.org/packages/b2/e5/fbd822d5c6f427cf158316d012c5a12f233473c2f9c5fe5ab1ae5d21f3d8/zstandard-0.25.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f187a0bb61b35119d1926aee039524d1f93aaf38a9916b8c4b78ac8514a0aaf", size = 5360113, upload-time = "2025-09-14T22:17:48.893Z" }, + { url = "https://files.pythonhosted.org/packages/8e/e0/69a553d2047f9a2c7347caa225bb3a63b6d7704ad74610cb7823baa08ed7/zstandard-0.25.0-cp313-cp313-win32.whl", hash = "sha256:7030defa83eef3e51ff26f0b7bfb229f0204b66fe18e04359ce3474ac33cbc09", size = 436936, upload-time = "2025-09-14T22:17:52.658Z" }, + { url = "https://files.pythonhosted.org/packages/d9/82/b9c06c870f3bd8767c201f1edbdf9e8dc34be5b0fbc5682c4f80fe948475/zstandard-0.25.0-cp313-cp313-win_amd64.whl", hash = "sha256:1f830a0dac88719af0ae43b8b2d6aef487d437036468ef3c2ea59c51f9d55fd5", size = 506232, upload-time = "2025-09-14T22:17:50.402Z" }, + { url = "https://files.pythonhosted.org/packages/d4/57/60c3c01243bb81d381c9916e2a6d9e149ab8627c0c7d7abb2d73384b3c0c/zstandard-0.25.0-cp313-cp313-win_arm64.whl", hash = "sha256:85304a43f4d513f5464ceb938aa02c1e78c2943b29f44a750b48b25ac999a049", size = 462671, upload-time = "2025-09-14T22:17:51.533Z" }, + { url = "https://files.pythonhosted.org/packages/3d/5c/f8923b595b55fe49e30612987ad8bf053aef555c14f05bb659dd5dbe3e8a/zstandard-0.25.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e29f0cf06974c899b2c188ef7f783607dbef36da4c242eb6c82dcd8b512855e3", size = 795887, upload-time = "2025-09-14T22:17:54.198Z" }, + { url = "https://files.pythonhosted.org/packages/8d/09/d0a2a14fc3439c5f874042dca72a79c70a532090b7ba0003be73fee37ae2/zstandard-0.25.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:05df5136bc5a011f33cd25bc9f506e7426c0c9b3f9954f056831ce68f3b6689f", size = 640658, upload-time = "2025-09-14T22:17:55.423Z" }, + { url = "https://files.pythonhosted.org/packages/5d/7c/8b6b71b1ddd517f68ffb55e10834388d4f793c49c6b83effaaa05785b0b4/zstandard-0.25.0-cp314-cp314-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:f604efd28f239cc21b3adb53eb061e2a205dc164be408e553b41ba2ffe0ca15c", size = 5379849, upload-time = "2025-09-14T22:17:57.372Z" }, + { url = "https://files.pythonhosted.org/packages/a4/86/a48e56320d0a17189ab7a42645387334fba2200e904ee47fc5a26c1fd8ca/zstandard-0.25.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:223415140608d0f0da010499eaa8ccdb9af210a543fac54bce15babbcfc78439", size = 5058095, upload-time = "2025-09-14T22:17:59.498Z" }, + { url = "https://files.pythonhosted.org/packages/f8/ad/eb659984ee2c0a779f9d06dbfe45e2dc39d99ff40a319895df2d3d9a48e5/zstandard-0.25.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2e54296a283f3ab5a26fc9b8b5d4978ea0532f37b231644f367aa588930aa043", size = 5551751, upload-time = "2025-09-14T22:18:01.618Z" }, + { url = "https://files.pythonhosted.org/packages/61/b3/b637faea43677eb7bd42ab204dfb7053bd5c4582bfe6b1baefa80ac0c47b/zstandard-0.25.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ca54090275939dc8ec5dea2d2afb400e0f83444b2fc24e07df7fdef677110859", size = 6364818, upload-time = "2025-09-14T22:18:03.769Z" }, + { url = "https://files.pythonhosted.org/packages/31/dc/cc50210e11e465c975462439a492516a73300ab8caa8f5e0902544fd748b/zstandard-0.25.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e09bb6252b6476d8d56100e8147b803befa9a12cea144bbe629dd508800d1ad0", size = 5560402, upload-time = "2025-09-14T22:18:05.954Z" }, + { url = "https://files.pythonhosted.org/packages/c9/ae/56523ae9c142f0c08efd5e868a6da613ae76614eca1305259c3bf6a0ed43/zstandard-0.25.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a9ec8c642d1ec73287ae3e726792dd86c96f5681eb8df274a757bf62b750eae7", size = 4955108, upload-time = "2025-09-14T22:18:07.68Z" }, + { url = "https://files.pythonhosted.org/packages/98/cf/c899f2d6df0840d5e384cf4c4121458c72802e8bda19691f3b16619f51e9/zstandard-0.25.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:a4089a10e598eae6393756b036e0f419e8c1d60f44a831520f9af41c14216cf2", size = 5269248, upload-time = "2025-09-14T22:18:09.753Z" }, + { url = "https://files.pythonhosted.org/packages/1b/c0/59e912a531d91e1c192d3085fc0f6fb2852753c301a812d856d857ea03c6/zstandard-0.25.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:f67e8f1a324a900e75b5e28ffb152bcac9fbed1cc7b43f99cd90f395c4375344", size = 5430330, upload-time = "2025-09-14T22:18:11.966Z" }, + { url = "https://files.pythonhosted.org/packages/a0/1d/7e31db1240de2df22a58e2ea9a93fc6e38cc29353e660c0272b6735d6669/zstandard-0.25.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:9654dbc012d8b06fc3d19cc825af3f7bf8ae242226df5f83936cb39f5fdc846c", size = 5811123, upload-time = "2025-09-14T22:18:13.907Z" }, + { url = "https://files.pythonhosted.org/packages/f6/49/fac46df5ad353d50535e118d6983069df68ca5908d4d65b8c466150a4ff1/zstandard-0.25.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4203ce3b31aec23012d3a4cf4a2ed64d12fea5269c49aed5e4c3611b938e4088", size = 5359591, upload-time = "2025-09-14T22:18:16.465Z" }, + { url = "https://files.pythonhosted.org/packages/c2/38/f249a2050ad1eea0bb364046153942e34abba95dd5520af199aed86fbb49/zstandard-0.25.0-cp314-cp314-win32.whl", hash = "sha256:da469dc041701583e34de852d8634703550348d5822e66a0c827d39b05365b12", size = 444513, upload-time = "2025-09-14T22:18:20.61Z" }, + { url = "https://files.pythonhosted.org/packages/3a/43/241f9615bcf8ba8903b3f0432da069e857fc4fd1783bd26183db53c4804b/zstandard-0.25.0-cp314-cp314-win_amd64.whl", hash = "sha256:c19bcdd826e95671065f8692b5a4aa95c52dc7a02a4c5a0cac46deb879a017a2", size = 516118, upload-time = "2025-09-14T22:18:17.849Z" }, + { url = "https://files.pythonhosted.org/packages/f0/ef/da163ce2450ed4febf6467d77ccb4cd52c4c30ab45624bad26ca0a27260c/zstandard-0.25.0-cp314-cp314-win_arm64.whl", hash = "sha256:d7541afd73985c630bafcd6338d2518ae96060075f9463d7dc14cfb33514383d", size = 476940, upload-time = "2025-09-14T22:18:19.088Z" }, +]