From b4f4c86c903b675a62881cfcfef896b5cb38c718 Mon Sep 17 00:00:00 2001 From: Bharat Verma Date: Wed, 9 Jul 2025 18:12:13 +0530 Subject: [PATCH 01/18] feat: added TestManager scopes for MCP tooling --- src/uipath/_cli/_auth/auth_config.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/uipath/_cli/_auth/auth_config.json b/src/uipath/_cli/_auth/auth_config.json index 5d624f54..d4852569 100644 --- a/src/uipath/_cli/_auth/auth_config.json +++ b/src/uipath/_cli/_auth/auth_config.json @@ -1,6 +1,6 @@ { "client_id": "36dea5b8-e8bb-423d-8e7b-c808df8f1c00", "redirect_uri": "http://localhost:__PY_REPLACE_PORT__/oidc/login", - "scope": "offline_access OrchestratorApiUserAccess IdentityServerApi ConnectionService DataService DocumentUnderstanding EnterpriseContextService Directory JamJamApi LLMGateway LLMOps OMS RCS.FolderAuthorization", + "scope": "offline_access OrchestratorApiUserAccess IdentityServerApi ConnectionService DataService DocumentUnderstanding EnterpriseContextService Directory JamJamApi LLMGateway LLMOps OMS RCS.FolderAuthorization TM.Projects TM.TestCases TM.Requirements TM.TestSets", "port": 8104 -} +} \ No newline at end of file From 6f3765fa15c35b8dad7d3313c987e055ce1cc27f Mon Sep 17 00:00:00 2001 From: ionmincu Date: Thu, 17 Jul 2025 14:53:09 +0300 Subject: [PATCH 02/18] feat(llm): expose llm from sdk --- docs/core/llm_gateway.md | 1 + mkdocs.yml | 1 + pyproject.toml | 2 +- src/uipath/_services/llm_gateway_service.py | 261 ++++++++++++++++---- src/uipath/_uipath.py | 10 + 5 files changed, 231 insertions(+), 44 deletions(-) create mode 100644 docs/core/llm_gateway.md diff --git a/docs/core/llm_gateway.md b/docs/core/llm_gateway.md new file mode 100644 index 00000000..91f273b3 --- /dev/null +++ b/docs/core/llm_gateway.md @@ -0,0 +1 @@ +::: uipath._services.llm_gateway_service diff --git a/mkdocs.yml b/mkdocs.yml index db403792..98055767 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -77,6 +77,7 @@ nav: - Connections: core/connections.md - Context Grounding: core/context_grounding.md - Jobs: core/jobs.md + - LLM Gateway: core/llm_gateway.md - Queues: core/queues.md - Processes: core/processes.md - How To Contribute: CONTRIBUTING.md diff --git a/pyproject.toml b/pyproject.toml index 65340d83..5a4c8190 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "uipath" -version = "2.0.81" +version = "2.0.82" description = "Python SDK and CLI for UiPath Platform, enabling programmatic interaction with automation services, process management, and deployment tools." readme = { file = "README.md", content-type = "text/markdown" } requires-python = ">=3.10" diff --git a/src/uipath/_services/llm_gateway_service.py b/src/uipath/_services/llm_gateway_service.py index 9d4342ff..fb0e3317 100644 --- a/src/uipath/_services/llm_gateway_service.py +++ b/src/uipath/_services/llm_gateway_service.py @@ -1,3 +1,21 @@ +"""UiPath LLM Gateway Services. + +This module provides services for interacting with UiPath's LLM (Large Language Model) Gateway, +offering both OpenAI-compatible and normalized API interfaces for chat completions and embeddings. + +The module includes: +- UiPathOpenAIService: OpenAI-compatible API for chat completions and embeddings +- UiPathLlmChatService: UiPath's normalized API with advanced features like tool calling +- ChatModels: Constants for available chat models +- EmbeddingModels: Constants for available embedding models + +Classes: + ChatModels: Container for supported chat model identifiers + EmbeddingModels: Container for supported embedding model identifiers + UiPathOpenAIService: Service using OpenAI-compatible API format + UiPathLlmChatService: Service using UiPath's normalized API format +""" + import json from typing import Any, Dict, List, Optional @@ -16,10 +34,12 @@ from ._base_service import BaseService # Common constants -API_VERSION = "2024-10-21" -NORMALIZED_API_VERSION = "2024-08-01-preview" +API_VERSION = "2024-10-21" # Standard API version for OpenAI-compatible endpoints +NORMALIZED_API_VERSION = ( + "2024-08-01-preview" # API version for UiPath's normalized endpoints +) -# Common headers +# Common headers used across all LLM Gateway requests DEFAULT_LLM_HEADERS = { "X-UIPATH-STREAMING-ENABLED": "false", "X-UiPath-LlmGateway-RequestingProduct": "uipath-python-sdk", @@ -28,6 +48,12 @@ class ChatModels(object): + """Available chat models for LLM Gateway services. + + This class provides constants for the supported chat models that can be used + with both UiPathOpenAIService and UiPathLlmChatService. + """ + gpt_4 = "gpt-4" gpt_4_1106_Preview = "gpt-4-1106-Preview" gpt_4_32k = "gpt-4-32k" @@ -40,16 +66,23 @@ class ChatModels(object): class EmbeddingModels(object): - text_embedding_3_large = "text-embedding-3-large" - text_embedding_ada_002 = "text-embedding-ada-002" + """Available embedding models for LLM Gateway services. + This class provides constants for the supported embedding models that can be used + with the embeddings functionality. + """ -API_VERSION = "2024-10-21" -NORMALIZED_API_VERSION = "2024-08-01-preview" + text_embedding_3_large = "text-embedding-3-large" + text_embedding_ada_002 = "text-embedding-ada-002" class UiPathOpenAIService(BaseService): - """Service calling llm gateway service.""" + """Service for calling UiPath's LLM Gateway using OpenAI-compatible API. + + This service provides access to Large Language Model capabilities through UiPath's + LLM Gateway, including chat completions and text embeddings. It uses the OpenAI-compatible + API format and is suitable for applications that need direct OpenAI API compatibility. + """ def __init__(self, config: Config, execution_context: ExecutionContext) -> None: super().__init__(config=config, execution_context=execution_context) @@ -61,13 +94,35 @@ async def embeddings( embedding_model: str = EmbeddingModels.text_embedding_ada_002, openai_api_version: str = API_VERSION, ): - """Embed the input text using llm gateway service. + """Generate text embeddings using UiPath's LLM Gateway service. + + This method converts input text into dense vector representations that can be used + for semantic search, similarity calculations, and other NLP tasks. Args: - input (str): The input text to embed. + input (str): The input text to embed. Can be a single sentence, paragraph, + or document that you want to convert to embeddings. + embedding_model (str, optional): The embedding model to use. + Defaults to EmbeddingModels.text_embedding_ada_002. + Available models are defined in the EmbeddingModels class. + openai_api_version (str, optional): The OpenAI API version to use. + Defaults to API_VERSION. Returns: - TextEmbedding: The embedding response. + TextEmbedding: The embedding response containing the vector representation + of the input text along with metadata. + + Examples: + ```python + # Basic embedding + embedding = await service.embeddings("Hello, world!") + + # Using a specific model + embedding = await service.embeddings( + "This is a longer text to embed", + embedding_model=EmbeddingModels.text_embedding_3_large + ) + ``` """ endpoint = EndpointManager.get_embeddings_endpoint().format( model=embedding_model, api_version=openai_api_version @@ -93,29 +148,57 @@ async def chat_completions( temperature: float = 0, api_version: str = API_VERSION, ): - """Get chat completions using llm gateway service. + """Generate chat completions using UiPath's LLM Gateway service. + + This method provides conversational AI capabilities by sending a series of messages + to a language model and receiving a generated response. It supports multi-turn + conversations and various OpenAI-compatible models. Args: messages (List[Dict[str, str]]): List of message dictionaries with 'role' and 'content' keys. - The supported roles are 'system', 'user', and 'assistant'. - - Example: - ``` - [ - {"role": "system", "content": "You are a helpful Python programming assistant."}, - {"role": "user", "content": "How do I read a file in Python?"}, - {"role": "assistant", "content": "You can use the built-in open() function."}, - {"role": "user", "content": "Can you show an example?"} - ] - ``` - The conversation history can be included to provide context to the model. - model (str, optional): The model to use for chat completion. Defaults to ChatModels.gpt_4o_mini_2024_07_18. - max_tokens (int, optional): Maximum number of tokens to generate. Defaults to 50. + The supported roles are 'system', 'user', and 'assistant'. System messages set + the behavior/context, user messages are from the human, and assistant messages + are from the AI. + model (str, optional): The model to use for chat completion. + Defaults to ChatModels.gpt_4o_mini_2024_07_18. + Available models are defined in the ChatModels class. + max_tokens (int, optional): Maximum number of tokens to generate in the response. + Defaults to 50. Higher values allow longer responses. temperature (float, optional): Temperature for sampling, between 0 and 1. - Lower values make output more deterministic. Defaults to 0. + Lower values (closer to 0) make output more deterministic and focused, + higher values make it more creative and random. Defaults to 0. + api_version (str, optional): The API version to use. Defaults to API_VERSION. Returns: - ChatCompletion: The chat completion response. + ChatCompletion: The chat completion response containing the generated message, + usage statistics, and other metadata. + + Examples: + ```python + # Simple conversation + messages = [ + {"role": "system", "content": "You are a helpful Python programming assistant."}, + {"role": "user", "content": "How do I read a file in Python?"} + ] + response = await service.chat_completions(messages) + + # Multi-turn conversation with more tokens + messages = [ + {"role": "system", "content": "You are a helpful assistant."}, + {"role": "user", "content": "What is machine learning?"}, + {"role": "assistant", "content": "Machine learning is a subset of AI..."}, + {"role": "user", "content": "Can you give me a practical example?"} + ] + response = await service.chat_completions( + messages, + max_tokens=200, + temperature=0.3 + ) + ``` + + Note: + The conversation history can be included to provide context to the model. + Each message should have both 'role' and 'content' keys. """ endpoint = EndpointManager.get_passthrough_endpoint().format( model=model, api_version=api_version @@ -140,7 +223,16 @@ async def chat_completions( class UiPathLlmChatService(BaseService): - """Service for calling UiPath's normalized LLM Gateway API.""" + """Service for calling UiPath's normalized LLM Gateway API. + + This service provides access to Large Language Model capabilities through UiPath's + normalized LLM Gateway API. Unlike the OpenAI-compatible service, this service uses + UiPath's standardized API format and supports advanced features like tool calling, + function calling, and more sophisticated conversation control. + + The normalized API provides a consistent interface across different underlying model + providers and includes enhanced features for enterprise use cases. + """ def __init__(self, config: Config, execution_context: ExecutionContext) -> None: super().__init__(config=config, execution_context=execution_context) @@ -160,25 +252,96 @@ async def chat_completions( tool_choice: Optional[ToolChoice] = None, api_version: str = NORMALIZED_API_VERSION, ): - """Get chat completions using UiPath's normalized LLM Gateway API. + """Generate chat completions using UiPath's normalized LLM Gateway API. + + This method provides advanced conversational AI capabilities with support for + tool calling, function calling, and sophisticated conversation control parameters. + It uses UiPath's normalized API format for consistent behavior across different + model providers. Args: messages (List[Dict[str, str]]): List of message dictionaries with 'role' and 'content' keys. - The supported roles are 'system', 'user', and 'assistant'. - model (str, optional): The model to use for chat completion. Defaults to ChatModels.gpt_4o_mini_2024_07_18. - max_tokens (int, optional): Maximum number of tokens to generate. Defaults to 250. + The supported roles are 'system', 'user', and 'assistant'. System messages set + the behavior/context, user messages are from the human, and assistant messages + are from the AI. + model (str, optional): The model to use for chat completion. + Defaults to ChatModels.gpt_4o_mini_2024_07_18. + Available models are defined in the ChatModels class. + max_tokens (int, optional): Maximum number of tokens to generate in the response. + Defaults to 250. Higher values allow longer responses. temperature (float, optional): Temperature for sampling, between 0 and 1. - Lower values make output more deterministic. Defaults to 0. - n (int, optional): Number of chat completion choices to generate. Defaults to 1. - frequency_penalty (float, optional): Penalty for token frequency. Defaults to 0. - presence_penalty (float, optional): Penalty for token presence. Defaults to 0. - top_p (float, optional): Nucleus sampling parameter. Defaults to 1. - tools (Optional[List[ToolDefinition]], optional): List of tool definitions. Defaults to None. - tool_choice (Optional[ToolChoice], optional): Tool choice configuration. - Can be "auto", "none", an AutoToolChoice, a RequiredToolChoice, or a SpecificToolChoice. Defaults to None. + Lower values (closer to 0) make output more deterministic and focused, + higher values make it more creative and random. Defaults to 0. + n (int, optional): Number of chat completion choices to generate for each input. + Defaults to 1. Higher values generate multiple alternative responses. + frequency_penalty (float, optional): Penalty for token frequency between -2.0 and 2.0. + Positive values reduce repetition of frequent tokens. Defaults to 0. + presence_penalty (float, optional): Penalty for token presence between -2.0 and 2.0. + Positive values encourage discussion of new topics. Defaults to 0. + top_p (float, optional): Nucleus sampling parameter between 0 and 1. + Controls diversity by considering only the top p probability mass. Defaults to 1. + tools (Optional[List[ToolDefinition]], optional): List of tool definitions that the + model can call. Tools enable the model to perform actions or retrieve information + beyond text generation. Defaults to None. + tool_choice (Optional[ToolChoice], optional): Controls which tools the model can call. + Can be "auto" (model decides), "none" (no tools), or a specific tool choice. + Defaults to None. + api_version (str, optional): The normalized API version to use. + Defaults to NORMALIZED_API_VERSION. Returns: - ChatCompletion: The chat completion response. + ChatCompletion: The chat completion response containing the generated message(s), + tool calls (if any), usage statistics, and other metadata. + + Examples: + ```python + # Basic conversation + messages = [ + {"role": "system", "content": "You are a helpful assistant."}, + {"role": "user", "content": "What is the weather like today?"} + ] + response = await service.chat_completions(messages) + + # Conversation with tool calling + tools = [ + ToolDefinition( + function=FunctionDefinition( + name="get_weather", + description="Get current weather for a location", + parameters=ParametersDefinition( + type="object", + properties={ + "location": PropertyDefinition( + type="string", + description="City name" + ) + }, + required=["location"] + ) + ) + ) + ] + response = await service.chat_completions( + messages, + tools=tools, + tool_choice="auto", + max_tokens=500 + ) + + # Advanced parameters for creative writing + response = await service.chat_completions( + messages, + temperature=0.8, + top_p=0.9, + frequency_penalty=0.3, + presence_penalty=0.2, + n=3 # Generate 3 alternative responses + ) + ``` + + Note: + This service uses UiPath's normalized API format which provides consistent + behavior across different underlying model providers and enhanced enterprise features. """ endpoint = EndpointManager.get_normalized_endpoint().format( model=model, api_version=api_version @@ -227,7 +390,19 @@ async def chat_completions( return ChatCompletion.model_validate(response.json()) def _convert_tool_to_uipath_format(self, tool: ToolDefinition) -> Dict[str, Any]: - """Convert an OpenAI-style tool definition directly to UiPath API format.""" + """Convert an OpenAI-style tool definition to UiPath API format. + + This internal method transforms tool definitions from the standard OpenAI format + to the format expected by UiPath's normalized LLM Gateway API. + + Args: + tool (ToolDefinition): The tool definition in OpenAI format containing + function name, description, and parameter schema. + + Returns: + Dict[str, Any]: The tool definition converted to UiPath API format + with the appropriate structure and field mappings. + """ parameters = { "type": tool.function.parameters.type, "properties": { diff --git a/src/uipath/_uipath.py b/src/uipath/_uipath.py index 3a2fbd1b..b54c286a 100644 --- a/src/uipath/_uipath.py +++ b/src/uipath/_uipath.py @@ -18,6 +18,8 @@ JobsService, ProcessesService, QueuesService, + UiPathLlmChatService, + UiPathOpenAIService, ) from ._utils import setup_logging from ._utils.constants import ( @@ -122,3 +124,11 @@ def folders(self) -> FolderService: if not self._folders_service: self._folders_service = FolderService(self._config, self._execution_context) return self._folders_service + + @property + def llm_openai(self) -> UiPathOpenAIService: + return UiPathOpenAIService(self._config, self._execution_context) + + @property + def llm(self) -> UiPathLlmChatService: + return UiPathLlmChatService(self._config, self._execution_context) From 4aae801e3451d1fbb4c2adf71bc9c814d6623af0 Mon Sep 17 00:00:00 2001 From: radu-mocanu Date: Tue, 22 Jul 2025 12:48:28 +0300 Subject: [PATCH 03/18] chore: include MCP docs --- .github/workflows/publish-docs.yml | 3 +++ docs/index.md | 11 ++++++++++ mkdocs.yml | 34 +++++++++++++++++------------- uv.lock | 2 +- 4 files changed, 34 insertions(+), 16 deletions(-) diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml index fb7b3e21..ac8c384f 100644 --- a/.github/workflows/publish-docs.yml +++ b/.github/workflows/publish-docs.yml @@ -40,13 +40,16 @@ jobs: run: | git clone https://x-access-token:${{ secrets.REPO_ACCESS }}@github.com/UiPath/uipath-langchain-python plugins/uipath-langchain-python git clone https://x-access-token:${{ secrets.REPO_ACCESS }}@github.com/UiPath/uipath-llamaindex-python plugins/uipath-llamaindex-python + git clone https://x-access-token:${{ secrets.REPO_ACCESS }}@github.com/UiPath/uipath-mcp-python plugins/uipath-mcp-python - name: Symlink plugin docs run: | ln -s ../plugins/uipath-langchain-python/docs docs/langchain ln -s ../plugins/uipath-llamaindex-python/docs docs/llamaindex + ln -s ../plugins/uipath-mcp-python/docs docs/mcp ls -a docs/langchain ls -a docs/llamaindex + ls -a docs/mcp - name: Publish Docs run: uv run mkdocs gh-deploy --force diff --git a/docs/index.md b/docs/index.md index 4d02c7c5..558afb9d 100644 --- a/docs/index.md +++ b/docs/index.md @@ -13,6 +13,17 @@ title: Getting Started +
+- __UiPath MCP SDK__ + + --- + + Build and host Coded MCP Servers within UiPath. + + [Start Building](./mcp/quick_start.md) + +
+

Extensions

- __UiPath Langchain SDK__ diff --git a/mkdocs.yml b/mkdocs.yml index 98055767..cee235bd 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -53,6 +53,21 @@ theme: - toc.follow nav: + - UiPath SDK: + - Getting Started: core/getting_started.md + - Environment Variables: core/environment_variables.md + - CLI Reference: cli/index.md + - Tracing: core/traced.md + - Services: + - Actions: core/actions.md + - Assets: core/assets.md + - Buckets: core/buckets.md + - Connections: core/connections.md + - Context Grounding: core/context_grounding.md + - Jobs: core/jobs.md + - LLM Gateway: core/llm_gateway.md + - Queues: core/queues.md + - Processes: core/processes.md - UiPath Langchain SDK: - Getting Started: langchain/quick_start.md - Chat Models: langchain/chat_models.md @@ -65,21 +80,10 @@ nav: - Context Grounding: llamaindex/context_grounding.md - Human In The Loop: llamaindex/human_in_the_loop.md - Sample Agents: https://github.com/UiPath/uipath-llamaindex-python/tree/main/samples - - UiPath SDK: - - Getting Started: core/getting_started.md - - Environment Variables: core/environment_variables.md - - CLI Reference: cli/index.md - - Tracing: core/traced.md - - Services: - - Actions: core/actions.md - - Assets: core/assets.md - - Buckets: core/buckets.md - - Connections: core/connections.md - - Context Grounding: core/context_grounding.md - - Jobs: core/jobs.md - - LLM Gateway: core/llm_gateway.md - - Queues: core/queues.md - - Processes: core/processes.md + - UiPath MCP SDK: + - Getting Started: mcp/quick_start.md + - How To Pack Binary: mcp/how_to_pack_binary.md + - Sample MCPs: https://github.com/UiPath/uipath-mcp-python/tree/main/samples - How To Contribute: CONTRIBUTING.md - FAQ: FAQ.md - Release Policy: release_policy.md diff --git a/uv.lock b/uv.lock index d88181ee..e498eba3 100644 --- a/uv.lock +++ b/uv.lock @@ -3121,7 +3121,7 @@ wheels = [ [[package]] name = "uipath" -version = "2.0.81" +version = "2.0.82" source = { editable = "." } dependencies = [ { name = "azure-monitor-opentelemetry" }, From ec4d497708f2502d563e0cb839aa288e985b484b Mon Sep 17 00:00:00 2001 From: radu-mocanu Date: Tue, 22 Jul 2025 14:26:41 +0300 Subject: [PATCH 04/18] chore: fix MCP docs --- mkdocs.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mkdocs.yml b/mkdocs.yml index cee235bd..c1837c1e 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -68,6 +68,10 @@ nav: - LLM Gateway: core/llm_gateway.md - Queues: core/queues.md - Processes: core/processes.md + - UiPath MCP SDK: + - Getting Started: mcp/quick_start.md + - How To Pack Binary: mcp/how_to_pack_binary.md + - Sample MCPs: https://github.com/UiPath/uipath-mcp-python/tree/main/samples - UiPath Langchain SDK: - Getting Started: langchain/quick_start.md - Chat Models: langchain/chat_models.md @@ -80,10 +84,6 @@ nav: - Context Grounding: llamaindex/context_grounding.md - Human In The Loop: llamaindex/human_in_the_loop.md - Sample Agents: https://github.com/UiPath/uipath-llamaindex-python/tree/main/samples - - UiPath MCP SDK: - - Getting Started: mcp/quick_start.md - - How To Pack Binary: mcp/how_to_pack_binary.md - - Sample MCPs: https://github.com/UiPath/uipath-mcp-python/tree/main/samples - How To Contribute: CONTRIBUTING.md - FAQ: FAQ.md - Release Policy: release_policy.md From 4dbcdb318cfbfb227915ee0f26a8c91ab7b4ec72 Mon Sep 17 00:00:00 2001 From: radu-mocanu Date: Tue, 22 Jul 2025 16:22:56 +0300 Subject: [PATCH 05/18] chore: fix MCP docs --- mkdocs.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mkdocs.yml b/mkdocs.yml index c1837c1e..ef868df8 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -70,8 +70,8 @@ nav: - Processes: core/processes.md - UiPath MCP SDK: - Getting Started: mcp/quick_start.md - - How To Pack Binary: mcp/how_to_pack_binary.md - - Sample MCPs: https://github.com/UiPath/uipath-mcp-python/tree/main/samples + - How To Pack Binary MCP Server: mcp/how_to_pack_binary.md + - Sample MCP Servers: https://github.com/UiPath/uipath-mcp-python/tree/main/samples - UiPath Langchain SDK: - Getting Started: langchain/quick_start.md - Chat Models: langchain/chat_models.md From 3ceb2f2331ec61297f04bf4a67ccadfd007c8818 Mon Sep 17 00:00:00 2001 From: radu-mocanu Date: Wed, 23 Jul 2025 11:06:35 +0300 Subject: [PATCH 06/18] chore: fix docs section name --- mkdocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index ef868df8..46a511b4 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -70,7 +70,7 @@ nav: - Processes: core/processes.md - UiPath MCP SDK: - Getting Started: mcp/quick_start.md - - How To Pack Binary MCP Server: mcp/how_to_pack_binary.md + - How To Pack Binary: mcp/how_to_pack_binary.md - Sample MCP Servers: https://github.com/UiPath/uipath-mcp-python/tree/main/samples - UiPath Langchain SDK: - Getting Started: langchain/quick_start.md From f090bf7d4527dfe94490c95c1f75f579335051cc Mon Sep 17 00:00:00 2001 From: Vasile Bujac Date: Thu, 24 Jul 2025 09:50:45 +0300 Subject: [PATCH 07/18] fix(cli): packing includes .md files by default --- pyproject.toml | 2 +- src/uipath/_cli/cli_pack.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 5a4c8190..d92255b5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "uipath" -version = "2.0.82" +version = "2.0.83" description = "Python SDK and CLI for UiPath Platform, enabling programmatic interaction with automation services, process management, and deployment tools." readme = { file = "README.md", content-type = "text/markdown" } requires-python = ">=3.10" diff --git a/src/uipath/_cli/cli_pack.py b/src/uipath/_cli/cli_pack.py index 726e6e14..b4d8e21f 100644 --- a/src/uipath/_cli/cli_pack.py +++ b/src/uipath/_cli/cli_pack.py @@ -264,7 +264,7 @@ def pack_fn( console.error("uipath.json not found, please run `uipath init`.") # Define the allowlist of file extensions to include - file_extensions_included = [".py", ".mermaid", ".json", ".yaml", ".yml"] + file_extensions_included = [".py", ".mermaid", ".json", ".yaml", ".yml", ".md"] files_included = [] with open(config_path, "r") as f: From 6f83856a293fd6150f76f395ef1a53a446791789 Mon Sep 17 00:00:00 2001 From: radu-mocanu Date: Thu, 3 Jul 2025 17:06:17 +0300 Subject: [PATCH 08/18] feat: debug and output file support --- pyproject.toml | 5 +- src/uipath/_cli/_runtime/_contracts.py | 20 + src/uipath/_cli/_utils/_debug.py | 4 + src/uipath/_cli/cli_run.py | 38 +- tests/cli/test_run.py | 34 +- uv.lock | 1310 +----------------------- 6 files changed, 99 insertions(+), 1312 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index d92255b5..3f2389d4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "uipath" -version = "2.0.83" +version = "2.1.0" description = "Python SDK and CLI for UiPath Platform, enabling programmatic interaction with automation services, process management, and deployment tools." readme = { file = "README.md", content-type = "text/markdown" } requires-python = ">=3.10" @@ -67,9 +67,6 @@ dev = [ "types-toml>=0.10.8", ] -[project.optional-dependencies] -langchain = ["uipath-langchain>=0.0.88,<0.1.0"] - [tool.hatch.build.targets.wheel] packages = ["src/uipath"] diff --git a/src/uipath/_cli/_runtime/_contracts.py b/src/uipath/_cli/_runtime/_contracts.py index bf1e6b12..24ee52af 100644 --- a/src/uipath/_cli/_runtime/_contracts.py +++ b/src/uipath/_cli/_runtime/_contracts.py @@ -148,6 +148,7 @@ class UiPathRuntimeContext(BaseModel): trace_context: Optional[UiPathTraceContext] = None tracing_enabled: Union[bool, str] = False resume: bool = False + debug: bool = False config_path: str = "uipath.json" runtime_dir: Optional[str] = "__uipath" logs_file: Optional[str] = "execution.log" @@ -155,6 +156,8 @@ class UiPathRuntimeContext(BaseModel): output_file: str = "output.json" state_file: str = "state.db" result: Optional[UiPathRuntimeResult] = None + execution_output_file: Optional[str] = None + input_file: Optional[str] = None model_config = {"arbitrary_types_allowed": True} @@ -295,6 +298,18 @@ async def __aenter__(self): Returns: The runtime instance """ + # Read the input from file if provided + if self.context.input_file: + _, file_extension = os.path.splitext(self.context.input_file) + if file_extension != ".json": + raise UiPathRuntimeError( + code="INVALID_INPUT_FILE_EXTENSION", + title="Invalid Input File Extension", + detail="The provided input file must be in JSON format.", + ) + with open(self.context.input_file) as f: + self.context.input = f.read() + # Intercept all stdout/stderr/logs and write them to a file (runtime), stdout (debug) self.logs_interceptor = LogsInterceptor( min_level=self.context.logs_min_level, @@ -370,6 +385,11 @@ async def __aexit__(self, exc_type, exc_val, exc_tb): with open(self.output_file_path, "w") as f: json.dump(content, f, indent=2, default=str) + # Write the execution output to file if requested + if self.context.execution_output_file: + with open(self.context.execution_output_file, "w") as f: + json.dump(execution_result.output or {}, f, indent=2, default=str) + # Don't suppress exceptions return False diff --git a/src/uipath/_cli/_utils/_debug.py b/src/uipath/_cli/_utils/_debug.py index 6a90969c..9759b7a6 100644 --- a/src/uipath/_cli/_utils/_debug.py +++ b/src/uipath/_cli/_utils/_debug.py @@ -42,6 +42,10 @@ def setup_debugging(debug: bool, debug_port: int = 5678) -> bool: console.info(f"🐛 Debug server started on port {debug_port}") console.info("📌 Waiting for debugger to attach...") console.info(" - VS Code: Run -> Start Debugging -> Python: Remote Attach") + console.link( + " CLI Documentation reference: ", + "https://uipath.github.io/uipath-python/cli/#run", + ) debugpy.wait_for_client() console.success("Debugger attached successfully!") diff --git a/src/uipath/_cli/cli_run.py b/src/uipath/_cli/cli_run.py index 62a6bb1b..fb3298d7 100644 --- a/src/uipath/_cli/cli_run.py +++ b/src/uipath/_cli/cli_run.py @@ -32,6 +32,7 @@ def python_run_middleware( entrypoint: Optional[str], input: Optional[str], resume: bool, + **kwargs, ) -> MiddlewareResult: """Middleware to handle Python script execution. @@ -70,6 +71,8 @@ async def execute(): context.resume = resume context.job_id = env.get("UIPATH_JOB_KEY") context.trace_id = env.get("UIPATH_TRACE_ID") + context.input_file = kwargs.get("input_file", None) + context.execution_output_file = kwargs.get("execution_output_file", None) context.tracing_enabled = env.get("UIPATH_TRACING_ENABLED", True) context.trace_context = UiPathTraceContext( trace_id=env.get("UIPATH_TRACE_ID"), @@ -118,6 +121,18 @@ async def execute(): type=click.Path(exists=True), help="File path for the .json input", ) +@click.option( + "--input-file", + required=False, + type=click.Path(exists=True), + help="Alias for '-f/--file' arguments", +) +@click.option( + "--output-file", + required=False, + type=click.Path(exists=False), + help="File path where the output will be written", +) @click.option( "--debug", is_flag=True, @@ -135,29 +150,36 @@ def run( input: Optional[str], resume: bool, file: Optional[str], + input_file: Optional[str], + output_file: Optional[str], debug: bool, debug_port: int, ) -> None: """Execute the project.""" - if file: - _, file_extension = os.path.splitext(file) - if file_extension != ".json": - console.error("Input file extension must be '.json'.") - with open(file) as f: - input = f.read() + input_file = file or input_file # Setup debugging if requested - if not setup_debugging(debug, debug_port): console.error(f"Failed to start debug server on port {debug_port}") # Process through middleware chain - result = Middlewares.next("run", entrypoint, input, resume) + result = Middlewares.next( + "run", + entrypoint, + input, + resume, + debug=debug, + debug_port=debug_port, + input_file=input_file, + execution_output_file=output_file, + ) if result.should_continue: result = python_run_middleware( entrypoint=entrypoint, input=input, resume=resume, + input_file=input_file, + execution_output_file=output_file, ) # Handle result from middleware diff --git a/tests/cli/test_run.py b/tests/cli/test_run.py index 79ee7391..fd37e448 100644 --- a/tests/cli/test_run.py +++ b/tests/cli/test_run.py @@ -53,6 +53,9 @@ def test_run_input_file_not_found( entrypoint: str, ): with runner.isolated_filesystem(temp_dir=temp_dir): + file_path = os.path.join(temp_dir, entrypoint) + with open(file_path, "w") as f: + f.write("script content") result = runner.invoke(run, [entrypoint, "--file", "not-here.json"]) assert result.exit_code != 0 assert "Error: Invalid value for '-f' / '--file'" in result.output @@ -65,12 +68,15 @@ def test_run_invalid_input_file( ): file_name = "not-json.txt" with runner.isolated_filesystem(temp_dir=temp_dir): + script_file_path = os.path.join(temp_dir, entrypoint) + with open(script_file_path, "w") as f: + f.write("script content") file_path = os.path.join(temp_dir, file_name) with open(file_path, "w") as f: f.write("file content") - result = runner.invoke(run, [entrypoint, "--file", file_path]) + result = runner.invoke(run, [script_file_path, "--file", file_path]) assert result.exit_code == 1 - assert "Input file extension must be '.json'." in result.output + assert "Invalid Input File Extension" in result.output def test_run_input_file_success( self, @@ -100,7 +106,14 @@ def test_run_input_file_success( assert "Successful execution." in result.output assert mock_middleware.call_count == 1 assert mock_middleware.call_args == mock.call( - "run", entrypoint, json_content, False + "run", + entrypoint, + "{}", + False, + debug=False, + debug_port=5678, + input_file=file_path, + execution_output_file=None, ) class TestMiddleware: @@ -134,6 +147,7 @@ def test_successful_execution( simple_script: str, ): input_file_name = "input.json" + output_file_name = "output.json" input_json_content = """ { "message": "Hello world", @@ -142,6 +156,7 @@ def test_successful_execution( with runner.isolated_filesystem(temp_dir=temp_dir): # create input file input_file_path = os.path.join(temp_dir, input_file_name) + output_file_path = os.path.join(temp_dir, output_file_name) with open(input_file_path, "w") as f: f.write(input_json_content) # Create test script @@ -152,11 +167,22 @@ def test_successful_execution( with open("uipath.json", "w") as f: f.write(uipath_json.to_json()) result = runner.invoke( - run, [script_file_path, "--file", input_file_path] + run, + [ + script_file_path, + "--input-file", + input_file_path, + "--output-file", + output_file_path, + ], ) assert result.exit_code == 0 assert "Successful execution." in result.output assert result.output.count("Hello world") == 2 + assert os.path.exists(output_file_path) + with open(output_file_path, "r") as f: + output = f.read() + assert output.count("Hello world") == 2 def test_no_main_function_found( self, diff --git a/uv.lock b/uv.lock index e498eba3..c3686eac 100644 --- a/uv.lock +++ b/uv.lock @@ -6,121 +6,6 @@ resolution-markers = [ "python_full_version < '3.12.4'", ] -[[package]] -name = "aiohappyeyeballs" -version = "2.6.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265 }, -] - -[[package]] -name = "aiohttp" -version = "3.11.16" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiohappyeyeballs" }, - { name = "aiosignal" }, - { name = "async-timeout", marker = "python_full_version < '3.11'" }, - { name = "attrs" }, - { name = "frozenlist" }, - { name = "multidict" }, - { name = "propcache" }, - { name = "yarl" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f1/d9/1c4721d143e14af753f2bf5e3b681883e1f24b592c0482df6fa6e33597fa/aiohttp-3.11.16.tar.gz", hash = "sha256:16f8a2c9538c14a557b4d309ed4d0a7c60f0253e8ed7b6c9a2859a7582f8b1b8", size = 7676826 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b8/21/6bd4cb580a323b64cda3b11fcb3f68deba77568e97806727a858de57349d/aiohttp-3.11.16-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fb46bb0f24813e6cede6cc07b1961d4b04f331f7112a23b5e21f567da4ee50aa", size = 708259 }, - { url = "https://files.pythonhosted.org/packages/96/8c/7b4b9debe90ffc31931b85ee8612a5c83f34d8fdc6d90ee3eb27b43639e4/aiohttp-3.11.16-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:54eb3aead72a5c19fad07219acd882c1643a1027fbcdefac9b502c267242f955", size = 468886 }, - { url = "https://files.pythonhosted.org/packages/13/da/a7fcd68e62acacf0a1930060afd2c970826f989265893082b6fb9eb25cb5/aiohttp-3.11.16-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:38bea84ee4fe24ebcc8edeb7b54bf20f06fd53ce4d2cc8b74344c5b9620597fd", size = 455846 }, - { url = "https://files.pythonhosted.org/packages/5d/12/b73d9423253f4c872d276a3771decb0722cb5f962352593bd617445977ba/aiohttp-3.11.16-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0666afbe984f6933fe72cd1f1c3560d8c55880a0bdd728ad774006eb4241ecd", size = 1587183 }, - { url = "https://files.pythonhosted.org/packages/75/d3/291b57d54719d996e6cb8c1db8b13d01bdb24dca90434815ac7e6a70393f/aiohttp-3.11.16-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ba92a2d9ace559a0a14b03d87f47e021e4fa7681dc6970ebbc7b447c7d4b7cd", size = 1634937 }, - { url = "https://files.pythonhosted.org/packages/be/85/4229eba92b433173065b0b459ab677ca11ead4a179f76ccfe55d8738b188/aiohttp-3.11.16-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ad1d59fd7114e6a08c4814983bb498f391c699f3c78712770077518cae63ff7", size = 1667980 }, - { url = "https://files.pythonhosted.org/packages/2b/0d/d2423936962e3c711fafd5bb9172a99e6b07dd63e086515aa957d8a991fd/aiohttp-3.11.16-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98b88a2bf26965f2015a771381624dd4b0839034b70d406dc74fd8be4cc053e3", size = 1590365 }, - { url = "https://files.pythonhosted.org/packages/ea/93/04209affc20834982c1ef4214b1afc07743667998a9975d69413e9c1e1c1/aiohttp-3.11.16-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:576f5ca28d1b3276026f7df3ec841ae460e0fc3aac2a47cbf72eabcfc0f102e1", size = 1547614 }, - { url = "https://files.pythonhosted.org/packages/f6/fb/194ad4e4cae98023ae19556e576347f402ce159e80d74cc0713d460c4a39/aiohttp-3.11.16-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a2a450bcce4931b295fc0848f384834c3f9b00edfc2150baafb4488c27953de6", size = 1532815 }, - { url = "https://files.pythonhosted.org/packages/33/6d/a4da7adbac90188bf1228c73b6768a607dd279c146721a9ff7dcb75c5ac6/aiohttp-3.11.16-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:37dcee4906454ae377be5937ab2a66a9a88377b11dd7c072df7a7c142b63c37c", size = 1559005 }, - { url = "https://files.pythonhosted.org/packages/7e/88/2fa9fbfd23fc16cb2cfdd1f290343e085e7e327438041e9c6aa0208a854d/aiohttp-3.11.16-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:4d0c970c0d602b1017e2067ff3b7dac41c98fef4f7472ec2ea26fd8a4e8c2149", size = 1535231 }, - { url = "https://files.pythonhosted.org/packages/f5/8f/9623cd2558e3e182d02dcda8b480643e1c48a0550a86e3050210e98dba27/aiohttp-3.11.16-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:004511d3413737700835e949433536a2fe95a7d0297edd911a1e9705c5b5ea43", size = 1609985 }, - { url = "https://files.pythonhosted.org/packages/f8/a2/53a8d1bfc67130710f1c8091f623cdefe7f85cd5d09e14637ed2ed6e1a6d/aiohttp-3.11.16-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:c15b2271c44da77ee9d822552201180779e5e942f3a71fb74e026bf6172ff287", size = 1628842 }, - { url = "https://files.pythonhosted.org/packages/49/3a/35fb43d07489573c6c1f8c6a3e6c657196124a63223705b7feeddaea06f1/aiohttp-3.11.16-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ad9509ffb2396483ceacb1eee9134724443ee45b92141105a4645857244aecc8", size = 1566929 }, - { url = "https://files.pythonhosted.org/packages/d5/82/bb3f4f2cc7677e790ba4c040db7dd8445c234a810ef893a858e217647d38/aiohttp-3.11.16-cp310-cp310-win32.whl", hash = "sha256:634d96869be6c4dc232fc503e03e40c42d32cfaa51712aee181e922e61d74814", size = 416935 }, - { url = "https://files.pythonhosted.org/packages/df/ad/a64db1c18063569d6dff474c46a7d4de7ab85ff55e2a35839b149b1850ea/aiohttp-3.11.16-cp310-cp310-win_amd64.whl", hash = "sha256:938f756c2b9374bbcc262a37eea521d8a0e6458162f2a9c26329cc87fdf06534", size = 442168 }, - { url = "https://files.pythonhosted.org/packages/b1/98/be30539cd84260d9f3ea1936d50445e25aa6029a4cb9707f3b64cfd710f7/aiohttp-3.11.16-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8cb0688a8d81c63d716e867d59a9ccc389e97ac7037ebef904c2b89334407180", size = 708664 }, - { url = "https://files.pythonhosted.org/packages/e6/27/d51116ce18bdfdea7a2244b55ad38d7b01a4298af55765eed7e8431f013d/aiohttp-3.11.16-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0ad1fb47da60ae1ddfb316f0ff16d1f3b8e844d1a1e154641928ea0583d486ed", size = 468953 }, - { url = "https://files.pythonhosted.org/packages/34/23/eedf80ec42865ea5355b46265a2433134138eff9a4fea17e1348530fa4ae/aiohttp-3.11.16-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:df7db76400bf46ec6a0a73192b14c8295bdb9812053f4fe53f4e789f3ea66bbb", size = 456065 }, - { url = "https://files.pythonhosted.org/packages/36/23/4a5b1ef6cff994936bf96d981dd817b487d9db755457a0d1c2939920d620/aiohttp-3.11.16-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc3a145479a76ad0ed646434d09216d33d08eef0d8c9a11f5ae5cdc37caa3540", size = 1687976 }, - { url = "https://files.pythonhosted.org/packages/d0/5d/c7474b4c3069bb35276d54c82997dff4f7575e4b73f0a7b1b08a39ece1eb/aiohttp-3.11.16-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d007aa39a52d62373bd23428ba4a2546eed0e7643d7bf2e41ddcefd54519842c", size = 1752711 }, - { url = "https://files.pythonhosted.org/packages/64/4c/ee416987b6729558f2eb1b727c60196580aafdb141e83bd78bb031d1c000/aiohttp-3.11.16-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f6ddd90d9fb4b501c97a4458f1c1720e42432c26cb76d28177c5b5ad4e332601", size = 1791305 }, - { url = "https://files.pythonhosted.org/packages/58/28/3e1e1884070b95f1f69c473a1995852a6f8516670bb1c29d6cb2dbb73e1c/aiohttp-3.11.16-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a2f451849e6b39e5c226803dcacfa9c7133e9825dcefd2f4e837a2ec5a3bb98", size = 1674499 }, - { url = "https://files.pythonhosted.org/packages/ad/55/a032b32fa80a662d25d9eb170ed1e2c2be239304ca114ec66c89dc40f37f/aiohttp-3.11.16-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8df6612df74409080575dca38a5237282865408016e65636a76a2eb9348c2567", size = 1622313 }, - { url = "https://files.pythonhosted.org/packages/b1/df/ca775605f72abbda4e4746e793c408c84373ca2c6ce7a106a09f853f1e89/aiohttp-3.11.16-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:78e6e23b954644737e385befa0deb20233e2dfddf95dd11e9db752bdd2a294d3", size = 1658274 }, - { url = "https://files.pythonhosted.org/packages/cc/6c/21c45b66124df5b4b0ab638271ecd8c6402b702977120cb4d5be6408e15d/aiohttp-3.11.16-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:696ef00e8a1f0cec5e30640e64eca75d8e777933d1438f4facc9c0cdf288a810", size = 1666704 }, - { url = "https://files.pythonhosted.org/packages/1d/e2/7d92adc03e3458edd18a21da2575ab84e58f16b1672ae98529e4eeee45ab/aiohttp-3.11.16-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e3538bc9fe1b902bef51372462e3d7c96fce2b566642512138a480b7adc9d508", size = 1652815 }, - { url = "https://files.pythonhosted.org/packages/3a/52/7549573cd654ad651e3c5786ec3946d8f0ee379023e22deb503ff856b16c/aiohttp-3.11.16-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:3ab3367bb7f61ad18793fea2ef71f2d181c528c87948638366bf1de26e239183", size = 1735669 }, - { url = "https://files.pythonhosted.org/packages/d5/54/dcd24a23c7a5a2922123e07a296a5f79ea87ce605f531be068415c326de6/aiohttp-3.11.16-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:56a3443aca82abda0e07be2e1ecb76a050714faf2be84256dae291182ba59049", size = 1760422 }, - { url = "https://files.pythonhosted.org/packages/a7/53/87327fe982fa310944e1450e97bf7b2a28015263771931372a1dfe682c58/aiohttp-3.11.16-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:61c721764e41af907c9d16b6daa05a458f066015abd35923051be8705108ed17", size = 1694457 }, - { url = "https://files.pythonhosted.org/packages/ce/6d/c5ccf41059267bcf89853d3db9d8d217dacf0a04f4086cb6bf278323011f/aiohttp-3.11.16-cp311-cp311-win32.whl", hash = "sha256:3e061b09f6fa42997cf627307f220315e313ece74907d35776ec4373ed718b86", size = 416817 }, - { url = "https://files.pythonhosted.org/packages/e7/dd/01f6fe028e054ef4f909c9d63e3a2399e77021bb2e1bb51d56ca8b543989/aiohttp-3.11.16-cp311-cp311-win_amd64.whl", hash = "sha256:745f1ed5e2c687baefc3c5e7b4304e91bf3e2f32834d07baaee243e349624b24", size = 442986 }, - { url = "https://files.pythonhosted.org/packages/db/38/100d01cbc60553743baf0fba658cb125f8ad674a8a771f765cdc155a890d/aiohttp-3.11.16-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:911a6e91d08bb2c72938bc17f0a2d97864c531536b7832abee6429d5296e5b27", size = 704881 }, - { url = "https://files.pythonhosted.org/packages/21/ed/b4102bb6245e36591209e29f03fe87e7956e54cb604ee12e20f7eb47f994/aiohttp-3.11.16-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6ac13b71761e49d5f9e4d05d33683bbafef753e876e8e5a7ef26e937dd766713", size = 464564 }, - { url = "https://files.pythonhosted.org/packages/3b/e1/a9ab6c47b62ecee080eeb33acd5352b40ecad08fb2d0779bcc6739271745/aiohttp-3.11.16-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fd36c119c5d6551bce374fcb5c19269638f8d09862445f85a5a48596fd59f4bb", size = 456548 }, - { url = "https://files.pythonhosted.org/packages/80/ad/216c6f71bdff2becce6c8776f0aa32cb0fa5d83008d13b49c3208d2e4016/aiohttp-3.11.16-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d489d9778522fbd0f8d6a5c6e48e3514f11be81cb0a5954bdda06f7e1594b321", size = 1691749 }, - { url = "https://files.pythonhosted.org/packages/bd/ea/7df7bcd3f4e734301605f686ffc87993f2d51b7acb6bcc9b980af223f297/aiohttp-3.11.16-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:69a2cbd61788d26f8f1e626e188044834f37f6ae3f937bd9f08b65fc9d7e514e", size = 1736874 }, - { url = "https://files.pythonhosted.org/packages/51/41/c7724b9c87a29b7cfd1202ec6446bae8524a751473d25e2ff438bc9a02bf/aiohttp-3.11.16-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd464ba806e27ee24a91362ba3621bfc39dbbb8b79f2e1340201615197370f7c", size = 1786885 }, - { url = "https://files.pythonhosted.org/packages/86/b3/f61f8492fa6569fa87927ad35a40c159408862f7e8e70deaaead349e2fba/aiohttp-3.11.16-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ce63ae04719513dd2651202352a2beb9f67f55cb8490c40f056cea3c5c355ce", size = 1698059 }, - { url = "https://files.pythonhosted.org/packages/ce/be/7097cf860a9ce8bbb0e8960704e12869e111abcd3fbd245153373079ccec/aiohttp-3.11.16-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09b00dd520d88eac9d1768439a59ab3d145065c91a8fab97f900d1b5f802895e", size = 1626527 }, - { url = "https://files.pythonhosted.org/packages/1d/1d/aaa841c340e8c143a8d53a1f644c2a2961c58cfa26e7b398d6bf75cf5d23/aiohttp-3.11.16-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7f6428fee52d2bcf96a8aa7b62095b190ee341ab0e6b1bcf50c615d7966fd45b", size = 1644036 }, - { url = "https://files.pythonhosted.org/packages/2c/88/59d870f76e9345e2b149f158074e78db457985c2b4da713038d9da3020a8/aiohttp-3.11.16-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:13ceac2c5cdcc3f64b9015710221ddf81c900c5febc505dbd8f810e770011540", size = 1685270 }, - { url = "https://files.pythonhosted.org/packages/2b/b1/c6686948d4c79c3745595efc469a9f8a43cab3c7efc0b5991be65d9e8cb8/aiohttp-3.11.16-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:fadbb8f1d4140825069db3fedbbb843290fd5f5bc0a5dbd7eaf81d91bf1b003b", size = 1650852 }, - { url = "https://files.pythonhosted.org/packages/fe/94/3e42a6916fd3441721941e0f1b8438e1ce2a4c49af0e28e0d3c950c9b3c9/aiohttp-3.11.16-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:6a792ce34b999fbe04a7a71a90c74f10c57ae4c51f65461a411faa70e154154e", size = 1704481 }, - { url = "https://files.pythonhosted.org/packages/b1/6d/6ab5854ff59b27075c7a8c610597d2b6c38945f9a1284ee8758bc3720ff6/aiohttp-3.11.16-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:f4065145bf69de124accdd17ea5f4dc770da0a6a6e440c53f6e0a8c27b3e635c", size = 1735370 }, - { url = "https://files.pythonhosted.org/packages/73/2a/08a68eec3c99a6659067d271d7553e4d490a0828d588e1daa3970dc2b771/aiohttp-3.11.16-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fa73e8c2656a3653ae6c307b3f4e878a21f87859a9afab228280ddccd7369d71", size = 1697619 }, - { url = "https://files.pythonhosted.org/packages/61/d5/fea8dbbfb0cd68fbb56f0ae913270a79422d9a41da442a624febf72d2aaf/aiohttp-3.11.16-cp312-cp312-win32.whl", hash = "sha256:f244b8e541f414664889e2c87cac11a07b918cb4b540c36f7ada7bfa76571ea2", size = 411710 }, - { url = "https://files.pythonhosted.org/packages/33/fb/41cde15fbe51365024550bf77b95a4fc84ef41365705c946da0421f0e1e0/aiohttp-3.11.16-cp312-cp312-win_amd64.whl", hash = "sha256:23a15727fbfccab973343b6d1b7181bfb0b4aa7ae280f36fd2f90f5476805682", size = 438012 }, - { url = "https://files.pythonhosted.org/packages/52/52/7c712b2d9fb4d5e5fd6d12f9ab76e52baddfee71e3c8203ca7a7559d7f51/aiohttp-3.11.16-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a3814760a1a700f3cfd2f977249f1032301d0a12c92aba74605cfa6ce9f78489", size = 698005 }, - { url = "https://files.pythonhosted.org/packages/51/3e/61057814f7247666d43ac538abcd6335b022869ade2602dab9bf33f607d2/aiohttp-3.11.16-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9b751a6306f330801665ae69270a8a3993654a85569b3469662efaad6cf5cc50", size = 461106 }, - { url = "https://files.pythonhosted.org/packages/4f/85/6b79fb0ea6e913d596d5b949edc2402b20803f51b1a59e1bbc5bb7ba7569/aiohttp-3.11.16-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ad497f38a0d6c329cb621774788583ee12321863cd4bd9feee1effd60f2ad133", size = 453394 }, - { url = "https://files.pythonhosted.org/packages/4b/04/e1bb3fcfbd2c26753932c759593a32299aff8625eaa0bf8ff7d9c0c34a36/aiohttp-3.11.16-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca37057625693d097543bd88076ceebeb248291df9d6ca8481349efc0b05dcd0", size = 1666643 }, - { url = "https://files.pythonhosted.org/packages/0e/27/97bc0fdd1f439b8f060beb3ba8fb47b908dc170280090801158381ad7942/aiohttp-3.11.16-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a5abcbba9f4b463a45c8ca8b7720891200658f6f46894f79517e6cd11f3405ca", size = 1721948 }, - { url = "https://files.pythonhosted.org/packages/2c/4f/bc4c5119e75c05ef15c5670ef1563bbe25d4ed4893b76c57b0184d815e8b/aiohttp-3.11.16-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f420bfe862fb357a6d76f2065447ef6f484bc489292ac91e29bc65d2d7a2c84d", size = 1774454 }, - { url = "https://files.pythonhosted.org/packages/73/5b/54b42b2150bb26fdf795464aa55ceb1a49c85f84e98e6896d211eabc6670/aiohttp-3.11.16-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58ede86453a6cf2d6ce40ef0ca15481677a66950e73b0a788917916f7e35a0bb", size = 1677785 }, - { url = "https://files.pythonhosted.org/packages/10/ee/a0fe68916d3f82eae199b8535624cf07a9c0a0958c7a76e56dd21140487a/aiohttp-3.11.16-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6fdec0213244c39973674ca2a7f5435bf74369e7d4e104d6c7473c81c9bcc8c4", size = 1608456 }, - { url = "https://files.pythonhosted.org/packages/8b/48/83afd779242b7cf7e1ceed2ff624a86d3221e17798061cf9a79e0b246077/aiohttp-3.11.16-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:72b1b03fb4655c1960403c131740755ec19c5898c82abd3961c364c2afd59fe7", size = 1622424 }, - { url = "https://files.pythonhosted.org/packages/6f/27/452f1d5fca1f516f9f731539b7f5faa9e9d3bf8a3a6c3cd7c4b031f20cbd/aiohttp-3.11.16-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:780df0d837276276226a1ff803f8d0fa5f8996c479aeef52eb040179f3156cbd", size = 1660943 }, - { url = "https://files.pythonhosted.org/packages/d6/e1/5c7d63143b8d00c83b958b9e78e7048c4a69903c760c1e329bf02bac57a1/aiohttp-3.11.16-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ecdb8173e6c7aa09eee342ac62e193e6904923bd232e76b4157ac0bfa670609f", size = 1622797 }, - { url = "https://files.pythonhosted.org/packages/46/9e/2ac29cca2746ee8e449e73cd2fcb3d454467393ec03a269d50e49af743f1/aiohttp-3.11.16-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:a6db7458ab89c7d80bc1f4e930cc9df6edee2200127cfa6f6e080cf619eddfbd", size = 1687162 }, - { url = "https://files.pythonhosted.org/packages/ad/6b/eaa6768e02edebaf37d77f4ffb74dd55f5cbcbb6a0dbf798ccec7b0ac23b/aiohttp-3.11.16-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:2540ddc83cc724b13d1838026f6a5ad178510953302a49e6d647f6e1de82bc34", size = 1718518 }, - { url = "https://files.pythonhosted.org/packages/e5/18/dda87cbad29472a51fa058d6d8257dfce168289adaeb358b86bd93af3b20/aiohttp-3.11.16-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3b4e6db8dc4879015b9955778cfb9881897339c8fab7b3676f8433f849425913", size = 1675254 }, - { url = "https://files.pythonhosted.org/packages/32/d9/d2fb08c614df401d92c12fcbc60e6e879608d5e8909ef75c5ad8d4ad8aa7/aiohttp-3.11.16-cp313-cp313-win32.whl", hash = "sha256:493910ceb2764f792db4dc6e8e4b375dae1b08f72e18e8f10f18b34ca17d0979", size = 410698 }, - { url = "https://files.pythonhosted.org/packages/ce/ed/853e36d5a33c24544cfa46585895547de152dfef0b5c79fa675f6e4b7b87/aiohttp-3.11.16-cp313-cp313-win_amd64.whl", hash = "sha256:42864e70a248f5f6a49fdaf417d9bc62d6e4d8ee9695b24c5916cb4bb666c802", size = 436395 }, -] - -[[package]] -name = "aiosignal" -version = "1.3.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "frozenlist" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ba/b5/6d55e80f6d8a08ce22b982eafa278d823b541c925f11ee774b0b9c43473d/aiosignal-1.3.2.tar.gz", hash = "sha256:a8c255c66fafb1e499c9351d0bf32ff2d8a0321595ebac3b93713656d2436f54", size = 19424 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/6a/bc7e17a3e87a2985d3e8f4da4cd0f481060eb78fb08596c42be62c90a4d9/aiosignal-1.3.2-py2.py3-none-any.whl", hash = "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5", size = 7597 }, -] - -[[package]] -name = "aiosqlite" -version = "0.20.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/0d/3a/22ff5415bf4d296c1e92b07fd746ad42c96781f13295a074d58e77747848/aiosqlite-0.20.0.tar.gz", hash = "sha256:6d35c8c256637f4672f843c31021464090805bf925385ac39473fb16eaaca3d7", size = 21691 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/00/c4/c93eb22025a2de6b83263dfe3d7df2e19138e345bca6f18dba7394120930/aiosqlite-0.20.0-py3-none-any.whl", hash = "sha256:36a1deaca0cac40ebe32aac9977a6e2bbc7f5189f23f4a54d5908986729e5bd6", size = 15564 }, -] - [[package]] name = "annotated-types" version = "0.7.0" @@ -157,15 +42,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/39/e3/893e8757be2612e6c266d9bb58ad2e3651524b5b40cf56761e985a28b13e/asgiref-3.8.1-py3-none-any.whl", hash = "sha256:3e1e3ecc849832fe52ccf2cb6686b7a55f82bb1d6aee72a58826471390335e47", size = 23828 }, ] -[[package]] -name = "async-timeout" -version = "4.0.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/87/d6/21b30a550dafea84b1b8eee21b5e23fa16d010ae006011221f33dcd8d7f8/async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f", size = 8345 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/fa/e01228c2938de91d47b307831c62ab9e4001e747789d0b05baf779a6488c/async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028", size = 5721 }, -] - [[package]] name = "attrs" version = "25.3.0" @@ -268,6 +144,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2", size = 10182537 }, ] +[[package]] +name = "backports-asyncio-runner" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/ff/70dca7d7cb1cbc0edb2c6cc0c38b65cba36cccc491eca64cabd5fe7f8670/backports_asyncio_runner-1.2.0.tar.gz", hash = "sha256:a5aa7b2b7d8f8bfcaa2b57313f70792df84e32a2a746f585213373f900b42162", size = 69893 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/59/76ab57e3fe74484f48a53f8e337171b4a2349e506eabe136d7e01d059086/backports_asyncio_runner-1.2.0-py3-none-any.whl", hash = "sha256:0da0a936a8aeb554eccb426dc55af3ba63bcdc69fa1a600b5bb305413a4477b5", size = 12313 }, +] + [[package]] name = "backrefs" version = "5.8" @@ -606,19 +491,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0f/e7/aa315e6a749d9b96c2504a1ba0ba031ba2d0517e972ce22682e3fccecb09/cssselect2-0.8.0-py3-none-any.whl", hash = "sha256:46fc70ebc41ced7a32cd42d58b1884d72ade23d21e5a4eaaf022401c13f0e76e", size = 15454 }, ] -[[package]] -name = "dataclasses-json" -version = "0.6.7" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "marshmallow" }, - { name = "typing-inspect" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/64/a4/f71d9cf3a5ac257c993b5ca3f93df5f7fb395c725e7f1e6479d2514173c3/dataclasses_json-0.6.7.tar.gz", hash = "sha256:b6b3e528266ea45b9535223bc53ca645f5208833c29229e847b3f26a1cc55fc0", size = 32227 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c3/be/d0d44e092656fe7a06b55e6103cbce807cdbdee17884a5367c68c9860853/dataclasses_json-0.6.7-py3-none-any.whl", hash = "sha256:0dbf33f26c8d5305befd61b39d2b3414e8a407bedc2834dea9b8d642666fb40a", size = 28686 }, -] - [[package]] name = "defusedxml" version = "0.7.1" @@ -649,15 +521,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/91/a1/cf2472db20f7ce4a6be1253a81cfdf85ad9c7885ffbed7047fb72c24cf87/distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87", size = 468973 }, ] -[[package]] -name = "distro" -version = "1.9.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277 }, -] - [[package]] name = "exceptiongroup" version = "1.2.2" @@ -685,75 +548,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c8/6d/8f5307d26ce700a89e5a67d1e1ad15eff977211f9ed3ae90d7b0d67f4e66/fixedint-0.1.6-py3-none-any.whl", hash = "sha256:b8cf9f913735d2904deadda7a6daa9f57100599da1de57a7448ea1be75ae8c9c", size = 12702 }, ] -[[package]] -name = "frozenlist" -version = "1.5.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8f/ed/0f4cec13a93c02c47ec32d81d11c0c1efbadf4a471e3f3ce7cad366cbbd3/frozenlist-1.5.0.tar.gz", hash = "sha256:81d5af29e61b9c8348e876d442253723928dce6433e0e76cd925cd83f1b4b817", size = 39930 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/54/79/29d44c4af36b2b240725dce566b20f63f9b36ef267aaaa64ee7466f4f2f8/frozenlist-1.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5b6a66c18b5b9dd261ca98dffcb826a525334b2f29e7caa54e182255c5f6a65a", size = 94451 }, - { url = "https://files.pythonhosted.org/packages/47/47/0c999aeace6ead8a44441b4f4173e2261b18219e4ad1fe9a479871ca02fc/frozenlist-1.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d1b3eb7b05ea246510b43a7e53ed1653e55c2121019a97e60cad7efb881a97bb", size = 54301 }, - { url = "https://files.pythonhosted.org/packages/8d/60/107a38c1e54176d12e06e9d4b5d755b677d71d1219217cee063911b1384f/frozenlist-1.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:15538c0cbf0e4fa11d1e3a71f823524b0c46299aed6e10ebb4c2089abd8c3bec", size = 52213 }, - { url = "https://files.pythonhosted.org/packages/17/62/594a6829ac5679c25755362a9dc93486a8a45241394564309641425d3ff6/frozenlist-1.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e79225373c317ff1e35f210dd5f1344ff31066ba8067c307ab60254cd3a78ad5", size = 240946 }, - { url = "https://files.pythonhosted.org/packages/7e/75/6c8419d8f92c80dd0ee3f63bdde2702ce6398b0ac8410ff459f9b6f2f9cb/frozenlist-1.5.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9272fa73ca71266702c4c3e2d4a28553ea03418e591e377a03b8e3659d94fa76", size = 264608 }, - { url = "https://files.pythonhosted.org/packages/88/3e/82a6f0b84bc6fb7e0be240e52863c6d4ab6098cd62e4f5b972cd31e002e8/frozenlist-1.5.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:498524025a5b8ba81695761d78c8dd7382ac0b052f34e66939c42df860b8ff17", size = 261361 }, - { url = "https://files.pythonhosted.org/packages/fd/85/14e5f9ccac1b64ff2f10c927b3ffdf88772aea875882406f9ba0cec8ad84/frozenlist-1.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:92b5278ed9d50fe610185ecd23c55d8b307d75ca18e94c0e7de328089ac5dcba", size = 231649 }, - { url = "https://files.pythonhosted.org/packages/ee/59/928322800306f6529d1852323014ee9008551e9bb027cc38d276cbc0b0e7/frozenlist-1.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f3c8c1dacd037df16e85227bac13cca58c30da836c6f936ba1df0c05d046d8d", size = 241853 }, - { url = "https://files.pythonhosted.org/packages/7d/bd/e01fa4f146a6f6c18c5d34cab8abdc4013774a26c4ff851128cd1bd3008e/frozenlist-1.5.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f2ac49a9bedb996086057b75bf93538240538c6d9b38e57c82d51f75a73409d2", size = 243652 }, - { url = "https://files.pythonhosted.org/packages/a5/bd/e4771fd18a8ec6757033f0fa903e447aecc3fbba54e3630397b61596acf0/frozenlist-1.5.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e66cc454f97053b79c2ab09c17fbe3c825ea6b4de20baf1be28919460dd7877f", size = 241734 }, - { url = "https://files.pythonhosted.org/packages/21/13/c83821fa5544af4f60c5d3a65d054af3213c26b14d3f5f48e43e5fb48556/frozenlist-1.5.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:5a3ba5f9a0dfed20337d3e966dc359784c9f96503674c2faf015f7fe8e96798c", size = 260959 }, - { url = "https://files.pythonhosted.org/packages/71/f3/1f91c9a9bf7ed0e8edcf52698d23f3c211d8d00291a53c9f115ceb977ab1/frozenlist-1.5.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6321899477db90bdeb9299ac3627a6a53c7399c8cd58d25da094007402b039ab", size = 262706 }, - { url = "https://files.pythonhosted.org/packages/4c/22/4a256fdf5d9bcb3ae32622c796ee5ff9451b3a13a68cfe3f68e2c95588ce/frozenlist-1.5.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:76e4753701248476e6286f2ef492af900ea67d9706a0155335a40ea21bf3b2f5", size = 250401 }, - { url = "https://files.pythonhosted.org/packages/af/89/c48ebe1f7991bd2be6d5f4ed202d94960c01b3017a03d6954dd5fa9ea1e8/frozenlist-1.5.0-cp310-cp310-win32.whl", hash = "sha256:977701c081c0241d0955c9586ffdd9ce44f7a7795df39b9151cd9a6fd0ce4cfb", size = 45498 }, - { url = "https://files.pythonhosted.org/packages/28/2f/cc27d5f43e023d21fe5c19538e08894db3d7e081cbf582ad5ed366c24446/frozenlist-1.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:189f03b53e64144f90990d29a27ec4f7997d91ed3d01b51fa39d2dbe77540fd4", size = 51622 }, - { url = "https://files.pythonhosted.org/packages/79/43/0bed28bf5eb1c9e4301003b74453b8e7aa85fb293b31dde352aac528dafc/frozenlist-1.5.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:fd74520371c3c4175142d02a976aee0b4cb4a7cc912a60586ffd8d5929979b30", size = 94987 }, - { url = "https://files.pythonhosted.org/packages/bb/bf/b74e38f09a246e8abbe1e90eb65787ed745ccab6eaa58b9c9308e052323d/frozenlist-1.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2f3f7a0fbc219fb4455264cae4d9f01ad41ae6ee8524500f381de64ffaa077d5", size = 54584 }, - { url = "https://files.pythonhosted.org/packages/2c/31/ab01375682f14f7613a1ade30149f684c84f9b8823a4391ed950c8285656/frozenlist-1.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f47c9c9028f55a04ac254346e92977bf0f166c483c74b4232bee19a6697e4778", size = 52499 }, - { url = "https://files.pythonhosted.org/packages/98/a8/d0ac0b9276e1404f58fec3ab6e90a4f76b778a49373ccaf6a563f100dfbc/frozenlist-1.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0996c66760924da6e88922756d99b47512a71cfd45215f3570bf1e0b694c206a", size = 276357 }, - { url = "https://files.pythonhosted.org/packages/ad/c9/c7761084fa822f07dac38ac29f841d4587570dd211e2262544aa0b791d21/frozenlist-1.5.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a2fe128eb4edeabe11896cb6af88fca5346059f6c8d807e3b910069f39157869", size = 287516 }, - { url = "https://files.pythonhosted.org/packages/a1/ff/cd7479e703c39df7bdab431798cef89dc75010d8aa0ca2514c5b9321db27/frozenlist-1.5.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1a8ea951bbb6cacd492e3948b8da8c502a3f814f5d20935aae74b5df2b19cf3d", size = 283131 }, - { url = "https://files.pythonhosted.org/packages/59/a0/370941beb47d237eca4fbf27e4e91389fd68699e6f4b0ebcc95da463835b/frozenlist-1.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:de537c11e4aa01d37db0d403b57bd6f0546e71a82347a97c6a9f0dcc532b3a45", size = 261320 }, - { url = "https://files.pythonhosted.org/packages/b8/5f/c10123e8d64867bc9b4f2f510a32042a306ff5fcd7e2e09e5ae5100ee333/frozenlist-1.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c2623347b933fcb9095841f1cc5d4ff0b278addd743e0e966cb3d460278840d", size = 274877 }, - { url = "https://files.pythonhosted.org/packages/fa/79/38c505601ae29d4348f21706c5d89755ceded02a745016ba2f58bd5f1ea6/frozenlist-1.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cee6798eaf8b1416ef6909b06f7dc04b60755206bddc599f52232606e18179d3", size = 269592 }, - { url = "https://files.pythonhosted.org/packages/19/e2/39f3a53191b8204ba9f0bb574b926b73dd2efba2a2b9d2d730517e8f7622/frozenlist-1.5.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f5f9da7f5dbc00a604fe74aa02ae7c98bcede8a3b8b9666f9f86fc13993bc71a", size = 265934 }, - { url = "https://files.pythonhosted.org/packages/d5/c9/3075eb7f7f3a91f1a6b00284af4de0a65a9ae47084930916f5528144c9dd/frozenlist-1.5.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:90646abbc7a5d5c7c19461d2e3eeb76eb0b204919e6ece342feb6032c9325ae9", size = 283859 }, - { url = "https://files.pythonhosted.org/packages/05/f5/549f44d314c29408b962fa2b0e69a1a67c59379fb143b92a0a065ffd1f0f/frozenlist-1.5.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:bdac3c7d9b705d253b2ce370fde941836a5f8b3c5c2b8fd70940a3ea3af7f4f2", size = 287560 }, - { url = "https://files.pythonhosted.org/packages/9d/f8/cb09b3c24a3eac02c4c07a9558e11e9e244fb02bf62c85ac2106d1eb0c0b/frozenlist-1.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:03d33c2ddbc1816237a67f66336616416e2bbb6beb306e5f890f2eb22b959cdf", size = 277150 }, - { url = "https://files.pythonhosted.org/packages/37/48/38c2db3f54d1501e692d6fe058f45b6ad1b358d82cd19436efab80cfc965/frozenlist-1.5.0-cp311-cp311-win32.whl", hash = "sha256:237f6b23ee0f44066219dae14c70ae38a63f0440ce6750f868ee08775073f942", size = 45244 }, - { url = "https://files.pythonhosted.org/packages/ca/8c/2ddffeb8b60a4bce3b196c32fcc30d8830d4615e7b492ec2071da801b8ad/frozenlist-1.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:0cc974cc93d32c42e7b0f6cf242a6bd941c57c61b618e78b6c0a96cb72788c1d", size = 51634 }, - { url = "https://files.pythonhosted.org/packages/79/73/fa6d1a96ab7fd6e6d1c3500700963eab46813847f01ef0ccbaa726181dd5/frozenlist-1.5.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:31115ba75889723431aa9a4e77d5f398f5cf976eea3bdf61749731f62d4a4a21", size = 94026 }, - { url = "https://files.pythonhosted.org/packages/ab/04/ea8bf62c8868b8eada363f20ff1b647cf2e93377a7b284d36062d21d81d1/frozenlist-1.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7437601c4d89d070eac8323f121fcf25f88674627505334654fd027b091db09d", size = 54150 }, - { url = "https://files.pythonhosted.org/packages/d0/9a/8e479b482a6f2070b26bda572c5e6889bb3ba48977e81beea35b5ae13ece/frozenlist-1.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7948140d9f8ece1745be806f2bfdf390127cf1a763b925c4a805c603df5e697e", size = 51927 }, - { url = "https://files.pythonhosted.org/packages/e3/12/2aad87deb08a4e7ccfb33600871bbe8f0e08cb6d8224371387f3303654d7/frozenlist-1.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feeb64bc9bcc6b45c6311c9e9b99406660a9c05ca8a5b30d14a78555088b0b3a", size = 282647 }, - { url = "https://files.pythonhosted.org/packages/77/f2/07f06b05d8a427ea0060a9cef6e63405ea9e0d761846b95ef3fb3be57111/frozenlist-1.5.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:683173d371daad49cffb8309779e886e59c2f369430ad28fe715f66d08d4ab1a", size = 289052 }, - { url = "https://files.pythonhosted.org/packages/bd/9f/8bf45a2f1cd4aa401acd271b077989c9267ae8463e7c8b1eb0d3f561b65e/frozenlist-1.5.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7d57d8f702221405a9d9b40f9da8ac2e4a1a8b5285aac6100f3393675f0a85ee", size = 291719 }, - { url = "https://files.pythonhosted.org/packages/41/d1/1f20fd05a6c42d3868709b7604c9f15538a29e4f734c694c6bcfc3d3b935/frozenlist-1.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30c72000fbcc35b129cb09956836c7d7abf78ab5416595e4857d1cae8d6251a6", size = 267433 }, - { url = "https://files.pythonhosted.org/packages/af/f2/64b73a9bb86f5a89fb55450e97cd5c1f84a862d4ff90d9fd1a73ab0f64a5/frozenlist-1.5.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:000a77d6034fbad9b6bb880f7ec073027908f1b40254b5d6f26210d2dab1240e", size = 283591 }, - { url = "https://files.pythonhosted.org/packages/29/e2/ffbb1fae55a791fd6c2938dd9ea779509c977435ba3940b9f2e8dc9d5316/frozenlist-1.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5d7f5a50342475962eb18b740f3beecc685a15b52c91f7d975257e13e029eca9", size = 273249 }, - { url = "https://files.pythonhosted.org/packages/2e/6e/008136a30798bb63618a114b9321b5971172a5abddff44a100c7edc5ad4f/frozenlist-1.5.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:87f724d055eb4785d9be84e9ebf0f24e392ddfad00b3fe036e43f489fafc9039", size = 271075 }, - { url = "https://files.pythonhosted.org/packages/ae/f0/4e71e54a026b06724cec9b6c54f0b13a4e9e298cc8db0f82ec70e151f5ce/frozenlist-1.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:6e9080bb2fb195a046e5177f10d9d82b8a204c0736a97a153c2466127de87784", size = 285398 }, - { url = "https://files.pythonhosted.org/packages/4d/36/70ec246851478b1c0b59f11ef8ade9c482ff447c1363c2bd5fad45098b12/frozenlist-1.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9b93d7aaa36c966fa42efcaf716e6b3900438632a626fb09c049f6a2f09fc631", size = 294445 }, - { url = "https://files.pythonhosted.org/packages/37/e0/47f87544055b3349b633a03c4d94b405956cf2437f4ab46d0928b74b7526/frozenlist-1.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:52ef692a4bc60a6dd57f507429636c2af8b6046db8b31b18dac02cbc8f507f7f", size = 280569 }, - { url = "https://files.pythonhosted.org/packages/f9/7c/490133c160fb6b84ed374c266f42800e33b50c3bbab1652764e6e1fc498a/frozenlist-1.5.0-cp312-cp312-win32.whl", hash = "sha256:29d94c256679247b33a3dc96cce0f93cbc69c23bf75ff715919332fdbb6a32b8", size = 44721 }, - { url = "https://files.pythonhosted.org/packages/b1/56/4e45136ffc6bdbfa68c29ca56ef53783ef4c2fd395f7cbf99a2624aa9aaa/frozenlist-1.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:8969190d709e7c48ea386db202d708eb94bdb29207a1f269bab1196ce0dcca1f", size = 51329 }, - { url = "https://files.pythonhosted.org/packages/da/3b/915f0bca8a7ea04483622e84a9bd90033bab54bdf485479556c74fd5eaf5/frozenlist-1.5.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7a1a048f9215c90973402e26c01d1cff8a209e1f1b53f72b95c13db61b00f953", size = 91538 }, - { url = "https://files.pythonhosted.org/packages/c7/d1/a7c98aad7e44afe5306a2b068434a5830f1470675f0e715abb86eb15f15b/frozenlist-1.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:dd47a5181ce5fcb463b5d9e17ecfdb02b678cca31280639255ce9d0e5aa67af0", size = 52849 }, - { url = "https://files.pythonhosted.org/packages/3a/c8/76f23bf9ab15d5f760eb48701909645f686f9c64fbb8982674c241fbef14/frozenlist-1.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1431d60b36d15cda188ea222033eec8e0eab488f39a272461f2e6d9e1a8e63c2", size = 50583 }, - { url = "https://files.pythonhosted.org/packages/1f/22/462a3dd093d11df623179d7754a3b3269de3b42de2808cddef50ee0f4f48/frozenlist-1.5.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6482a5851f5d72767fbd0e507e80737f9c8646ae7fd303def99bfe813f76cf7f", size = 265636 }, - { url = "https://files.pythonhosted.org/packages/80/cf/e075e407fc2ae7328155a1cd7e22f932773c8073c1fc78016607d19cc3e5/frozenlist-1.5.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:44c49271a937625619e862baacbd037a7ef86dd1ee215afc298a417ff3270608", size = 270214 }, - { url = "https://files.pythonhosted.org/packages/a1/58/0642d061d5de779f39c50cbb00df49682832923f3d2ebfb0fedf02d05f7f/frozenlist-1.5.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:12f78f98c2f1c2429d42e6a485f433722b0061d5c0b0139efa64f396efb5886b", size = 273905 }, - { url = "https://files.pythonhosted.org/packages/ab/66/3fe0f5f8f2add5b4ab7aa4e199f767fd3b55da26e3ca4ce2cc36698e50c4/frozenlist-1.5.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ce3aa154c452d2467487765e3adc730a8c153af77ad84096bc19ce19a2400840", size = 250542 }, - { url = "https://files.pythonhosted.org/packages/f6/b8/260791bde9198c87a465224e0e2bb62c4e716f5d198fc3a1dacc4895dbd1/frozenlist-1.5.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b7dc0c4338e6b8b091e8faf0db3168a37101943e687f373dce00959583f7439", size = 267026 }, - { url = "https://files.pythonhosted.org/packages/2e/a4/3d24f88c527f08f8d44ade24eaee83b2627793fa62fa07cbb7ff7a2f7d42/frozenlist-1.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:45e0896250900b5aa25180f9aec243e84e92ac84bd4a74d9ad4138ef3f5c97de", size = 257690 }, - { url = "https://files.pythonhosted.org/packages/de/9a/d311d660420b2beeff3459b6626f2ab4fb236d07afbdac034a4371fe696e/frozenlist-1.5.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:561eb1c9579d495fddb6da8959fd2a1fca2c6d060d4113f5844b433fc02f2641", size = 253893 }, - { url = "https://files.pythonhosted.org/packages/c6/23/e491aadc25b56eabd0f18c53bb19f3cdc6de30b2129ee0bc39cd387cd560/frozenlist-1.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:df6e2f325bfee1f49f81aaac97d2aa757c7646534a06f8f577ce184afe2f0a9e", size = 267006 }, - { url = "https://files.pythonhosted.org/packages/08/c4/ab918ce636a35fb974d13d666dcbe03969592aeca6c3ab3835acff01f79c/frozenlist-1.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:140228863501b44b809fb39ec56b5d4071f4d0aa6d216c19cbb08b8c5a7eadb9", size = 276157 }, - { url = "https://files.pythonhosted.org/packages/c0/29/3b7a0bbbbe5a34833ba26f686aabfe982924adbdcafdc294a7a129c31688/frozenlist-1.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7707a25d6a77f5d27ea7dc7d1fc608aa0a478193823f88511ef5e6b8a48f9d03", size = 264642 }, - { url = "https://files.pythonhosted.org/packages/ab/42/0595b3dbffc2e82d7fe658c12d5a5bafcd7516c6bf2d1d1feb5387caa9c1/frozenlist-1.5.0-cp313-cp313-win32.whl", hash = "sha256:31a9ac2b38ab9b5a8933b693db4939764ad3f299fcaa931a3e605bc3460e693c", size = 44914 }, - { url = "https://files.pythonhosted.org/packages/17/c4/b7db1206a3fea44bf3b838ca61deb6f74424a8a5db1dd53ecb21da669be6/frozenlist-1.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:11aabdd62b8b9c4b84081a3c246506d1cddd2dd93ff0ad53ede5defec7886b28", size = 51167 }, - { url = "https://files.pythonhosted.org/packages/c6/c8/a5be5b7550c10858fcf9b0ea054baccab474da77d37f1e828ce043a3a5d4/frozenlist-1.5.0-py3-none-any.whl", hash = "sha256:d994863bba198a4a518b467bb971c56e1db3f180a25c6cf7bb1949c267f748c3", size = 11901 }, -] - [[package]] name = "ghp-import" version = "2.1.0" @@ -766,57 +560,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f7/ec/67fbef5d497f86283db54c22eec6f6140243aae73265799baaaa19cd17fb/ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619", size = 11034 }, ] -[[package]] -name = "greenlet" -version = "3.1.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2f/ff/df5fede753cc10f6a5be0931204ea30c35fa2f2ea7a35b25bdaf4fe40e46/greenlet-3.1.1.tar.gz", hash = "sha256:4ce3ac6cdb6adf7946475d7ef31777c26d94bccc377e070a7986bd2d5c515467", size = 186022 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/25/90/5234a78dc0ef6496a6eb97b67a42a8e96742a56f7dc808cb954a85390448/greenlet-3.1.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:0bbae94a29c9e5c7e4a2b7f0aae5c17e8e90acbfd3bf6270eeba60c39fce3563", size = 271235 }, - { url = "https://files.pythonhosted.org/packages/7c/16/cd631fa0ab7d06ef06387135b7549fdcc77d8d859ed770a0d28e47b20972/greenlet-3.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fde093fb93f35ca72a556cf72c92ea3ebfda3d79fc35bb19fbe685853869a83", size = 637168 }, - { url = "https://files.pythonhosted.org/packages/2f/b1/aed39043a6fec33c284a2c9abd63ce191f4f1a07319340ffc04d2ed3256f/greenlet-3.1.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:36b89d13c49216cadb828db8dfa6ce86bbbc476a82d3a6c397f0efae0525bdd0", size = 648826 }, - { url = "https://files.pythonhosted.org/packages/76/25/40e0112f7f3ebe54e8e8ed91b2b9f970805143efef16d043dfc15e70f44b/greenlet-3.1.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94b6150a85e1b33b40b1464a3f9988dcc5251d6ed06842abff82e42632fac120", size = 644443 }, - { url = "https://files.pythonhosted.org/packages/fb/2f/3850b867a9af519794784a7eeed1dd5bc68ffbcc5b28cef703711025fd0a/greenlet-3.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93147c513fac16385d1036b7e5b102c7fbbdb163d556b791f0f11eada7ba65dc", size = 643295 }, - { url = "https://files.pythonhosted.org/packages/cf/69/79e4d63b9387b48939096e25115b8af7cd8a90397a304f92436bcb21f5b2/greenlet-3.1.1-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:da7a9bff22ce038e19bf62c4dd1ec8391062878710ded0a845bcf47cc0200617", size = 599544 }, - { url = "https://files.pythonhosted.org/packages/46/1d/44dbcb0e6c323bd6f71b8c2f4233766a5faf4b8948873225d34a0b7efa71/greenlet-3.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b2795058c23988728eec1f36a4e5e4ebad22f8320c85f3587b539b9ac84128d7", size = 1125456 }, - { url = "https://files.pythonhosted.org/packages/e0/1d/a305dce121838d0278cee39d5bb268c657f10a5363ae4b726848f833f1bb/greenlet-3.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ed10eac5830befbdd0c32f83e8aa6288361597550ba669b04c48f0f9a2c843c6", size = 1149111 }, - { url = "https://files.pythonhosted.org/packages/96/28/d62835fb33fb5652f2e98d34c44ad1a0feacc8b1d3f1aecab035f51f267d/greenlet-3.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:77c386de38a60d1dfb8e55b8c1101d68c79dfdd25c7095d51fec2dd800892b80", size = 298392 }, - { url = "https://files.pythonhosted.org/packages/28/62/1c2665558618553c42922ed47a4e6d6527e2fa3516a8256c2f431c5d0441/greenlet-3.1.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:e4d333e558953648ca09d64f13e6d8f0523fa705f51cae3f03b5983489958c70", size = 272479 }, - { url = "https://files.pythonhosted.org/packages/76/9d/421e2d5f07285b6e4e3a676b016ca781f63cfe4a0cd8eaecf3fd6f7a71ae/greenlet-3.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09fc016b73c94e98e29af67ab7b9a879c307c6731a2c9da0db5a7d9b7edd1159", size = 640404 }, - { url = "https://files.pythonhosted.org/packages/e5/de/6e05f5c59262a584e502dd3d261bbdd2c97ab5416cc9c0b91ea38932a901/greenlet-3.1.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d5e975ca70269d66d17dd995dafc06f1b06e8cb1ec1e9ed54c1d1e4a7c4cf26e", size = 652813 }, - { url = "https://files.pythonhosted.org/packages/49/93/d5f93c84241acdea15a8fd329362c2c71c79e1a507c3f142a5d67ea435ae/greenlet-3.1.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b2813dc3de8c1ee3f924e4d4227999285fd335d1bcc0d2be6dc3f1f6a318ec1", size = 648517 }, - { url = "https://files.pythonhosted.org/packages/15/85/72f77fc02d00470c86a5c982b8daafdf65d38aefbbe441cebff3bf7037fc/greenlet-3.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e347b3bfcf985a05e8c0b7d462ba6f15b1ee1c909e2dcad795e49e91b152c383", size = 647831 }, - { url = "https://files.pythonhosted.org/packages/f7/4b/1c9695aa24f808e156c8f4813f685d975ca73c000c2a5056c514c64980f6/greenlet-3.1.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e8f8c9cb53cdac7ba9793c276acd90168f416b9ce36799b9b885790f8ad6c0a", size = 602413 }, - { url = "https://files.pythonhosted.org/packages/76/70/ad6e5b31ef330f03b12559d19fda2606a522d3849cde46b24f223d6d1619/greenlet-3.1.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:62ee94988d6b4722ce0028644418d93a52429e977d742ca2ccbe1c4f4a792511", size = 1129619 }, - { url = "https://files.pythonhosted.org/packages/f4/fb/201e1b932e584066e0f0658b538e73c459b34d44b4bd4034f682423bc801/greenlet-3.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1776fd7f989fc6b8d8c8cb8da1f6b82c5814957264d1f6cf818d475ec2bf6395", size = 1155198 }, - { url = "https://files.pythonhosted.org/packages/12/da/b9ed5e310bb8b89661b80cbcd4db5a067903bbcd7fc854923f5ebb4144f0/greenlet-3.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:48ca08c771c268a768087b408658e216133aecd835c0ded47ce955381105ba39", size = 298930 }, - { url = "https://files.pythonhosted.org/packages/7d/ec/bad1ac26764d26aa1353216fcbfa4670050f66d445448aafa227f8b16e80/greenlet-3.1.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:4afe7ea89de619adc868e087b4d2359282058479d7cfb94970adf4b55284574d", size = 274260 }, - { url = "https://files.pythonhosted.org/packages/66/d4/c8c04958870f482459ab5956c2942c4ec35cac7fe245527f1039837c17a9/greenlet-3.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f406b22b7c9a9b4f8aa9d2ab13d6ae0ac3e85c9a809bd590ad53fed2bf70dc79", size = 649064 }, - { url = "https://files.pythonhosted.org/packages/51/41/467b12a8c7c1303d20abcca145db2be4e6cd50a951fa30af48b6ec607581/greenlet-3.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c3a701fe5a9695b238503ce5bbe8218e03c3bcccf7e204e455e7462d770268aa", size = 663420 }, - { url = "https://files.pythonhosted.org/packages/27/8f/2a93cd9b1e7107d5c7b3b7816eeadcac2ebcaf6d6513df9abaf0334777f6/greenlet-3.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2846930c65b47d70b9d178e89c7e1a69c95c1f68ea5aa0a58646b7a96df12441", size = 658035 }, - { url = "https://files.pythonhosted.org/packages/57/5c/7c6f50cb12be092e1dccb2599be5a942c3416dbcfb76efcf54b3f8be4d8d/greenlet-3.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99cfaa2110534e2cf3ba31a7abcac9d328d1d9f1b95beede58294a60348fba36", size = 660105 }, - { url = "https://files.pythonhosted.org/packages/f1/66/033e58a50fd9ec9df00a8671c74f1f3a320564c6415a4ed82a1c651654ba/greenlet-3.1.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1443279c19fca463fc33e65ef2a935a5b09bb90f978beab37729e1c3c6c25fe9", size = 613077 }, - { url = "https://files.pythonhosted.org/packages/19/c5/36384a06f748044d06bdd8776e231fadf92fc896bd12cb1c9f5a1bda9578/greenlet-3.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b7cede291382a78f7bb5f04a529cb18e068dd29e0fb27376074b6d0317bf4dd0", size = 1135975 }, - { url = "https://files.pythonhosted.org/packages/38/f9/c0a0eb61bdf808d23266ecf1d63309f0e1471f284300ce6dac0ae1231881/greenlet-3.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:23f20bb60ae298d7d8656c6ec6db134bca379ecefadb0b19ce6f19d1f232a942", size = 1163955 }, - { url = "https://files.pythonhosted.org/packages/43/21/a5d9df1d21514883333fc86584c07c2b49ba7c602e670b174bd73cfc9c7f/greenlet-3.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:7124e16b4c55d417577c2077be379514321916d5790fa287c9ed6f23bd2ffd01", size = 299655 }, - { url = "https://files.pythonhosted.org/packages/f3/57/0db4940cd7bb461365ca8d6fd53e68254c9dbbcc2b452e69d0d41f10a85e/greenlet-3.1.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:05175c27cb459dcfc05d026c4232f9de8913ed006d42713cb8a5137bd49375f1", size = 272990 }, - { url = "https://files.pythonhosted.org/packages/1c/ec/423d113c9f74e5e402e175b157203e9102feeb7088cee844d735b28ef963/greenlet-3.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:935e943ec47c4afab8965954bf49bfa639c05d4ccf9ef6e924188f762145c0ff", size = 649175 }, - { url = "https://files.pythonhosted.org/packages/a9/46/ddbd2db9ff209186b7b7c621d1432e2f21714adc988703dbdd0e65155c77/greenlet-3.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:667a9706c970cb552ede35aee17339a18e8f2a87a51fba2ed39ceeeb1004798a", size = 663425 }, - { url = "https://files.pythonhosted.org/packages/bc/f9/9c82d6b2b04aa37e38e74f0c429aece5eeb02bab6e3b98e7db89b23d94c6/greenlet-3.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b8a678974d1f3aa55f6cc34dc480169d58f2e6d8958895d68845fa4ab566509e", size = 657736 }, - { url = "https://files.pythonhosted.org/packages/d9/42/b87bc2a81e3a62c3de2b0d550bf91a86939442b7ff85abb94eec3fc0e6aa/greenlet-3.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efc0f674aa41b92da8c49e0346318c6075d734994c3c4e4430b1c3f853e498e4", size = 660347 }, - { url = "https://files.pythonhosted.org/packages/37/fa/71599c3fd06336cdc3eac52e6871cfebab4d9d70674a9a9e7a482c318e99/greenlet-3.1.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0153404a4bb921f0ff1abeb5ce8a5131da56b953eda6e14b88dc6bbc04d2049e", size = 615583 }, - { url = "https://files.pythonhosted.org/packages/4e/96/e9ef85de031703ee7a4483489b40cf307f93c1824a02e903106f2ea315fe/greenlet-3.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:275f72decf9932639c1c6dd1013a1bc266438eb32710016a1c742df5da6e60a1", size = 1133039 }, - { url = "https://files.pythonhosted.org/packages/87/76/b2b6362accd69f2d1889db61a18c94bc743e961e3cab344c2effaa4b4a25/greenlet-3.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c4aab7f6381f38a4b42f269057aee279ab0fc7bf2e929e3d4abfae97b682a12c", size = 1160716 }, - { url = "https://files.pythonhosted.org/packages/1f/1b/54336d876186920e185066d8c3024ad55f21d7cc3683c856127ddb7b13ce/greenlet-3.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:b42703b1cf69f2aa1df7d1030b9d77d3e584a70755674d60e710f0af570f3761", size = 299490 }, - { url = "https://files.pythonhosted.org/packages/5f/17/bea55bf36990e1638a2af5ba10c1640273ef20f627962cf97107f1e5d637/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1695e76146579f8c06c1509c7ce4dfe0706f49c6831a817ac04eebb2fd02011", size = 643731 }, - { url = "https://files.pythonhosted.org/packages/78/d2/aa3d2157f9ab742a08e0fd8f77d4699f37c22adfbfeb0c610a186b5f75e0/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7876452af029456b3f3549b696bb36a06db7c90747740c5302f74a9e9fa14b13", size = 649304 }, - { url = "https://files.pythonhosted.org/packages/f1/8e/d0aeffe69e53ccff5a28fa86f07ad1d2d2d6537a9506229431a2a02e2f15/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ead44c85f8ab905852d3de8d86f6f8baf77109f9da589cb4fa142bd3b57b475", size = 646537 }, - { url = "https://files.pythonhosted.org/packages/05/79/e15408220bbb989469c8871062c97c6c9136770657ba779711b90870d867/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8320f64b777d00dd7ccdade271eaf0cad6636343293a25074cc5566160e4de7b", size = 642506 }, - { url = "https://files.pythonhosted.org/packages/18/87/470e01a940307796f1d25f8167b551a968540fbe0551c0ebb853cb527dd6/greenlet-3.1.1-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6510bf84a6b643dabba74d3049ead221257603a253d0a9873f55f6a59a65f822", size = 602753 }, - { url = "https://files.pythonhosted.org/packages/e2/72/576815ba674eddc3c25028238f74d7b8068902b3968cbe456771b166455e/greenlet-3.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:04b013dc07c96f83134b1e99888e7a79979f1a247e2a9f59697fa14b5862ed01", size = 1122731 }, - { url = "https://files.pythonhosted.org/packages/ac/38/08cc303ddddc4b3d7c628c3039a61a3aae36c241ed01393d00c2fd663473/greenlet-3.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:411f015496fec93c1c8cd4e5238da364e1da7a124bcb293f085bf2860c32c6f6", size = 1142112 }, -] - [[package]] name = "griffe" version = "1.6.0" @@ -866,15 +609,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 }, ] -[[package]] -name = "httpx-sse" -version = "0.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4c/60/8f4281fa9bbf3c8034fd54c0e7412e66edbab6bc74c4996bd616f8d0406e/httpx-sse-0.4.0.tar.gz", hash = "sha256:1e81a3a3070ce322add1d3529ed42eb5f70817f45ed6ec915ab753f961139721", size = 12624 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e1/9b/a181f281f65d776426002f330c31849b86b31fc9d848db62e16f03ff739f/httpx_sse-0.4.0-py3-none-any.whl", hash = "sha256:f329af6eae57eaa2bdfd962b42524764af68075ea87370a2de920af5341e318f", size = 7819 }, -] - [[package]] name = "identify" version = "2.6.7" @@ -944,242 +678,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899 }, ] -[[package]] -name = "jiter" -version = "0.9.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1e/c2/e4562507f52f0af7036da125bb699602ead37a2332af0788f8e0a3417f36/jiter-0.9.0.tar.gz", hash = "sha256:aadba0964deb424daa24492abc3d229c60c4a31bfee205aedbf1acc7639d7893", size = 162604 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b0/82/39f7c9e67b3b0121f02a0b90d433626caa95a565c3d2449fea6bcfa3f5f5/jiter-0.9.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:816ec9b60fdfd1fec87da1d7ed46c66c44ffec37ab2ef7de5b147b2fce3fd5ad", size = 314540 }, - { url = "https://files.pythonhosted.org/packages/01/07/7bf6022c5a152fca767cf5c086bb41f7c28f70cf33ad259d023b53c0b858/jiter-0.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9b1d3086f8a3ee0194ecf2008cf81286a5c3e540d977fa038ff23576c023c0ea", size = 321065 }, - { url = "https://files.pythonhosted.org/packages/6c/b2/de3f3446ecba7c48f317568e111cc112613da36c7b29a6de45a1df365556/jiter-0.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1339f839b91ae30b37c409bf16ccd3dc453e8b8c3ed4bd1d6a567193651a4a51", size = 341664 }, - { url = "https://files.pythonhosted.org/packages/13/cf/6485a4012af5d407689c91296105fcdb080a3538e0658d2abf679619c72f/jiter-0.9.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ffba79584b3b670fefae66ceb3a28822365d25b7bf811e030609a3d5b876f538", size = 364635 }, - { url = "https://files.pythonhosted.org/packages/0d/f7/4a491c568f005553240b486f8e05c82547340572d5018ef79414b4449327/jiter-0.9.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cfc7d0a8e899089d11f065e289cb5b2daf3d82fbe028f49b20d7b809193958d", size = 406288 }, - { url = "https://files.pythonhosted.org/packages/d3/ca/f4263ecbce7f5e6bded8f52a9f1a66540b270c300b5c9f5353d163f9ac61/jiter-0.9.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e00a1a2bbfaaf237e13c3d1592356eab3e9015d7efd59359ac8b51eb56390a12", size = 397499 }, - { url = "https://files.pythonhosted.org/packages/ac/a2/522039e522a10bac2f2194f50e183a49a360d5f63ebf46f6d890ef8aa3f9/jiter-0.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1d9870561eb26b11448854dce0ff27a9a27cb616b632468cafc938de25e9e51", size = 352926 }, - { url = "https://files.pythonhosted.org/packages/b1/67/306a5c5abc82f2e32bd47333a1c9799499c1c3a415f8dde19dbf876f00cb/jiter-0.9.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9872aeff3f21e437651df378cb75aeb7043e5297261222b6441a620218b58708", size = 384506 }, - { url = "https://files.pythonhosted.org/packages/0f/89/c12fe7b65a4fb74f6c0d7b5119576f1f16c79fc2953641f31b288fad8a04/jiter-0.9.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:1fd19112d1049bdd47f17bfbb44a2c0001061312dcf0e72765bfa8abd4aa30e5", size = 520621 }, - { url = "https://files.pythonhosted.org/packages/c4/2b/d57900c5c06e6273fbaa76a19efa74dbc6e70c7427ab421bf0095dfe5d4a/jiter-0.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6ef5da104664e526836070e4a23b5f68dec1cc673b60bf1edb1bfbe8a55d0678", size = 512613 }, - { url = "https://files.pythonhosted.org/packages/89/05/d8b90bfb21e58097d5a4e0224f2940568366f68488a079ae77d4b2653500/jiter-0.9.0-cp310-cp310-win32.whl", hash = "sha256:cb12e6d65ebbefe5518de819f3eda53b73187b7089040b2d17f5b39001ff31c4", size = 206613 }, - { url = "https://files.pythonhosted.org/packages/2c/1d/5767f23f88e4f885090d74bbd2755518050a63040c0f59aa059947035711/jiter-0.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:c43ca669493626d8672be3b645dbb406ef25af3f4b6384cfd306da7eb2e70322", size = 208371 }, - { url = "https://files.pythonhosted.org/packages/23/44/e241a043f114299254e44d7e777ead311da400517f179665e59611ab0ee4/jiter-0.9.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:6c4d99c71508912a7e556d631768dcdef43648a93660670986916b297f1c54af", size = 314654 }, - { url = "https://files.pythonhosted.org/packages/fb/1b/a7e5e42db9fa262baaa9489d8d14ca93f8663e7f164ed5e9acc9f467fc00/jiter-0.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8f60fb8ce7df529812bf6c625635a19d27f30806885139e367af93f6e734ef58", size = 320909 }, - { url = "https://files.pythonhosted.org/packages/60/bf/8ebdfce77bc04b81abf2ea316e9c03b4a866a7d739cf355eae4d6fd9f6fe/jiter-0.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:51c4e1a4f8ea84d98b7b98912aa4290ac3d1eabfde8e3c34541fae30e9d1f08b", size = 341733 }, - { url = "https://files.pythonhosted.org/packages/a8/4e/754ebce77cff9ab34d1d0fa0fe98f5d42590fd33622509a3ba6ec37ff466/jiter-0.9.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f4c677c424dc76684fea3e7285a7a2a7493424bea89ac441045e6a1fb1d7b3b", size = 365097 }, - { url = "https://files.pythonhosted.org/packages/32/2c/6019587e6f5844c612ae18ca892f4cd7b3d8bbf49461ed29e384a0f13d98/jiter-0.9.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2221176dfec87f3470b21e6abca056e6b04ce9bff72315cb0b243ca9e835a4b5", size = 406603 }, - { url = "https://files.pythonhosted.org/packages/da/e9/c9e6546c817ab75a1a7dab6dcc698e62e375e1017113e8e983fccbd56115/jiter-0.9.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3c7adb66f899ffa25e3c92bfcb593391ee1947dbdd6a9a970e0d7e713237d572", size = 396625 }, - { url = "https://files.pythonhosted.org/packages/be/bd/976b458add04271ebb5a255e992bd008546ea04bb4dcadc042a16279b4b4/jiter-0.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c98d27330fdfb77913c1097a7aab07f38ff2259048949f499c9901700789ac15", size = 351832 }, - { url = "https://files.pythonhosted.org/packages/07/51/fe59e307aaebec9265dbad44d9d4381d030947e47b0f23531579b9a7c2df/jiter-0.9.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:eda3f8cc74df66892b1d06b5d41a71670c22d95a1ca2cbab73654745ce9d0419", size = 384590 }, - { url = "https://files.pythonhosted.org/packages/db/55/5dcd2693794d8e6f4889389ff66ef3be557a77f8aeeca8973a97a7c00557/jiter-0.9.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:dd5ab5ddc11418dce28343123644a100f487eaccf1de27a459ab36d6cca31043", size = 520690 }, - { url = "https://files.pythonhosted.org/packages/54/d5/9f51dc90985e9eb251fbbb747ab2b13b26601f16c595a7b8baba964043bd/jiter-0.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:42f8a68a69f047b310319ef8e2f52fdb2e7976fb3313ef27df495cf77bcad965", size = 512649 }, - { url = "https://files.pythonhosted.org/packages/a6/e5/4e385945179bcf128fa10ad8dca9053d717cbe09e258110e39045c881fe5/jiter-0.9.0-cp311-cp311-win32.whl", hash = "sha256:a25519efb78a42254d59326ee417d6f5161b06f5da827d94cf521fed961b1ff2", size = 206920 }, - { url = "https://files.pythonhosted.org/packages/4c/47/5e0b94c603d8e54dd1faab439b40b832c277d3b90743e7835879ab663757/jiter-0.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:923b54afdd697dfd00d368b7ccad008cccfeb1efb4e621f32860c75e9f25edbd", size = 210119 }, - { url = "https://files.pythonhosted.org/packages/af/d7/c55086103d6f29b694ec79156242304adf521577530d9031317ce5338c59/jiter-0.9.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:7b46249cfd6c48da28f89eb0be3f52d6fdb40ab88e2c66804f546674e539ec11", size = 309203 }, - { url = "https://files.pythonhosted.org/packages/b0/01/f775dfee50beb420adfd6baf58d1c4d437de41c9b666ddf127c065e5a488/jiter-0.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:609cf3c78852f1189894383cf0b0b977665f54cb38788e3e6b941fa6d982c00e", size = 319678 }, - { url = "https://files.pythonhosted.org/packages/ab/b8/09b73a793714726893e5d46d5c534a63709261af3d24444ad07885ce87cb/jiter-0.9.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d726a3890a54561e55a9c5faea1f7655eda7f105bd165067575ace6e65f80bb2", size = 341816 }, - { url = "https://files.pythonhosted.org/packages/35/6f/b8f89ec5398b2b0d344257138182cc090302854ed63ed9c9051e9c673441/jiter-0.9.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2e89dc075c1fef8fa9be219e249f14040270dbc507df4215c324a1839522ea75", size = 364152 }, - { url = "https://files.pythonhosted.org/packages/9b/ca/978cc3183113b8e4484cc7e210a9ad3c6614396e7abd5407ea8aa1458eef/jiter-0.9.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:04e8ffa3c353b1bc4134f96f167a2082494351e42888dfcf06e944f2729cbe1d", size = 406991 }, - { url = "https://files.pythonhosted.org/packages/13/3a/72861883e11a36d6aa314b4922125f6ae90bdccc225cd96d24cc78a66385/jiter-0.9.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:203f28a72a05ae0e129b3ed1f75f56bc419d5f91dfacd057519a8bd137b00c42", size = 395824 }, - { url = "https://files.pythonhosted.org/packages/87/67/22728a86ef53589c3720225778f7c5fdb617080e3deaed58b04789418212/jiter-0.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fca1a02ad60ec30bb230f65bc01f611c8608b02d269f998bc29cca8619a919dc", size = 351318 }, - { url = "https://files.pythonhosted.org/packages/69/b9/f39728e2e2007276806d7a6609cda7fac44ffa28ca0d02c49a4f397cc0d9/jiter-0.9.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:237e5cee4d5d2659aaf91bbf8ec45052cc217d9446070699441a91b386ae27dc", size = 384591 }, - { url = "https://files.pythonhosted.org/packages/eb/8f/8a708bc7fd87b8a5d861f1c118a995eccbe6d672fe10c9753e67362d0dd0/jiter-0.9.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:528b6b71745e7326eed73c53d4aa57e2a522242320b6f7d65b9c5af83cf49b6e", size = 520746 }, - { url = "https://files.pythonhosted.org/packages/95/1e/65680c7488bd2365dbd2980adaf63c562d3d41d3faac192ebc7ef5b4ae25/jiter-0.9.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9f48e86b57bc711eb5acdfd12b6cb580a59cc9a993f6e7dcb6d8b50522dcd50d", size = 512754 }, - { url = "https://files.pythonhosted.org/packages/78/f3/fdc43547a9ee6e93c837685da704fb6da7dba311fc022e2766d5277dfde5/jiter-0.9.0-cp312-cp312-win32.whl", hash = "sha256:699edfde481e191d81f9cf6d2211debbfe4bd92f06410e7637dffb8dd5dfde06", size = 207075 }, - { url = "https://files.pythonhosted.org/packages/cd/9d/742b289016d155f49028fe1bfbeb935c9bf0ffeefdf77daf4a63a42bb72b/jiter-0.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:099500d07b43f61d8bd780466d429c45a7b25411b334c60ca875fa775f68ccb0", size = 207999 }, - { url = "https://files.pythonhosted.org/packages/e7/1b/4cd165c362e8f2f520fdb43245e2b414f42a255921248b4f8b9c8d871ff1/jiter-0.9.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:2764891d3f3e8b18dce2cff24949153ee30c9239da7c00f032511091ba688ff7", size = 308197 }, - { url = "https://files.pythonhosted.org/packages/13/aa/7a890dfe29c84c9a82064a9fe36079c7c0309c91b70c380dc138f9bea44a/jiter-0.9.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:387b22fbfd7a62418d5212b4638026d01723761c75c1c8232a8b8c37c2f1003b", size = 318160 }, - { url = "https://files.pythonhosted.org/packages/6a/38/5888b43fc01102f733f085673c4f0be5a298f69808ec63de55051754e390/jiter-0.9.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d8da8629ccae3606c61d9184970423655fb4e33d03330bcdfe52d234d32f69", size = 341259 }, - { url = "https://files.pythonhosted.org/packages/3d/5e/bbdbb63305bcc01006de683b6228cd061458b9b7bb9b8d9bc348a58e5dc2/jiter-0.9.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a1be73d8982bdc278b7b9377426a4b44ceb5c7952073dd7488e4ae96b88e1103", size = 363730 }, - { url = "https://files.pythonhosted.org/packages/75/85/53a3edc616992fe4af6814c25f91ee3b1e22f7678e979b6ea82d3bc0667e/jiter-0.9.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2228eaaaa111ec54b9e89f7481bffb3972e9059301a878d085b2b449fbbde635", size = 405126 }, - { url = "https://files.pythonhosted.org/packages/ae/b3/1ee26b12b2693bd3f0b71d3188e4e5d817b12e3c630a09e099e0a89e28fa/jiter-0.9.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:11509bfecbc319459647d4ac3fd391d26fdf530dad00c13c4dadabf5b81f01a4", size = 393668 }, - { url = "https://files.pythonhosted.org/packages/11/87/e084ce261950c1861773ab534d49127d1517b629478304d328493f980791/jiter-0.9.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f22238da568be8bbd8e0650e12feeb2cfea15eda4f9fc271d3b362a4fa0604d", size = 352350 }, - { url = "https://files.pythonhosted.org/packages/f0/06/7dca84b04987e9df563610aa0bc154ea176e50358af532ab40ffb87434df/jiter-0.9.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:17f5d55eb856597607562257c8e36c42bc87f16bef52ef7129b7da11afc779f3", size = 384204 }, - { url = "https://files.pythonhosted.org/packages/16/2f/82e1c6020db72f397dd070eec0c85ebc4df7c88967bc86d3ce9864148f28/jiter-0.9.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:6a99bed9fbb02f5bed416d137944419a69aa4c423e44189bc49718859ea83bc5", size = 520322 }, - { url = "https://files.pythonhosted.org/packages/36/fd/4f0cd3abe83ce208991ca61e7e5df915aa35b67f1c0633eb7cf2f2e88ec7/jiter-0.9.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:e057adb0cd1bd39606100be0eafe742de2de88c79df632955b9ab53a086b3c8d", size = 512184 }, - { url = "https://files.pythonhosted.org/packages/a0/3c/8a56f6d547731a0b4410a2d9d16bf39c861046f91f57c98f7cab3d2aa9ce/jiter-0.9.0-cp313-cp313-win32.whl", hash = "sha256:f7e6850991f3940f62d387ccfa54d1a92bd4bb9f89690b53aea36b4364bcab53", size = 206504 }, - { url = "https://files.pythonhosted.org/packages/f4/1c/0c996fd90639acda75ed7fa698ee5fd7d80243057185dc2f63d4c1c9f6b9/jiter-0.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:c8ae3bf27cd1ac5e6e8b7a27487bf3ab5f82318211ec2e1346a5b058756361f7", size = 204943 }, - { url = "https://files.pythonhosted.org/packages/78/0f/77a63ca7aa5fed9a1b9135af57e190d905bcd3702b36aca46a01090d39ad/jiter-0.9.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f0b2827fb88dda2cbecbbc3e596ef08d69bda06c6f57930aec8e79505dc17001", size = 317281 }, - { url = "https://files.pythonhosted.org/packages/f9/39/a3a1571712c2bf6ec4c657f0d66da114a63a2e32b7e4eb8e0b83295ee034/jiter-0.9.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:062b756ceb1d40b0b28f326cba26cfd575a4918415b036464a52f08632731e5a", size = 350273 }, - { url = "https://files.pythonhosted.org/packages/ee/47/3729f00f35a696e68da15d64eb9283c330e776f3b5789bac7f2c0c4df209/jiter-0.9.0-cp313-cp313t-win_amd64.whl", hash = "sha256:6f7838bc467ab7e8ef9f387bd6de195c43bad82a569c1699cb822f6609dd4cdf", size = 206867 }, -] - -[[package]] -name = "jsonpatch" -version = "1.33" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jsonpointer" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/42/78/18813351fe5d63acad16aec57f94ec2b70a09e53ca98145589e185423873/jsonpatch-1.33.tar.gz", hash = "sha256:9fcd4009c41e6d12348b4a0ff2563ba56a2923a7dfee731d004e212e1ee5030c", size = 21699 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/73/07/02e16ed01e04a374e644b575638ec7987ae846d25ad97bcc9945a3ee4b0e/jsonpatch-1.33-py2.py3-none-any.whl", hash = "sha256:0ae28c0cd062bbd8b8ecc26d7d164fbbea9652a1a3693f3b956c1eae5145dade", size = 12898 }, -] - -[[package]] -name = "jsonpointer" -version = "3.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6a/0a/eebeb1fa92507ea94016a2a790b93c2ae41a7e18778f85471dc54475ed25/jsonpointer-3.0.0.tar.gz", hash = "sha256:2b2d729f2091522d61c3b31f82e11870f60b68f43fbc705cb76bf4b832af59ef", size = 9114 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/71/92/5e77f98553e9e75130c78900d000368476aed74276eb8ae8796f65f00918/jsonpointer-3.0.0-py2.py3-none-any.whl", hash = "sha256:13e088adc14fca8b6aa8177c044e12701e6ad4b28ff10e65f2267a90109c9942", size = 7595 }, -] - -[[package]] -name = "langchain" -version = "0.3.23" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "async-timeout", marker = "python_full_version < '3.11'" }, - { name = "langchain-core" }, - { name = "langchain-text-splitters" }, - { name = "langsmith" }, - { name = "pydantic" }, - { name = "pyyaml" }, - { name = "requests" }, - { name = "sqlalchemy" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/47/ea/b0de568ca17614d5c00275c4ca506af4139cc7c51d0418802b2447055c00/langchain-0.3.23.tar.gz", hash = "sha256:d95004afe8abebb52d51d6026270248da3f4b53d93e9bf699f76005e0c83ad34", size = 10225576 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d4/49/6e933837da1931c9db745967282ff8bfff51bc3faec0eade846b12203b75/langchain-0.3.23-py3-none-any.whl", hash = "sha256:084f05ee7e80b7c3f378ebadd7309f2a37868ce2906fa0ae64365a67843ade3d", size = 1011778 }, -] - -[[package]] -name = "langchain-community" -version = "0.3.21" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiohttp" }, - { name = "dataclasses-json" }, - { name = "httpx-sse" }, - { name = "langchain" }, - { name = "langchain-core" }, - { name = "langsmith" }, - { name = "numpy" }, - { name = "pydantic-settings" }, - { name = "pyyaml" }, - { name = "requests" }, - { name = "sqlalchemy" }, - { name = "tenacity" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d8/be/5288a737069570741d46390028b4e8518354329345294ca89fcb2d44a9c1/langchain_community-0.3.21.tar.gz", hash = "sha256:b87b9992cbeea7553ed93e3d39faf9893a8690318485f7dc861751c7878729f7", size = 33226597 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bb/72/4046a132a180b569265bc8aa7ecd6f958f6c11085bdf68c7e1bbe52f1907/langchain_community-0.3.21-py3-none-any.whl", hash = "sha256:8cb9bbb7ef15e5eea776193528dd0e0e1299047146d0c78b6c696ae2dc62e81f", size = 2526687 }, -] - -[[package]] -name = "langchain-core" -version = "0.3.51" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jsonpatch" }, - { name = "langsmith" }, - { name = "packaging" }, - { name = "pydantic" }, - { name = "pyyaml" }, - { name = "tenacity" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/6e/24/74dfce829f63aaf09885ae569121335a62ecfa5043a35d9e819cd0e046f0/langchain_core-0.3.51.tar.gz", hash = "sha256:db76b9cc331411602cb40ba0469a161febe7a0663fbcaddbc9056046ac2d22f4", size = 542003 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/bf/3464d759bf8687a3bbdfeb9af2f2aeb0a265c6d5ef5fd9274c2a70449f77/langchain_core-0.3.51-py3-none-any.whl", hash = "sha256:4bd71e8acd45362aa428953f2a91d8162318014544a2216e4b769463caf68e13", size = 423303 }, -] - -[[package]] -name = "langchain-openai" -version = "0.3.12" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "langchain-core" }, - { name = "openai" }, - { name = "tiktoken" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/32/01/94d861be321df40f104324d3c64115917c7e774bb5d6af45d4af967c863c/langchain_openai-0.3.12.tar.gz", hash = "sha256:c9dbff63551f6bd91913bca9f99a2d057fd95dc58d4778657d67e5baa1737f61", size = 269015 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/07/7e/0d8838972ffead497b40cd42a1676f9ad90427d422c92dff2fb5461c4308/langchain_openai-0.3.12-py3-none-any.whl", hash = "sha256:0fab64d58ec95e65ffbaf659470cd362e815685e15edbcb171641e90eca4eb86", size = 61320 }, -] - -[[package]] -name = "langchain-text-splitters" -version = "0.3.8" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "langchain-core" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e7/ac/b4a25c5716bb0103b1515f1f52cc69ffb1035a5a225ee5afe3aed28bf57b/langchain_text_splitters-0.3.8.tar.gz", hash = "sha256:116d4b9f2a22dda357d0b79e30acf005c5518177971c66a9f1ab0edfdb0f912e", size = 42128 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8b/a3/3696ff2444658053c01b6b7443e761f28bb71217d82bb89137a978c5f66f/langchain_text_splitters-0.3.8-py3-none-any.whl", hash = "sha256:e75cc0f4ae58dcf07d9f18776400cf8ade27fadd4ff6d264df6278bb302f6f02", size = 32440 }, -] - -[[package]] -name = "langgraph" -version = "0.2.74" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "langchain-core" }, - { name = "langgraph-checkpoint" }, - { name = "langgraph-sdk" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/40/d2/e9daa099fc875ffe2ef5bab1af2ee95cec26489176babe4b0dc34f8ef0f2/langgraph-0.2.74.tar.gz", hash = "sha256:db6e63e0771e2e8fb17dc0e040007b32f009e0f114e35d8348e336eb15f068e5", size = 131360 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/df/3d/d6034923e0a03aa406b067e4777b611ffc65a8906a2efa214e845b3f84d4/langgraph-0.2.74-py3-none-any.whl", hash = "sha256:91a522df764e66068f1a6de09ea748cea0687912838f29218c1d1b92b1ca025f", size = 151433 }, -] - -[[package]] -name = "langgraph-checkpoint" -version = "2.0.16" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "langchain-core" }, - { name = "msgpack" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/01/66/5d4a2013a84c511be289bb4a5ef91cbaad28c091b6b366fdb79710a1458b/langgraph_checkpoint-2.0.16.tar.gz", hash = "sha256:49ba8cfa12b2aae845ccc3b1fbd1d7a8d3a6c4a2e387ab3a92fca40dd3d4baa5", size = 34206 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7c/63/03bc3dd304ead45b53313cab8727329e1d139a2d220f2d030c72242c860e/langgraph_checkpoint-2.0.16-py3-none-any.whl", hash = "sha256:dfab51076a6eddb5f9e146cfe1b977e3dd6419168b2afa23ff3f4e47973bf06f", size = 38291 }, -] - -[[package]] -name = "langgraph-checkpoint-sqlite" -version = "2.0.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiosqlite" }, - { name = "langgraph-checkpoint" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/bb/8f/98c26f209d9023cff127000e133d75fd6d934b33db2739d0c32796297809/langgraph_checkpoint_sqlite-2.0.5.tar.gz", hash = "sha256:13e6b6f1149e7858b7ef16a4a8b1c86967961dad62b711d3eb1c35ede501d12c", size = 9564 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/00/3c/c05e04a3a175bc60afa24edc7a0693b694984f6925559dab6b7f8f9df7bb/langgraph_checkpoint_sqlite-2.0.5-py3-none-any.whl", hash = "sha256:479a1851d5e91c1e15b95ca54cc75c9bd60896824c21f559dac2eaa10e49453f", size = 12763 }, -] - -[[package]] -name = "langgraph-sdk" -version = "0.1.51" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "httpx" }, - { name = "orjson" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/32/d1/95ae599428e8e7d90229e402adf3056072f2ebd0c45c7f7154a5243ff35a/langgraph_sdk-0.1.51.tar.gz", hash = "sha256:dea1363e72562cb1e82a2d156be8d5b1a69ff3fe8815eee0e1e7a2f423242ec1", size = 41591 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/86/e9/d5d2ea883ddb3e16d4c18213457b3f3d04380089d410db71faae52a3c34a/langgraph_sdk-0.1.51-py3-none-any.whl", hash = "sha256:ce2b58466d1700d06149782ed113157a8694a6d7932c801f316cd13fab315fe4", size = 44652 }, -] - -[[package]] -name = "langsmith" -version = "0.3.8" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "httpx" }, - { name = "orjson", marker = "platform_python_implementation != 'PyPy'" }, - { name = "pydantic" }, - { name = "requests" }, - { name = "requests-toolbelt" }, - { name = "zstandard" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d8/1a/974b66a9e7c43f41bec067e1f393a296803aee48fafcf183941c31295b59/langsmith-0.3.8.tar.gz", hash = "sha256:97f9bebe0b7cb0a4f278e6ff30ae7d5ededff3883b014442ec6d7d575b02a0f1", size = 321394 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8b/e4/5380e8229c442e406404977d2ec71a9db6a3e6a89fce7791c6ad7cd2bdbe/langsmith-0.3.8-py3-none-any.whl", hash = "sha256:fbb9dd97b0f090219447fca9362698d07abaeda1da85aa7cc6ec6517b36581b1", size = 332800 }, -] - [[package]] name = "markdown" version = "3.7" @@ -1259,18 +757,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739 }, ] -[[package]] -name = "marshmallow" -version = "3.26.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "packaging" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ab/5e/5e53d26b42ab75491cda89b871dab9e97c840bf12c63ec58a1919710cd06/marshmallow-3.26.1.tar.gz", hash = "sha256:e6d8affb6cb61d39d26402096dc0aee12d5a26d490a121f118d2e81dc0719dc6", size = 221825 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/34/75/51952c7b2d3873b44a0028b1bd26a25078c18f92f256608e8d1dc61b39fd/marshmallow-3.26.1-py3-none-any.whl", hash = "sha256:3350409f20a70a7e4e11a27661187b77cdcaeb20abca41c1454fe33636bea09c", size = 50878 }, -] - [[package]] name = "mdurl" version = "0.1.2" @@ -1478,58 +964,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5e/75/bd9b7bb966668920f06b200e84454c8f3566b102183bc55c5473d96cb2b9/msal_extensions-1.3.1-py3-none-any.whl", hash = "sha256:96d3de4d034504e969ac5e85bae8106c8373b5c6568e4c8fa7af2eca9dbe6bca", size = 20583 }, ] -[[package]] -name = "msgpack" -version = "1.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cb/d0/7555686ae7ff5731205df1012ede15dd9d927f6227ea151e901c7406af4f/msgpack-1.1.0.tar.gz", hash = "sha256:dd432ccc2c72b914e4cb77afce64aab761c1137cc698be3984eee260bcb2896e", size = 167260 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4b/f9/a892a6038c861fa849b11a2bb0502c07bc698ab6ea53359e5771397d883b/msgpack-1.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7ad442d527a7e358a469faf43fda45aaf4ac3249c8310a82f0ccff9164e5dccd", size = 150428 }, - { url = "https://files.pythonhosted.org/packages/df/7a/d174cc6a3b6bb85556e6a046d3193294a92f9a8e583cdbd46dc8a1d7e7f4/msgpack-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:74bed8f63f8f14d75eec75cf3d04ad581da6b914001b474a5d3cd3372c8cc27d", size = 84131 }, - { url = "https://files.pythonhosted.org/packages/08/52/bf4fbf72f897a23a56b822997a72c16de07d8d56d7bf273242f884055682/msgpack-1.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:914571a2a5b4e7606997e169f64ce53a8b1e06f2cf2c3a7273aa106236d43dd5", size = 81215 }, - { url = "https://files.pythonhosted.org/packages/02/95/dc0044b439b518236aaf012da4677c1b8183ce388411ad1b1e63c32d8979/msgpack-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c921af52214dcbb75e6bdf6a661b23c3e6417f00c603dd2070bccb5c3ef499f5", size = 371229 }, - { url = "https://files.pythonhosted.org/packages/ff/75/09081792db60470bef19d9c2be89f024d366b1e1973c197bb59e6aabc647/msgpack-1.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8ce0b22b890be5d252de90d0e0d119f363012027cf256185fc3d474c44b1b9e", size = 378034 }, - { url = "https://files.pythonhosted.org/packages/32/d3/c152e0c55fead87dd948d4b29879b0f14feeeec92ef1fd2ec21b107c3f49/msgpack-1.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:73322a6cc57fcee3c0c57c4463d828e9428275fb85a27aa2aa1a92fdc42afd7b", size = 363070 }, - { url = "https://files.pythonhosted.org/packages/d9/2c/82e73506dd55f9e43ac8aa007c9dd088c6f0de2aa19e8f7330e6a65879fc/msgpack-1.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e1f3c3d21f7cf67bcf2da8e494d30a75e4cf60041d98b3f79875afb5b96f3a3f", size = 359863 }, - { url = "https://files.pythonhosted.org/packages/cb/a0/3d093b248837094220e1edc9ec4337de3443b1cfeeb6e0896af8ccc4cc7a/msgpack-1.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:64fc9068d701233effd61b19efb1485587560b66fe57b3e50d29c5d78e7fef68", size = 368166 }, - { url = "https://files.pythonhosted.org/packages/e4/13/7646f14f06838b406cf5a6ddbb7e8dc78b4996d891ab3b93c33d1ccc8678/msgpack-1.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:42f754515e0f683f9c79210a5d1cad631ec3d06cea5172214d2176a42e67e19b", size = 370105 }, - { url = "https://files.pythonhosted.org/packages/67/fa/dbbd2443e4578e165192dabbc6a22c0812cda2649261b1264ff515f19f15/msgpack-1.1.0-cp310-cp310-win32.whl", hash = "sha256:3df7e6b05571b3814361e8464f9304c42d2196808e0119f55d0d3e62cd5ea044", size = 68513 }, - { url = "https://files.pythonhosted.org/packages/24/ce/c2c8fbf0ded750cb63cbcbb61bc1f2dfd69e16dca30a8af8ba80ec182dcd/msgpack-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:685ec345eefc757a7c8af44a3032734a739f8c45d1b0ac45efc5d8977aa4720f", size = 74687 }, - { url = "https://files.pythonhosted.org/packages/b7/5e/a4c7154ba65d93be91f2f1e55f90e76c5f91ccadc7efc4341e6f04c8647f/msgpack-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3d364a55082fb2a7416f6c63ae383fbd903adb5a6cf78c5b96cc6316dc1cedc7", size = 150803 }, - { url = "https://files.pythonhosted.org/packages/60/c2/687684164698f1d51c41778c838d854965dd284a4b9d3a44beba9265c931/msgpack-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:79ec007767b9b56860e0372085f8504db5d06bd6a327a335449508bbee9648fa", size = 84343 }, - { url = "https://files.pythonhosted.org/packages/42/ae/d3adea9bb4a1342763556078b5765e666f8fdf242e00f3f6657380920972/msgpack-1.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6ad622bf7756d5a497d5b6836e7fc3752e2dd6f4c648e24b1803f6048596f701", size = 81408 }, - { url = "https://files.pythonhosted.org/packages/dc/17/6313325a6ff40ce9c3207293aee3ba50104aed6c2c1559d20d09e5c1ff54/msgpack-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e59bca908d9ca0de3dc8684f21ebf9a690fe47b6be93236eb40b99af28b6ea6", size = 396096 }, - { url = "https://files.pythonhosted.org/packages/a8/a1/ad7b84b91ab5a324e707f4c9761633e357820b011a01e34ce658c1dda7cc/msgpack-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e1da8f11a3dd397f0a32c76165cf0c4eb95b31013a94f6ecc0b280c05c91b59", size = 403671 }, - { url = "https://files.pythonhosted.org/packages/bb/0b/fd5b7c0b308bbf1831df0ca04ec76fe2f5bf6319833646b0a4bd5e9dc76d/msgpack-1.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:452aff037287acb1d70a804ffd022b21fa2bb7c46bee884dbc864cc9024128a0", size = 387414 }, - { url = "https://files.pythonhosted.org/packages/f0/03/ff8233b7c6e9929a1f5da3c7860eccd847e2523ca2de0d8ef4878d354cfa/msgpack-1.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8da4bf6d54ceed70e8861f833f83ce0814a2b72102e890cbdfe4b34764cdd66e", size = 383759 }, - { url = "https://files.pythonhosted.org/packages/1f/1b/eb82e1fed5a16dddd9bc75f0854b6e2fe86c0259c4353666d7fab37d39f4/msgpack-1.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:41c991beebf175faf352fb940bf2af9ad1fb77fd25f38d9142053914947cdbf6", size = 394405 }, - { url = "https://files.pythonhosted.org/packages/90/2e/962c6004e373d54ecf33d695fb1402f99b51832631e37c49273cc564ffc5/msgpack-1.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a52a1f3a5af7ba1c9ace055b659189f6c669cf3657095b50f9602af3a3ba0fe5", size = 396041 }, - { url = "https://files.pythonhosted.org/packages/f8/20/6e03342f629474414860c48aeffcc2f7f50ddaf351d95f20c3f1c67399a8/msgpack-1.1.0-cp311-cp311-win32.whl", hash = "sha256:58638690ebd0a06427c5fe1a227bb6b8b9fdc2bd07701bec13c2335c82131a88", size = 68538 }, - { url = "https://files.pythonhosted.org/packages/aa/c4/5a582fc9a87991a3e6f6800e9bb2f3c82972912235eb9539954f3e9997c7/msgpack-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:fd2906780f25c8ed5d7b323379f6138524ba793428db5d0e9d226d3fa6aa1788", size = 74871 }, - { url = "https://files.pythonhosted.org/packages/e1/d6/716b7ca1dbde63290d2973d22bbef1b5032ca634c3ff4384a958ec3f093a/msgpack-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d46cf9e3705ea9485687aa4001a76e44748b609d260af21c4ceea7f2212a501d", size = 152421 }, - { url = "https://files.pythonhosted.org/packages/70/da/5312b067f6773429cec2f8f08b021c06af416bba340c912c2ec778539ed6/msgpack-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5dbad74103df937e1325cc4bfeaf57713be0b4f15e1c2da43ccdd836393e2ea2", size = 85277 }, - { url = "https://files.pythonhosted.org/packages/28/51/da7f3ae4462e8bb98af0d5bdf2707f1b8c65a0d4f496e46b6afb06cbc286/msgpack-1.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:58dfc47f8b102da61e8949708b3eafc3504509a5728f8b4ddef84bd9e16ad420", size = 82222 }, - { url = "https://files.pythonhosted.org/packages/33/af/dc95c4b2a49cff17ce47611ca9ba218198806cad7796c0b01d1e332c86bb/msgpack-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4676e5be1b472909b2ee6356ff425ebedf5142427842aa06b4dfd5117d1ca8a2", size = 392971 }, - { url = "https://files.pythonhosted.org/packages/f1/54/65af8de681fa8255402c80eda2a501ba467921d5a7a028c9c22a2c2eedb5/msgpack-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17fb65dd0bec285907f68b15734a993ad3fc94332b5bb21b0435846228de1f39", size = 401403 }, - { url = "https://files.pythonhosted.org/packages/97/8c/e333690777bd33919ab7024269dc3c41c76ef5137b211d776fbb404bfead/msgpack-1.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a51abd48c6d8ac89e0cfd4fe177c61481aca2d5e7ba42044fd218cfd8ea9899f", size = 385356 }, - { url = "https://files.pythonhosted.org/packages/57/52/406795ba478dc1c890559dd4e89280fa86506608a28ccf3a72fbf45df9f5/msgpack-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2137773500afa5494a61b1208619e3871f75f27b03bcfca7b3a7023284140247", size = 383028 }, - { url = "https://files.pythonhosted.org/packages/e7/69/053b6549bf90a3acadcd8232eae03e2fefc87f066a5b9fbb37e2e608859f/msgpack-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:398b713459fea610861c8a7b62a6fec1882759f308ae0795b5413ff6a160cf3c", size = 391100 }, - { url = "https://files.pythonhosted.org/packages/23/f0/d4101d4da054f04274995ddc4086c2715d9b93111eb9ed49686c0f7ccc8a/msgpack-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:06f5fd2f6bb2a7914922d935d3b8bb4a7fff3a9a91cfce6d06c13bc42bec975b", size = 394254 }, - { url = "https://files.pythonhosted.org/packages/1c/12/cf07458f35d0d775ff3a2dc5559fa2e1fcd06c46f1ef510e594ebefdca01/msgpack-1.1.0-cp312-cp312-win32.whl", hash = "sha256:ad33e8400e4ec17ba782f7b9cf868977d867ed784a1f5f2ab46e7ba53b6e1e1b", size = 69085 }, - { url = "https://files.pythonhosted.org/packages/73/80/2708a4641f7d553a63bc934a3eb7214806b5b39d200133ca7f7afb0a53e8/msgpack-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:115a7af8ee9e8cddc10f87636767857e7e3717b7a2e97379dc2054712693e90f", size = 75347 }, - { url = "https://files.pythonhosted.org/packages/c8/b0/380f5f639543a4ac413e969109978feb1f3c66e931068f91ab6ab0f8be00/msgpack-1.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:071603e2f0771c45ad9bc65719291c568d4edf120b44eb36324dcb02a13bfddf", size = 151142 }, - { url = "https://files.pythonhosted.org/packages/c8/ee/be57e9702400a6cb2606883d55b05784fada898dfc7fd12608ab1fdb054e/msgpack-1.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0f92a83b84e7c0749e3f12821949d79485971f087604178026085f60ce109330", size = 84523 }, - { url = "https://files.pythonhosted.org/packages/7e/3a/2919f63acca3c119565449681ad08a2f84b2171ddfcff1dba6959db2cceb/msgpack-1.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4a1964df7b81285d00a84da4e70cb1383f2e665e0f1f2a7027e683956d04b734", size = 81556 }, - { url = "https://files.pythonhosted.org/packages/7c/43/a11113d9e5c1498c145a8925768ea2d5fce7cbab15c99cda655aa09947ed/msgpack-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59caf6a4ed0d164055ccff8fe31eddc0ebc07cf7326a2aaa0dbf7a4001cd823e", size = 392105 }, - { url = "https://files.pythonhosted.org/packages/2d/7b/2c1d74ca6c94f70a1add74a8393a0138172207dc5de6fc6269483519d048/msgpack-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0907e1a7119b337971a689153665764adc34e89175f9a34793307d9def08e6ca", size = 399979 }, - { url = "https://files.pythonhosted.org/packages/82/8c/cf64ae518c7b8efc763ca1f1348a96f0e37150061e777a8ea5430b413a74/msgpack-1.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:65553c9b6da8166e819a6aa90ad15288599b340f91d18f60b2061f402b9a4915", size = 383816 }, - { url = "https://files.pythonhosted.org/packages/69/86/a847ef7a0f5ef3fa94ae20f52a4cacf596a4e4a010197fbcc27744eb9a83/msgpack-1.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7a946a8992941fea80ed4beae6bff74ffd7ee129a90b4dd5cf9c476a30e9708d", size = 380973 }, - { url = "https://files.pythonhosted.org/packages/aa/90/c74cf6e1126faa93185d3b830ee97246ecc4fe12cf9d2d31318ee4246994/msgpack-1.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4b51405e36e075193bc051315dbf29168d6141ae2500ba8cd80a522964e31434", size = 387435 }, - { url = "https://files.pythonhosted.org/packages/7a/40/631c238f1f338eb09f4acb0f34ab5862c4e9d7eda11c1b685471a4c5ea37/msgpack-1.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4c01941fd2ff87c2a934ee6055bda4ed353a7846b8d4f341c428109e9fcde8c", size = 399082 }, - { url = "https://files.pythonhosted.org/packages/e9/1b/fa8a952be252a1555ed39f97c06778e3aeb9123aa4cccc0fd2acd0b4e315/msgpack-1.1.0-cp313-cp313-win32.whl", hash = "sha256:7c9a35ce2c2573bada929e0b7b3576de647b0defbd25f5139dcdaba0ae35a4cc", size = 69037 }, - { url = "https://files.pythonhosted.org/packages/b6/bc/8bd826dd03e022153bfa1766dcdec4976d6c818865ed54223d71f07862b3/msgpack-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:bce7d9e614a04d0883af0b3d4d501171fbfca038f12c77fa838d9f198147a23f", size = 75140 }, -] - [[package]] name = "msrest" version = "0.7.1" @@ -1546,93 +980,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/15/cf/f2966a2638144491f8696c27320d5219f48a072715075d168b31d3237720/msrest-0.7.1-py3-none-any.whl", hash = "sha256:21120a810e1233e5e6cc7fe40b474eeb4ec6f757a15d7cf86702c369f9567c32", size = 85384 }, ] -[[package]] -name = "multidict" -version = "6.3.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/fa/2d/6e0d6771cadd5ad14d13193cc8326dc0b341cc1659c306cbfce7a5058fff/multidict-6.3.2.tar.gz", hash = "sha256:c1035eea471f759fa853dd6e76aaa1e389f93b3e1403093fa0fd3ab4db490678", size = 88060 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4e/9f/96bed056ed3e1fa86fa9880963e21a098e1e94dc6e2ced51a960d56ed802/multidict-6.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8b3dc0eec9304fa04d84a51ea13b0ec170bace5b7ddeaac748149efd316f1504", size = 62769 }, - { url = "https://files.pythonhosted.org/packages/69/6a/c3197d0ff579d2393bab259c6129c963ebec50014fbd757440645402b4c0/multidict-6.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9534f3d84addd3b6018fa83f97c9d4247aaa94ac917d1ed7b2523306f99f5c16", size = 37127 }, - { url = "https://files.pythonhosted.org/packages/a6/d8/21b15813270d56486041452a44b02b4c02cd492edb8eb13c3ce1de7744f1/multidict-6.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a003ce1413ae01f0b8789c1c987991346a94620a4d22210f7a8fe753646d3209", size = 36399 }, - { url = "https://files.pythonhosted.org/packages/8f/f6/2ffe2d4b565551bf0b1b1e9630c6f21f728fc24cd7d880f1baf5e7025be3/multidict-6.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b43f7384e68b1b982c99f489921a459467b5584bdb963b25e0df57c9039d0ad", size = 236561 }, - { url = "https://files.pythonhosted.org/packages/c4/d7/935810c224360c63fe3b9233433ea9197399431e362e38ff0daf082624ee/multidict-6.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d142ae84047262dc75c1f92eaf95b20680f85ce11d35571b4c97e267f96fadc4", size = 249850 }, - { url = "https://files.pythonhosted.org/packages/d0/10/355802a51e4426354b645585a9f0a4a4f0352b7619251da152f0235069ed/multidict-6.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ec7e86fbc48aa1d6d686501a8547818ba8d645e7e40eaa98232a5d43ee4380ad", size = 245566 }, - { url = "https://files.pythonhosted.org/packages/72/cb/2a2b44b207c05018d0909b7c748983753dc9587699664e10709e27605158/multidict-6.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe019fb437632b016e6cac67a7e964f1ef827ef4023f1ca0227b54be354da97e", size = 232030 }, - { url = "https://files.pythonhosted.org/packages/8e/e5/3db1745e939f4c02275154a5ed2816ea70eb1625e1d9363881047a0f9620/multidict-6.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b60cb81214a9da7cfd8ae2853d5e6e47225ece55fe5833142fe0af321c35299", size = 224255 }, - { url = "https://files.pythonhosted.org/packages/48/2d/f187f506ff5ee3a91a8207a744311649cb1541b180514ea9dc235747ac2a/multidict-6.3.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:32d9e8ef2e0312d4e96ca9adc88e0675b6d8e144349efce4a7c95d5ccb6d88e0", size = 233961 }, - { url = "https://files.pythonhosted.org/packages/8f/e3/5023396bb5f1858f897d1a44199d0abc3072bb7b7bb47dec94c10b535568/multidict-6.3.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:335d584312e3fa43633d63175dfc1a5f137dd7aa03d38d1310237d54c3032774", size = 232430 }, - { url = "https://files.pythonhosted.org/packages/73/d8/f7b80e886af062dbb9d517e5161d841a08bcf44f6bccfccf9cb0ba92e7de/multidict-6.3.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:b8df917faa6b8cac3d6870fc21cb7e4d169faca68e43ffe568c156c9c6408a4d", size = 243102 }, - { url = "https://files.pythonhosted.org/packages/53/a5/78e3c05c1916ce3e7beca8da8a026c1a4d9e4ce892f472463be22ddd030d/multidict-6.3.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:cc060b9b89b701dd8fedef5b99e1f1002b8cb95072693233a63389d37e48212d", size = 235610 }, - { url = "https://files.pythonhosted.org/packages/00/29/97b470984a545d09bc5b6b8534559c48d4c427bd6737dc5a6f516061a581/multidict-6.3.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f2ce3be2500658f3c644494b934628bb0c82e549dde250d2119689ce791cc8b8", size = 232287 }, - { url = "https://files.pythonhosted.org/packages/78/a5/121d40559fceb8d78ffe38ee06519fba8c8300ef1eb796c0a790fa0cfbf3/multidict-6.3.2-cp310-cp310-win32.whl", hash = "sha256:dbcb4490d8e74b484449abd51751b8f560dd0a4812eb5dacc6a588498222a9ab", size = 34895 }, - { url = "https://files.pythonhosted.org/packages/04/b1/e90e666158b2c65567f8bb2c1507e9983c4b1b7156cd67e69349a38bd2b2/multidict-6.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:06944f9ced30f8602be873563ed4df7e3f40958f60b2db39732c11d615a33687", size = 38307 }, - { url = "https://files.pythonhosted.org/packages/b1/e3/443e682e42eaddad0b217b7a59627927fa42b6cd7ba7174f0a01eb3fe6b8/multidict-6.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:45a034f41fcd16968c0470d8912d293d7b0d0822fc25739c5c2ff7835b85bc56", size = 62734 }, - { url = "https://files.pythonhosted.org/packages/b1/4f/2126e9bc37f5be2fdfa36cc192e7ef10b3e9c58eec75a4468706aca96891/multidict-6.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:352585cec45f5d83d886fc522955492bb436fca032b11d487b12d31c5a81b9e3", size = 37115 }, - { url = "https://files.pythonhosted.org/packages/6a/af/5aae0c05a66fdf8bf015ee6903d3a250a7d9c6cc75c9478d04995e6ff1e2/multidict-6.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:da9d89d293511fd0a83a90559dc131f8b3292b6975eb80feff19e5f4663647e2", size = 36371 }, - { url = "https://files.pythonhosted.org/packages/94/27/42390b75c20ff63f43fce44f36f9f66be466cd9ee05326051e4caacdb75b/multidict-6.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79fa716592224aa652b9347a586cfe018635229074565663894eb4eb21f8307f", size = 243444 }, - { url = "https://files.pythonhosted.org/packages/21/55/77077af851d7678fe0845c4050a537321d82fb12a04d4f6db334a1cc6ff7/multidict-6.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0326278a44c56e94792475268e5cd3d47fbc0bd41ee56928c3bbb103ba7f58fe", size = 256750 }, - { url = "https://files.pythonhosted.org/packages/f1/09/4c5bfeb2fc8a1e14002239bd6a4d9ba2963fb148889d444b05a20db32a41/multidict-6.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bb1ea87f7fe45e5079f6315e95d64d4ca8b43ef656d98bed63a02e3756853a22", size = 251630 }, - { url = "https://files.pythonhosted.org/packages/24/a9/286756a1afb8648772de851f8f39d2dd4076506f0c0fc2b751259fcbf0dd/multidict-6.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7cff3c5a98d037024a9065aafc621a8599fad7b423393685dc83cf7a32f8b691", size = 238522 }, - { url = "https://files.pythonhosted.org/packages/c2/03/4bb17df70742aae786fcbc27e89e2e49c322134698cd0739aec93e91c669/multidict-6.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed99834b053c655d980fb98029003cb24281e47a796052faad4543aa9e01b8e8", size = 230230 }, - { url = "https://files.pythonhosted.org/packages/53/cc/30df95ba07a9f233ae48d0605b3f72457364836b61a8a8e3d333fdcd32c0/multidict-6.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7048440e505d2b4741e5d0b32bd2f427c901f38c7760fc245918be2cf69b3b85", size = 239676 }, - { url = "https://files.pythonhosted.org/packages/25/37/2d9fe2944c2df5b71ba90cf657b90ad65f1542989cdabe4d1bdbf8c51530/multidict-6.3.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:27248c27b563f5889556da8a96e18e98a56ff807ac1a7d56cf4453c2c9e4cd91", size = 238143 }, - { url = "https://files.pythonhosted.org/packages/ce/13/8f833f9f992eae49f4cb1a1ad05b8fbe183721a154d51c2136b177a41bdb/multidict-6.3.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:6323b4ba0e018bd266f776c35f3f0943fc4ee77e481593c9f93bd49888f24e94", size = 248817 }, - { url = "https://files.pythonhosted.org/packages/15/d4/4f49c41af6c4cab962ad51436e6c5acfbdab4fa54f5e98faa56f66f89b03/multidict-6.3.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:81f7ce5ec7c27d0b45c10449c8f0fed192b93251e2e98cb0b21fec779ef1dc4d", size = 241268 }, - { url = "https://files.pythonhosted.org/packages/af/60/e723a00f7bb44366eab8d02fe6f076ecfad58331e10f6f0ce94cb989819c/multidict-6.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:03bfcf2825b3bed0ba08a9d854acd18b938cab0d2dba3372b51c78e496bac811", size = 238267 }, - { url = "https://files.pythonhosted.org/packages/62/a6/f6b63fc51c8a4e228e6d2105061be3048b02d490d47e67f7ec2de575f1d0/multidict-6.3.2-cp311-cp311-win32.whl", hash = "sha256:f32c2790512cae6ca886920e58cdc8c784bdc4bb2a5ec74127c71980369d18dc", size = 34986 }, - { url = "https://files.pythonhosted.org/packages/85/56/ea976a5e3ebe0e871e004d9cacfe4c803f8ade353eaf4a247580e9dd7b9d/multidict-6.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:0b0c15e58e038a2cd75ef7cf7e072bc39b5e0488b165902efb27978984bbad70", size = 38427 }, - { url = "https://files.pythonhosted.org/packages/83/ae/bd7518193b4374484c04ba0f6522d0572dc17fcd53d238deb3cb3643c858/multidict-6.3.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d1e0ba1ce1b8cc79117196642d95f4365e118eaf5fb85f57cdbcc5a25640b2a4", size = 62680 }, - { url = "https://files.pythonhosted.org/packages/59/e0/a0a9247c32f385ac4c1afefe9c3f2271fb8e235aad72332d42384c41b9cb/multidict-6.3.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:029bbd7d782251a78975214b78ee632672310f9233d49531fc93e8e99154af25", size = 37366 }, - { url = "https://files.pythonhosted.org/packages/c3/fa/8c23cdd4492d59bea0e762662285f2163766e69e5ea715fe6a03a8670660/multidict-6.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d7db41e3b56817d9175264e5fe00192fbcb8e1265307a59f53dede86161b150e", size = 36103 }, - { url = "https://files.pythonhosted.org/packages/87/35/3bcc3616cb54d3a327b1d26dbec284c3eb7b179e8a78a6075852dbb51dac/multidict-6.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fcab18e65cc555ac29981a581518c23311f2b1e72d8f658f9891590465383be", size = 248231 }, - { url = "https://files.pythonhosted.org/packages/b8/c3/17ddbfd6fc3eed9ab7326a43651e1a97da73f7acc69b78a7bb04b59c073d/multidict-6.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0d50eff89aa4d145a5486b171a2177042d08ea5105f813027eb1050abe91839f", size = 259423 }, - { url = "https://files.pythonhosted.org/packages/1f/67/64b18180e8f559cc93efaaaac2fe0746b9c978560866b6fdd626d3237129/multidict-6.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:643e57b403d3e240045a3681f9e6a04d35a33eddc501b4cbbbdbc9c70122e7bc", size = 256204 }, - { url = "https://files.pythonhosted.org/packages/21/f6/e81a8e4817c2d32787b33ae58c72dc3fe08e0ba8e56e660a225df3cb8619/multidict-6.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d17b37b9715b30605b5bab1460569742d0c309e5c20079263b440f5d7746e7e", size = 249663 }, - { url = "https://files.pythonhosted.org/packages/3e/e8/44ca66758df031a8119483cf5385e2ff3b09b9c6df8f3396d626c325b553/multidict-6.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:68acd51fa94e63312b8ddf84bfc9c3d3442fe1f9988bbe1b6c703043af8867fe", size = 232236 }, - { url = "https://files.pythonhosted.org/packages/93/76/d2faabbac582dc100a4d7ecf7d0ab8dd2aadf7f10d5d5a19e9932cf63a2e/multidict-6.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:347eea2852ab7f697cc5ed9b1aae96b08f8529cca0c6468f747f0781b1842898", size = 252638 }, - { url = "https://files.pythonhosted.org/packages/63/37/f5a6ea10dab96491b7300be940f86a5490dc474d18473c438f2550b78da3/multidict-6.3.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e4d3f8e57027dcda84a1aa181501c15c45eab9566eb6fcc274cbd1e7561224f8", size = 247917 }, - { url = "https://files.pythonhosted.org/packages/d4/b1/2c32b684763b69becbaaa61b7af8a45a6f757fc82d9b4b123ca90cb69f75/multidict-6.3.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:9ca57a841ffcf712e47875d026aa49d6e67f9560624d54b51628603700d5d287", size = 261754 }, - { url = "https://files.pythonhosted.org/packages/cd/f2/badedad94e1731debe56d076c9e61a1658c5e9d65dfa9c1ee74d1e3d31d7/multidict-6.3.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:7cafdafb44c4e646118410368307693e49d19167e5f119cbe3a88697d2d1a636", size = 256389 }, - { url = "https://files.pythonhosted.org/packages/c6/3a/0a3488be2e5a6499f512e748d31e8fb90b753eb35793ecf390b9d8548e66/multidict-6.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:430120c6ce3715a9c6075cabcee557daccbcca8ba25a9fedf05c7bf564532f2d", size = 251902 }, - { url = "https://files.pythonhosted.org/packages/fe/44/62f76d0a5d836b96168f39a402a75dd3114d0df3cbb5669e0310034b71be/multidict-6.3.2-cp312-cp312-win32.whl", hash = "sha256:13bec31375235a68457ab887ce1bbf4f59d5810d838ae5d7e5b416242e1f3ed4", size = 35101 }, - { url = "https://files.pythonhosted.org/packages/8f/a4/7aaf2313e1766710010c35f9d738fd6309fb71a758f8c0e81853b90afb3d/multidict-6.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:c3b6d7620e6e90c6d97eaf3a63bf7fbd2ba253aab89120a4a9c660bf2d675391", size = 38479 }, - { url = "https://files.pythonhosted.org/packages/b1/b2/15db2b1bec1fe8ab5e7c210e3cd247ed902ef86b58b9f39b0a75476d0e8d/multidict-6.3.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:b9ca24700322816ae0d426aa33671cf68242f8cc85cee0d0e936465ddaee90b5", size = 62345 }, - { url = "https://files.pythonhosted.org/packages/5f/91/22ea27da2c3ffb8266a92f91f17a84dec2cbdd0f91aa7e5f7d514534dd92/multidict-6.3.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d9fbbe23667d596ff4f9f74d44b06e40ebb0ab6b262cf14a284f859a66f86457", size = 37205 }, - { url = "https://files.pythonhosted.org/packages/23/cb/563a7481ae677531da84aad86c2de7ebc23446d856d2f6d9794ad4fff375/multidict-6.3.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9cb602c5bea0589570ad3a4a6f2649c4f13cc7a1e97b4c616e5e9ff8dc490987", size = 35931 }, - { url = "https://files.pythonhosted.org/packages/7c/b7/98fe4f4cd7a0b77a4a48fd3f619848b9e8af4e692eb681f9df9f58d86456/multidict-6.3.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93ca81dd4d1542e20000ed90f4cc84b7713776f620d04c2b75b8efbe61106c99", size = 246946 }, - { url = "https://files.pythonhosted.org/packages/7e/a3/22dcbd0b58d253719acaf0257a2f35bf609bfd6b73690fcc9e7bdbd3b392/multidict-6.3.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:18b6310b5454c62242577a128c87df8897f39dd913311cf2e1298e47dfc089eb", size = 260559 }, - { url = "https://files.pythonhosted.org/packages/1c/d4/25eb076f0c2c28d73e7959f3fcc8371e7a029815b5d06e79ea3a265500d2/multidict-6.3.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7a6dda57de1fc9aedfdb600a8640c99385cdab59a5716cb714b52b6005797f77", size = 257122 }, - { url = "https://files.pythonhosted.org/packages/28/f8/18c81f5c5b7453dd8d15dc61ceca23d03c55e69f1937842039be2d8c4428/multidict-6.3.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d8ec42d03cc6b29845552a68151f9e623c541f1708328353220af571e24a247", size = 248535 }, - { url = "https://files.pythonhosted.org/packages/9b/17/c175fab75ecfe1c2dd4f28382dd7e80da6d6f0d73c68036f64b6dce9aeeb/multidict-6.3.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:80681969cee2fa84dafeb53615d51d24246849984e3e87fbe4fe39956f2e23bf", size = 234013 }, - { url = "https://files.pythonhosted.org/packages/2f/03/1611ecf91d7d6249633cb1dd3fb26d456e0dc0dc80cecccfeb89931a126b/multidict-6.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:01489b0c3592bb9d238e5690e9566db7f77a5380f054b57077d2c4deeaade0eb", size = 249222 }, - { url = "https://files.pythonhosted.org/packages/66/04/0035b77bbffb55f276f00b427e45870194002f9f42e1e3de785d45880372/multidict-6.3.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:522d9f1fd995d04dfedc0a40bca7e2591bc577d920079df50b56245a4a252c1c", size = 245594 }, - { url = "https://files.pythonhosted.org/packages/fe/4c/b52ebcd8ff13a3c833b07cfffa0f50f736b061954a151ee5fe6669bb1bd8/multidict-6.3.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:2014e9cf0b4e9c75bbad49c1758e5a9bf967a56184fc5fcc51527425baf5abba", size = 258709 }, - { url = "https://files.pythonhosted.org/packages/fd/78/9c4433517e8f09035a14aba469617c9cf41a214ca987d9127b84b3de4848/multidict-6.3.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:78ced9fcbee79e446ff4bb3018ac7ba1670703de7873d9c1f6f9883db53c71bc", size = 254015 }, - { url = "https://files.pythonhosted.org/packages/6d/76/8464b4d2e9980bd754aa1850919caef9854453f0400c60f84c79947b799d/multidict-6.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1faf01af972bd01216a107c195f5294f9f393531bc3e4faddc9b333581255d4d", size = 249475 }, - { url = "https://files.pythonhosted.org/packages/c4/e2/2b35b7ce226a2ca8c38125f702090faa8d0a35050461fb111fbaa2e023c4/multidict-6.3.2-cp313-cp313-win32.whl", hash = "sha256:7a699ab13d8d8e1f885de1535b4f477fb93836c87168318244c2685da7b7f655", size = 35204 }, - { url = "https://files.pythonhosted.org/packages/c6/c7/09b85dc11cfa83c9a1e3f8367402d56157624e31a05eecd40d5feed1eed1/multidict-6.3.2-cp313-cp313-win_amd64.whl", hash = "sha256:8666bb0d883310c83be01676e302587834dfd185b52758caeab32ef0eb387bc6", size = 38436 }, - { url = "https://files.pythonhosted.org/packages/63/d6/b27f9db9a8dcca95b50911436c9f187047911be0d78ade3352a6bcabb87a/multidict-6.3.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:d82c95aabee29612b1c4f48b98be98181686eb7d6c0152301f72715705cc787b", size = 67526 }, - { url = "https://files.pythonhosted.org/packages/2d/23/bbf220b0fa6378526890f37fd9a63d4e2ea990a4a344b221618adc3fb8b0/multidict-6.3.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:f47709173ea9e87a7fd05cd7e5cf1e5d4158924ff988a9a8e0fbd853705f0e68", size = 39390 }, - { url = "https://files.pythonhosted.org/packages/0d/a9/4d1b795b50e6b54609fd7a63db8df30fa0480405b9a46cf8e336f5f28560/multidict-6.3.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:0c7f9d0276ceaab41b8ae78534ff28ea33d5de85db551cbf80c44371f2b55d13", size = 38869 }, - { url = "https://files.pythonhosted.org/packages/e4/8c/854ee8ad8921335d0b4e740f373390d85d23f6b3956387562de5891ac503/multidict-6.3.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a6eab22df44a25acab2e738f882f5ec551282ab45b2bbda5301e6d2cfb323036", size = 246911 }, - { url = "https://files.pythonhosted.org/packages/40/65/d6ae9fecb61d1c2fa86a2889f8b58dbfb91fa6a6d7754597e472c8523f6c/multidict-6.3.2-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a947cb7c657f57874021b9b70c7aac049c877fb576955a40afa8df71d01a1390", size = 251680 }, - { url = "https://files.pythonhosted.org/packages/a3/6c/098304889a699f5fbad8e74b723847a38d22547743baacdfcc8a17777b5b/multidict-6.3.2-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5faa346e8e1c371187cf345ab1e02a75889f9f510c9cbc575c31b779f7df084d", size = 246706 }, - { url = "https://files.pythonhosted.org/packages/da/9f/a58a04ac1d18f0a2431c48763a8948d0ce65f5911000cc425f8778eb6611/multidict-6.3.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc6e08d977aebf1718540533b4ba5b351ccec2db093370958a653b1f7f9219cc", size = 242359 }, - { url = "https://files.pythonhosted.org/packages/40/fd/3a76265f2748f718cc05f313c44440658ecd1939fa2b5e66087a5edd605f/multidict-6.3.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:98eab7acf55275b5bf09834125fa3a80b143a9f241cdcdd3f1295ffdc3c6d097", size = 229881 }, - { url = "https://files.pythonhosted.org/packages/22/a9/5780f71e34adf93443ec0660591d877367991badadab9cc6ac02d7a64760/multidict-6.3.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:36863655630becc224375c0b99364978a0f95aebfb27fb6dd500f7fb5fb36e79", size = 248520 }, - { url = "https://files.pythonhosted.org/packages/f3/72/10988db397e1e819b669213c76a41fde670ba60ecec2c05d5ecdea05526c/multidict-6.3.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:d9c0979c096c0d46a963331b0e400d3a9e560e41219df4b35f0d7a2f28f39710", size = 237649 }, - { url = "https://files.pythonhosted.org/packages/29/75/52a7d3d1c0ffb2e8367f72845f309850113ea9201a50e4d4cdf8ac9f7d72/multidict-6.3.2-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:0efc04f70f05e70e5945890767e8874da5953a196f5b07c552d305afae0f3bf6", size = 251467 }, - { url = "https://files.pythonhosted.org/packages/82/24/e42400008eff60d4af53a2ff313abf0b2715fdd3a71b845d85025844f198/multidict-6.3.2-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:2c519b3b82c34539fae3e22e4ea965869ac6b628794b1eb487780dde37637ab7", size = 245310 }, - { url = "https://files.pythonhosted.org/packages/91/32/8b2e247539d4fdcc6cee36aa71c8898e0acd70e5d0e8a2ce9796a60790e5/multidict-6.3.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:329160e301f2afd7b43725d3dda8a7ef8ee41d4ceac2083fc0d8c1cc8a4bd56b", size = 243574 }, - { url = "https://files.pythonhosted.org/packages/d2/86/cc42cfa9b85b7d174948a17f828ebcacb0247e727fbedf06506ba93387ef/multidict-6.3.2-cp313-cp313t-win32.whl", hash = "sha256:420e5144a5f598dad8db3128f1695cd42a38a0026c2991091dab91697832f8cc", size = 41908 }, - { url = "https://files.pythonhosted.org/packages/2a/36/5c015523a7650fb5c55380d1c779b938379bd091968ee822d719e4264ab7/multidict-6.3.2-cp313-cp313t-win_amd64.whl", hash = "sha256:875faded2861c7af2682c67088e6313fec35ede811e071c96d36b081873cea14", size = 45635 }, - { url = "https://files.pythonhosted.org/packages/aa/c1/7832c95a50641148b567b5366dd3354489950dcfd01c8fc28472bec63b9a/multidict-6.3.2-py3-none-any.whl", hash = "sha256:71409d4579f716217f23be2f5e7afca5ca926aaeb398aa11b72d793bff637a1f", size = 10347 }, -] - [[package]] name = "mypy" version = "1.15.0" @@ -1689,68 +1036,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314 }, ] -[[package]] -name = "numpy" -version = "2.2.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e1/78/31103410a57bc2c2b93a3597340a8119588571f6a4539067546cb9a0bfac/numpy-2.2.4.tar.gz", hash = "sha256:9ba03692a45d3eef66559efe1d1096c4b9b75c0986b5dff5530c378fb8331d4f", size = 20270701 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/04/89/a79e86e5c1433926ed7d60cb267fb64aa578b6101ab645800fd43b4801de/numpy-2.2.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8146f3550d627252269ac42ae660281d673eb6f8b32f113538e0cc2a9aed42b9", size = 21250661 }, - { url = "https://files.pythonhosted.org/packages/79/c2/f50921beb8afd60ed9589ad880332cfefdb805422210d327fb48f12b7a81/numpy-2.2.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e642d86b8f956098b564a45e6f6ce68a22c2c97a04f5acd3f221f57b8cb850ae", size = 14389926 }, - { url = "https://files.pythonhosted.org/packages/c7/b9/2c4e96130b0b0f97b0ef4a06d6dae3b39d058b21a5e2fa2decd7fd6b1c8f/numpy-2.2.4-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:a84eda42bd12edc36eb5b53bbcc9b406820d3353f1994b6cfe453a33ff101775", size = 5428329 }, - { url = "https://files.pythonhosted.org/packages/7f/a5/3d7094aa898f4fc5c84cdfb26beeae780352d43f5d8bdec966c4393d644c/numpy-2.2.4-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:4ba5054787e89c59c593a4169830ab362ac2bee8a969249dc56e5d7d20ff8df9", size = 6963559 }, - { url = "https://files.pythonhosted.org/packages/4c/22/fb1be710a14434c09080dd4a0acc08939f612ec02efcb04b9e210474782d/numpy-2.2.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7716e4a9b7af82c06a2543c53ca476fa0b57e4d760481273e09da04b74ee6ee2", size = 14368066 }, - { url = "https://files.pythonhosted.org/packages/c2/07/2e5cc71193e3ef3a219ffcf6ca4858e46ea2be09c026ddd480d596b32867/numpy-2.2.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:adf8c1d66f432ce577d0197dceaac2ac00c0759f573f28516246351c58a85020", size = 16417040 }, - { url = "https://files.pythonhosted.org/packages/1a/97/3b1537776ad9a6d1a41813818343745e8dd928a2916d4c9edcd9a8af1dac/numpy-2.2.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:218f061d2faa73621fa23d6359442b0fc658d5b9a70801373625d958259eaca3", size = 15879862 }, - { url = "https://files.pythonhosted.org/packages/b0/b7/4472f603dd45ef36ff3d8e84e84fe02d9467c78f92cc121633dce6da307b/numpy-2.2.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:df2f57871a96bbc1b69733cd4c51dc33bea66146b8c63cacbfed73eec0883017", size = 18206032 }, - { url = "https://files.pythonhosted.org/packages/0d/bd/6a092963fb82e6c5aa0d0440635827bbb2910da229545473bbb58c537ed3/numpy-2.2.4-cp310-cp310-win32.whl", hash = "sha256:a0258ad1f44f138b791327961caedffbf9612bfa504ab9597157806faa95194a", size = 6608517 }, - { url = "https://files.pythonhosted.org/packages/01/e3/cb04627bc2a1638948bc13e818df26495aa18e20d5be1ed95ab2b10b6847/numpy-2.2.4-cp310-cp310-win_amd64.whl", hash = "sha256:0d54974f9cf14acf49c60f0f7f4084b6579d24d439453d5fc5805d46a165b542", size = 12943498 }, - { url = "https://files.pythonhosted.org/packages/16/fb/09e778ee3a8ea0d4dc8329cca0a9c9e65fed847d08e37eba74cb7ed4b252/numpy-2.2.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e9e0a277bb2eb5d8a7407e14688b85fd8ad628ee4e0c7930415687b6564207a4", size = 21254989 }, - { url = "https://files.pythonhosted.org/packages/a2/0a/1212befdbecab5d80eca3cde47d304cad986ad4eec7d85a42e0b6d2cc2ef/numpy-2.2.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9eeea959168ea555e556b8188da5fa7831e21d91ce031e95ce23747b7609f8a4", size = 14425910 }, - { url = "https://files.pythonhosted.org/packages/2b/3e/e7247c1d4f15086bb106c8d43c925b0b2ea20270224f5186fa48d4fb5cbd/numpy-2.2.4-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:bd3ad3b0a40e713fc68f99ecfd07124195333f1e689387c180813f0e94309d6f", size = 5426490 }, - { url = "https://files.pythonhosted.org/packages/5d/fa/aa7cd6be51419b894c5787a8a93c3302a1ed4f82d35beb0613ec15bdd0e2/numpy-2.2.4-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:cf28633d64294969c019c6df4ff37f5698e8326db68cc2b66576a51fad634880", size = 6967754 }, - { url = "https://files.pythonhosted.org/packages/d5/ee/96457c943265de9fadeb3d2ffdbab003f7fba13d971084a9876affcda095/numpy-2.2.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2fa8fa7697ad1646b5c93de1719965844e004fcad23c91228aca1cf0800044a1", size = 14373079 }, - { url = "https://files.pythonhosted.org/packages/c5/5c/ceefca458559f0ccc7a982319f37ed07b0d7b526964ae6cc61f8ad1b6119/numpy-2.2.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f4162988a360a29af158aeb4a2f4f09ffed6a969c9776f8f3bdee9b06a8ab7e5", size = 16428819 }, - { url = "https://files.pythonhosted.org/packages/22/31/9b2ac8eee99e001eb6add9fa27514ef5e9faf176169057a12860af52704c/numpy-2.2.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:892c10d6a73e0f14935c31229e03325a7b3093fafd6ce0af704be7f894d95687", size = 15881470 }, - { url = "https://files.pythonhosted.org/packages/f0/dc/8569b5f25ff30484b555ad8a3f537e0225d091abec386c9420cf5f7a2976/numpy-2.2.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:db1f1c22173ac1c58db249ae48aa7ead29f534b9a948bc56828337aa84a32ed6", size = 18218144 }, - { url = "https://files.pythonhosted.org/packages/5e/05/463c023a39bdeb9bb43a99e7dee2c664cb68d5bb87d14f92482b9f6011cc/numpy-2.2.4-cp311-cp311-win32.whl", hash = "sha256:ea2bb7e2ae9e37d96835b3576a4fa4b3a97592fbea8ef7c3587078b0068b8f09", size = 6606368 }, - { url = "https://files.pythonhosted.org/packages/8b/72/10c1d2d82101c468a28adc35de6c77b308f288cfd0b88e1070f15b98e00c/numpy-2.2.4-cp311-cp311-win_amd64.whl", hash = "sha256:f7de08cbe5551911886d1ab60de58448c6df0f67d9feb7d1fb21e9875ef95e91", size = 12947526 }, - { url = "https://files.pythonhosted.org/packages/a2/30/182db21d4f2a95904cec1a6f779479ea1ac07c0647f064dea454ec650c42/numpy-2.2.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a7b9084668aa0f64e64bd00d27ba5146ef1c3a8835f3bd912e7a9e01326804c4", size = 20947156 }, - { url = "https://files.pythonhosted.org/packages/24/6d/9483566acfbda6c62c6bc74b6e981c777229d2af93c8eb2469b26ac1b7bc/numpy-2.2.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:dbe512c511956b893d2dacd007d955a3f03d555ae05cfa3ff1c1ff6df8851854", size = 14133092 }, - { url = "https://files.pythonhosted.org/packages/27/f6/dba8a258acbf9d2bed2525cdcbb9493ef9bae5199d7a9cb92ee7e9b2aea6/numpy-2.2.4-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:bb649f8b207ab07caebba230d851b579a3c8711a851d29efe15008e31bb4de24", size = 5163515 }, - { url = "https://files.pythonhosted.org/packages/62/30/82116199d1c249446723c68f2c9da40d7f062551036f50b8c4caa42ae252/numpy-2.2.4-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:f34dc300df798742b3d06515aa2a0aee20941c13579d7a2f2e10af01ae4901ee", size = 6696558 }, - { url = "https://files.pythonhosted.org/packages/0e/b2/54122b3c6df5df3e87582b2e9430f1bdb63af4023c739ba300164c9ae503/numpy-2.2.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c3f7ac96b16955634e223b579a3e5798df59007ca43e8d451a0e6a50f6bfdfba", size = 14084742 }, - { url = "https://files.pythonhosted.org/packages/02/e2/e2cbb8d634151aab9528ef7b8bab52ee4ab10e076509285602c2a3a686e0/numpy-2.2.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f92084defa704deadd4e0a5ab1dc52d8ac9e8a8ef617f3fbb853e79b0ea3592", size = 16134051 }, - { url = "https://files.pythonhosted.org/packages/8e/21/efd47800e4affc993e8be50c1b768de038363dd88865920439ef7b422c60/numpy-2.2.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7a4e84a6283b36632e2a5b56e121961f6542ab886bc9e12f8f9818b3c266bfbb", size = 15578972 }, - { url = "https://files.pythonhosted.org/packages/04/1e/f8bb88f6157045dd5d9b27ccf433d016981032690969aa5c19e332b138c0/numpy-2.2.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:11c43995255eb4127115956495f43e9343736edb7fcdb0d973defd9de14cd84f", size = 17898106 }, - { url = "https://files.pythonhosted.org/packages/2b/93/df59a5a3897c1f036ae8ff845e45f4081bb06943039ae28a3c1c7c780f22/numpy-2.2.4-cp312-cp312-win32.whl", hash = "sha256:65ef3468b53269eb5fdb3a5c09508c032b793da03251d5f8722b1194f1790c00", size = 6311190 }, - { url = "https://files.pythonhosted.org/packages/46/69/8c4f928741c2a8efa255fdc7e9097527c6dc4e4df147e3cadc5d9357ce85/numpy-2.2.4-cp312-cp312-win_amd64.whl", hash = "sha256:2aad3c17ed2ff455b8eaafe06bcdae0062a1db77cb99f4b9cbb5f4ecb13c5146", size = 12644305 }, - { url = "https://files.pythonhosted.org/packages/2a/d0/bd5ad792e78017f5decfb2ecc947422a3669a34f775679a76317af671ffc/numpy-2.2.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cf4e5c6a278d620dee9ddeb487dc6a860f9b199eadeecc567f777daace1e9e7", size = 20933623 }, - { url = "https://files.pythonhosted.org/packages/c3/bc/2b3545766337b95409868f8e62053135bdc7fa2ce630aba983a2aa60b559/numpy-2.2.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1974afec0b479e50438fc3648974268f972e2d908ddb6d7fb634598cdb8260a0", size = 14148681 }, - { url = "https://files.pythonhosted.org/packages/6a/70/67b24d68a56551d43a6ec9fe8c5f91b526d4c1a46a6387b956bf2d64744e/numpy-2.2.4-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:79bd5f0a02aa16808fcbc79a9a376a147cc1045f7dfe44c6e7d53fa8b8a79392", size = 5148759 }, - { url = "https://files.pythonhosted.org/packages/1c/8b/e2fc8a75fcb7be12d90b31477c9356c0cbb44abce7ffb36be39a0017afad/numpy-2.2.4-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:3387dd7232804b341165cedcb90694565a6015433ee076c6754775e85d86f1fc", size = 6683092 }, - { url = "https://files.pythonhosted.org/packages/13/73/41b7b27f169ecf368b52533edb72e56a133f9e86256e809e169362553b49/numpy-2.2.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f527d8fdb0286fd2fd97a2a96c6be17ba4232da346931d967a0630050dfd298", size = 14081422 }, - { url = "https://files.pythonhosted.org/packages/4b/04/e208ff3ae3ddfbafc05910f89546382f15a3f10186b1f56bd99f159689c2/numpy-2.2.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bce43e386c16898b91e162e5baaad90c4b06f9dcbe36282490032cec98dc8ae7", size = 16132202 }, - { url = "https://files.pythonhosted.org/packages/fe/bc/2218160574d862d5e55f803d88ddcad88beff94791f9c5f86d67bd8fbf1c/numpy-2.2.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:31504f970f563d99f71a3512d0c01a645b692b12a63630d6aafa0939e52361e6", size = 15573131 }, - { url = "https://files.pythonhosted.org/packages/a5/78/97c775bc4f05abc8a8426436b7cb1be806a02a2994b195945600855e3a25/numpy-2.2.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:81413336ef121a6ba746892fad881a83351ee3e1e4011f52e97fba79233611fd", size = 17894270 }, - { url = "https://files.pythonhosted.org/packages/b9/eb/38c06217a5f6de27dcb41524ca95a44e395e6a1decdc0c99fec0832ce6ae/numpy-2.2.4-cp313-cp313-win32.whl", hash = "sha256:f486038e44caa08dbd97275a9a35a283a8f1d2f0ee60ac260a1790e76660833c", size = 6308141 }, - { url = "https://files.pythonhosted.org/packages/52/17/d0dd10ab6d125c6d11ffb6dfa3423c3571befab8358d4f85cd4471964fcd/numpy-2.2.4-cp313-cp313-win_amd64.whl", hash = "sha256:207a2b8441cc8b6a2a78c9ddc64d00d20c303d79fba08c577752f080c4007ee3", size = 12636885 }, - { url = "https://files.pythonhosted.org/packages/fa/e2/793288ede17a0fdc921172916efb40f3cbc2aa97e76c5c84aba6dc7e8747/numpy-2.2.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:8120575cb4882318c791f839a4fd66161a6fa46f3f0a5e613071aae35b5dd8f8", size = 20961829 }, - { url = "https://files.pythonhosted.org/packages/3a/75/bb4573f6c462afd1ea5cbedcc362fe3e9bdbcc57aefd37c681be1155fbaa/numpy-2.2.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a761ba0fa886a7bb33c6c8f6f20213735cb19642c580a931c625ee377ee8bd39", size = 14161419 }, - { url = "https://files.pythonhosted.org/packages/03/68/07b4cd01090ca46c7a336958b413cdbe75002286295f2addea767b7f16c9/numpy-2.2.4-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:ac0280f1ba4a4bfff363a99a6aceed4f8e123f8a9b234c89140f5e894e452ecd", size = 5196414 }, - { url = "https://files.pythonhosted.org/packages/a5/fd/d4a29478d622fedff5c4b4b4cedfc37a00691079623c0575978d2446db9e/numpy-2.2.4-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:879cf3a9a2b53a4672a168c21375166171bc3932b7e21f622201811c43cdd3b0", size = 6709379 }, - { url = "https://files.pythonhosted.org/packages/41/78/96dddb75bb9be730b87c72f30ffdd62611aba234e4e460576a068c98eff6/numpy-2.2.4-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f05d4198c1bacc9124018109c5fba2f3201dbe7ab6e92ff100494f236209c960", size = 14051725 }, - { url = "https://files.pythonhosted.org/packages/00/06/5306b8199bffac2a29d9119c11f457f6c7d41115a335b78d3f86fad4dbe8/numpy-2.2.4-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2f085ce2e813a50dfd0e01fbfc0c12bbe5d2063d99f8b29da30e544fb6483b8", size = 16101638 }, - { url = "https://files.pythonhosted.org/packages/fa/03/74c5b631ee1ded596945c12027649e6344614144369fd3ec1aaced782882/numpy-2.2.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:92bda934a791c01d6d9d8e038363c50918ef7c40601552a58ac84c9613a665bc", size = 15571717 }, - { url = "https://files.pythonhosted.org/packages/cb/dc/4fc7c0283abe0981e3b89f9b332a134e237dd476b0c018e1e21083310c31/numpy-2.2.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ee4d528022f4c5ff67332469e10efe06a267e32f4067dc76bb7e2cddf3cd25ff", size = 17879998 }, - { url = "https://files.pythonhosted.org/packages/e5/2b/878576190c5cfa29ed896b518cc516aecc7c98a919e20706c12480465f43/numpy-2.2.4-cp313-cp313t-win32.whl", hash = "sha256:05c076d531e9998e7e694c36e8b349969c56eadd2cdcd07242958489d79a7286", size = 6366896 }, - { url = "https://files.pythonhosted.org/packages/3e/05/eb7eec66b95cf697f08c754ef26c3549d03ebd682819f794cb039574a0a6/numpy-2.2.4-cp313-cp313t-win_amd64.whl", hash = "sha256:188dcbca89834cc2e14eb2f106c96d6d46f200fe0200310fc29089657379c58d", size = 12739119 }, - { url = "https://files.pythonhosted.org/packages/b2/5c/f09c33a511aff41a098e6ef3498465d95f6360621034a3d95f47edbc9119/numpy-2.2.4-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7051ee569db5fbac144335e0f3b9c2337e0c8d5c9fee015f259a5bd70772b7e8", size = 21081956 }, - { url = "https://files.pythonhosted.org/packages/ba/30/74c48b3b6494c4b820b7fa1781d441e94d87a08daa5b35d222f06ba41a6f/numpy-2.2.4-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:ab2939cd5bec30a7430cbdb2287b63151b77cf9624de0532d629c9a1c59b1d5c", size = 6827143 }, - { url = "https://files.pythonhosted.org/packages/54/f5/ab0d2f48b490535c7a80e05da4a98902b632369efc04f0e47bb31ca97d8f/numpy-2.2.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d0f35b19894a9e08639fd60a1ec1978cb7f5f7f1eace62f38dd36be8aecdef4d", size = 16233350 }, - { url = "https://files.pythonhosted.org/packages/3b/3a/2f6d8c1f8e45d496bca6baaec93208035faeb40d5735c25afac092ec9a12/numpy-2.2.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:b4adfbbc64014976d2f91084915ca4e626fbf2057fb81af209c1a6d776d23e3d", size = 12857565 }, -] - [[package]] name = "oauthlib" version = "3.2.2" @@ -1760,25 +1045,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7e/80/cab10959dc1faead58dc8384a781dfbf93cb4d33d50988f7a69f1b7c9bbe/oauthlib-3.2.2-py3-none-any.whl", hash = "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca", size = 151688 }, ] -[[package]] -name = "openai" -version = "1.71.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "anyio" }, - { name = "distro" }, - { name = "httpx" }, - { name = "jiter" }, - { name = "pydantic" }, - { name = "sniffio" }, - { name = "tqdm" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d9/19/b8f0347090a649dce55a008ec54ac6abb50553a06508cdb5e7abb2813e99/openai-1.71.0.tar.gz", hash = "sha256:52b20bb990a1780f9b0b8ccebac93416343ebd3e4e714e3eff730336833ca207", size = 409926 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c4/f7/049e85faf6a000890e5ca0edca8e9183f8a43c9e7bba869cad871da0caba/openai-1.71.0-py3-none-any.whl", hash = "sha256:e1c643738f1fff1af52bce6ef06a7716c95d089281e7011777179614f32937aa", size = 598975 }, -] - [[package]] name = "opentelemetry-api" version = "1.31.1" @@ -2010,66 +1276,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2c/00/1591b397c9efc0e4215d223553a1cb9090c8499888a4447f842443077d31/opentelemetry_util_http-0.52b1-py3-none-any.whl", hash = "sha256:6a6ab6bfa23fef96f4995233e874f67602adf9d224895981b4ab9d4dde23de78", size = 7305 }, ] -[[package]] -name = "orjson" -version = "3.10.15" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ae/f9/5dea21763eeff8c1590076918a446ea3d6140743e0e36f58f369928ed0f4/orjson-3.10.15.tar.gz", hash = "sha256:05ca7fe452a2e9d8d9d706a2984c95b9c2ebc5db417ce0b7a49b91d50642a23e", size = 5282482 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/52/09/e5ff18ad009e6f97eb7edc5f67ef98b3ce0c189da9c3eaca1f9587cd4c61/orjson-3.10.15-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:552c883d03ad185f720d0c09583ebde257e41b9521b74ff40e08b7dec4559c04", size = 249532 }, - { url = "https://files.pythonhosted.org/packages/bd/b8/a75883301fe332bd433d9b0ded7d2bb706ccac679602c3516984f8814fb5/orjson-3.10.15-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:616e3e8d438d02e4854f70bfdc03a6bcdb697358dbaa6bcd19cbe24d24ece1f8", size = 125229 }, - { url = "https://files.pythonhosted.org/packages/83/4b/22f053e7a364cc9c685be203b1e40fc5f2b3f164a9b2284547504eec682e/orjson-3.10.15-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7c2c79fa308e6edb0ffab0a31fd75a7841bf2a79a20ef08a3c6e3b26814c8ca8", size = 150148 }, - { url = "https://files.pythonhosted.org/packages/63/64/1b54fc75ca328b57dd810541a4035fe48c12a161d466e3cf5b11a8c25649/orjson-3.10.15-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73cb85490aa6bf98abd20607ab5c8324c0acb48d6da7863a51be48505646c814", size = 139748 }, - { url = "https://files.pythonhosted.org/packages/5e/ff/ff0c5da781807bb0a5acd789d9a7fbcb57f7b0c6e1916595da1f5ce69f3c/orjson-3.10.15-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:763dadac05e4e9d2bc14938a45a2d0560549561287d41c465d3c58aec818b164", size = 154559 }, - { url = "https://files.pythonhosted.org/packages/4e/9a/11e2974383384ace8495810d4a2ebef5f55aacfc97b333b65e789c9d362d/orjson-3.10.15-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a330b9b4734f09a623f74a7490db713695e13b67c959713b78369f26b3dee6bf", size = 130349 }, - { url = "https://files.pythonhosted.org/packages/2d/c4/dd9583aea6aefee1b64d3aed13f51d2aadb014028bc929fe52936ec5091f/orjson-3.10.15-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a61a4622b7ff861f019974f73d8165be1bd9a0855e1cad18ee167acacabeb061", size = 138514 }, - { url = "https://files.pythonhosted.org/packages/53/3e/dcf1729230654f5c5594fc752de1f43dcf67e055ac0d300c8cdb1309269a/orjson-3.10.15-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:acd271247691574416b3228db667b84775c497b245fa275c6ab90dc1ffbbd2b3", size = 130940 }, - { url = "https://files.pythonhosted.org/packages/e8/2b/b9759fe704789937705c8a56a03f6c03e50dff7df87d65cba9a20fec5282/orjson-3.10.15-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:e4759b109c37f635aa5c5cc93a1b26927bfde24b254bcc0e1149a9fada253d2d", size = 414713 }, - { url = "https://files.pythonhosted.org/packages/a7/6b/b9dfdbd4b6e20a59238319eb203ae07c3f6abf07eef909169b7a37ae3bba/orjson-3.10.15-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:9e992fd5cfb8b9f00bfad2fd7a05a4299db2bbe92e6440d9dd2fab27655b3182", size = 141028 }, - { url = "https://files.pythonhosted.org/packages/7c/b5/40f5bbea619c7caf75eb4d652a9821875a8ed04acc45fe3d3ef054ca69fb/orjson-3.10.15-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f95fb363d79366af56c3f26b71df40b9a583b07bbaaf5b317407c4d58497852e", size = 129715 }, - { url = "https://files.pythonhosted.org/packages/38/60/2272514061cbdf4d672edbca6e59c7e01cd1c706e881427d88f3c3e79761/orjson-3.10.15-cp310-cp310-win32.whl", hash = "sha256:f9875f5fea7492da8ec2444839dcc439b0ef298978f311103d0b7dfd775898ab", size = 142473 }, - { url = "https://files.pythonhosted.org/packages/11/5d/be1490ff7eafe7fef890eb4527cf5bcd8cfd6117f3efe42a3249ec847b60/orjson-3.10.15-cp310-cp310-win_amd64.whl", hash = "sha256:17085a6aa91e1cd70ca8533989a18b5433e15d29c574582f76f821737c8d5806", size = 133564 }, - { url = "https://files.pythonhosted.org/packages/7a/a2/21b25ce4a2c71dbb90948ee81bd7a42b4fbfc63162e57faf83157d5540ae/orjson-3.10.15-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:c4cc83960ab79a4031f3119cc4b1a1c627a3dc09df125b27c4201dff2af7eaa6", size = 249533 }, - { url = "https://files.pythonhosted.org/packages/b2/85/2076fc12d8225698a51278009726750c9c65c846eda741e77e1761cfef33/orjson-3.10.15-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ddbeef2481d895ab8be5185f2432c334d6dec1f5d1933a9c83014d188e102cef", size = 125230 }, - { url = "https://files.pythonhosted.org/packages/06/df/a85a7955f11274191eccf559e8481b2be74a7c6d43075d0a9506aa80284d/orjson-3.10.15-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9e590a0477b23ecd5b0ac865b1b907b01b3c5535f5e8a8f6ab0e503efb896334", size = 150148 }, - { url = "https://files.pythonhosted.org/packages/37/b3/94c55625a29b8767c0eed194cb000b3787e3c23b4cdd13be17bae6ccbb4b/orjson-3.10.15-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a6be38bd103d2fd9bdfa31c2720b23b5d47c6796bcb1d1b598e3924441b4298d", size = 139749 }, - { url = "https://files.pythonhosted.org/packages/53/ba/c608b1e719971e8ddac2379f290404c2e914cf8e976369bae3cad88768b1/orjson-3.10.15-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ff4f6edb1578960ed628a3b998fa54d78d9bb3e2eb2cfc5c2a09732431c678d0", size = 154558 }, - { url = "https://files.pythonhosted.org/packages/b2/c4/c1fb835bb23ad788a39aa9ebb8821d51b1c03588d9a9e4ca7de5b354fdd5/orjson-3.10.15-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b0482b21d0462eddd67e7fce10b89e0b6ac56570424662b685a0d6fccf581e13", size = 130349 }, - { url = "https://files.pythonhosted.org/packages/78/14/bb2b48b26ab3c570b284eb2157d98c1ef331a8397f6c8bd983b270467f5c/orjson-3.10.15-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bb5cc3527036ae3d98b65e37b7986a918955f85332c1ee07f9d3f82f3a6899b5", size = 138513 }, - { url = "https://files.pythonhosted.org/packages/4a/97/d5b353a5fe532e92c46467aa37e637f81af8468aa894cd77d2ec8a12f99e/orjson-3.10.15-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d569c1c462912acdd119ccbf719cf7102ea2c67dd03b99edcb1a3048651ac96b", size = 130942 }, - { url = "https://files.pythonhosted.org/packages/b5/5d/a067bec55293cca48fea8b9928cfa84c623be0cce8141d47690e64a6ca12/orjson-3.10.15-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:1e6d33efab6b71d67f22bf2962895d3dc6f82a6273a965fab762e64fa90dc399", size = 414717 }, - { url = "https://files.pythonhosted.org/packages/6f/9a/1485b8b05c6b4c4db172c438cf5db5dcfd10e72a9bc23c151a1137e763e0/orjson-3.10.15-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:c33be3795e299f565681d69852ac8c1bc5c84863c0b0030b2b3468843be90388", size = 141033 }, - { url = "https://files.pythonhosted.org/packages/f8/d2/fc67523656e43a0c7eaeae9007c8b02e86076b15d591e9be11554d3d3138/orjson-3.10.15-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:eea80037b9fae5339b214f59308ef0589fc06dc870578b7cce6d71eb2096764c", size = 129720 }, - { url = "https://files.pythonhosted.org/packages/79/42/f58c7bd4e5b54da2ce2ef0331a39ccbbaa7699b7f70206fbf06737c9ed7d/orjson-3.10.15-cp311-cp311-win32.whl", hash = "sha256:d5ac11b659fd798228a7adba3e37c010e0152b78b1982897020a8e019a94882e", size = 142473 }, - { url = "https://files.pythonhosted.org/packages/00/f8/bb60a4644287a544ec81df1699d5b965776bc9848d9029d9f9b3402ac8bb/orjson-3.10.15-cp311-cp311-win_amd64.whl", hash = "sha256:cf45e0214c593660339ef63e875f32ddd5aa3b4adc15e662cdb80dc49e194f8e", size = 133570 }, - { url = "https://files.pythonhosted.org/packages/66/85/22fe737188905a71afcc4bf7cc4c79cd7f5bbe9ed1fe0aac4ce4c33edc30/orjson-3.10.15-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9d11c0714fc85bfcf36ada1179400862da3288fc785c30e8297844c867d7505a", size = 249504 }, - { url = "https://files.pythonhosted.org/packages/48/b7/2622b29f3afebe938a0a9037e184660379797d5fd5234e5998345d7a5b43/orjson-3.10.15-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dba5a1e85d554e3897fa9fe6fbcff2ed32d55008973ec9a2b992bd9a65d2352d", size = 125080 }, - { url = "https://files.pythonhosted.org/packages/ce/8f/0b72a48f4403d0b88b2a41450c535b3e8989e8a2d7800659a967efc7c115/orjson-3.10.15-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7723ad949a0ea502df656948ddd8b392780a5beaa4c3b5f97e525191b102fff0", size = 150121 }, - { url = "https://files.pythonhosted.org/packages/06/ec/acb1a20cd49edb2000be5a0404cd43e3c8aad219f376ac8c60b870518c03/orjson-3.10.15-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6fd9bc64421e9fe9bd88039e7ce8e58d4fead67ca88e3a4014b143cec7684fd4", size = 139796 }, - { url = "https://files.pythonhosted.org/packages/33/e1/f7840a2ea852114b23a52a1c0b2bea0a1ea22236efbcdb876402d799c423/orjson-3.10.15-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dadba0e7b6594216c214ef7894c4bd5f08d7c0135f4dd0145600be4fbcc16767", size = 154636 }, - { url = "https://files.pythonhosted.org/packages/fa/da/31543337febd043b8fa80a3b67de627669b88c7b128d9ad4cc2ece005b7a/orjson-3.10.15-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b48f59114fe318f33bbaee8ebeda696d8ccc94c9e90bc27dbe72153094e26f41", size = 130621 }, - { url = "https://files.pythonhosted.org/packages/ed/78/66115dc9afbc22496530d2139f2f4455698be444c7c2475cb48f657cefc9/orjson-3.10.15-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:035fb83585e0f15e076759b6fedaf0abb460d1765b6a36f48018a52858443514", size = 138516 }, - { url = "https://files.pythonhosted.org/packages/22/84/cd4f5fb5427ffcf823140957a47503076184cb1ce15bcc1165125c26c46c/orjson-3.10.15-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d13b7fe322d75bf84464b075eafd8e7dd9eae05649aa2a5354cfa32f43c59f17", size = 130762 }, - { url = "https://files.pythonhosted.org/packages/93/1f/67596b711ba9f56dd75d73b60089c5c92057f1130bb3a25a0f53fb9a583b/orjson-3.10.15-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:7066b74f9f259849629e0d04db6609db4cf5b973248f455ba5d3bd58a4daaa5b", size = 414700 }, - { url = "https://files.pythonhosted.org/packages/7c/0c/6a3b3271b46443d90efb713c3e4fe83fa8cd71cda0d11a0f69a03f437c6e/orjson-3.10.15-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:88dc3f65a026bd3175eb157fea994fca6ac7c4c8579fc5a86fc2114ad05705b7", size = 141077 }, - { url = "https://files.pythonhosted.org/packages/3b/9b/33c58e0bfc788995eccd0d525ecd6b84b40d7ed182dd0751cd4c1322ac62/orjson-3.10.15-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b342567e5465bd99faa559507fe45e33fc76b9fb868a63f1642c6bc0735ad02a", size = 129898 }, - { url = "https://files.pythonhosted.org/packages/01/c1/d577ecd2e9fa393366a1ea0a9267f6510d86e6c4bb1cdfb9877104cac44c/orjson-3.10.15-cp312-cp312-win32.whl", hash = "sha256:0a4f27ea5617828e6b58922fdbec67b0aa4bb844e2d363b9244c47fa2180e665", size = 142566 }, - { url = "https://files.pythonhosted.org/packages/ed/eb/a85317ee1732d1034b92d56f89f1de4d7bf7904f5c8fb9dcdd5b1c83917f/orjson-3.10.15-cp312-cp312-win_amd64.whl", hash = "sha256:ef5b87e7aa9545ddadd2309efe6824bd3dd64ac101c15dae0f2f597911d46eaa", size = 133732 }, - { url = "https://files.pythonhosted.org/packages/06/10/fe7d60b8da538e8d3d3721f08c1b7bff0491e8fa4dd3bf11a17e34f4730e/orjson-3.10.15-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:bae0e6ec2b7ba6895198cd981b7cca95d1487d0147c8ed751e5632ad16f031a6", size = 249399 }, - { url = "https://files.pythonhosted.org/packages/6b/83/52c356fd3a61abd829ae7e4366a6fe8e8863c825a60d7ac5156067516edf/orjson-3.10.15-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f93ce145b2db1252dd86af37d4165b6faa83072b46e3995ecc95d4b2301b725a", size = 125044 }, - { url = "https://files.pythonhosted.org/packages/55/b2/d06d5901408e7ded1a74c7c20d70e3a127057a6d21355f50c90c0f337913/orjson-3.10.15-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7c203f6f969210128af3acae0ef9ea6aab9782939f45f6fe02d05958fe761ef9", size = 150066 }, - { url = "https://files.pythonhosted.org/packages/75/8c/60c3106e08dc593a861755781c7c675a566445cc39558677d505878d879f/orjson-3.10.15-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8918719572d662e18b8af66aef699d8c21072e54b6c82a3f8f6404c1f5ccd5e0", size = 139737 }, - { url = "https://files.pythonhosted.org/packages/6a/8c/ae00d7d0ab8a4490b1efeb01ad4ab2f1982e69cc82490bf8093407718ff5/orjson-3.10.15-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f71eae9651465dff70aa80db92586ad5b92df46a9373ee55252109bb6b703307", size = 154804 }, - { url = "https://files.pythonhosted.org/packages/22/86/65dc69bd88b6dd254535310e97bc518aa50a39ef9c5a2a5d518e7a223710/orjson-3.10.15-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e117eb299a35f2634e25ed120c37c641398826c2f5a3d3cc39f5993b96171b9e", size = 130583 }, - { url = "https://files.pythonhosted.org/packages/bb/00/6fe01ededb05d52be42fabb13d93a36e51f1fd9be173bd95707d11a8a860/orjson-3.10.15-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:13242f12d295e83c2955756a574ddd6741c81e5b99f2bef8ed8d53e47a01e4b7", size = 138465 }, - { url = "https://files.pythonhosted.org/packages/db/2f/4cc151c4b471b0cdc8cb29d3eadbce5007eb0475d26fa26ed123dca93b33/orjson-3.10.15-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7946922ada8f3e0b7b958cc3eb22cfcf6c0df83d1fe5521b4a100103e3fa84c8", size = 130742 }, - { url = "https://files.pythonhosted.org/packages/9f/13/8a6109e4b477c518498ca37963d9c0eb1508b259725553fb53d53b20e2ea/orjson-3.10.15-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:b7155eb1623347f0f22c38c9abdd738b287e39b9982e1da227503387b81b34ca", size = 414669 }, - { url = "https://files.pythonhosted.org/packages/22/7b/1d229d6d24644ed4d0a803de1b0e2df832032d5beda7346831c78191b5b2/orjson-3.10.15-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:208beedfa807c922da4e81061dafa9c8489c6328934ca2a562efa707e049e561", size = 141043 }, - { url = "https://files.pythonhosted.org/packages/cc/d3/6dc91156cf12ed86bed383bcb942d84d23304a1e57b7ab030bf60ea130d6/orjson-3.10.15-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eca81f83b1b8c07449e1d6ff7074e82e3fd6777e588f1a6632127f286a968825", size = 129826 }, - { url = "https://files.pythonhosted.org/packages/b3/38/c47c25b86f6996f1343be721b6ea4367bc1c8bc0fc3f6bbcd995d18cb19d/orjson-3.10.15-cp313-cp313-win32.whl", hash = "sha256:c03cd6eea1bd3b949d0d007c8d57049aa2b39bd49f58b4b2af571a5d3833d890", size = 142542 }, - { url = "https://files.pythonhosted.org/packages/27/f1/1d7ec15b20f8ce9300bc850de1e059132b88990e46cd0ccac29cbf11e4f9/orjson-3.10.15-cp313-cp313-win_amd64.whl", hash = "sha256:fd56a26a04f6ba5fb2045b0acc487a63162a958ed837648c5781e1fe3316cfbf", size = 133444 }, -] - [[package]] name = "outcome" version = "1.3.0.post0" @@ -2223,95 +1429,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/43/b3/df14c580d82b9627d173ceea305ba898dca135feb360b6d84019d0803d3b/pre_commit-4.1.0-py2.py3-none-any.whl", hash = "sha256:d29e7cb346295bcc1cc75fc3e92e343495e3ea0196c9ec6ba53f49f10ab6ae7b", size = 220560 }, ] -[[package]] -name = "propcache" -version = "0.3.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/07/c8/fdc6686a986feae3541ea23dcaa661bd93972d3940460646c6bb96e21c40/propcache-0.3.1.tar.gz", hash = "sha256:40d980c33765359098837527e18eddefc9a24cea5b45e078a7f3bb5b032c6ecf", size = 43651 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/20/56/e27c136101addf877c8291dbda1b3b86ae848f3837ce758510a0d806c92f/propcache-0.3.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f27785888d2fdd918bc36de8b8739f2d6c791399552333721b58193f68ea3e98", size = 80224 }, - { url = "https://files.pythonhosted.org/packages/63/bd/88e98836544c4f04db97eefd23b037c2002fa173dd2772301c61cd3085f9/propcache-0.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4e89cde74154c7b5957f87a355bb9c8ec929c167b59c83d90654ea36aeb6180", size = 46491 }, - { url = "https://files.pythonhosted.org/packages/15/43/0b8eb2a55753c4a574fc0899885da504b521068d3b08ca56774cad0bea2b/propcache-0.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:730178f476ef03d3d4d255f0c9fa186cb1d13fd33ffe89d39f2cda4da90ceb71", size = 45927 }, - { url = "https://files.pythonhosted.org/packages/ad/6c/d01f9dfbbdc613305e0a831016844987a1fb4861dd221cd4c69b1216b43f/propcache-0.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:967a8eec513dbe08330f10137eacb427b2ca52118769e82ebcfcab0fba92a649", size = 206135 }, - { url = "https://files.pythonhosted.org/packages/9a/8a/e6e1c77394088f4cfdace4a91a7328e398ebed745d59c2f6764135c5342d/propcache-0.3.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b9145c35cc87313b5fd480144f8078716007656093d23059e8993d3a8fa730f", size = 220517 }, - { url = "https://files.pythonhosted.org/packages/19/3b/6c44fa59d6418f4239d5db8b1ece757351e85d6f3ca126dfe37d427020c8/propcache-0.3.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9e64e948ab41411958670f1093c0a57acfdc3bee5cf5b935671bbd5313bcf229", size = 218952 }, - { url = "https://files.pythonhosted.org/packages/7c/e4/4aeb95a1cd085e0558ab0de95abfc5187329616193a1012a6c4c930e9f7a/propcache-0.3.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:319fa8765bfd6a265e5fa661547556da381e53274bc05094fc9ea50da51bfd46", size = 206593 }, - { url = "https://files.pythonhosted.org/packages/da/6a/29fa75de1cbbb302f1e1d684009b969976ca603ee162282ae702287b6621/propcache-0.3.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c66d8ccbc902ad548312b96ed8d5d266d0d2c6d006fd0f66323e9d8f2dd49be7", size = 196745 }, - { url = "https://files.pythonhosted.org/packages/19/7e/2237dad1dbffdd2162de470599fa1a1d55df493b16b71e5d25a0ac1c1543/propcache-0.3.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2d219b0dbabe75e15e581fc1ae796109b07c8ba7d25b9ae8d650da582bed01b0", size = 203369 }, - { url = "https://files.pythonhosted.org/packages/a4/bc/a82c5878eb3afb5c88da86e2cf06e1fe78b7875b26198dbb70fe50a010dc/propcache-0.3.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:cd6a55f65241c551eb53f8cf4d2f4af33512c39da5d9777694e9d9c60872f519", size = 198723 }, - { url = "https://files.pythonhosted.org/packages/17/76/9632254479c55516f51644ddbf747a45f813031af5adcb8db91c0b824375/propcache-0.3.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:9979643ffc69b799d50d3a7b72b5164a2e97e117009d7af6dfdd2ab906cb72cd", size = 200751 }, - { url = "https://files.pythonhosted.org/packages/3e/c3/a90b773cf639bd01d12a9e20c95be0ae978a5a8abe6d2d343900ae76cd71/propcache-0.3.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4cf9e93a81979f1424f1a3d155213dc928f1069d697e4353edb8a5eba67c6259", size = 210730 }, - { url = "https://files.pythonhosted.org/packages/ed/ec/ad5a952cdb9d65c351f88db7c46957edd3d65ffeee72a2f18bd6341433e0/propcache-0.3.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2fce1df66915909ff6c824bbb5eb403d2d15f98f1518e583074671a30fe0c21e", size = 213499 }, - { url = "https://files.pythonhosted.org/packages/83/c0/ea5133dda43e298cd2010ec05c2821b391e10980e64ee72c0a76cdbb813a/propcache-0.3.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:4d0dfdd9a2ebc77b869a0b04423591ea8823f791293b527dc1bb896c1d6f1136", size = 207132 }, - { url = "https://files.pythonhosted.org/packages/79/dd/71aae9dec59333064cfdd7eb31a63fa09f64181b979802a67a90b2abfcba/propcache-0.3.1-cp310-cp310-win32.whl", hash = "sha256:1f6cc0ad7b4560e5637eb2c994e97b4fa41ba8226069c9277eb5ea7101845b42", size = 40952 }, - { url = "https://files.pythonhosted.org/packages/31/0a/49ff7e5056c17dfba62cbdcbb90a29daffd199c52f8e65e5cb09d5f53a57/propcache-0.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:47ef24aa6511e388e9894ec16f0fbf3313a53ee68402bc428744a367ec55b833", size = 45163 }, - { url = "https://files.pythonhosted.org/packages/90/0f/5a5319ee83bd651f75311fcb0c492c21322a7fc8f788e4eef23f44243427/propcache-0.3.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7f30241577d2fef2602113b70ef7231bf4c69a97e04693bde08ddab913ba0ce5", size = 80243 }, - { url = "https://files.pythonhosted.org/packages/ce/84/3db5537e0879942783e2256616ff15d870a11d7ac26541336fe1b673c818/propcache-0.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:43593c6772aa12abc3af7784bff4a41ffa921608dd38b77cf1dfd7f5c4e71371", size = 46503 }, - { url = "https://files.pythonhosted.org/packages/e2/c8/b649ed972433c3f0d827d7f0cf9ea47162f4ef8f4fe98c5f3641a0bc63ff/propcache-0.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a75801768bbe65499495660b777e018cbe90c7980f07f8aa57d6be79ea6f71da", size = 45934 }, - { url = "https://files.pythonhosted.org/packages/59/f9/4c0a5cf6974c2c43b1a6810c40d889769cc8f84cea676cbe1e62766a45f8/propcache-0.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f6f1324db48f001c2ca26a25fa25af60711e09b9aaf4b28488602776f4f9a744", size = 233633 }, - { url = "https://files.pythonhosted.org/packages/e7/64/66f2f4d1b4f0007c6e9078bd95b609b633d3957fe6dd23eac33ebde4b584/propcache-0.3.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cdb0f3e1eb6dfc9965d19734d8f9c481b294b5274337a8cb5cb01b462dcb7e0", size = 241124 }, - { url = "https://files.pythonhosted.org/packages/aa/bf/7b8c9fd097d511638fa9b6af3d986adbdf567598a567b46338c925144c1b/propcache-0.3.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1eb34d90aac9bfbced9a58b266f8946cb5935869ff01b164573a7634d39fbcb5", size = 240283 }, - { url = "https://files.pythonhosted.org/packages/fa/c9/e85aeeeaae83358e2a1ef32d6ff50a483a5d5248bc38510d030a6f4e2816/propcache-0.3.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f35c7070eeec2cdaac6fd3fe245226ed2a6292d3ee8c938e5bb645b434c5f256", size = 232498 }, - { url = "https://files.pythonhosted.org/packages/8e/66/acb88e1f30ef5536d785c283af2e62931cb934a56a3ecf39105887aa8905/propcache-0.3.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b23c11c2c9e6d4e7300c92e022046ad09b91fd00e36e83c44483df4afa990073", size = 221486 }, - { url = "https://files.pythonhosted.org/packages/f5/f9/233ddb05ffdcaee4448508ee1d70aa7deff21bb41469ccdfcc339f871427/propcache-0.3.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3e19ea4ea0bf46179f8a3652ac1426e6dcbaf577ce4b4f65be581e237340420d", size = 222675 }, - { url = "https://files.pythonhosted.org/packages/98/b8/eb977e28138f9e22a5a789daf608d36e05ed93093ef12a12441030da800a/propcache-0.3.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:bd39c92e4c8f6cbf5f08257d6360123af72af9f4da75a690bef50da77362d25f", size = 215727 }, - { url = "https://files.pythonhosted.org/packages/89/2d/5f52d9c579f67b8ee1edd9ec073c91b23cc5b7ff7951a1e449e04ed8fdf3/propcache-0.3.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:b0313e8b923b3814d1c4a524c93dfecea5f39fa95601f6a9b1ac96cd66f89ea0", size = 217878 }, - { url = "https://files.pythonhosted.org/packages/7a/fd/5283e5ed8a82b00c7a989b99bb6ea173db1ad750bf0bf8dff08d3f4a4e28/propcache-0.3.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e861ad82892408487be144906a368ddbe2dc6297074ade2d892341b35c59844a", size = 230558 }, - { url = "https://files.pythonhosted.org/packages/90/38/ab17d75938ef7ac87332c588857422ae126b1c76253f0f5b1242032923ca/propcache-0.3.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:61014615c1274df8da5991a1e5da85a3ccb00c2d4701ac6f3383afd3ca47ab0a", size = 233754 }, - { url = "https://files.pythonhosted.org/packages/06/5d/3b921b9c60659ae464137508d3b4c2b3f52f592ceb1964aa2533b32fcf0b/propcache-0.3.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:71ebe3fe42656a2328ab08933d420df5f3ab121772eef78f2dc63624157f0ed9", size = 226088 }, - { url = "https://files.pythonhosted.org/packages/54/6e/30a11f4417d9266b5a464ac5a8c5164ddc9dd153dfa77bf57918165eb4ae/propcache-0.3.1-cp311-cp311-win32.whl", hash = "sha256:58aa11f4ca8b60113d4b8e32d37e7e78bd8af4d1a5b5cb4979ed856a45e62005", size = 40859 }, - { url = "https://files.pythonhosted.org/packages/1d/3a/8a68dd867da9ca2ee9dfd361093e9cb08cb0f37e5ddb2276f1b5177d7731/propcache-0.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:9532ea0b26a401264b1365146c440a6d78269ed41f83f23818d4b79497aeabe7", size = 45153 }, - { url = "https://files.pythonhosted.org/packages/41/aa/ca78d9be314d1e15ff517b992bebbed3bdfef5b8919e85bf4940e57b6137/propcache-0.3.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f78eb8422acc93d7b69964012ad7048764bb45a54ba7a39bb9e146c72ea29723", size = 80430 }, - { url = "https://files.pythonhosted.org/packages/1a/d8/f0c17c44d1cda0ad1979af2e593ea290defdde9eaeb89b08abbe02a5e8e1/propcache-0.3.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:89498dd49c2f9a026ee057965cdf8192e5ae070ce7d7a7bd4b66a8e257d0c976", size = 46637 }, - { url = "https://files.pythonhosted.org/packages/ae/bd/c1e37265910752e6e5e8a4c1605d0129e5b7933c3dc3cf1b9b48ed83b364/propcache-0.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:09400e98545c998d57d10035ff623266927cb784d13dd2b31fd33b8a5316b85b", size = 46123 }, - { url = "https://files.pythonhosted.org/packages/d4/b0/911eda0865f90c0c7e9f0415d40a5bf681204da5fd7ca089361a64c16b28/propcache-0.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa8efd8c5adc5a2c9d3b952815ff8f7710cefdcaf5f2c36d26aff51aeca2f12f", size = 243031 }, - { url = "https://files.pythonhosted.org/packages/0a/06/0da53397c76a74271621807265b6eb61fb011451b1ddebf43213df763669/propcache-0.3.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c2fe5c910f6007e716a06d269608d307b4f36e7babee5f36533722660e8c4a70", size = 249100 }, - { url = "https://files.pythonhosted.org/packages/f1/eb/13090e05bf6b963fc1653cdc922133ced467cb4b8dab53158db5a37aa21e/propcache-0.3.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a0ab8cf8cdd2194f8ff979a43ab43049b1df0b37aa64ab7eca04ac14429baeb7", size = 250170 }, - { url = "https://files.pythonhosted.org/packages/3b/4c/f72c9e1022b3b043ec7dc475a0f405d4c3e10b9b1d378a7330fecf0652da/propcache-0.3.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:563f9d8c03ad645597b8d010ef4e9eab359faeb11a0a2ac9f7b4bc8c28ebef25", size = 245000 }, - { url = "https://files.pythonhosted.org/packages/e8/fd/970ca0e22acc829f1adf5de3724085e778c1ad8a75bec010049502cb3a86/propcache-0.3.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fb6e0faf8cb6b4beea5d6ed7b5a578254c6d7df54c36ccd3d8b3eb00d6770277", size = 230262 }, - { url = "https://files.pythonhosted.org/packages/c4/42/817289120c6b9194a44f6c3e6b2c3277c5b70bbad39e7df648f177cc3634/propcache-0.3.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1c5c7ab7f2bb3f573d1cb921993006ba2d39e8621019dffb1c5bc94cdbae81e8", size = 236772 }, - { url = "https://files.pythonhosted.org/packages/7c/9c/3b3942b302badd589ad6b672da3ca7b660a6c2f505cafd058133ddc73918/propcache-0.3.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:050b571b2e96ec942898f8eb46ea4bfbb19bd5502424747e83badc2d4a99a44e", size = 231133 }, - { url = "https://files.pythonhosted.org/packages/98/a1/75f6355f9ad039108ff000dfc2e19962c8dea0430da9a1428e7975cf24b2/propcache-0.3.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e1c4d24b804b3a87e9350f79e2371a705a188d292fd310e663483af6ee6718ee", size = 230741 }, - { url = "https://files.pythonhosted.org/packages/67/0c/3e82563af77d1f8731132166da69fdfd95e71210e31f18edce08a1eb11ea/propcache-0.3.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:e4fe2a6d5ce975c117a6bb1e8ccda772d1e7029c1cca1acd209f91d30fa72815", size = 244047 }, - { url = "https://files.pythonhosted.org/packages/f7/50/9fb7cca01532a08c4d5186d7bb2da6c4c587825c0ae134b89b47c7d62628/propcache-0.3.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:feccd282de1f6322f56f6845bf1207a537227812f0a9bf5571df52bb418d79d5", size = 246467 }, - { url = "https://files.pythonhosted.org/packages/a9/02/ccbcf3e1c604c16cc525309161d57412c23cf2351523aedbb280eb7c9094/propcache-0.3.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ec314cde7314d2dd0510c6787326bbffcbdc317ecee6b7401ce218b3099075a7", size = 241022 }, - { url = "https://files.pythonhosted.org/packages/db/19/e777227545e09ca1e77a6e21274ae9ec45de0f589f0ce3eca2a41f366220/propcache-0.3.1-cp312-cp312-win32.whl", hash = "sha256:7d2d5a0028d920738372630870e7d9644ce437142197f8c827194fca404bf03b", size = 40647 }, - { url = "https://files.pythonhosted.org/packages/24/bb/3b1b01da5dd04c77a204c84e538ff11f624e31431cfde7201d9110b092b1/propcache-0.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:88c423efef9d7a59dae0614eaed718449c09a5ac79a5f224a8b9664d603f04a3", size = 44784 }, - { url = "https://files.pythonhosted.org/packages/58/60/f645cc8b570f99be3cf46714170c2de4b4c9d6b827b912811eff1eb8a412/propcache-0.3.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f1528ec4374617a7a753f90f20e2f551121bb558fcb35926f99e3c42367164b8", size = 77865 }, - { url = "https://files.pythonhosted.org/packages/6f/d4/c1adbf3901537582e65cf90fd9c26fde1298fde5a2c593f987112c0d0798/propcache-0.3.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:dc1915ec523b3b494933b5424980831b636fe483d7d543f7afb7b3bf00f0c10f", size = 45452 }, - { url = "https://files.pythonhosted.org/packages/d1/b5/fe752b2e63f49f727c6c1c224175d21b7d1727ce1d4873ef1c24c9216830/propcache-0.3.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a110205022d077da24e60b3df8bcee73971be9575dec5573dd17ae5d81751111", size = 44800 }, - { url = "https://files.pythonhosted.org/packages/62/37/fc357e345bc1971e21f76597028b059c3d795c5ca7690d7a8d9a03c9708a/propcache-0.3.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d249609e547c04d190e820d0d4c8ca03ed4582bcf8e4e160a6969ddfb57b62e5", size = 225804 }, - { url = "https://files.pythonhosted.org/packages/0d/f1/16e12c33e3dbe7f8b737809bad05719cff1dccb8df4dafbcff5575002c0e/propcache-0.3.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ced33d827625d0a589e831126ccb4f5c29dfdf6766cac441d23995a65825dcb", size = 230650 }, - { url = "https://files.pythonhosted.org/packages/3e/a2/018b9f2ed876bf5091e60153f727e8f9073d97573f790ff7cdf6bc1d1fb8/propcache-0.3.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4114c4ada8f3181af20808bedb250da6bae56660e4b8dfd9cd95d4549c0962f7", size = 234235 }, - { url = "https://files.pythonhosted.org/packages/45/5f/3faee66fc930dfb5da509e34c6ac7128870631c0e3582987fad161fcb4b1/propcache-0.3.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:975af16f406ce48f1333ec5e912fe11064605d5c5b3f6746969077cc3adeb120", size = 228249 }, - { url = "https://files.pythonhosted.org/packages/62/1e/a0d5ebda5da7ff34d2f5259a3e171a94be83c41eb1e7cd21a2105a84a02e/propcache-0.3.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a34aa3a1abc50740be6ac0ab9d594e274f59960d3ad253cd318af76b996dd654", size = 214964 }, - { url = "https://files.pythonhosted.org/packages/db/a0/d72da3f61ceab126e9be1f3bc7844b4e98c6e61c985097474668e7e52152/propcache-0.3.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9cec3239c85ed15bfaded997773fdad9fb5662b0a7cbc854a43f291eb183179e", size = 222501 }, - { url = "https://files.pythonhosted.org/packages/18/6d/a008e07ad7b905011253adbbd97e5b5375c33f0b961355ca0a30377504ac/propcache-0.3.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:05543250deac8e61084234d5fc54f8ebd254e8f2b39a16b1dce48904f45b744b", size = 217917 }, - { url = "https://files.pythonhosted.org/packages/98/37/02c9343ffe59e590e0e56dc5c97d0da2b8b19fa747ebacf158310f97a79a/propcache-0.3.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:5cb5918253912e088edbf023788de539219718d3b10aef334476b62d2b53de53", size = 217089 }, - { url = "https://files.pythonhosted.org/packages/53/1b/d3406629a2c8a5666d4674c50f757a77be119b113eedd47b0375afdf1b42/propcache-0.3.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f3bbecd2f34d0e6d3c543fdb3b15d6b60dd69970c2b4c822379e5ec8f6f621d5", size = 228102 }, - { url = "https://files.pythonhosted.org/packages/cd/a7/3664756cf50ce739e5f3abd48febc0be1a713b1f389a502ca819791a6b69/propcache-0.3.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:aca63103895c7d960a5b9b044a83f544b233c95e0dcff114389d64d762017af7", size = 230122 }, - { url = "https://files.pythonhosted.org/packages/35/36/0bbabaacdcc26dac4f8139625e930f4311864251276033a52fd52ff2a274/propcache-0.3.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5a0a9898fdb99bf11786265468571e628ba60af80dc3f6eb89a3545540c6b0ef", size = 226818 }, - { url = "https://files.pythonhosted.org/packages/cc/27/4e0ef21084b53bd35d4dae1634b6d0bad35e9c58ed4f032511acca9d4d26/propcache-0.3.1-cp313-cp313-win32.whl", hash = "sha256:3a02a28095b5e63128bcae98eb59025924f121f048a62393db682f049bf4ac24", size = 40112 }, - { url = "https://files.pythonhosted.org/packages/a6/2c/a54614d61895ba6dd7ac8f107e2b2a0347259ab29cbf2ecc7b94fa38c4dc/propcache-0.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:813fbb8b6aea2fc9659815e585e548fe706d6f663fa73dff59a1677d4595a037", size = 44034 }, - { url = "https://files.pythonhosted.org/packages/5a/a8/0a4fd2f664fc6acc66438370905124ce62e84e2e860f2557015ee4a61c7e/propcache-0.3.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:a444192f20f5ce8a5e52761a031b90f5ea6288b1eef42ad4c7e64fef33540b8f", size = 82613 }, - { url = "https://files.pythonhosted.org/packages/4d/e5/5ef30eb2cd81576256d7b6caaa0ce33cd1d2c2c92c8903cccb1af1a4ff2f/propcache-0.3.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0fbe94666e62ebe36cd652f5fc012abfbc2342de99b523f8267a678e4dfdee3c", size = 47763 }, - { url = "https://files.pythonhosted.org/packages/87/9a/87091ceb048efeba4d28e903c0b15bcc84b7c0bf27dc0261e62335d9b7b8/propcache-0.3.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f011f104db880f4e2166bcdcf7f58250f7a465bc6b068dc84c824a3d4a5c94dc", size = 47175 }, - { url = "https://files.pythonhosted.org/packages/3e/2f/854e653c96ad1161f96194c6678a41bbb38c7947d17768e8811a77635a08/propcache-0.3.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e584b6d388aeb0001d6d5c2bd86b26304adde6d9bb9bfa9c4889805021b96de", size = 292265 }, - { url = "https://files.pythonhosted.org/packages/40/8d/090955e13ed06bc3496ba4a9fb26c62e209ac41973cb0d6222de20c6868f/propcache-0.3.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8a17583515a04358b034e241f952f1715243482fc2c2945fd99a1b03a0bd77d6", size = 294412 }, - { url = "https://files.pythonhosted.org/packages/39/e6/d51601342e53cc7582449e6a3c14a0479fab2f0750c1f4d22302e34219c6/propcache-0.3.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5aed8d8308215089c0734a2af4f2e95eeb360660184ad3912686c181e500b2e7", size = 294290 }, - { url = "https://files.pythonhosted.org/packages/3b/4d/be5f1a90abc1881884aa5878989a1acdafd379a91d9c7e5e12cef37ec0d7/propcache-0.3.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d8e309ff9a0503ef70dc9a0ebd3e69cf7b3894c9ae2ae81fc10943c37762458", size = 282926 }, - { url = "https://files.pythonhosted.org/packages/57/2b/8f61b998c7ea93a2b7eca79e53f3e903db1787fca9373af9e2cf8dc22f9d/propcache-0.3.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b655032b202028a582d27aeedc2e813299f82cb232f969f87a4fde491a233f11", size = 267808 }, - { url = "https://files.pythonhosted.org/packages/11/1c/311326c3dfce59c58a6098388ba984b0e5fb0381ef2279ec458ef99bd547/propcache-0.3.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9f64d91b751df77931336b5ff7bafbe8845c5770b06630e27acd5dbb71e1931c", size = 290916 }, - { url = "https://files.pythonhosted.org/packages/4b/74/91939924b0385e54dc48eb2e4edd1e4903ffd053cf1916ebc5347ac227f7/propcache-0.3.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:19a06db789a4bd896ee91ebc50d059e23b3639c25d58eb35be3ca1cbe967c3bf", size = 262661 }, - { url = "https://files.pythonhosted.org/packages/c2/d7/e6079af45136ad325c5337f5dd9ef97ab5dc349e0ff362fe5c5db95e2454/propcache-0.3.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:bef100c88d8692864651b5f98e871fb090bd65c8a41a1cb0ff2322db39c96c27", size = 264384 }, - { url = "https://files.pythonhosted.org/packages/b7/d5/ba91702207ac61ae6f1c2da81c5d0d6bf6ce89e08a2b4d44e411c0bbe867/propcache-0.3.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:87380fb1f3089d2a0b8b00f006ed12bd41bd858fabfa7330c954c70f50ed8757", size = 291420 }, - { url = "https://files.pythonhosted.org/packages/58/70/2117780ed7edcd7ba6b8134cb7802aada90b894a9810ec56b7bb6018bee7/propcache-0.3.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e474fc718e73ba5ec5180358aa07f6aded0ff5f2abe700e3115c37d75c947e18", size = 290880 }, - { url = "https://files.pythonhosted.org/packages/4a/1f/ecd9ce27710021ae623631c0146719280a929d895a095f6d85efb6a0be2e/propcache-0.3.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:17d1c688a443355234f3c031349da69444be052613483f3e4158eef751abcd8a", size = 287407 }, - { url = "https://files.pythonhosted.org/packages/3e/66/2e90547d6b60180fb29e23dc87bd8c116517d4255240ec6d3f7dc23d1926/propcache-0.3.1-cp313-cp313t-win32.whl", hash = "sha256:359e81a949a7619802eb601d66d37072b79b79c2505e6d3fd8b945538411400d", size = 42573 }, - { url = "https://files.pythonhosted.org/packages/cb/8f/50ad8599399d1861b4d2b6b45271f0ef6af1b09b0a2386a46dbaf19c9535/propcache-0.3.1-cp313-cp313t-win_amd64.whl", hash = "sha256:e7fb9a84c9abbf2b2683fa3e7b0d7da4d8ecf139a1c635732a8bda29c5214b0e", size = 46757 }, - { url = "https://files.pythonhosted.org/packages/b8/d3/c3cb8f1d6ae3b37f83e1de806713a9b3642c5895f0215a62e1a4bd6e5e34/propcache-0.3.1-py3-none-any.whl", hash = "sha256:9a8ecf38de50a7f518c21568c80f985e776397b902f1ce0b01f799aba1608b40", size = 12376 }, -] - [[package]] name = "psutil" version = "6.1.1" @@ -2438,19 +1555,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2f/f8/66f328e411f1c9574b13c2c28ab01f308b53688bbbe6ca8fb981e6cabc42/pydantic_core-2.33.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:4b6d77c75a57f041c5ee915ff0b0bb58eabb78728b69ed967bc5b780e8f701b8", size = 2082099 }, ] -[[package]] -name = "pydantic-settings" -version = "2.8.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pydantic" }, - { name = "python-dotenv" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/88/82/c79424d7d8c29b994fb01d277da57b0a9b09cc03c3ff875f9bd8a86b2145/pydantic_settings-2.8.1.tar.gz", hash = "sha256:d5c663dfbe9db9d5e1c646b2e161da12f0d734d422ee56f567d0ea2cee4e8585", size = 83550 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0b/53/a64f03044927dc47aafe029c42a5b7aabc38dfb813475e0e1bf71c4a59d0/pydantic_settings-2.8.1-py3-none-any.whl", hash = "sha256:81942d5ac3d905f7f3ee1a70df5dfb62d5569c12f51a5a647defc1c3d9ee2e9c", size = 30839 }, -] - [[package]] name = "pygments" version = "2.19.1" @@ -2506,14 +1610,15 @@ wheels = [ [[package]] name = "pytest-asyncio" -version = "1.0.0" +version = "1.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ + { name = "backports-asyncio-runner", marker = "python_full_version < '3.11'" }, { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d0/d4/14f53324cb1a6381bef29d698987625d80052bb33932d8e7cbf9b337b17c/pytest_asyncio-1.0.0.tar.gz", hash = "sha256:d15463d13f4456e1ead2594520216b225a16f781e144f8fdf6c5bb4667c48b3f", size = 46960 } +sdist = { url = "https://files.pythonhosted.org/packages/4e/51/f8794af39eeb870e87a8c8068642fc07bce0c854d6865d7dd0f2a9d338c2/pytest_asyncio-1.1.0.tar.gz", hash = "sha256:796aa822981e01b68c12e4827b8697108f7205020f24b5793b3c41555dab68ea", size = 46652 } wheels = [ - { url = "https://files.pythonhosted.org/packages/30/05/ce271016e351fddc8399e546f6e23761967ee09c8c568bbfbecb0c150171/pytest_asyncio-1.0.0-py3-none-any.whl", hash = "sha256:4f024da9f1ef945e680dc68610b52550e36590a67fd31bb3b4943979a1f90ef3", size = 15976 }, + { url = "https://files.pythonhosted.org/packages/c7/9d/bf86eddabf8c6c9cb1ea9a869d6873b46f105a5d292d3a6f7071f5b07935/pytest_asyncio-1.1.0-py3-none-any.whl", hash = "sha256:5fe2d69607b0bd75c656d1211f969cadba035030156745ee09e7d71740e58ecf", size = 15157 }, ] [[package]] @@ -2645,75 +1750,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5a/66/bbb1dd374f5c870f59c5bb1db0e18cbe7fa739415a24cbd95b2d1f5ae0c4/pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069", size = 3911 }, ] -[[package]] -name = "regex" -version = "2024.11.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8e/5f/bd69653fbfb76cf8604468d3b4ec4c403197144c7bfe0e6a5fc9e02a07cb/regex-2024.11.6.tar.gz", hash = "sha256:7ab159b063c52a0333c884e4679f8d7a85112ee3078fe3d9004b2dd875585519", size = 399494 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/95/3c/4651f6b130c6842a8f3df82461a8950f923925db8b6961063e82744bddcc/regex-2024.11.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff590880083d60acc0433f9c3f713c51f7ac6ebb9adf889c79a261ecf541aa91", size = 482674 }, - { url = "https://files.pythonhosted.org/packages/15/51/9f35d12da8434b489c7b7bffc205c474a0a9432a889457026e9bc06a297a/regex-2024.11.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:658f90550f38270639e83ce492f27d2c8d2cd63805c65a13a14d36ca126753f0", size = 287684 }, - { url = "https://files.pythonhosted.org/packages/bd/18/b731f5510d1b8fb63c6b6d3484bfa9a59b84cc578ac8b5172970e05ae07c/regex-2024.11.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:164d8b7b3b4bcb2068b97428060b2a53be050085ef94eca7f240e7947f1b080e", size = 284589 }, - { url = "https://files.pythonhosted.org/packages/78/a2/6dd36e16341ab95e4c6073426561b9bfdeb1a9c9b63ab1b579c2e96cb105/regex-2024.11.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3660c82f209655a06b587d55e723f0b813d3a7db2e32e5e7dc64ac2a9e86fde", size = 782511 }, - { url = "https://files.pythonhosted.org/packages/1b/2b/323e72d5d2fd8de0d9baa443e1ed70363ed7e7b2fb526f5950c5cb99c364/regex-2024.11.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d22326fcdef5e08c154280b71163ced384b428343ae16a5ab2b3354aed12436e", size = 821149 }, - { url = "https://files.pythonhosted.org/packages/90/30/63373b9ea468fbef8a907fd273e5c329b8c9535fee36fc8dba5fecac475d/regex-2024.11.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1ac758ef6aebfc8943560194e9fd0fa18bcb34d89fd8bd2af18183afd8da3a2", size = 809707 }, - { url = "https://files.pythonhosted.org/packages/f2/98/26d3830875b53071f1f0ae6d547f1d98e964dd29ad35cbf94439120bb67a/regex-2024.11.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:997d6a487ff00807ba810e0f8332c18b4eb8d29463cfb7c820dc4b6e7562d0cf", size = 781702 }, - { url = "https://files.pythonhosted.org/packages/87/55/eb2a068334274db86208ab9d5599ffa63631b9f0f67ed70ea7c82a69bbc8/regex-2024.11.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:02a02d2bb04fec86ad61f3ea7f49c015a0681bf76abb9857f945d26159d2968c", size = 771976 }, - { url = "https://files.pythonhosted.org/packages/74/c0/be707bcfe98254d8f9d2cff55d216e946f4ea48ad2fd8cf1428f8c5332ba/regex-2024.11.6-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f02f93b92358ee3f78660e43b4b0091229260c5d5c408d17d60bf26b6c900e86", size = 697397 }, - { url = "https://files.pythonhosted.org/packages/49/dc/bb45572ceb49e0f6509f7596e4ba7031f6819ecb26bc7610979af5a77f45/regex-2024.11.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:06eb1be98df10e81ebaded73fcd51989dcf534e3c753466e4b60c4697a003b67", size = 768726 }, - { url = "https://files.pythonhosted.org/packages/5a/db/f43fd75dc4c0c2d96d0881967897926942e935d700863666f3c844a72ce6/regex-2024.11.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:040df6fe1a5504eb0f04f048e6d09cd7c7110fef851d7c567a6b6e09942feb7d", size = 775098 }, - { url = "https://files.pythonhosted.org/packages/99/d7/f94154db29ab5a89d69ff893159b19ada89e76b915c1293e98603d39838c/regex-2024.11.6-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabbfc59f2c6edba2a6622c647b716e34e8e3867e0ab975412c5c2f79b82da2", size = 839325 }, - { url = "https://files.pythonhosted.org/packages/f7/17/3cbfab1f23356fbbf07708220ab438a7efa1e0f34195bf857433f79f1788/regex-2024.11.6-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8447d2d39b5abe381419319f942de20b7ecd60ce86f16a23b0698f22e1b70008", size = 843277 }, - { url = "https://files.pythonhosted.org/packages/7e/f2/48b393b51900456155de3ad001900f94298965e1cad1c772b87f9cfea011/regex-2024.11.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:da8f5fc57d1933de22a9e23eec290a0d8a5927a5370d24bda9a6abe50683fe62", size = 773197 }, - { url = "https://files.pythonhosted.org/packages/45/3f/ef9589aba93e084cd3f8471fded352826dcae8489b650d0b9b27bc5bba8a/regex-2024.11.6-cp310-cp310-win32.whl", hash = "sha256:b489578720afb782f6ccf2840920f3a32e31ba28a4b162e13900c3e6bd3f930e", size = 261714 }, - { url = "https://files.pythonhosted.org/packages/42/7e/5f1b92c8468290c465fd50c5318da64319133231415a8aa6ea5ab995a815/regex-2024.11.6-cp310-cp310-win_amd64.whl", hash = "sha256:5071b2093e793357c9d8b2929dfc13ac5f0a6c650559503bb81189d0a3814519", size = 274042 }, - { url = "https://files.pythonhosted.org/packages/58/58/7e4d9493a66c88a7da6d205768119f51af0f684fe7be7bac8328e217a52c/regex-2024.11.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5478c6962ad548b54a591778e93cd7c456a7a29f8eca9c49e4f9a806dcc5d638", size = 482669 }, - { url = "https://files.pythonhosted.org/packages/34/4c/8f8e631fcdc2ff978609eaeef1d6994bf2f028b59d9ac67640ed051f1218/regex-2024.11.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2c89a8cc122b25ce6945f0423dc1352cb9593c68abd19223eebbd4e56612c5b7", size = 287684 }, - { url = "https://files.pythonhosted.org/packages/c5/1b/f0e4d13e6adf866ce9b069e191f303a30ab1277e037037a365c3aad5cc9c/regex-2024.11.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:94d87b689cdd831934fa3ce16cc15cd65748e6d689f5d2b8f4f4df2065c9fa20", size = 284589 }, - { url = "https://files.pythonhosted.org/packages/25/4d/ab21047f446693887f25510887e6820b93f791992994f6498b0318904d4a/regex-2024.11.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1062b39a0a2b75a9c694f7a08e7183a80c63c0d62b301418ffd9c35f55aaa114", size = 792121 }, - { url = "https://files.pythonhosted.org/packages/45/ee/c867e15cd894985cb32b731d89576c41a4642a57850c162490ea34b78c3b/regex-2024.11.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:167ed4852351d8a750da48712c3930b031f6efdaa0f22fa1933716bfcd6bf4a3", size = 831275 }, - { url = "https://files.pythonhosted.org/packages/b3/12/b0f480726cf1c60f6536fa5e1c95275a77624f3ac8fdccf79e6727499e28/regex-2024.11.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d548dafee61f06ebdb584080621f3e0c23fff312f0de1afc776e2a2ba99a74f", size = 818257 }, - { url = "https://files.pythonhosted.org/packages/bf/ce/0d0e61429f603bac433910d99ef1a02ce45a8967ffbe3cbee48599e62d88/regex-2024.11.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2a19f302cd1ce5dd01a9099aaa19cae6173306d1302a43b627f62e21cf18ac0", size = 792727 }, - { url = "https://files.pythonhosted.org/packages/e4/c1/243c83c53d4a419c1556f43777ccb552bccdf79d08fda3980e4e77dd9137/regex-2024.11.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bec9931dfb61ddd8ef2ebc05646293812cb6b16b60cf7c9511a832b6f1854b55", size = 780667 }, - { url = "https://files.pythonhosted.org/packages/c5/f4/75eb0dd4ce4b37f04928987f1d22547ddaf6c4bae697623c1b05da67a8aa/regex-2024.11.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9714398225f299aa85267fd222f7142fcb5c769e73d7733344efc46f2ef5cf89", size = 776963 }, - { url = "https://files.pythonhosted.org/packages/16/5d/95c568574e630e141a69ff8a254c2f188b4398e813c40d49228c9bbd9875/regex-2024.11.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:202eb32e89f60fc147a41e55cb086db2a3f8cb82f9a9a88440dcfc5d37faae8d", size = 784700 }, - { url = "https://files.pythonhosted.org/packages/8e/b5/f8495c7917f15cc6fee1e7f395e324ec3e00ab3c665a7dc9d27562fd5290/regex-2024.11.6-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:4181b814e56078e9b00427ca358ec44333765f5ca1b45597ec7446d3a1ef6e34", size = 848592 }, - { url = "https://files.pythonhosted.org/packages/1c/80/6dd7118e8cb212c3c60b191b932dc57db93fb2e36fb9e0e92f72a5909af9/regex-2024.11.6-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:068376da5a7e4da51968ce4c122a7cd31afaaec4fccc7856c92f63876e57b51d", size = 852929 }, - { url = "https://files.pythonhosted.org/packages/11/9b/5a05d2040297d2d254baf95eeeb6df83554e5e1df03bc1a6687fc4ba1f66/regex-2024.11.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ac10f2c4184420d881a3475fb2c6f4d95d53a8d50209a2500723d831036f7c45", size = 781213 }, - { url = "https://files.pythonhosted.org/packages/26/b7/b14e2440156ab39e0177506c08c18accaf2b8932e39fb092074de733d868/regex-2024.11.6-cp311-cp311-win32.whl", hash = "sha256:c36f9b6f5f8649bb251a5f3f66564438977b7ef8386a52460ae77e6070d309d9", size = 261734 }, - { url = "https://files.pythonhosted.org/packages/80/32/763a6cc01d21fb3819227a1cc3f60fd251c13c37c27a73b8ff4315433a8e/regex-2024.11.6-cp311-cp311-win_amd64.whl", hash = "sha256:02e28184be537f0e75c1f9b2f8847dc51e08e6e171c6bde130b2687e0c33cf60", size = 274052 }, - { url = "https://files.pythonhosted.org/packages/ba/30/9a87ce8336b172cc232a0db89a3af97929d06c11ceaa19d97d84fa90a8f8/regex-2024.11.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:52fb28f528778f184f870b7cf8f225f5eef0a8f6e3778529bdd40c7b3920796a", size = 483781 }, - { url = "https://files.pythonhosted.org/packages/01/e8/00008ad4ff4be8b1844786ba6636035f7ef926db5686e4c0f98093612add/regex-2024.11.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdd6028445d2460f33136c55eeb1f601ab06d74cb3347132e1c24250187500d9", size = 288455 }, - { url = "https://files.pythonhosted.org/packages/60/85/cebcc0aff603ea0a201667b203f13ba75d9fc8668fab917ac5b2de3967bc/regex-2024.11.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:805e6b60c54bf766b251e94526ebad60b7de0c70f70a4e6210ee2891acb70bf2", size = 284759 }, - { url = "https://files.pythonhosted.org/packages/94/2b/701a4b0585cb05472a4da28ee28fdfe155f3638f5e1ec92306d924e5faf0/regex-2024.11.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b85c2530be953a890eaffde05485238f07029600e8f098cdf1848d414a8b45e4", size = 794976 }, - { url = "https://files.pythonhosted.org/packages/4b/bf/fa87e563bf5fee75db8915f7352e1887b1249126a1be4813837f5dbec965/regex-2024.11.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb26437975da7dc36b7efad18aa9dd4ea569d2357ae6b783bf1118dabd9ea577", size = 833077 }, - { url = "https://files.pythonhosted.org/packages/a1/56/7295e6bad94b047f4d0834e4779491b81216583c00c288252ef625c01d23/regex-2024.11.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:abfa5080c374a76a251ba60683242bc17eeb2c9818d0d30117b4486be10c59d3", size = 823160 }, - { url = "https://files.pythonhosted.org/packages/fb/13/e3b075031a738c9598c51cfbc4c7879e26729c53aa9cca59211c44235314/regex-2024.11.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b7fa6606c2881c1db9479b0eaa11ed5dfa11c8d60a474ff0e095099f39d98e", size = 796896 }, - { url = "https://files.pythonhosted.org/packages/24/56/0b3f1b66d592be6efec23a795b37732682520b47c53da5a32c33ed7d84e3/regex-2024.11.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c32f75920cf99fe6b6c539c399a4a128452eaf1af27f39bce8909c9a3fd8cbe", size = 783997 }, - { url = "https://files.pythonhosted.org/packages/f9/a1/eb378dada8b91c0e4c5f08ffb56f25fcae47bf52ad18f9b2f33b83e6d498/regex-2024.11.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:982e6d21414e78e1f51cf595d7f321dcd14de1f2881c5dc6a6e23bbbbd68435e", size = 781725 }, - { url = "https://files.pythonhosted.org/packages/83/f2/033e7dec0cfd6dda93390089864732a3409246ffe8b042e9554afa9bff4e/regex-2024.11.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a7c2155f790e2fb448faed6dd241386719802296ec588a8b9051c1f5c481bc29", size = 789481 }, - { url = "https://files.pythonhosted.org/packages/83/23/15d4552ea28990a74e7696780c438aadd73a20318c47e527b47a4a5a596d/regex-2024.11.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:149f5008d286636e48cd0b1dd65018548944e495b0265b45e1bffecce1ef7f39", size = 852896 }, - { url = "https://files.pythonhosted.org/packages/e3/39/ed4416bc90deedbfdada2568b2cb0bc1fdb98efe11f5378d9892b2a88f8f/regex-2024.11.6-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e5364a4502efca094731680e80009632ad6624084aff9a23ce8c8c6820de3e51", size = 860138 }, - { url = "https://files.pythonhosted.org/packages/93/2d/dd56bb76bd8e95bbce684326302f287455b56242a4f9c61f1bc76e28360e/regex-2024.11.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0a86e7eeca091c09e021db8eb72d54751e527fa47b8d5787caf96d9831bd02ad", size = 787692 }, - { url = "https://files.pythonhosted.org/packages/0b/55/31877a249ab7a5156758246b9c59539abbeba22461b7d8adc9e8475ff73e/regex-2024.11.6-cp312-cp312-win32.whl", hash = "sha256:32f9a4c643baad4efa81d549c2aadefaeba12249b2adc5af541759237eee1c54", size = 262135 }, - { url = "https://files.pythonhosted.org/packages/38/ec/ad2d7de49a600cdb8dd78434a1aeffe28b9d6fc42eb36afab4a27ad23384/regex-2024.11.6-cp312-cp312-win_amd64.whl", hash = "sha256:a93c194e2df18f7d264092dc8539b8ffb86b45b899ab976aa15d48214138e81b", size = 273567 }, - { url = "https://files.pythonhosted.org/packages/90/73/bcb0e36614601016552fa9344544a3a2ae1809dc1401b100eab02e772e1f/regex-2024.11.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a6ba92c0bcdf96cbf43a12c717eae4bc98325ca3730f6b130ffa2e3c3c723d84", size = 483525 }, - { url = "https://files.pythonhosted.org/packages/0f/3f/f1a082a46b31e25291d830b369b6b0c5576a6f7fb89d3053a354c24b8a83/regex-2024.11.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:525eab0b789891ac3be914d36893bdf972d483fe66551f79d3e27146191a37d4", size = 288324 }, - { url = "https://files.pythonhosted.org/packages/09/c9/4e68181a4a652fb3ef5099e077faf4fd2a694ea6e0f806a7737aff9e758a/regex-2024.11.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:086a27a0b4ca227941700e0b31425e7a28ef1ae8e5e05a33826e17e47fbfdba0", size = 284617 }, - { url = "https://files.pythonhosted.org/packages/fc/fd/37868b75eaf63843165f1d2122ca6cb94bfc0271e4428cf58c0616786dce/regex-2024.11.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bde01f35767c4a7899b7eb6e823b125a64de314a8ee9791367c9a34d56af18d0", size = 795023 }, - { url = "https://files.pythonhosted.org/packages/c4/7c/d4cd9c528502a3dedb5c13c146e7a7a539a3853dc20209c8e75d9ba9d1b2/regex-2024.11.6-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b583904576650166b3d920d2bcce13971f6f9e9a396c673187f49811b2769dc7", size = 833072 }, - { url = "https://files.pythonhosted.org/packages/4f/db/46f563a08f969159c5a0f0e722260568425363bea43bb7ae370becb66a67/regex-2024.11.6-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c4de13f06a0d54fa0d5ab1b7138bfa0d883220965a29616e3ea61b35d5f5fc7", size = 823130 }, - { url = "https://files.pythonhosted.org/packages/db/60/1eeca2074f5b87df394fccaa432ae3fc06c9c9bfa97c5051aed70e6e00c2/regex-2024.11.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3cde6e9f2580eb1665965ce9bf17ff4952f34f5b126beb509fee8f4e994f143c", size = 796857 }, - { url = "https://files.pythonhosted.org/packages/10/db/ac718a08fcee981554d2f7bb8402f1faa7e868c1345c16ab1ebec54b0d7b/regex-2024.11.6-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d7f453dca13f40a02b79636a339c5b62b670141e63efd511d3f8f73fba162b3", size = 784006 }, - { url = "https://files.pythonhosted.org/packages/c2/41/7da3fe70216cea93144bf12da2b87367590bcf07db97604edeea55dac9ad/regex-2024.11.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:59dfe1ed21aea057a65c6b586afd2a945de04fc7db3de0a6e3ed5397ad491b07", size = 781650 }, - { url = "https://files.pythonhosted.org/packages/a7/d5/880921ee4eec393a4752e6ab9f0fe28009435417c3102fc413f3fe81c4e5/regex-2024.11.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b97c1e0bd37c5cd7902e65f410779d39eeda155800b65fc4d04cc432efa9bc6e", size = 789545 }, - { url = "https://files.pythonhosted.org/packages/dc/96/53770115e507081122beca8899ab7f5ae28ae790bfcc82b5e38976df6a77/regex-2024.11.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f9d1e379028e0fc2ae3654bac3cbbef81bf3fd571272a42d56c24007979bafb6", size = 853045 }, - { url = "https://files.pythonhosted.org/packages/31/d3/1372add5251cc2d44b451bd94f43b2ec78e15a6e82bff6a290ef9fd8f00a/regex-2024.11.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:13291b39131e2d002a7940fb176e120bec5145f3aeb7621be6534e46251912c4", size = 860182 }, - { url = "https://files.pythonhosted.org/packages/ed/e3/c446a64984ea9f69982ba1a69d4658d5014bc7a0ea468a07e1a1265db6e2/regex-2024.11.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f51f88c126370dcec4908576c5a627220da6c09d0bff31cfa89f2523843316d", size = 787733 }, - { url = "https://files.pythonhosted.org/packages/2b/f1/e40c8373e3480e4f29f2692bd21b3e05f296d3afebc7e5dcf21b9756ca1c/regex-2024.11.6-cp313-cp313-win32.whl", hash = "sha256:63b13cfd72e9601125027202cad74995ab26921d8cd935c25f09c630436348ff", size = 262122 }, - { url = "https://files.pythonhosted.org/packages/45/94/bc295babb3062a731f52621cdc992d123111282e291abaf23faa413443ea/regex-2024.11.6-cp313-cp313-win_amd64.whl", hash = "sha256:2b3361af3198667e99927da8b84c1b010752fa4b1115ee30beaa332cabc3ef1a", size = 273545 }, -] - [[package]] name = "requests" version = "2.32.3" @@ -2742,18 +1778,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3b/5d/63d4ae3b9daea098d5d6f5da83984853c1bbacd5dc826764b249fe119d24/requests_oauthlib-2.0.0-py2.py3-none-any.whl", hash = "sha256:7dd8a5c40426b779b0868c404bdef9768deccf22749cde15852df527e6269b36", size = 24179 }, ] -[[package]] -name = "requests-toolbelt" -version = "1.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/f3/61/d7545dafb7ac2230c70d38d31cbfe4cc64f7144dc41f6e4e4b78ecd9f5bb/requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6", size = 206888 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/51/d4db610ef29373b879047326cbf6fa98b6c1969d6f6dc423279de2b1be2c/requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06", size = 54481 }, -] - [[package]] name = "rich" version = "13.9.4" @@ -2851,51 +1875,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/32/46/9cb0e58b2deb7f82b84065f37f3bffeb12413f947f9388e4cac22c4621ce/sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0", size = 29575 }, ] -[[package]] -name = "sqlalchemy" -version = "2.0.40" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "greenlet", marker = "(python_full_version < '3.14' and platform_machine == 'AMD64') or (python_full_version < '3.14' and platform_machine == 'WIN32') or (python_full_version < '3.14' and platform_machine == 'aarch64') or (python_full_version < '3.14' and platform_machine == 'amd64') or (python_full_version < '3.14' and platform_machine == 'ppc64le') or (python_full_version < '3.14' and platform_machine == 'win32') or (python_full_version < '3.14' and platform_machine == 'x86_64')" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/68/c3/3f2bfa5e4dcd9938405fe2fab5b6ab94a9248a4f9536ea2fd497da20525f/sqlalchemy-2.0.40.tar.gz", hash = "sha256:d827099289c64589418ebbcaead0145cd19f4e3e8a93919a0100247af245fa00", size = 9664299 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/87/fa/8e8fd93684b04e65816be864bebf0000fe1602e5452d006f9acc5db14ce5/sqlalchemy-2.0.40-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f1ea21bef99c703f44444ad29c2c1b6bd55d202750b6de8e06a955380f4725d7", size = 2112843 }, - { url = "https://files.pythonhosted.org/packages/ba/87/06992f78a9ce545dfd1fea3dd99262bec5221f6f9d2d2066c3e94662529f/sqlalchemy-2.0.40-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:afe63b208153f3a7a2d1a5b9df452b0673082588933e54e7c8aac457cf35e758", size = 2104032 }, - { url = "https://files.pythonhosted.org/packages/92/ee/57dc77282e8be22d686bd4681825299aa1069bbe090564868ea270ed5214/sqlalchemy-2.0.40-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a8aae085ea549a1eddbc9298b113cffb75e514eadbb542133dd2b99b5fb3b6af", size = 3086406 }, - { url = "https://files.pythonhosted.org/packages/94/3f/ceb9ab214b2e42d2e74a9209b3a2f2f073504eee16cddd2df81feeb67c2f/sqlalchemy-2.0.40-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ea9181284754d37db15156eb7be09c86e16e50fbe77610e9e7bee09291771a1", size = 3094652 }, - { url = "https://files.pythonhosted.org/packages/00/0a/3401232a5b6d91a2df16c1dc39c6504c54575744c2faafa1e5a50de96621/sqlalchemy-2.0.40-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5434223b795be5c5ef8244e5ac98056e290d3a99bdcc539b916e282b160dda00", size = 3050503 }, - { url = "https://files.pythonhosted.org/packages/93/c2/ea7171415ab131397f71a2673645c2fe29ebe9a93063d458eb89e42bf051/sqlalchemy-2.0.40-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:15d08d5ef1b779af6a0909b97be6c1fd4298057504eb6461be88bd1696cb438e", size = 3076011 }, - { url = "https://files.pythonhosted.org/packages/3d/ee/d8e229280d621bed8c51eebf1dd413aa09ca89e309b1fff40d881dd149af/sqlalchemy-2.0.40-cp310-cp310-win32.whl", hash = "sha256:cd2f75598ae70bcfca9117d9e51a3b06fe29edd972fdd7fd57cc97b4dbf3b08a", size = 2085136 }, - { url = "https://files.pythonhosted.org/packages/60/7f/ea1086136bc648cd4713a1e01869f7fc31979d67b3a8f973f5d9ab8de7e1/sqlalchemy-2.0.40-cp310-cp310-win_amd64.whl", hash = "sha256:2cbafc8d39ff1abdfdda96435f38fab141892dc759a2165947d1a8fffa7ef596", size = 2109421 }, - { url = "https://files.pythonhosted.org/packages/77/7e/55044a9ec48c3249bb38d5faae93f09579c35e862bb318ebd1ed7a1994a5/sqlalchemy-2.0.40-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f6bacab7514de6146a1976bc56e1545bee247242fab030b89e5f70336fc0003e", size = 2114025 }, - { url = "https://files.pythonhosted.org/packages/77/0f/dcf7bba95f847aec72f638750747b12d37914f71c8cc7c133cf326ab945c/sqlalchemy-2.0.40-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5654d1ac34e922b6c5711631f2da497d3a7bffd6f9f87ac23b35feea56098011", size = 2104419 }, - { url = "https://files.pythonhosted.org/packages/75/70/c86a5c20715e4fe903dde4c2fd44fc7e7a0d5fb52c1b954d98526f65a3ea/sqlalchemy-2.0.40-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35904d63412db21088739510216e9349e335f142ce4a04b69e2528020ee19ed4", size = 3222720 }, - { url = "https://files.pythonhosted.org/packages/12/cf/b891a8c1d0c27ce9163361664c2128c7a57de3f35000ea5202eb3a2917b7/sqlalchemy-2.0.40-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c7a80ed86d6aaacb8160a1caef6680d4ddd03c944d985aecee940d168c411d1", size = 3222682 }, - { url = "https://files.pythonhosted.org/packages/15/3f/7709d8c8266953d945435a96b7f425ae4172a336963756b58e996fbef7f3/sqlalchemy-2.0.40-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:519624685a51525ddaa7d8ba8265a1540442a2ec71476f0e75241eb8263d6f51", size = 3159542 }, - { url = "https://files.pythonhosted.org/packages/85/7e/717eaabaf0f80a0132dc2032ea8f745b7a0914451c984821a7c8737fb75a/sqlalchemy-2.0.40-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:2ee5f9999a5b0e9689bed96e60ee53c3384f1a05c2dd8068cc2e8361b0df5b7a", size = 3179864 }, - { url = "https://files.pythonhosted.org/packages/e4/cc/03eb5dfcdb575cbecd2bd82487b9848f250a4b6ecfb4707e834b4ce4ec07/sqlalchemy-2.0.40-cp311-cp311-win32.whl", hash = "sha256:c0cae71e20e3c02c52f6b9e9722bca70e4a90a466d59477822739dc31ac18b4b", size = 2084675 }, - { url = "https://files.pythonhosted.org/packages/9a/48/440946bf9dc4dc231f4f31ef0d316f7135bf41d4b86aaba0c0655150d370/sqlalchemy-2.0.40-cp311-cp311-win_amd64.whl", hash = "sha256:574aea2c54d8f1dd1699449f332c7d9b71c339e04ae50163a3eb5ce4c4325ee4", size = 2110099 }, - { url = "https://files.pythonhosted.org/packages/92/06/552c1f92e880b57d8b92ce6619bd569b25cead492389b1d84904b55989d8/sqlalchemy-2.0.40-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9d3b31d0a1c44b74d3ae27a3de422dfccd2b8f0b75e51ecb2faa2bf65ab1ba0d", size = 2112620 }, - { url = "https://files.pythonhosted.org/packages/01/72/a5bc6e76c34cebc071f758161dbe1453de8815ae6e662393910d3be6d70d/sqlalchemy-2.0.40-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:37f7a0f506cf78c80450ed1e816978643d3969f99c4ac6b01104a6fe95c5490a", size = 2103004 }, - { url = "https://files.pythonhosted.org/packages/bf/fd/0e96c8e6767618ed1a06e4d7a167fe13734c2f8113c4cb704443e6783038/sqlalchemy-2.0.40-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bb933a650323e476a2e4fbef8997a10d0003d4da996aad3fd7873e962fdde4d", size = 3252440 }, - { url = "https://files.pythonhosted.org/packages/cd/6a/eb82e45b15a64266a2917a6833b51a334ea3c1991728fd905bfccbf5cf63/sqlalchemy-2.0.40-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6959738971b4745eea16f818a2cd086fb35081383b078272c35ece2b07012716", size = 3263277 }, - { url = "https://files.pythonhosted.org/packages/45/97/ebe41ab4530f50af99e3995ebd4e0204bf1b0dc0930f32250dde19c389fe/sqlalchemy-2.0.40-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:110179728e442dae85dd39591beb74072ae4ad55a44eda2acc6ec98ead80d5f2", size = 3198591 }, - { url = "https://files.pythonhosted.org/packages/e6/1c/a569c1b2b2f5ac20ba6846a1321a2bf52e9a4061001f282bf1c5528dcd69/sqlalchemy-2.0.40-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e8040680eaacdce4d635f12c55c714f3d4c7f57da2bc47a01229d115bd319191", size = 3225199 }, - { url = "https://files.pythonhosted.org/packages/8f/91/87cc71a6b10065ca0209d19a4bb575378abda6085e72fa0b61ffb2201b84/sqlalchemy-2.0.40-cp312-cp312-win32.whl", hash = "sha256:650490653b110905c10adac69408380688cefc1f536a137d0d69aca1069dc1d1", size = 2082959 }, - { url = "https://files.pythonhosted.org/packages/2a/9f/14c511cda174aa1ad9b0e42b64ff5a71db35d08b0d80dc044dae958921e5/sqlalchemy-2.0.40-cp312-cp312-win_amd64.whl", hash = "sha256:2be94d75ee06548d2fc591a3513422b873490efb124048f50556369a834853b0", size = 2108526 }, - { url = "https://files.pythonhosted.org/packages/8c/18/4e3a86cc0232377bc48c373a9ba6a1b3fb79ba32dbb4eda0b357f5a2c59d/sqlalchemy-2.0.40-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:915866fd50dd868fdcc18d61d8258db1bf9ed7fbd6dfec960ba43365952f3b01", size = 2107887 }, - { url = "https://files.pythonhosted.org/packages/cb/60/9fa692b1d2ffc4cbd5f47753731fd332afed30137115d862d6e9a1e962c7/sqlalchemy-2.0.40-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4a4c5a2905a9ccdc67a8963e24abd2f7afcd4348829412483695c59e0af9a705", size = 2098367 }, - { url = "https://files.pythonhosted.org/packages/4c/9f/84b78357ca641714a439eb3fbbddb17297dacfa05d951dbf24f28d7b5c08/sqlalchemy-2.0.40-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55028d7a3ebdf7ace492fab9895cbc5270153f75442a0472d8516e03159ab364", size = 3184806 }, - { url = "https://files.pythonhosted.org/packages/4b/7d/e06164161b6bfce04c01bfa01518a20cccbd4100d5c951e5a7422189191a/sqlalchemy-2.0.40-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6cfedff6878b0e0d1d0a50666a817ecd85051d12d56b43d9d425455e608b5ba0", size = 3198131 }, - { url = "https://files.pythonhosted.org/packages/6d/51/354af20da42d7ec7b5c9de99edafbb7663a1d75686d1999ceb2c15811302/sqlalchemy-2.0.40-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bb19e30fdae77d357ce92192a3504579abe48a66877f476880238a962e5b96db", size = 3131364 }, - { url = "https://files.pythonhosted.org/packages/7a/2f/48a41ff4e6e10549d83fcc551ab85c268bde7c03cf77afb36303c6594d11/sqlalchemy-2.0.40-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:16d325ea898f74b26ffcd1cf8c593b0beed8714f0317df2bed0d8d1de05a8f26", size = 3159482 }, - { url = "https://files.pythonhosted.org/packages/33/ac/e5e0a807163652a35be878c0ad5cfd8b1d29605edcadfb5df3c512cdf9f3/sqlalchemy-2.0.40-cp313-cp313-win32.whl", hash = "sha256:a669cbe5be3c63f75bcbee0b266779706f1a54bcb1000f302685b87d1b8c1500", size = 2080704 }, - { url = "https://files.pythonhosted.org/packages/1c/cb/f38c61f7f2fd4d10494c1c135ff6a6ddb63508d0b47bccccd93670637309/sqlalchemy-2.0.40-cp313-cp313-win_amd64.whl", hash = "sha256:641ee2e0834812d657862f3a7de95e0048bdcb6c55496f39c6fa3d435f6ac6ad", size = 2104564 }, - { url = "https://files.pythonhosted.org/packages/d1/7c/5fc8e802e7506fe8b55a03a2e1dab156eae205c91bee46305755e086d2e2/sqlalchemy-2.0.40-py3-none-any.whl", hash = "sha256:32587e2e1e359276957e6fe5dad089758bc042a971a8a09ae8ecf7a8fe23d07a", size = 1903894 }, -] - [[package]] name = "stevedore" version = "5.4.0" @@ -2929,42 +1908,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5e/2d/a00bb03c0db99de80944fa522d5fbfdc844b9c5aa45db31e6edb0cb5e02c/termynal-0.13.0-py3-none-any.whl", hash = "sha256:0507eba6d8d96a2b29fddb8ae4d58b07b2a48b9144bda976629de3b9e2a47ced", size = 10620 }, ] -[[package]] -name = "tiktoken" -version = "0.9.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "regex" }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ea/cf/756fedf6981e82897f2d570dd25fa597eb3f4459068ae0572d7e888cfd6f/tiktoken-0.9.0.tar.gz", hash = "sha256:d02a5ca6a938e0490e1ff957bc48c8b078c88cb83977be1625b1fd8aac792c5d", size = 35991 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/64/f3/50ec5709fad61641e4411eb1b9ac55b99801d71f1993c29853f256c726c9/tiktoken-0.9.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:586c16358138b96ea804c034b8acf3f5d3f0258bd2bc3b0227af4af5d622e382", size = 1065770 }, - { url = "https://files.pythonhosted.org/packages/d6/f8/5a9560a422cf1755b6e0a9a436e14090eeb878d8ec0f80e0cd3d45b78bf4/tiktoken-0.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d9c59ccc528c6c5dd51820b3474402f69d9a9e1d656226848ad68a8d5b2e5108", size = 1009314 }, - { url = "https://files.pythonhosted.org/packages/bc/20/3ed4cfff8f809cb902900ae686069e029db74567ee10d017cb254df1d598/tiktoken-0.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0968d5beeafbca2a72c595e8385a1a1f8af58feaebb02b227229b69ca5357fd", size = 1143140 }, - { url = "https://files.pythonhosted.org/packages/f1/95/cc2c6d79df8f113bdc6c99cdec985a878768120d87d839a34da4bd3ff90a/tiktoken-0.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:92a5fb085a6a3b7350b8fc838baf493317ca0e17bd95e8642f95fc69ecfed1de", size = 1197860 }, - { url = "https://files.pythonhosted.org/packages/c7/6c/9c1a4cc51573e8867c9381db1814223c09ebb4716779c7f845d48688b9c8/tiktoken-0.9.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:15a2752dea63d93b0332fb0ddb05dd909371ededa145fe6a3242f46724fa7990", size = 1259661 }, - { url = "https://files.pythonhosted.org/packages/cd/4c/22eb8e9856a2b1808d0a002d171e534eac03f96dbe1161978d7389a59498/tiktoken-0.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:26113fec3bd7a352e4b33dbaf1bd8948de2507e30bd95a44e2b1156647bc01b4", size = 894026 }, - { url = "https://files.pythonhosted.org/packages/4d/ae/4613a59a2a48e761c5161237fc850eb470b4bb93696db89da51b79a871f1/tiktoken-0.9.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:f32cc56168eac4851109e9b5d327637f15fd662aa30dd79f964b7c39fbadd26e", size = 1065987 }, - { url = "https://files.pythonhosted.org/packages/3f/86/55d9d1f5b5a7e1164d0f1538a85529b5fcba2b105f92db3622e5d7de6522/tiktoken-0.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:45556bc41241e5294063508caf901bf92ba52d8ef9222023f83d2483a3055348", size = 1009155 }, - { url = "https://files.pythonhosted.org/packages/03/58/01fb6240df083b7c1916d1dcb024e2b761213c95d576e9f780dfb5625a76/tiktoken-0.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:03935988a91d6d3216e2ec7c645afbb3d870b37bcb67ada1943ec48678e7ee33", size = 1142898 }, - { url = "https://files.pythonhosted.org/packages/b1/73/41591c525680cd460a6becf56c9b17468d3711b1df242c53d2c7b2183d16/tiktoken-0.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b3d80aad8d2c6b9238fc1a5524542087c52b860b10cbf952429ffb714bc1136", size = 1197535 }, - { url = "https://files.pythonhosted.org/packages/7d/7c/1069f25521c8f01a1a182f362e5c8e0337907fae91b368b7da9c3e39b810/tiktoken-0.9.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b2a21133be05dc116b1d0372af051cd2c6aa1d2188250c9b553f9fa49301b336", size = 1259548 }, - { url = "https://files.pythonhosted.org/packages/6f/07/c67ad1724b8e14e2b4c8cca04b15da158733ac60136879131db05dda7c30/tiktoken-0.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:11a20e67fdf58b0e2dea7b8654a288e481bb4fc0289d3ad21291f8d0849915fb", size = 893895 }, - { url = "https://files.pythonhosted.org/packages/cf/e5/21ff33ecfa2101c1bb0f9b6df750553bd873b7fb532ce2cb276ff40b197f/tiktoken-0.9.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e88f121c1c22b726649ce67c089b90ddda8b9662545a8aeb03cfef15967ddd03", size = 1065073 }, - { url = "https://files.pythonhosted.org/packages/8e/03/a95e7b4863ee9ceec1c55983e4cc9558bcfd8f4f80e19c4f8a99642f697d/tiktoken-0.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a6600660f2f72369acb13a57fb3e212434ed38b045fd8cc6cdd74947b4b5d210", size = 1008075 }, - { url = "https://files.pythonhosted.org/packages/40/10/1305bb02a561595088235a513ec73e50b32e74364fef4de519da69bc8010/tiktoken-0.9.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95e811743b5dfa74f4b227927ed86cbc57cad4df859cb3b643be797914e41794", size = 1140754 }, - { url = "https://files.pythonhosted.org/packages/1b/40/da42522018ca496432ffd02793c3a72a739ac04c3794a4914570c9bb2925/tiktoken-0.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99376e1370d59bcf6935c933cb9ba64adc29033b7e73f5f7569f3aad86552b22", size = 1196678 }, - { url = "https://files.pythonhosted.org/packages/5c/41/1e59dddaae270ba20187ceb8aa52c75b24ffc09f547233991d5fd822838b/tiktoken-0.9.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:badb947c32739fb6ddde173e14885fb3de4d32ab9d8c591cbd013c22b4c31dd2", size = 1259283 }, - { url = "https://files.pythonhosted.org/packages/5b/64/b16003419a1d7728d0d8c0d56a4c24325e7b10a21a9dd1fc0f7115c02f0a/tiktoken-0.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:5a62d7a25225bafed786a524c1b9f0910a1128f4232615bf3f8257a73aaa3b16", size = 894897 }, - { url = "https://files.pythonhosted.org/packages/7a/11/09d936d37f49f4f494ffe660af44acd2d99eb2429d60a57c71318af214e0/tiktoken-0.9.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2b0e8e05a26eda1249e824156d537015480af7ae222ccb798e5234ae0285dbdb", size = 1064919 }, - { url = "https://files.pythonhosted.org/packages/80/0e/f38ba35713edb8d4197ae602e80837d574244ced7fb1b6070b31c29816e0/tiktoken-0.9.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:27d457f096f87685195eea0165a1807fae87b97b2161fe8c9b1df5bd74ca6f63", size = 1007877 }, - { url = "https://files.pythonhosted.org/packages/fe/82/9197f77421e2a01373e27a79dd36efdd99e6b4115746ecc553318ecafbf0/tiktoken-0.9.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cf8ded49cddf825390e36dd1ad35cd49589e8161fdcb52aa25f0583e90a3e01", size = 1140095 }, - { url = "https://files.pythonhosted.org/packages/f2/bb/4513da71cac187383541facd0291c4572b03ec23c561de5811781bbd988f/tiktoken-0.9.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc156cb314119a8bb9748257a2eaebd5cc0753b6cb491d26694ed42fc7cb3139", size = 1195649 }, - { url = "https://files.pythonhosted.org/packages/fa/5c/74e4c137530dd8504e97e3a41729b1103a4ac29036cbfd3250b11fd29451/tiktoken-0.9.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:cd69372e8c9dd761f0ab873112aba55a0e3e506332dd9f7522ca466e817b1b7a", size = 1258465 }, - { url = "https://files.pythonhosted.org/packages/de/a8/8f499c179ec900783ffe133e9aab10044481679bb9aad78436d239eee716/tiktoken-0.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:5ea0edb6f83dc56d794723286215918c1cde03712cbbafa0348b33448faf5b95", size = 894669 }, -] - [[package]] name = "tinycss2" version = "1.4.0" @@ -3025,18 +1968,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257 }, ] -[[package]] -name = "tqdm" -version = "4.67.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540 }, -] - [[package]] name = "trio" version = "0.30.0" @@ -3064,18 +1995,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/bc/df/8ad635bdcfa8214c399e5614f7c2121dced47defb755a85ea1fa702ffb1c/truststore-0.10.1-py3-none-any.whl", hash = "sha256:b64e6025a409a43ebdd2807b0c41c8bff49ea7ae6550b5087ac6df6619352d4c", size = 18496 }, ] -[[package]] -name = "types-requests" -version = "2.32.0.20250306" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/09/1a/beaeff79ef9efd186566ba5f0d95b44ae21f6d31e9413bcfbef3489b6ae3/types_requests-2.32.0.20250306.tar.gz", hash = "sha256:0962352694ec5b2f95fda877ee60a159abdf84a0fc6fdace599f20acb41a03d1", size = 23012 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/99/26/645d89f56004aa0ba3b96fec27793e3c7e62b40982ee069e52568922b6db/types_requests-2.32.0.20250306-py3-none-any.whl", hash = "sha256:25f2cbb5c8710b2022f8bbee7b2b66f319ef14aeea2f35d80f18c9dbf3b60a0b", size = 20673 }, -] - [[package]] name = "types-toml" version = "0.10.8.20240310" @@ -3094,19 +2013,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 }, ] -[[package]] -name = "typing-inspect" -version = "0.9.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mypy-extensions" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/dc/74/1789779d91f1961fa9438e9a8710cdae6bd138c80d7303996933d117264a/typing_inspect-0.9.0.tar.gz", hash = "sha256:b23fc42ff6f6ef6954e4852c1fb512cdd18dbea03134f91f856a95ccc9461f78", size = 13825 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/65/f3/107a22063bf27bdccf2024833d3445f4eea42b2e598abfbd46f6a63b6cb0/typing_inspect-0.9.0-py3-none-any.whl", hash = "sha256:9ee6fc59062311ef8547596ab6b955e1b8aa46242d854bfc78f4f6b0eff35f9f", size = 8827 }, -] - [[package]] name = "typing-inspection" version = "0.4.0" @@ -3121,7 +2027,7 @@ wheels = [ [[package]] name = "uipath" -version = "2.0.82" +version = "2.1.0" source = { editable = "." } dependencies = [ { name = "azure-monitor-opentelemetry" }, @@ -3137,11 +2043,6 @@ dependencies = [ { name = "truststore" }, ] -[package.optional-dependencies] -langchain = [ - { name = "uipath-langchain" }, -] - [package.dev-dependencies] dev = [ { name = "bandit" }, @@ -3180,9 +2081,7 @@ requires-dist = [ { name = "tenacity", specifier = ">=9.0.0" }, { name = "tomli", specifier = ">=2.2.1" }, { name = "truststore", specifier = ">=0.10.1" }, - { name = "uipath-langchain", marker = "extra == 'langchain'", specifier = ">=0.0.88,<0.1.0" }, ] -provides-extras = ["langchain"] [package.metadata.requires-dev] dev = [ @@ -3209,30 +2108,6 @@ dev = [ { name = "types-toml", specifier = ">=0.10.8" }, ] -[[package]] -name = "uipath-langchain" -version = "0.0.88" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "httpx" }, - { name = "langchain" }, - { name = "langchain-community" }, - { name = "langchain-core" }, - { name = "langchain-openai" }, - { name = "langgraph" }, - { name = "langgraph-checkpoint-sqlite" }, - { name = "openai" }, - { name = "pydantic-settings" }, - { name = "python-dotenv" }, - { name = "requests" }, - { name = "types-requests" }, - { name = "uipath" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/80/0e/e05ecfc5a5b6b7f277dca18e61724c1a923ae4c9af39f71f2c8dce52343d/uipath_langchain-0.0.88.tar.gz", hash = "sha256:f06cc80c9499ae4309275119a9abf5d74c881aaef6731c6efb5b525a1291ef33", size = 1188502 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3a/7a/68ec6fcb4698af7f27c30a2107c349fb6115b84373dba38413346e8c5a57/uipath_langchain-0.0.88-py3-none-any.whl", hash = "sha256:78c92bebbd4a3651adcf914d3563a551222bef86f54cb3eb270d93a39ede7d28", size = 50641 }, -] - [[package]] name = "urllib3" version = "2.3.0" @@ -3361,88 +2236,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2d/82/f56956041adef78f849db6b289b282e72b55ab8045a75abad81898c28d19/wrapt-1.17.2-py3-none-any.whl", hash = "sha256:b18f2d1533a71f069c7f82d524a52599053d4c7166e9dd374ae2136b7f40f7c8", size = 23594 }, ] -[[package]] -name = "yarl" -version = "1.19.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "idna" }, - { name = "multidict" }, - { name = "propcache" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/fc/4d/8a8f57caccce49573e567744926f88c6ab3ca0b47a257806d1cf88584c5f/yarl-1.19.0.tar.gz", hash = "sha256:01e02bb80ae0dbed44273c304095295106e1d9470460e773268a27d11e594892", size = 184396 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/96/0f/e5bd0d7d98bb194a30740dea2c4324f85dfc2f8daba9d7bc7e47b45d1034/yarl-1.19.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0bae32f8ebd35c04d6528cedb4a26b8bf25339d3616b04613b97347f919b76d3", size = 144954 }, - { url = "https://files.pythonhosted.org/packages/07/bf/2acc4b643dbdfc823d0d2058768197198a3d93b41fffb41b83359c520a4d/yarl-1.19.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8015a076daf77823e7ebdcba474156587391dab4e70c732822960368c01251e6", size = 96613 }, - { url = "https://files.pythonhosted.org/packages/ca/38/c60ccca9aad0bb939e665b63a4e1550fecc922971f1f246dd7ad709a1a72/yarl-1.19.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9973ac95327f5d699eb620286c39365990b240031672b5c436a4cd00539596c5", size = 94408 }, - { url = "https://files.pythonhosted.org/packages/9a/43/2d5b49b4784743d88054e612a97aee2a9d2d463983c6a8e2fa4c872b294a/yarl-1.19.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd4b5fbd7b9dde785cfeb486b8cca211a0b138d4f3a7da27db89a25b3c482e5c", size = 330774 }, - { url = "https://files.pythonhosted.org/packages/3b/48/7decce219b6eedce321345f61461ee140ee6b3faf4875efe518f0e7b5817/yarl-1.19.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:75460740005de5a912b19f657848aef419387426a40f581b1dc9fac0eb9addb5", size = 323399 }, - { url = "https://files.pythonhosted.org/packages/67/2f/d6253528e49ce1c6f5119ec5269314752b06dd670f5a81721648d98b1dc7/yarl-1.19.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:57abd66ca913f2cfbb51eb3dbbbac3648f1f6983f614a4446e0802e241441d2a", size = 343329 }, - { url = "https://files.pythonhosted.org/packages/fc/6b/efeb1a088e8addbf5841a84b74dad2a06346b0e4a712eb269a0cd9ada8b7/yarl-1.19.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:46ade37911b7c99ce28a959147cb28bffbd14cea9e7dd91021e06a8d2359a5aa", size = 338275 }, - { url = "https://files.pythonhosted.org/packages/a6/b6/31acc2efcaf6999fd256d11f26ccc95ea773bc790ad1973331d7294b25db/yarl-1.19.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8346ec72ada749a6b5d82bff7be72578eab056ad7ec38c04f668a685abde6af0", size = 334014 }, - { url = "https://files.pythonhosted.org/packages/79/16/1deb54324842479e4d8b34841a383653587dfcc403c132f88b493f0c513e/yarl-1.19.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e4cb14a6ee5b6649ccf1c6d648b4da9220e8277d4d4380593c03cc08d8fe937", size = 322007 }, - { url = "https://files.pythonhosted.org/packages/80/77/4a073cec4f40ce84897510ee9d347bc10128f715be59b36e5c037463523b/yarl-1.19.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:66fc1c2926a73a2fb46e4b92e3a6c03904d9bc3a0b65e01cb7d2b84146a8bd3b", size = 336569 }, - { url = "https://files.pythonhosted.org/packages/73/e1/2f0455379bbee5f4ece8bc0968106386ec4e74237e8d68ced00bbff0a1fc/yarl-1.19.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:5a70201dd1e0a4304849b6445a9891d7210604c27e67da59091d5412bc19e51c", size = 336384 }, - { url = "https://files.pythonhosted.org/packages/74/e0/307aa8ae96bc0e72644855c76e8960019fc24c511a5dda73f05214da46f0/yarl-1.19.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e4807aab1bdeab6ae6f296be46337a260ae4b1f3a8c2fcd373e236b4b2b46efd", size = 340454 }, - { url = "https://files.pythonhosted.org/packages/af/19/2dcdb1e5eef26751c9e79369d1f80d6a1162dababb5070f62bc5b1a8f81e/yarl-1.19.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:ae584afe81a1de4c1bb06672481050f0d001cad13163e3c019477409f638f9b7", size = 355804 }, - { url = "https://files.pythonhosted.org/packages/c1/af/8c1e102c6d61713ed31022ab8f8866d263b87cb8f466c37f20a99019d169/yarl-1.19.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:30eaf4459df6e91f21b2999d1ee18f891bcd51e3cbe1de301b4858c84385895b", size = 359877 }, - { url = "https://files.pythonhosted.org/packages/1a/cf/c3c4bd85ecc7f189e14d21c3bea67ce389511d9178a302d97281868477aa/yarl-1.19.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0e617d45d03c8dec0dfce6f51f3e1b8a31aa81aaf4a4d1442fdb232bcf0c6d8c", size = 351282 }, - { url = "https://files.pythonhosted.org/packages/c6/85/0994f1c607b0520ef007717ff74f3317df3f7b7f32756ba2bf26c0c58ddf/yarl-1.19.0-cp310-cp310-win32.whl", hash = "sha256:32ba32d0fa23893fd8ea8d05bdb05de6eb19d7f2106787024fd969f4ba5466cb", size = 86529 }, - { url = "https://files.pythonhosted.org/packages/59/00/39bc8da1f67614633a099a44a5f69d056bb4d65a8e52a4003460e3fa4cc7/yarl-1.19.0-cp310-cp310-win_amd64.whl", hash = "sha256:545575ecfcd465891b51546c2bcafdde0acd2c62c2097d8d71902050b20e4922", size = 92707 }, - { url = "https://files.pythonhosted.org/packages/9b/df/5fa7cd75e46306e0f9baf38a7c8969ff6730ea503b86232e85cb740304cf/yarl-1.19.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:163ff326680de5f6d4966954cf9e3fe1bf980f5fee2255e46e89b8cf0f3418b5", size = 145126 }, - { url = "https://files.pythonhosted.org/packages/2a/be/c1b52129cd2166ab7337f08e701a61baa7c260c7b03b534098cc8297aecc/yarl-1.19.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a626c4d9cca298d1be8625cff4b17004a9066330ac82d132bbda64a4c17c18d3", size = 96691 }, - { url = "https://files.pythonhosted.org/packages/8d/39/ad62139b45515f9bf129c805aeaaedf86fd93ae57ffe911f4caeabef3e74/yarl-1.19.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:961c3e401ea7f13d02b8bb7cb0c709152a632a6e14cdc8119e9c6ee5596cd45d", size = 94505 }, - { url = "https://files.pythonhosted.org/packages/be/be/04e3202cdc9bb5f81761e327af7095cffb0d81e32421a6b87f926052d2ae/yarl-1.19.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a39d7b807ab58e633ed760f80195cbd145b58ba265436af35f9080f1810dfe64", size = 355485 }, - { url = "https://files.pythonhosted.org/packages/00/7d/1463203663ca1ae62af8fb9ebc9601dd07f04dbced7edb1df3141a2cb2fe/yarl-1.19.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c4228978fb59c6b10f60124ba8e311c26151e176df364e996f3f8ff8b93971b5", size = 344569 }, - { url = "https://files.pythonhosted.org/packages/b0/1b/5263203017348669e637bb73856fb9632110538e92d5e9f8214fcc764da9/yarl-1.19.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9ba536b17ecf3c74a94239ec1137a3ad3caea8c0e4deb8c8d2ffe847d870a8c5", size = 371426 }, - { url = "https://files.pythonhosted.org/packages/78/59/90ca5f16d56b7741e5383951acc2e065fce41920eb5d8fda3065b5e288dc/yarl-1.19.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a251e00e445d2e9df7b827c9843c0b87f58a3254aaa3f162fb610747491fe00f", size = 368102 }, - { url = "https://files.pythonhosted.org/packages/84/f2/5e33aa0251ffd2c2a9041bf887e163eeefdc1dca238fdabac444d9463c3f/yarl-1.19.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9b92431d8b4d4ca5ccbfdbac95b05a3a6cd70cd73aa62f32f9627acfde7549c", size = 358740 }, - { url = "https://files.pythonhosted.org/packages/22/9e/ba92d234c81cf94495fc01eaa0b6000175733f76bd63e60ff748bce22c81/yarl-1.19.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ec2f56edaf476f70b5831bbd59700b53d9dd011b1f77cd4846b5ab5c5eafdb3f", size = 346965 }, - { url = "https://files.pythonhosted.org/packages/8d/0b/d4f53136ef12ddad540855a886d7503a6cc17cfabb9a03ce0c179f3b9e51/yarl-1.19.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:acf9b92c4245ac8b59bc7ec66a38d3dcb8d1f97fac934672529562bb824ecadb", size = 368547 }, - { url = "https://files.pythonhosted.org/packages/31/4b/35ec8622908a728f378a8511f0ab2d47878b2c0b8cbe035f2d907914a5fc/yarl-1.19.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:57711f1465c06fee8825b95c0b83e82991e6d9425f9a042c3c19070a70ac92bf", size = 357610 }, - { url = "https://files.pythonhosted.org/packages/c1/71/1f39f7c55b0684834d945a2bcfdfe59e6e02ca2483a3d33c2f77a0c3b177/yarl-1.19.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:528e86f5b1de0ad8dd758ddef4e0ed24f5d946d4a1cef80ffb2d4fca4e10f122", size = 365331 }, - { url = "https://files.pythonhosted.org/packages/2e/13/57675964de5c8ccf6427df93ac97f9bb7328f3f8f7ebc31a5f5a286ab1c0/yarl-1.19.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:3b77173663e075d9e5a57e09d711e9da2f3266be729ecca0b8ae78190990d260", size = 378624 }, - { url = "https://files.pythonhosted.org/packages/d4/c6/5868e40f8da041ed0c3b5fd8c08cece849d9f609e970e6043308767fbb60/yarl-1.19.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:d8717924cf0a825b62b1a96fc7d28aab7f55a81bf5338b8ef41d7a76ab9223e9", size = 383981 }, - { url = "https://files.pythonhosted.org/packages/f4/3f/e40124c986d96741d3d341ffac35be42b6df82ef8c18b5984ca2e7d838dd/yarl-1.19.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0df9f0221a78d858793f40cbea3915c29f969c11366646a92ca47e080a14f881", size = 378868 }, - { url = "https://files.pythonhosted.org/packages/01/eb/caf2774c770288bd87a818b11f3a56ada6a855f1987d93421aae01a175bf/yarl-1.19.0-cp311-cp311-win32.whl", hash = "sha256:8b3ade62678ee2c7c10dcd6be19045135e9badad53108f7d2ed14896ee396045", size = 86446 }, - { url = "https://files.pythonhosted.org/packages/4a/97/d4fe6168c1bb789507ffeb58c2e8c675a7e71de732dc02e12bda904c1362/yarl-1.19.0-cp311-cp311-win_amd64.whl", hash = "sha256:0626ee31edb23ac36bdffe607231de2cca055ad3a5e2dc5da587ef8bc6a321bc", size = 93121 }, - { url = "https://files.pythonhosted.org/packages/b8/70/44ef8f69d61cb5123167a4dda87f6c739a833fbdb2ed52960b4e8409d65c/yarl-1.19.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:7b687c334da3ff8eab848c9620c47a253d005e78335e9ce0d6868ed7e8fd170b", size = 146855 }, - { url = "https://files.pythonhosted.org/packages/c3/94/38c14d6c8217cc818647689f2dd647b976ced8fea08d0ac84e3c8168252b/yarl-1.19.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b0fe766febcf523a2930b819c87bb92407ae1368662c1bc267234e79b20ff894", size = 97523 }, - { url = "https://files.pythonhosted.org/packages/35/a5/43a613586a6255105c4655a911c307ef3420e49e540d6ae2c5829863fb25/yarl-1.19.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:742ceffd3c7beeb2b20d47cdb92c513eef83c9ef88c46829f88d5b06be6734ee", size = 95540 }, - { url = "https://files.pythonhosted.org/packages/d4/60/ed26049f4a8b06ebfa6d5f3cb6a51b152fd57081aa818b6497474f65a631/yarl-1.19.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2af682a1e97437382ee0791eacbf540318bd487a942e068e7e0a6c571fadbbd3", size = 344386 }, - { url = "https://files.pythonhosted.org/packages/49/a6/b84899cab411f49af5986cfb44b514040788d81c8084f5811e6a7c0f1ce6/yarl-1.19.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:63702f1a098d0eaaea755e9c9d63172be1acb9e2d4aeb28b187092bcc9ca2d17", size = 338889 }, - { url = "https://files.pythonhosted.org/packages/cc/ce/0704f7166a781b1f81bdd45c4f49eadbae0230ebd35b9ec7cd7769d3a6ff/yarl-1.19.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3560dcba3c71ae7382975dc1e912ee76e50b4cd7c34b454ed620d55464f11876", size = 353107 }, - { url = "https://files.pythonhosted.org/packages/75/e5/0ecd6f2a9cc4264c16d8dfb0d3d71ba8d03cb58f3bcd42b1df4358331189/yarl-1.19.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:68972df6a0cc47c8abaf77525a76ee5c5f6ea9bbdb79b9565b3234ded3c5e675", size = 353128 }, - { url = "https://files.pythonhosted.org/packages/ad/c7/cd0fd1de581f1c2e8f996e704c9fd979e00106f18eebd91b0173cf1a13c6/yarl-1.19.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5684e7ff93ea74e47542232bd132f608df4d449f8968fde6b05aaf9e08a140f9", size = 349107 }, - { url = "https://files.pythonhosted.org/packages/e6/34/ba3e5a20bd1d6a09034fc7985aaf1309976f2a7a5aefd093c9e56f6e1e0c/yarl-1.19.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8182ad422bfacdebd4759ce3adc6055c0c79d4740aea1104e05652a81cd868c6", size = 335144 }, - { url = "https://files.pythonhosted.org/packages/1e/98/d9b7beb932fade015906efe0980aa7d522b8f93cf5ebf1082e74faa314b7/yarl-1.19.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:aee5b90a5a9b71ac57400a7bdd0feaa27c51e8f961decc8d412e720a004a1791", size = 360795 }, - { url = "https://files.pythonhosted.org/packages/9a/11/70b8770039cc54af5948970591517a1e1d093df3f04f328c655c9a0fefb7/yarl-1.19.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:8c0b2371858d5a814b08542d5d548adb03ff2d7ab32f23160e54e92250961a72", size = 360140 }, - { url = "https://files.pythonhosted.org/packages/d4/67/708e3e36fafc4d9d96b4eecc6c8b9f37c8ad50df8a16c7a1d5ba9df53050/yarl-1.19.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:cd430c2b7df4ae92498da09e9b12cad5bdbb140d22d138f9e507de1aa3edfea3", size = 364431 }, - { url = "https://files.pythonhosted.org/packages/c3/8b/937fbbcc895553a7e16fcd86ae4e0724c6ac9468237ad8e7c29cc3b1c9d9/yarl-1.19.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a93208282c0ccdf73065fd76c6c129bd428dba5ff65d338ae7d2ab27169861a0", size = 373832 }, - { url = "https://files.pythonhosted.org/packages/f8/ca/288ddc2230c9b6647fe907504f1119adb41252ac533eb564d3fc73511215/yarl-1.19.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:b8179280cdeb4c36eb18d6534a328f9d40da60d2b96ac4a295c5f93e2799e9d9", size = 378122 }, - { url = "https://files.pythonhosted.org/packages/4f/5a/79e1ef31d14968fbfc0ecec70a6683b574890d9c7550c376dd6d40de7754/yarl-1.19.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:eda3c2b42dc0c389b7cfda2c4df81c12eeb552019e0de28bde8f913fc3d1fcf3", size = 375178 }, - { url = "https://files.pythonhosted.org/packages/95/38/9b0e56bf14026c3f550ad6425679f6d1a2f4821d70767f39d6f4c56a0820/yarl-1.19.0-cp312-cp312-win32.whl", hash = "sha256:57f3fed859af367b9ca316ecc05ce79ce327d6466342734305aa5cc380e4d8be", size = 86172 }, - { url = "https://files.pythonhosted.org/packages/b3/96/5c2f3987c4bb4e5cdebea3caf99a45946b13a9516f849c02222203d99860/yarl-1.19.0-cp312-cp312-win_amd64.whl", hash = "sha256:5507c1f7dd3d41251b67eecba331c8b2157cfd324849879bebf74676ce76aff7", size = 92617 }, - { url = "https://files.pythonhosted.org/packages/cd/a7/222144efa2f4a47363a5fee27d8a1d24851283b5a7f628890805fe7f7a66/yarl-1.19.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:59281b9ed27bc410e0793833bcbe7fc149739d56ffa071d1e0fe70536a4f7b61", size = 144789 }, - { url = "https://files.pythonhosted.org/packages/72/4f/3ee8de3f94baa33c0716260b0048b1fd5306f104b3efc6e1713693e7063e/yarl-1.19.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d27a6482ad5e05e8bafd47bf42866f8a1c0c3345abcb48d4511b3c29ecc197dc", size = 96685 }, - { url = "https://files.pythonhosted.org/packages/3e/7c/fbeebf875c1ededd872d6fefabd8a8526ef8aba6e9e8bcdf230d895d487b/yarl-1.19.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7a8e19fd5a6fdf19a91f2409665c7a089ffe7b9b5394ab33c0eec04cbecdd01f", size = 94307 }, - { url = "https://files.pythonhosted.org/packages/f3/ff/b7a9c1d7df37e594b43b7a8030e228ccd4ce361eeff24a92b17fe210e57d/yarl-1.19.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cda34ab19099c3a1685ad48fe45172536610c312b993310b5f1ca3eb83453b36", size = 342811 }, - { url = "https://files.pythonhosted.org/packages/79/e2/9e092876b2156c1d386e4864e85eba541ccabf2b9dcc47da64624bad0cc9/yarl-1.19.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7908a25d33f94852b479910f9cae6cdb9e2a509894e8d5f416c8342c0253c397", size = 336928 }, - { url = "https://files.pythonhosted.org/packages/71/24/648d99c134f2e14fc01ba790ad36ab56815e00069e60a12a4af893448b83/yarl-1.19.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e66c14d162bac94973e767b24de5d7e6c5153f7305a64ff4fcba701210bcd638", size = 351021 }, - { url = "https://files.pythonhosted.org/packages/0c/ee/7278d475784d407d1990a5939722e66a0fef057046fb5f1721f0a6eb156c/yarl-1.19.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c03607bf932aa4cfae371e2dc9ca8b76faf031f106dac6a6ff1458418140c165", size = 354454 }, - { url = "https://files.pythonhosted.org/packages/15/ae/242546114e052a7de21a75bd7d4860266439f90bbc21c5e4dd696866d91d/yarl-1.19.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9931343d1c1f4e77421687b6b94bbebd8a15a64ab8279adf6fbb047eff47e536", size = 347594 }, - { url = "https://files.pythonhosted.org/packages/46/2c/35f4347f76ea4c986e9c1f774b085f489b3a1bf1503c67a4dfc5d8e68e92/yarl-1.19.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:262087a8a0d73e1d169d45c2baf968126f93c97cf403e1af23a7d5455d52721f", size = 334113 }, - { url = "https://files.pythonhosted.org/packages/20/89/3086bc8ec8d7bd505531c51056452d7ae6af906d29c427374f1170ac1938/yarl-1.19.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:70f384921c24e703d249a6ccdabeb57dd6312b568b504c69e428a8dd3e8e68ca", size = 361037 }, - { url = "https://files.pythonhosted.org/packages/a1/5b/2c9765524a70d1c51922b41c91caa30c8094a416734349166e1a3d8de055/yarl-1.19.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:756b9ea5292a2c180d1fe782a377bc4159b3cfefaca7e41b5b0a00328ef62fa9", size = 361025 }, - { url = "https://files.pythonhosted.org/packages/ca/f8/c4a190bcc3cd98fb428d1dd31519e58004153dc7f2acd1236ecae54e3433/yarl-1.19.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cbeb9c145d534c240a63b6ecc8a8dd451faeb67b3dc61d729ec197bb93e29497", size = 364397 }, - { url = "https://files.pythonhosted.org/packages/6b/fb/f65b1347be8e12ac4e3e37a9bb880e6b9b604f252aaafd88e4879b1e9348/yarl-1.19.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:087ae8f8319848c18e0d114d0f56131a9c017f29200ab1413b0137ad7c83e2ae", size = 374065 }, - { url = "https://files.pythonhosted.org/packages/1c/c5/102cc3b9baad1a76f9127453ad08e0f5bc9c996c18128b1e28fe03817d6c/yarl-1.19.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362f5480ba527b6c26ff58cff1f229afe8b7fdd54ee5ffac2ab827c1a75fc71c", size = 381341 }, - { url = "https://files.pythonhosted.org/packages/f7/ce/f5dc0439320dfe59fadab8cdd24ac324be19cf6ae4736422c7e2a510ddf3/yarl-1.19.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f408d4b4315e814e5c3668094e33d885f13c7809cbe831cbdc5b1bb8c7a448f4", size = 376552 }, - { url = "https://files.pythonhosted.org/packages/a9/4a/4833a134c76af987eff3ce8cb71e42932234120e6be061eb2555061e8844/yarl-1.19.0-cp313-cp313-win32.whl", hash = "sha256:24e4c367ad69988a2283dd45ea88172561ca24b2326b9781e164eb46eea68345", size = 85878 }, - { url = "https://files.pythonhosted.org/packages/32/e9/59327daab3af8f79221638a8f0d11474d20f6a8fbc41e9da80c5ef69e688/yarl-1.19.0-cp313-cp313-win_amd64.whl", hash = "sha256:0110f91c57ab43d1538dfa92d61c45e33b84df9257bd08fcfcda90cce931cbc9", size = 92448 }, - { url = "https://files.pythonhosted.org/packages/a4/06/ae25a353e8f032322df6f30d6bb1fc329773ee48e1a80a2196ccb8d1206b/yarl-1.19.0-py3-none-any.whl", hash = "sha256:a727101eb27f66727576630d02985d8a065d09cd0b5fcbe38a5793f71b2a97ef", size = 45990 }, -] - [[package]] name = "zipp" version = "3.21.0" @@ -3451,78 +2244,3 @@ sdist = { url = "https://files.pythonhosted.org/packages/3f/50/bad581df71744867e wheels = [ { url = "https://files.pythonhosted.org/packages/b7/1a/7e4798e9339adc931158c9d69ecc34f5e6791489d469f5e50ec15e35f458/zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931", size = 9630 }, ] - -[[package]] -name = "zstandard" -version = "0.23.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cffi", marker = "platform_python_implementation == 'PyPy'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ed/f6/2ac0287b442160a89d726b17a9184a4c615bb5237db763791a7fd16d9df1/zstandard-0.23.0.tar.gz", hash = "sha256:b2d8c62d08e7255f68f7a740bae85b3c9b8e5466baa9cbf7f57f1cde0ac6bc09", size = 681701 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/55/bd0487e86679db1823fc9ee0d8c9c78ae2413d34c0b461193b5f4c31d22f/zstandard-0.23.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bf0a05b6059c0528477fba9054d09179beb63744355cab9f38059548fedd46a9", size = 788701 }, - { url = "https://files.pythonhosted.org/packages/e1/8a/ccb516b684f3ad987dfee27570d635822e3038645b1a950c5e8022df1145/zstandard-0.23.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fc9ca1c9718cb3b06634c7c8dec57d24e9438b2aa9a0f02b8bb36bf478538880", size = 633678 }, - { url = "https://files.pythonhosted.org/packages/12/89/75e633d0611c028e0d9af6df199423bf43f54bea5007e6718ab7132e234c/zstandard-0.23.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77da4c6bfa20dd5ea25cbf12c76f181a8e8cd7ea231c673828d0386b1740b8dc", size = 4941098 }, - { url = "https://files.pythonhosted.org/packages/4a/7a/bd7f6a21802de358b63f1ee636ab823711c25ce043a3e9f043b4fcb5ba32/zstandard-0.23.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b2170c7e0367dde86a2647ed5b6f57394ea7f53545746104c6b09fc1f4223573", size = 5308798 }, - { url = "https://files.pythonhosted.org/packages/79/3b/775f851a4a65013e88ca559c8ae42ac1352db6fcd96b028d0df4d7d1d7b4/zstandard-0.23.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c16842b846a8d2a145223f520b7e18b57c8f476924bda92aeee3a88d11cfc391", size = 5341840 }, - { url = "https://files.pythonhosted.org/packages/09/4f/0cc49570141dd72d4d95dd6fcf09328d1b702c47a6ec12fbed3b8aed18a5/zstandard-0.23.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:157e89ceb4054029a289fb504c98c6a9fe8010f1680de0201b3eb5dc20aa6d9e", size = 5440337 }, - { url = "https://files.pythonhosted.org/packages/e7/7c/aaa7cd27148bae2dc095191529c0570d16058c54c4597a7d118de4b21676/zstandard-0.23.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:203d236f4c94cd8379d1ea61db2fce20730b4c38d7f1c34506a31b34edc87bdd", size = 4861182 }, - { url = "https://files.pythonhosted.org/packages/ac/eb/4b58b5c071d177f7dc027129d20bd2a44161faca6592a67f8fcb0b88b3ae/zstandard-0.23.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:dc5d1a49d3f8262be192589a4b72f0d03b72dcf46c51ad5852a4fdc67be7b9e4", size = 4932936 }, - { url = "https://files.pythonhosted.org/packages/44/f9/21a5fb9bb7c9a274b05ad700a82ad22ce82f7ef0f485980a1e98ed6e8c5f/zstandard-0.23.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:752bf8a74412b9892f4e5b58f2f890a039f57037f52c89a740757ebd807f33ea", size = 5464705 }, - { url = "https://files.pythonhosted.org/packages/49/74/b7b3e61db3f88632776b78b1db597af3f44c91ce17d533e14a25ce6a2816/zstandard-0.23.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:80080816b4f52a9d886e67f1f96912891074903238fe54f2de8b786f86baded2", size = 4857882 }, - { url = "https://files.pythonhosted.org/packages/4a/7f/d8eb1cb123d8e4c541d4465167080bec88481ab54cd0b31eb4013ba04b95/zstandard-0.23.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:84433dddea68571a6d6bd4fbf8ff398236031149116a7fff6f777ff95cad3df9", size = 4697672 }, - { url = "https://files.pythonhosted.org/packages/5e/05/f7dccdf3d121309b60342da454d3e706453a31073e2c4dac8e1581861e44/zstandard-0.23.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:ab19a2d91963ed9e42b4e8d77cd847ae8381576585bad79dbd0a8837a9f6620a", size = 5206043 }, - { url = "https://files.pythonhosted.org/packages/86/9d/3677a02e172dccd8dd3a941307621c0cbd7691d77cb435ac3c75ab6a3105/zstandard-0.23.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:59556bf80a7094d0cfb9f5e50bb2db27fefb75d5138bb16fb052b61b0e0eeeb0", size = 5667390 }, - { url = "https://files.pythonhosted.org/packages/41/7e/0012a02458e74a7ba122cd9cafe491facc602c9a17f590367da369929498/zstandard-0.23.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:27d3ef2252d2e62476389ca8f9b0cf2bbafb082a3b6bfe9d90cbcbb5529ecf7c", size = 5198901 }, - { url = "https://files.pythonhosted.org/packages/65/3a/8f715b97bd7bcfc7342d8adcd99a026cb2fb550e44866a3b6c348e1b0f02/zstandard-0.23.0-cp310-cp310-win32.whl", hash = "sha256:5d41d5e025f1e0bccae4928981e71b2334c60f580bdc8345f824e7c0a4c2a813", size = 430596 }, - { url = "https://files.pythonhosted.org/packages/19/b7/b2b9eca5e5a01111e4fe8a8ffb56bdcdf56b12448a24effe6cfe4a252034/zstandard-0.23.0-cp310-cp310-win_amd64.whl", hash = "sha256:519fbf169dfac1222a76ba8861ef4ac7f0530c35dd79ba5727014613f91613d4", size = 495498 }, - { url = "https://files.pythonhosted.org/packages/9e/40/f67e7d2c25a0e2dc1744dd781110b0b60306657f8696cafb7ad7579469bd/zstandard-0.23.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:34895a41273ad33347b2fc70e1bff4240556de3c46c6ea430a7ed91f9042aa4e", size = 788699 }, - { url = "https://files.pythonhosted.org/packages/e8/46/66d5b55f4d737dd6ab75851b224abf0afe5774976fe511a54d2eb9063a41/zstandard-0.23.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:77ea385f7dd5b5676d7fd943292ffa18fbf5c72ba98f7d09fc1fb9e819b34c23", size = 633681 }, - { url = "https://files.pythonhosted.org/packages/63/b6/677e65c095d8e12b66b8f862b069bcf1f1d781b9c9c6f12eb55000d57583/zstandard-0.23.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:983b6efd649723474f29ed42e1467f90a35a74793437d0bc64a5bf482bedfa0a", size = 4944328 }, - { url = "https://files.pythonhosted.org/packages/59/cc/e76acb4c42afa05a9d20827116d1f9287e9c32b7ad58cc3af0721ce2b481/zstandard-0.23.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80a539906390591dd39ebb8d773771dc4db82ace6372c4d41e2d293f8e32b8db", size = 5311955 }, - { url = "https://files.pythonhosted.org/packages/78/e4/644b8075f18fc7f632130c32e8f36f6dc1b93065bf2dd87f03223b187f26/zstandard-0.23.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:445e4cb5048b04e90ce96a79b4b63140e3f4ab5f662321975679b5f6360b90e2", size = 5344944 }, - { url = "https://files.pythonhosted.org/packages/76/3f/dbafccf19cfeca25bbabf6f2dd81796b7218f768ec400f043edc767015a6/zstandard-0.23.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd30d9c67d13d891f2360b2a120186729c111238ac63b43dbd37a5a40670b8ca", size = 5442927 }, - { url = "https://files.pythonhosted.org/packages/0c/c3/d24a01a19b6733b9f218e94d1a87c477d523237e07f94899e1c10f6fd06c/zstandard-0.23.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d20fd853fbb5807c8e84c136c278827b6167ded66c72ec6f9a14b863d809211c", size = 4864910 }, - { url = "https://files.pythonhosted.org/packages/1c/a9/cf8f78ead4597264f7618d0875be01f9bc23c9d1d11afb6d225b867cb423/zstandard-0.23.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ed1708dbf4d2e3a1c5c69110ba2b4eb6678262028afd6c6fbcc5a8dac9cda68e", size = 4935544 }, - { url = "https://files.pythonhosted.org/packages/2c/96/8af1e3731b67965fb995a940c04a2c20997a7b3b14826b9d1301cf160879/zstandard-0.23.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:be9b5b8659dff1f913039c2feee1aca499cfbc19e98fa12bc85e037c17ec6ca5", size = 5467094 }, - { url = "https://files.pythonhosted.org/packages/ff/57/43ea9df642c636cb79f88a13ab07d92d88d3bfe3e550b55a25a07a26d878/zstandard-0.23.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:65308f4b4890aa12d9b6ad9f2844b7ee42c7f7a4fd3390425b242ffc57498f48", size = 4860440 }, - { url = "https://files.pythonhosted.org/packages/46/37/edb78f33c7f44f806525f27baa300341918fd4c4af9472fbc2c3094be2e8/zstandard-0.23.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:98da17ce9cbf3bfe4617e836d561e433f871129e3a7ac16d6ef4c680f13a839c", size = 4700091 }, - { url = "https://files.pythonhosted.org/packages/c1/f1/454ac3962671a754f3cb49242472df5c2cced4eb959ae203a377b45b1a3c/zstandard-0.23.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:8ed7d27cb56b3e058d3cf684d7200703bcae623e1dcc06ed1e18ecda39fee003", size = 5208682 }, - { url = "https://files.pythonhosted.org/packages/85/b2/1734b0fff1634390b1b887202d557d2dd542de84a4c155c258cf75da4773/zstandard-0.23.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:b69bb4f51daf461b15e7b3db033160937d3ff88303a7bc808c67bbc1eaf98c78", size = 5669707 }, - { url = "https://files.pythonhosted.org/packages/52/5a/87d6971f0997c4b9b09c495bf92189fb63de86a83cadc4977dc19735f652/zstandard-0.23.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:034b88913ecc1b097f528e42b539453fa82c3557e414b3de9d5632c80439a473", size = 5201792 }, - { url = "https://files.pythonhosted.org/packages/79/02/6f6a42cc84459d399bd1a4e1adfc78d4dfe45e56d05b072008d10040e13b/zstandard-0.23.0-cp311-cp311-win32.whl", hash = "sha256:f2d4380bf5f62daabd7b751ea2339c1a21d1c9463f1feb7fc2bdcea2c29c3160", size = 430586 }, - { url = "https://files.pythonhosted.org/packages/be/a2/4272175d47c623ff78196f3c10e9dc7045c1b9caf3735bf041e65271eca4/zstandard-0.23.0-cp311-cp311-win_amd64.whl", hash = "sha256:62136da96a973bd2557f06ddd4e8e807f9e13cbb0bfb9cc06cfe6d98ea90dfe0", size = 495420 }, - { url = "https://files.pythonhosted.org/packages/7b/83/f23338c963bd9de687d47bf32efe9fd30164e722ba27fb59df33e6b1719b/zstandard-0.23.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b4567955a6bc1b20e9c31612e615af6b53733491aeaa19a6b3b37f3b65477094", size = 788713 }, - { url = "https://files.pythonhosted.org/packages/5b/b3/1a028f6750fd9227ee0b937a278a434ab7f7fdc3066c3173f64366fe2466/zstandard-0.23.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e172f57cd78c20f13a3415cc8dfe24bf388614324d25539146594c16d78fcc8", size = 633459 }, - { url = "https://files.pythonhosted.org/packages/26/af/36d89aae0c1f95a0a98e50711bc5d92c144939efc1f81a2fcd3e78d7f4c1/zstandard-0.23.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b0e166f698c5a3e914947388c162be2583e0c638a4703fc6a543e23a88dea3c1", size = 4945707 }, - { url = "https://files.pythonhosted.org/packages/cd/2e/2051f5c772f4dfc0aae3741d5fc72c3dcfe3aaeb461cc231668a4db1ce14/zstandard-0.23.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12a289832e520c6bd4dcaad68e944b86da3bad0d339ef7989fb7e88f92e96072", size = 5306545 }, - { url = "https://files.pythonhosted.org/packages/0a/9e/a11c97b087f89cab030fa71206963090d2fecd8eb83e67bb8f3ffb84c024/zstandard-0.23.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d50d31bfedd53a928fed6707b15a8dbeef011bb6366297cc435accc888b27c20", size = 5337533 }, - { url = "https://files.pythonhosted.org/packages/fc/79/edeb217c57fe1bf16d890aa91a1c2c96b28c07b46afed54a5dcf310c3f6f/zstandard-0.23.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72c68dda124a1a138340fb62fa21b9bf4848437d9ca60bd35db36f2d3345f373", size = 5436510 }, - { url = "https://files.pythonhosted.org/packages/81/4f/c21383d97cb7a422ddf1ae824b53ce4b51063d0eeb2afa757eb40804a8ef/zstandard-0.23.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:53dd9d5e3d29f95acd5de6802e909ada8d8d8cfa37a3ac64836f3bc4bc5512db", size = 4859973 }, - { url = "https://files.pythonhosted.org/packages/ab/15/08d22e87753304405ccac8be2493a495f529edd81d39a0870621462276ef/zstandard-0.23.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:6a41c120c3dbc0d81a8e8adc73312d668cd34acd7725f036992b1b72d22c1772", size = 4936968 }, - { url = "https://files.pythonhosted.org/packages/eb/fa/f3670a597949fe7dcf38119a39f7da49a8a84a6f0b1a2e46b2f71a0ab83f/zstandard-0.23.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:40b33d93c6eddf02d2c19f5773196068d875c41ca25730e8288e9b672897c105", size = 5467179 }, - { url = "https://files.pythonhosted.org/packages/4e/a9/dad2ab22020211e380adc477a1dbf9f109b1f8d94c614944843e20dc2a99/zstandard-0.23.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9206649ec587e6b02bd124fb7799b86cddec350f6f6c14bc82a2b70183e708ba", size = 4848577 }, - { url = "https://files.pythonhosted.org/packages/08/03/dd28b4484b0770f1e23478413e01bee476ae8227bbc81561f9c329e12564/zstandard-0.23.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:76e79bc28a65f467e0409098fa2c4376931fd3207fbeb6b956c7c476d53746dd", size = 4693899 }, - { url = "https://files.pythonhosted.org/packages/2b/64/3da7497eb635d025841e958bcd66a86117ae320c3b14b0ae86e9e8627518/zstandard-0.23.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:66b689c107857eceabf2cf3d3fc699c3c0fe8ccd18df2219d978c0283e4c508a", size = 5199964 }, - { url = "https://files.pythonhosted.org/packages/43/a4/d82decbab158a0e8a6ebb7fc98bc4d903266bce85b6e9aaedea1d288338c/zstandard-0.23.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9c236e635582742fee16603042553d276cca506e824fa2e6489db04039521e90", size = 5655398 }, - { url = "https://files.pythonhosted.org/packages/f2/61/ac78a1263bc83a5cf29e7458b77a568eda5a8f81980691bbc6eb6a0d45cc/zstandard-0.23.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a8fffdbd9d1408006baaf02f1068d7dd1f016c6bcb7538682622c556e7b68e35", size = 5191313 }, - { url = "https://files.pythonhosted.org/packages/e7/54/967c478314e16af5baf849b6ee9d6ea724ae5b100eb506011f045d3d4e16/zstandard-0.23.0-cp312-cp312-win32.whl", hash = "sha256:dc1d33abb8a0d754ea4763bad944fd965d3d95b5baef6b121c0c9013eaf1907d", size = 430877 }, - { url = "https://files.pythonhosted.org/packages/75/37/872d74bd7739639c4553bf94c84af7d54d8211b626b352bc57f0fd8d1e3f/zstandard-0.23.0-cp312-cp312-win_amd64.whl", hash = "sha256:64585e1dba664dc67c7cdabd56c1e5685233fbb1fc1966cfba2a340ec0dfff7b", size = 495595 }, - { url = "https://files.pythonhosted.org/packages/80/f1/8386f3f7c10261fe85fbc2c012fdb3d4db793b921c9abcc995d8da1b7a80/zstandard-0.23.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:576856e8594e6649aee06ddbfc738fec6a834f7c85bf7cadd1c53d4a58186ef9", size = 788975 }, - { url = "https://files.pythonhosted.org/packages/16/e8/cbf01077550b3e5dc86089035ff8f6fbbb312bc0983757c2d1117ebba242/zstandard-0.23.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:38302b78a850ff82656beaddeb0bb989a0322a8bbb1bf1ab10c17506681d772a", size = 633448 }, - { url = "https://files.pythonhosted.org/packages/06/27/4a1b4c267c29a464a161aeb2589aff212b4db653a1d96bffe3598f3f0d22/zstandard-0.23.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d2240ddc86b74966c34554c49d00eaafa8200a18d3a5b6ffbf7da63b11d74ee2", size = 4945269 }, - { url = "https://files.pythonhosted.org/packages/7c/64/d99261cc57afd9ae65b707e38045ed8269fbdae73544fd2e4a4d50d0ed83/zstandard-0.23.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2ef230a8fd217a2015bc91b74f6b3b7d6522ba48be29ad4ea0ca3a3775bf7dd5", size = 5306228 }, - { url = "https://files.pythonhosted.org/packages/7a/cf/27b74c6f22541f0263016a0fd6369b1b7818941de639215c84e4e94b2a1c/zstandard-0.23.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:774d45b1fac1461f48698a9d4b5fa19a69d47ece02fa469825b442263f04021f", size = 5336891 }, - { url = "https://files.pythonhosted.org/packages/fa/18/89ac62eac46b69948bf35fcd90d37103f38722968e2981f752d69081ec4d/zstandard-0.23.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f77fa49079891a4aab203d0b1744acc85577ed16d767b52fc089d83faf8d8ed", size = 5436310 }, - { url = "https://files.pythonhosted.org/packages/a8/a8/5ca5328ee568a873f5118d5b5f70d1f36c6387716efe2e369010289a5738/zstandard-0.23.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ac184f87ff521f4840e6ea0b10c0ec90c6b1dcd0bad2f1e4a9a1b4fa177982ea", size = 4859912 }, - { url = "https://files.pythonhosted.org/packages/ea/ca/3781059c95fd0868658b1cf0440edd832b942f84ae60685d0cfdb808bca1/zstandard-0.23.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c363b53e257246a954ebc7c488304b5592b9c53fbe74d03bc1c64dda153fb847", size = 4936946 }, - { url = "https://files.pythonhosted.org/packages/ce/11/41a58986f809532742c2b832c53b74ba0e0a5dae7e8ab4642bf5876f35de/zstandard-0.23.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:e7792606d606c8df5277c32ccb58f29b9b8603bf83b48639b7aedf6df4fe8171", size = 5466994 }, - { url = "https://files.pythonhosted.org/packages/83/e3/97d84fe95edd38d7053af05159465d298c8b20cebe9ccb3d26783faa9094/zstandard-0.23.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a0817825b900fcd43ac5d05b8b3079937073d2b1ff9cf89427590718b70dd840", size = 4848681 }, - { url = "https://files.pythonhosted.org/packages/6e/99/cb1e63e931de15c88af26085e3f2d9af9ce53ccafac73b6e48418fd5a6e6/zstandard-0.23.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:9da6bc32faac9a293ddfdcb9108d4b20416219461e4ec64dfea8383cac186690", size = 4694239 }, - { url = "https://files.pythonhosted.org/packages/ab/50/b1e703016eebbc6501fc92f34db7b1c68e54e567ef39e6e59cf5fb6f2ec0/zstandard-0.23.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:fd7699e8fd9969f455ef2926221e0233f81a2542921471382e77a9e2f2b57f4b", size = 5200149 }, - { url = "https://files.pythonhosted.org/packages/aa/e0/932388630aaba70197c78bdb10cce2c91fae01a7e553b76ce85471aec690/zstandard-0.23.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:d477ed829077cd945b01fc3115edd132c47e6540ddcd96ca169facff28173057", size = 5655392 }, - { url = "https://files.pythonhosted.org/packages/02/90/2633473864f67a15526324b007a9f96c96f56d5f32ef2a56cc12f9548723/zstandard-0.23.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa6ce8b52c5987b3e34d5674b0ab529a4602b632ebab0a93b07bfb4dfc8f8a33", size = 5191299 }, - { url = "https://files.pythonhosted.org/packages/b0/4c/315ca5c32da7e2dc3455f3b2caee5c8c2246074a61aac6ec3378a97b7136/zstandard-0.23.0-cp313-cp313-win32.whl", hash = "sha256:a9b07268d0c3ca5c170a385a0ab9fb7fdd9f5fd866be004c4ea39e44edce47dd", size = 430862 }, - { url = "https://files.pythonhosted.org/packages/a2/bf/c6aaba098e2d04781e8f4f7c0ba3c7aa73d00e4c436bcc0cf059a66691d1/zstandard-0.23.0-cp313-cp313-win_amd64.whl", hash = "sha256:f3513916e8c645d0610815c257cbfd3242adfd5c4cfa78be514e5a3ebb42a41b", size = 495578 }, -] From 30a4c0cf98b6660640ce59023f14806238577c92 Mon Sep 17 00:00:00 2001 From: Vasile Bujac Date: Thu, 24 Jul 2025 13:21:57 +0300 Subject: [PATCH 09/18] fix(cli): don't double include README.md in package --- pyproject.toml | 2 +- src/uipath/_cli/cli_pack.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 3f2389d4..f47415ce 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "uipath" -version = "2.1.0" +version = "2.1.1" description = "Python SDK and CLI for UiPath Platform, enabling programmatic interaction with automation services, process management, and deployment tools." readme = { file = "README.md", content-type = "text/markdown" } requires-python = ">=3.10" diff --git a/src/uipath/_cli/cli_pack.py b/src/uipath/_cli/cli_pack.py index b4d8e21f..be2e86f8 100644 --- a/src/uipath/_cli/cli_pack.py +++ b/src/uipath/_cli/cli_pack.py @@ -347,7 +347,7 @@ def pack_fn( z.writestr(f"content/{rel_path}", f.read()) # Handle optional files, conditionally including uv.lock - optional_files = ["pyproject.toml", "README.md"] + optional_files = ["pyproject.toml"] if include_uv_lock: optional_files.append("uv.lock") From 0cfa372fa663c193a5a16a8d68032786ae6ca918 Mon Sep 17 00:00:00 2001 From: giulia Date: Thu, 24 Jul 2025 16:00:13 +0300 Subject: [PATCH 10/18] feat(llm_service): add response format to chat_completions --- pyproject.toml | 2 +- src/uipath/_services/llm_gateway_service.py | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index f47415ce..f97977d2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "uipath" -version = "2.1.1" +version = "2.1.2" description = "Python SDK and CLI for UiPath Platform, enabling programmatic interaction with automation services, process management, and deployment tools." readme = { file = "README.md", content-type = "text/markdown" } requires-python = ">=3.10" diff --git a/src/uipath/_services/llm_gateway_service.py b/src/uipath/_services/llm_gateway_service.py index fb0e3317..61258261 100644 --- a/src/uipath/_services/llm_gateway_service.py +++ b/src/uipath/_services/llm_gateway_service.py @@ -146,6 +146,7 @@ async def chat_completions( model: str = ChatModels.gpt_4o_mini_2024_07_18, max_tokens: int = 50, temperature: float = 0, + response_format: Optional[Dict[str, Any]] = None, api_version: str = API_VERSION, ): """Generate chat completions using UiPath's LLM Gateway service. @@ -167,6 +168,9 @@ async def chat_completions( temperature (float, optional): Temperature for sampling, between 0 and 1. Lower values (closer to 0) make output more deterministic and focused, higher values make it more creative and random. Defaults to 0. + response_format (Optional[Dict[str, Any]], optional): An object specifying the format + that the model must output. Used to enable JSON mode or other structured outputs. + Defaults to None. api_version (str, optional): The API version to use. Defaults to API_VERSION. Returns: @@ -211,6 +215,10 @@ async def chat_completions( "temperature": temperature, } + # Add response_format if provided + if response_format: + request_body["response_format"] = response_format + response = await self.request_async( "POST", endpoint, @@ -250,6 +258,7 @@ async def chat_completions( top_p: float = 1, tools: Optional[List[ToolDefinition]] = None, tool_choice: Optional[ToolChoice] = None, + response_format: Optional[Dict[str, Any]] = None, api_version: str = NORMALIZED_API_VERSION, ): """Generate chat completions using UiPath's normalized LLM Gateway API. @@ -286,6 +295,9 @@ async def chat_completions( tool_choice (Optional[ToolChoice], optional): Controls which tools the model can call. Can be "auto" (model decides), "none" (no tools), or a specific tool choice. Defaults to None. + response_format (Optional[Dict[str, Any]], optional): An object specifying the format + that the model must output. Used to enable JSON mode or other structured outputs. + Defaults to None. api_version (str, optional): The normalized API version to use. Defaults to NORMALIZED_API_VERSION. @@ -358,6 +370,10 @@ async def chat_completions( "top_p": top_p, } + # Add response_format if provided + if response_format: + request_body["response_format"] = response_format + # Add tools if provided - convert to UiPath format if tools: request_body["tools"] = [ From 4e8c6ad09d910cc5723a4cb9c46e8d2bb9fd77b1 Mon Sep 17 00:00:00 2001 From: radu-mocanu Date: Mon, 7 Jul 2025 20:59:55 +0300 Subject: [PATCH 11/18] feat: cli add push command --- src/uipath/_cli/__init__.py | 2 + src/uipath/_cli/_utils/_constants.py | 6 + src/uipath/_cli/_utils/_project_files.py | 361 +++++++++++++++ src/uipath/_cli/_utils/_studio_project.py | 138 ++++++ src/uipath/_cli/_utils/_uv_helpers.py | 114 +++++ src/uipath/_cli/cli_push.py | 528 ++++++++++++++++++++++ tests/cli/mocks/pyproject.toml | 2 +- tests/cli/test_push.py | 512 +++++++++++++++++++++ 8 files changed, 1662 insertions(+), 1 deletion(-) create mode 100644 src/uipath/_cli/_utils/_project_files.py create mode 100644 src/uipath/_cli/_utils/_studio_project.py create mode 100644 src/uipath/_cli/_utils/_uv_helpers.py create mode 100644 src/uipath/_cli/cli_push.py create mode 100644 tests/cli/test_push.py diff --git a/src/uipath/_cli/__init__.py b/src/uipath/_cli/__init__.py index 5e7c93ef..ef13e365 100644 --- a/src/uipath/_cli/__init__.py +++ b/src/uipath/_cli/__init__.py @@ -10,6 +10,7 @@ from .cli_new import new as new # type: ignore from .cli_pack import pack as pack # type: ignore from .cli_publish import publish as publish # type: ignore +from .cli_push import push as push # type: ignore from .cli_run import run as run # type: ignore @@ -63,3 +64,4 @@ def cli(lv: bool, v: bool) -> None: cli.add_command(deploy) cli.add_command(auth) cli.add_command(invoke) +cli.add_command(push) diff --git a/src/uipath/_cli/_utils/_constants.py b/src/uipath/_cli/_utils/_constants.py index 87e9a044..6fbd3736 100644 --- a/src/uipath/_cli/_utils/_constants.py +++ b/src/uipath/_cli/_utils/_constants.py @@ -1,5 +1,11 @@ BINDINGS_VERSION = "2.2" +# Agent.json constants +AGENT_VERSION = "1.0.0" +AGENT_STORAGE_VERSION = "1.0.0" +AGENT_INITIAL_CODE_VERSION = "1.0.0" +AGENT_TARGET_RUNTIME = "python" + # Binary file extension categories IMAGE_EXTENSIONS = { ".png", diff --git a/src/uipath/_cli/_utils/_project_files.py b/src/uipath/_cli/_utils/_project_files.py new file mode 100644 index 00000000..cb5027b1 --- /dev/null +++ b/src/uipath/_cli/_utils/_project_files.py @@ -0,0 +1,361 @@ +# type: ignore +import json +import os +import re +from typing import Any, Dict, Optional, Tuple + +from pydantic import BaseModel + +from .._utils._console import ConsoleLogger + +try: + import tomllib +except ImportError: + import tomli as tomllib + + +class FileInfo(BaseModel): + """Information about a file to be included in the project. + + Attributes: + file_path: The absolute path to the file + relative_path: The path relative to the project root + is_binary: Whether the file should be treated as binary + """ + + file_path: str + relative_path: str + is_binary: bool + + +console = ConsoleLogger() + + +def get_project_config(directory: str) -> dict[str, str]: + """Retrieve and combine project configuration from uipath.json and pyproject.toml. + + Args: + directory: The root directory containing the configuration files + + Returns: + dict[str, str]: Combined configuration including project name, description, + entry points, version, and authors + + Raises: + SystemExit: If required configuration files are missing or invalid + """ + config_path = os.path.join(directory, "uipath.json") + toml_path = os.path.join(directory, "pyproject.toml") + + if not os.path.isfile(config_path): + console.error("uipath.json not found, please run `uipath init`.") + if not os.path.isfile(toml_path): + console.error("pyproject.toml not found.") + + with open(config_path, "r") as config_file: + config_data = json.load(config_file) + + validate_config_structure(config_data) + + toml_data = read_toml_project(toml_path) + + return { + "project_name": toml_data["name"], + "description": toml_data["description"], + "entryPoints": config_data["entryPoints"], + "version": toml_data["version"], + "authors": toml_data["authors"], + "dependencies": toml_data.get("dependencies", {}), + } + + +def validate_config(config: dict[str, str]) -> None: + """Validate the combined project configuration. + + Checks for required fields and invalid characters in project name and description. + + Args: + config: The combined configuration dictionary from uipath.json and pyproject.toml + + Raises: + SystemExit: If validation fails for any required field or contains invalid characters + """ + if not config["project_name"] or config["project_name"].strip() == "": + console.error( + "Project name cannot be empty. Please specify a name in pyproject.toml." + ) + + if not config["description"] or config["description"].strip() == "": + console.error( + "Project description cannot be empty. Please specify a description in pyproject.toml." + ) + + if not config["authors"] or config["authors"].strip() == "": + console.error( + 'Project authors cannot be empty. Please specify authors in pyproject.toml:\n authors = [{ name = "John Doe" }]' + ) + + invalid_chars = ["&", "<", ">", '"', "'", ";"] + for char in invalid_chars: + if char in config["project_name"]: + console.error(f"Project name contains invalid character: '{char}'") + + for char in invalid_chars: + if char in config["description"]: + console.error(f"Project description contains invalid character: '{char}'") + + +def validate_config_structure(config_data: dict[str, Any]) -> None: + """Validate the structure of uipath.json configuration. + + Args: + config_data: The raw configuration data from uipath.json + + Raises: + SystemExit: If required fields are missing from the configuration + """ + required_fields = ["entryPoints"] + for field in required_fields: + if field not in config_data: + console.error(f"uipath.json is missing the required field: {field}.") + + +def ensure_config_file(directory: str) -> None: + """Check if uipath.json exists in the specified directory. + + Args: + directory: The directory to check for uipath.json + + Raises: + SystemExit: If uipath.json is not found in the directory + """ + if not os.path.isfile(os.path.join(directory, "uipath.json")): + console.error( + "uipath.json not found. Please run `uipath init` in the project directory." + ) + + +def extract_dependencies_from_toml(project_data: Dict) -> Dict[str, str]: + """Extract and parse dependencies from pyproject.toml project data. + + Args: + project_data: The "project" section from pyproject.toml + + Returns: + Dictionary mapping package names to version specifiers + """ + dependencies = {} + + if "dependencies" not in project_data: + return dependencies + + deps_list = project_data["dependencies"] + if not isinstance(deps_list, list): + console.warning("dependencies should be a list in pyproject.toml") + return dependencies + + for dep in deps_list: + if not isinstance(dep, str): + console.warning(f"Skipping non-string dependency: {dep}") + continue + + try: + name, version_spec = parse_dependency_string(dep) + if name: # Only add if we got a valid name + dependencies[name] = version_spec + except Exception as e: + console.warning(f"Failed to parse dependency '{dep}': {e}") + continue + + return dependencies + + +def parse_dependency_string(dependency: str) -> Tuple[str, str]: + """Parse a dependency string into package name and version specifier. + + Handles PEP 508 dependency specifications including: + - Simple names: "requests" + - Version specifiers: "requests>=2.28.0" + - Complex specifiers: "requests>=2.28.0,<3.0.0" + - Extras: "requests[security]>=2.28.0" + - Environment markers: "requests>=2.28.0; python_version>='3.8'" + + Args: + dependency: Raw dependency string from pyproject.toml + + Returns: + Tuple of (package_name, version_specifier) + + Examples: + "requests" -> ("requests", "*") + "requests>=2.28.0" -> ("requests", ">=2.28.0") + "requests>=2.28.0,<3.0.0" -> ("requests", ">=2.28.0,<3.0.0") + "requests[security]>=2.28.0" -> ("requests", ">=2.28.0") + """ + # Remove whitespace + dependency = dependency.strip() + + # Handle environment markers (everything after semicolon) + if ";" in dependency: + dependency = dependency.split(";")[0].strip() + + # Pattern to match package name with optional extras and version specifiers + # Matches: package_name[extras] version_specs + pattern = r"^([a-zA-Z0-9]([a-zA-Z0-9._-]*[a-zA-Z0-9])?)(\[[^\]]+\])?(.*)" + match = re.match(pattern, dependency) + + if not match: + # Fallback for edge cases + return dependency, "*" + + package_name = match.group(1) + version_part = match.group(4).strip() if match.group(4) else "" + + # If no version specifier, return wildcard + if not version_part: + return package_name, "*" + + # Clean up version specifier + version_spec = version_part.strip() + + # Validate that version specifier starts with a valid operator + valid_operators = [">=", "<=", "==", "!=", "~=", ">", "<"] + if not any(version_spec.startswith(op) for op in valid_operators): + # If it doesn't start with an operator, treat as exact version + if version_spec: + version_spec = f"=={version_spec}" + else: + version_spec = "*" + + return package_name, version_spec + + +def read_toml_project(file_path: str) -> dict: + """Read and parse pyproject.toml file with improved error handling and validation. + + Args: + file_path: Path to pyproject.toml file + + Returns: + Dictionary containing project metadata and dependencies + """ + try: + with open(file_path, "rb") as f: + content = tomllib.load(f) + except Exception as e: + console.error(f"Failed to read or parse pyproject.toml: {e}") + + # Validate required sections + if "project" not in content: + console.error("pyproject.toml is missing the required field: project.") + + project = content["project"] + + # Validate required fields with better error messages + required_fields = { + "name": "Project name is required in pyproject.toml", + "description": "Project description is required in pyproject.toml", + "version": "Project version is required in pyproject.toml", + } + + for field, error_msg in required_fields.items(): + if field not in project: + console.error( + f"pyproject.toml is missing the required field: project.{field}. {error_msg}" + ) + + # Check for empty values only if field exists + if field in project and ( + not project[field] + or (isinstance(project[field], str) and not project[field].strip()) + ): + console.error( + f"Project {field} cannot be empty. Please specify a {field} in pyproject.toml." + ) + + # Extract author information safely + authors = project.get("authors", []) + author_name = "" + + if authors and isinstance(authors, list) and len(authors) > 0: + first_author = authors[0] + if isinstance(first_author, dict): + author_name = first_author.get("name", "") + elif isinstance(first_author, str): + # Handle case where authors is a list of strings + author_name = first_author + + # Extract dependencies with improved parsing + dependencies = extract_dependencies_from_toml(project) + + return { + "name": project["name"].strip(), + "description": project["description"].strip(), + "version": project["version"].strip(), + "authors": author_name.strip(), + "dependencies": dependencies, + } + + +def files_to_include( + config_data: Optional[dict[Any, Any]], directory: str +) -> list[FileInfo]: + """Get list of files to include in the project based on configuration. + + Walks through the directory tree and identifies files to include based on extensions + and explicit inclusion rules. Skips virtual environments and hidden directories. + + Args: + settings_section: Configuration section containing file inclusion rules + directory: Root directory to search for files + + Returns: + list[FileInfo]: List of file information objects for included files + """ + file_extensions_included = [".py", ".mermaid", ".json", ".yaml", ".yml"] + files_included = [] + binary_extensions = [".exe", "", ".xlsx", ".xls"] + if "settings" in config_data: + settings = config_data["settings"] + if "fileExtensionsIncluded" in settings: + file_extensions_included.extend(settings["fileExtensionsIncluded"]) + if "filesIncluded" in settings: + files_included = settings["filesIncluded"] + + def is_venv_dir(d: str) -> bool: + """Check if a directory is a Python virtual environment. + + Args: + d: Directory path to check + + Returns: + bool: True if directory is a virtual environment, False otherwise + """ + return ( + os.path.exists(os.path.join(d, "Scripts", "activate")) + if os.name == "nt" + else os.path.exists(os.path.join(d, "bin", "activate")) + ) + + extra_files: list[FileInfo] = [] + # Walk through directory and return all files in the allowlist + for root, dirs, files in os.walk(directory): + # Skip all directories that start with . or are a venv + dirs[:] = [ + d + for d in dirs + if not d.startswith(".") and not is_venv_dir(os.path.join(root, d)) + ] + for file in files: + file_extension = os.path.splitext(file)[1].lower() + if file_extension in file_extensions_included or file in files_included: + file_path = os.path.join(root, file) + rel_path = os.path.relpath(file_path, directory) + extra_files.append( + FileInfo( + file_path=file_path, + relative_path=rel_path, + is_binary=file_extension in binary_extensions, + ) + ) + return extra_files diff --git a/src/uipath/_cli/_utils/_studio_project.py b/src/uipath/_cli/_utils/_studio_project.py new file mode 100644 index 00000000..c277d1cf --- /dev/null +++ b/src/uipath/_cli/_utils/_studio_project.py @@ -0,0 +1,138 @@ +from typing import List, Optional, Union + +from pydantic import BaseModel, ConfigDict, Field, field_validator + + +class ProjectFile(BaseModel): + """Model representing a file in a UiPath project. + + Attributes: + id: The unique identifier of the file + name: The name of the file + is_main: Whether this is a main file + file_type: The type of the file + is_entry_point: Whether this is an entry point + ignored_from_publish: Whether this file is ignored during publish + app_form_id: The ID of the associated app form + external_automation_id: The ID of the external automation + test_case_id: The ID of the associated test case + """ + + model_config = ConfigDict( + validate_by_name=True, + validate_by_alias=True, + use_enum_values=True, + arbitrary_types_allowed=True, + extra="allow", + ) + + id: str = Field(alias="id") + name: str = Field(alias="name") + is_main: Optional[bool] = Field(default=None, alias="isMain") + file_type: Optional[str] = Field(default=None, alias="fileType") + is_entry_point: Optional[bool] = Field(default=None, alias="isEntryPoint") + ignored_from_publish: Optional[bool] = Field( + default=None, alias="ignoredFromPublish" + ) + app_form_id: Optional[str] = Field(default=None, alias="appFormId") + external_automation_id: Optional[str] = Field( + default=None, alias="externalAutomationId" + ) + test_case_id: Optional[str] = Field(default=None, alias="testCaseId") + + @field_validator("file_type", mode="before") + @classmethod + def convert_file_type(cls, v: Union[str, int, None]) -> Optional[str]: + """Convert numeric file type to string. + + Args: + v: The value to convert + + Returns: + Optional[str]: The converted value or None + """ + if isinstance(v, int): + return str(v) + return v + + +class ProjectFolder(BaseModel): + """Model representing a folder in a UiPath project structure. + + Attributes: + id: The unique identifier of the folder + name: The name of the folder + folders: List of subfolders + files: List of files in the folder + folder_type: The type of the folder + """ + + model_config = ConfigDict( + validate_by_name=True, + validate_by_alias=True, + use_enum_values=True, + arbitrary_types_allowed=True, + extra="allow", + ) + + id: str = Field(alias="id") + name: str = Field(alias="name") + folders: List["ProjectFolder"] = Field(default_factory=list) + files: List[ProjectFile] = Field(default_factory=list) + folder_type: Optional[str] = Field(default=None, alias="folderType") + + @field_validator("folder_type", mode="before") + @classmethod + def convert_folder_type(cls, v: Union[str, int, None]) -> Optional[str]: + """Convert numeric folder type to string. + + Args: + v: The value to convert + + Returns: + Optional[str]: The converted value or None + """ + if isinstance(v, int): + return str(v) + return v + + +class ProjectStructure(BaseModel): + """Model representing the complete file structure of a UiPath project. + + Attributes: + id: The unique identifier of the root folder (optional) + name: The name of the root folder (optional) + folders: List of folders in the project + files: List of files at the root level + folder_type: The type of the root folder (optional) + """ + + model_config = ConfigDict( + validate_by_name=True, + validate_by_alias=True, + use_enum_values=True, + arbitrary_types_allowed=True, + extra="allow", + ) + + id: Optional[str] = Field(default=None, alias="id") + name: Optional[str] = Field(default=None, alias="name") + folders: List[ProjectFolder] = Field(default_factory=list) + files: List[ProjectFile] = Field(default_factory=list) + folder_type: Optional[str] = Field(default=None, alias="folderType") + + @field_validator("folder_type", mode="before") + @classmethod + def convert_folder_type(cls, v: Union[str, int, None]) -> Optional[str]: + """Convert numeric folder type to string. + + Args: + v: The value to convert + + Returns: + Optional[str]: The converted value or None + """ + if isinstance(v, int): + return str(v) + return v diff --git a/src/uipath/_cli/_utils/_uv_helpers.py b/src/uipath/_cli/_utils/_uv_helpers.py new file mode 100644 index 00000000..9464fb4e --- /dev/null +++ b/src/uipath/_cli/_utils/_uv_helpers.py @@ -0,0 +1,114 @@ +import os +import subprocess + +from .._utils._console import ConsoleLogger + +console = ConsoleLogger() + + +def handle_uv_operations(directory: str) -> None: + """Handle UV package manager operations for the project. + + This function checks if UV is available and if the project uses UV, + then ensures the lock file is up to date by running 'uv lock'. + + Args: + directory: The project root directory where UV operations should be performed + + Note: + This function will silently return if UV is not available or if the project + doesn't use UV (no uv.lock file present). + """ + if not is_uv_available(): + return + if not is_uv_project(directory): + return + # Always run uv lock to ensure lock file is up to date + run_uv_lock(directory) + + +def is_uv_available() -> bool: + """Check if UV package manager is available in the system. + + Attempts to run 'uv --version' to verify UV is installed and accessible. + + Returns: + bool: True if UV is available and working, False otherwise + + Note: + This function will return False if: + - UV is not installed + - UV command fails to execute + - Any unexpected error occurs during version check + """ + try: + subprocess.run(["uv", "--version"], capture_output=True, check=True, timeout=20) + return True + except (subprocess.CalledProcessError, FileNotFoundError): + return False + except Exception as e: + console.warning( + f"An unexpected error occurred while checking uv availability: {str(e)}" + ) + return False + + +def is_uv_project(directory: str) -> bool: + """Check if the project uses UV package manager. + + Determines if this is a UV project by checking for the presence + of a uv.lock file in the project directory. + + Args: + directory: The project root directory to check + + Returns: + bool: True if uv.lock exists, indicating a UV project, False otherwise + """ + uv_lock_path = os.path.join(directory, "uv.lock") + + # If uv.lock exists, it's definitely a uv project + if os.path.exists(uv_lock_path): + return True + + return False + + +def run_uv_lock(directory: str) -> bool: + """Run 'uv lock' command to update the project's lock file. + + Executes UV lock command to ensure dependencies are properly locked + and the lock file is up to date. + + Args: + directory: The project root directory where the lock command should be run + + Returns: + bool: True if the lock command succeeds, False if it fails for any reason + + Note: + This function will log warnings and return False if: + - The UV command fails to execute + - UV is not found in the system + - The lock command times out (60 seconds) + - Any unexpected error occurs during execution + """ + try: + subprocess.run( + ["uv", "lock"], + cwd=directory, + capture_output=True, + text=True, + check=True, + timeout=60, + ) + return True + except subprocess.CalledProcessError as e: + console.warning(f"uv lock failed: {e.stderr}") + return False + except FileNotFoundError: + console.warning("uv command not found. Skipping lock file update.") + return False + except Exception as e: + console.warning(f"An unexpected error occurred while running uv lock: {str(e)}") + return False diff --git a/src/uipath/_cli/cli_push.py b/src/uipath/_cli/cli_push.py new file mode 100644 index 00000000..84c3afe6 --- /dev/null +++ b/src/uipath/_cli/cli_push.py @@ -0,0 +1,528 @@ +# type: ignore +"""CLI command for pushing local project files to UiPath StudioWeb solution. + +This module provides functionality to push local project files to a UiPath StudioWeb solution. +It handles: +- File uploads and updates +- File deletions for removed local files +- Optional UV lock file management +- Project structure pushing + +The push process ensures that the remote project structure matches the local files, +taking into account: +- Entry point files from uipath.json +- Project configuration from pyproject.toml +- Optional UV lock file for dependency management +""" + +import json +import os +from datetime import datetime, timezone +from typing import Any, Dict, Optional, Set, Tuple +from urllib.parse import urlparse + +import click +import httpx +from dotenv import load_dotenv + +from .._utils._ssl_context import get_httpx_client_kwargs +from ..telemetry import track +from ._utils._common import get_env_vars +from ._utils._console import ConsoleLogger +from ._utils._constants import ( + AGENT_INITIAL_CODE_VERSION, + AGENT_STORAGE_VERSION, + AGENT_TARGET_RUNTIME, + AGENT_VERSION, +) +from ._utils._project_files import ( + ensure_config_file, + files_to_include, + get_project_config, + read_toml_project, + validate_config, +) +from ._utils._studio_project import ProjectFile, ProjectFolder, ProjectStructure +from ._utils._uv_helpers import handle_uv_operations + +console = ConsoleLogger() +load_dotenv(override=True) + + +def get_org_scoped_url(base_url: str) -> str: + parsed = urlparse(base_url) + org_name, *_ = parsed.path.strip("/").split("/") + + # Construct the new scoped URL (scheme + domain + org_name) + org_scoped_url = f"{parsed.scheme}://{parsed.netloc}/{org_name}" + return org_scoped_url + + +def get_project_structure( + project_id: str, + base_url: str, + token: str, + client: httpx.Client, +) -> ProjectStructure: + """Retrieve the project's file structure from UiPath Cloud. + + Makes an API call to fetch the complete file structure of a project, + including all files and folders. The response is validated against + the ProjectStructure model. + + Args: + project_id: The ID of the project + base_url: The base URL for the API + token: Authentication token + client: HTTP client to use for requests + + Returns: + ProjectStructure: The complete project structure + + Raises: + httpx.HTTPError: If the API request fails + """ + url = get_org_scoped_url(base_url) + headers = {"Authorization": f"Bearer {token}"} + url = f"{url}/studio_/backend/api/Project/{project_id}/FileOperations/Structure" + + response = client.get(url, headers=headers) + response.raise_for_status() + return ProjectStructure.model_validate(response.json()) + + +def collect_all_files( + folder: ProjectFolder, files_dict: Dict[str, ProjectFile] +) -> None: + """Recursively collect all files from a folder and its subfolders. + + Traverses the folder structure recursively and adds all files to the + provided dictionary, using the file name as the key. + """ + # Add files from current folder + for file in folder.files: + files_dict[file.name] = file + + # Recursively process subfolders + for subfolder in folder.folders: + collect_all_files(subfolder, files_dict) + + +def get_folder_by_name( + structure: ProjectStructure, folder_name: str +) -> Optional[ProjectFolder]: + """Get a folder from the project structure by name. + + Args: + structure: The project structure + folder_name: Name of the folder to find + + Returns: + Optional[ProjectFolder]: The found folder or None + """ + for folder in structure.folders: + if folder.name == folder_name: + return folder + return None + + +def get_all_remote_files( + structure: ProjectStructure, source_code_folder: Optional[ProjectFolder] = None +) -> Tuple[Dict[str, ProjectFile], Dict[str, ProjectFile]]: + """Get all files from the project structure indexed by name. + + Creates two flat dictionaries of files in the project: + 1. Root level files + 2. Files in the source_code folder (if exists) + + Args: + structure: The project structure + source_code_folder: Optional source_code folder to collect files from + + Returns: + Tuple[Dict[str, ProjectFile], Dict[str, ProjectFile]]: + (root_files, source_code_files) + """ + root_files: Dict[str, ProjectFile] = {} + source_code_files: Dict[str, ProjectFile] = {} + + # Add files from root level + for file in structure.files: + root_files[file.name] = file + + # Add files from source_code folder if it exists + if source_code_folder: + collect_all_files(source_code_folder, source_code_files) + + return root_files, source_code_files + + +def delete_remote_file( + project_id: str, file_id: str, base_url: str, token: str, client: httpx.Client +) -> None: + """Delete a file from the remote project. + + Makes an API call to delete a specific file from the UiPath Cloud project. + + Args: + project_id: The ID of the project + file_id: The ID of the file to delete + base_url: The base URL for the API + token: Authentication token + client: HTTP client to use for the request + + Raises: + httpx.HTTPError: If the API request fails + """ + url = get_org_scoped_url(base_url) + headers = {"Authorization": f"Bearer {token}"} + url = f"{url}/studio_/backend/api/Project/{project_id}/FileOperations/Delete/{file_id}" + + response = client.delete(url, headers=headers) + response.raise_for_status() + + +def update_agent_json( + project_id: str, + base_url: str, + token: str, + directory: str, + client: httpx.Client, + processed_files: Optional[Set[str]] = None, + agent_json_file: Optional[ProjectFile] = None, +) -> None: + """Update agent.json file with metadata from uipath.json. + + This function: + 1. Downloads existing agent.json if it exists + 2. Updates metadata based on uipath.json content + 3. Increments code version + 4. Updates author from pyproject.toml + 5. Uploads updated agent.json + + Args: + project_id: The ID of the project + base_url: The base URL for the API + token: Authentication token + directory: Project root directory + client: HTTP client to use for requests + processed_files: Optional set to track processed files + agent_json_file: Optional existing agent.json file + + Raises: + httpx.HTTPError: If API requests fail + FileNotFoundError: If required files are missing + json.JSONDecodeError: If JSON parsing fails + """ + url = get_org_scoped_url(base_url) + headers = {"Authorization": f"Bearer {token}"} + + # Read uipath.json + with open(os.path.join(directory, "uipath.json"), "r") as f: + uipath_config = json.load(f) + + try: + entrypoints = [ + {"input": entry_point["input"], "output": entry_point["output"]} + for entry_point in uipath_config["entryPoints"] + ] + except (FileNotFoundError, KeyError) as e: + console.error( + f"Unable to extract entrypoints from configuration file. Please run 'uipath init' : {str(e)}", + ) + + # Read pyproject.toml for author info + toml_data = read_toml_project(os.path.join(directory, "pyproject.toml")) + author = toml_data.get("authors", "").strip() + + # Initialize agent.json structure + agent_json = { + "version": AGENT_VERSION, + "metadata": { + "storageVersion": AGENT_STORAGE_VERSION, + "targetRuntime": AGENT_TARGET_RUNTIME, + "isConversational": False, + "codeVersion": AGENT_INITIAL_CODE_VERSION, + "author": author, + "pushDate": datetime.now(timezone.utc).isoformat(), + }, + "entryPoints": entrypoints, + "bindings": uipath_config.get("bindings", {"version": "2.0", "resources": []}), + } + + base_api_url = f"{url}/studio_/backend/api/Project/{project_id}/FileOperations" + if agent_json_file: + # Download existing agent.json + file_url = f"{base_api_url}/File/{agent_json_file.id}" + response = client.get(file_url, headers=headers) + response.raise_for_status() + + try: + existing_agent = response.json() + # Get current version and increment patch version + version_parts = existing_agent["metadata"]["codeVersion"].split(".") + if len(version_parts) >= 3: + version_parts[-1] = str(int(version_parts[-1]) + 1) + agent_json["metadata"]["codeVersion"] = ".".join(version_parts) + else: + # If version format is invalid, start from initial version + 1 + agent_json["metadata"]["codeVersion"] = ( + AGENT_INITIAL_CODE_VERSION[:-1] + "1" + ) + except (json.JSONDecodeError, KeyError, ValueError): + console.warning( + "Could not parse existing agent.json, using default version" + ) + + # Upload updated agent.json + files_data = {"file": ("agent.json", json.dumps(agent_json), "application/json")} + + # if agent.json already exists update it, otherwise upload it + if agent_json_file: + url = f"{base_api_url}/File/{agent_json_file.id}" + response = client.put(url, files=files_data, headers=headers) + else: + url = f"{base_api_url}/File" + response = client.post(url, files=files_data, headers=headers) + + response.raise_for_status() + console.success(f"Updated {click.style('agent.json', fg='cyan')}") + + # Mark agent.json as processed to prevent deletion + if processed_files is not None: + processed_files.add("agent.json") + + +def create_project_folder( + project_id: str, + folder_name: str, + base_url: str, + token: str, + client: httpx.Client, +) -> ProjectFolder: + """Create a new folder in the project. + + Args: + project_id: The ID of the project + folder_name: Name of the folder to create + base_url: The base URL for the API + token: Authentication token + client: HTTP client to use for requests + + Returns: + ProjectFolder: The created folder object + + Raises: + httpx.HTTPError: If the API request fails + """ + url = get_org_scoped_url(base_url) + headers = {"Authorization": f"Bearer {token}"} + url = f"{url}/studio_/backend/api/Project/{project_id}/FileOperations/Folder" + + data = {"name": folder_name} + response = client.post(url, json=data, headers=headers) + response.raise_for_status() + return ProjectFolder(name="source_code", id=response.content.decode("utf-8")) + + +def upload_source_files_to_project( + project_id: str, + config_data: dict[Any, str], + directory: str, + base_url: str, + token: str, + include_uv_lock: bool = True, +) -> None: + """Upload source files to UiPath project. + + This function handles the pushing of local files to the remote project: + - Updates existing files that have changed + - Uploads new files that don't exist remotely + - Deletes remote files that no longer exist locally + - Optionally includes the UV lock file + """ + files = [ + file.file_path.replace("./", "", 1) + for file in files_to_include(config_data, directory) + ] + optional_files = ["pyproject.toml"] + + for file in optional_files: + file_path = os.path.join(directory, file) + if os.path.exists(file_path): + files.append(file) + if include_uv_lock: + files.append("uv.lock") + + url = get_org_scoped_url(base_url) + headers = {"Authorization": f"Bearer {token}"} + base_api_url = f"{url}/studio_/backend/api/Project/{project_id}/FileOperations" + + with httpx.Client(**get_httpx_client_kwargs()) as client: + # get existing project structure + try: + structure = get_project_structure(project_id, base_url, token, client) + source_code_folder = get_folder_by_name(structure, "source_code") + root_files, source_code_files = get_all_remote_files( + structure, source_code_folder + ) + except Exception as e: + console.error(f"Failed to get project structure: {str(e)}") + raise + + # keep track of processed files to identify which ones to delete later + processed_root_files: Set[str] = set() + processed_source_files: Set[str] = set() + + # Create source_code folder if it doesn't exist + if not source_code_folder: + try: + source_code_folder = create_project_folder( + project_id, "source_code", base_url, token, client + ) + console.success( + f"Created {click.style('source_code', fg='cyan')} folder" + ) + source_code_files = {} # Initialize empty dict for new folder + except httpx.HTTPStatusError as http_err: + if http_err.response.status_code == 423: + console.error( + "Resource is locked. Unable to create 'source_code' folder." + ) + raise + + except Exception as e: + console.error(f"Failed to create 'source_code' folder: {str(e)}") + raise + + # Update agent.json first at root level + try: + update_agent_json( + project_id, + base_url, + token, + directory, + client, + processed_root_files, + root_files.get("agent.json", None), + ) + except Exception as e: + console.error(f"Failed to update agent.json: {str(e)}") + raise + + # Continue with rest of files in source_code folder + for file_path in files: + try: + abs_path = os.path.abspath(os.path.join(directory, file_path)) + if not os.path.exists(abs_path): + console.warning( + f"File not found: {click.style(abs_path, fg='cyan')}" + ) + continue + + file_name = os.path.basename(file_path) + + # Skip agent.json as it's already handled + if file_name == "agent.json": + continue + + remote_file = source_code_files.get(file_name) + processed_source_files.add(file_name) + + with open(abs_path, "rb") as f: + files_data = {"file": (file_name, f, "application/octet-stream")} + form_data = {"parentId": source_code_folder.id} + + if remote_file: + # File exists in source_code folder, use PUT to update + url = f"{base_api_url}/File/{remote_file.id}" + response = client.put(url, files=files_data, headers=headers) + action = "Updated" + else: + # File doesn't exist, use POST to create in source_code folder + url = f"{base_api_url}/File" + response = client.post( + url, files=files_data, data=form_data, headers=headers + ) + action = "Uploaded" + + response.raise_for_status() + console.success(f"{action} {click.style(file_path, fg='cyan')}") + + except Exception as e: + console.error( + f"Failed to upload {click.style(file_path, fg='cyan')}: {str(e)}" + ) + raise + + # Delete files that no longer exist locally + if source_code_files: + for file_name, remote_file in source_code_files.items(): + if file_name not in processed_source_files: + try: + delete_remote_file( + project_id, remote_file.id, base_url, token, client + ) + console.success( + f"Deleted remote file {click.style(file_name, fg='cyan')}" + ) + except Exception as e: + console.error( + f"Failed to delete remote file {click.style(file_name, fg='cyan')}: {str(e)}" + ) + raise + + +@click.command() +@click.argument("root", type=str, default="./") +@click.option( + "--nolock", + is_flag=True, + help="Skip running uv lock and exclude uv.lock from the package", +) +@track +def push(root: str, nolock: bool) -> None: + """Push local project files to Studio Web Project. + + This command pushes the local project files to a UiPath Studio Web project. + It ensures that the remote project structure matches the local files by: + - Updating existing files that have changed + - Uploading new files + - Deleting remote files that no longer exist locally + - Optionally managing the UV lock file + + Args: + root: The root directory of the project + nolock: Whether to skip UV lock operations and exclude uv.lock from push + + Environment Variables: + UIPATH_PROJECT_ID: Required. The ID of the UiPath Cloud project + + Example: + $ uipath push + $ uipath push --nolock + """ + ensure_config_file(root) + config = get_project_config(root) + validate_config(config) + + if not os.getenv("UIPATH_PROJECT_ID", False): + console.error("UIPATH_PROJECT_ID environment variable not found.") + [base_url, token] = get_env_vars() + + with console.spinner("Pushing coded UiPath project to Studio Web..."): + try: + # Handle uv operations before packaging, unless nolock is specified + if not nolock: + handle_uv_operations(root) + + upload_source_files_to_project( + os.getenv("UIPATH_PROJECT_ID"), + config, + root, + base_url, + token, + include_uv_lock=not nolock, + ) + except Exception: + console.error("Failed to push UiPath project") diff --git a/tests/cli/mocks/pyproject.toml b/tests/cli/mocks/pyproject.toml index 0e85471f..2bb2ae40 100644 --- a/tests/cli/mocks/pyproject.toml +++ b/tests/cli/mocks/pyproject.toml @@ -1,5 +1,5 @@ [project] -name = "project name" +name = "project_name" version = "0.0.1" description = "project description" authors = [{ name = "John Doe", email = "john.doe@myemail.com" }] diff --git a/tests/cli/test_push.py b/tests/cli/test_push.py new file mode 100644 index 00000000..feab540d --- /dev/null +++ b/tests/cli/test_push.py @@ -0,0 +1,512 @@ +# type: ignore +import json +import os +import re +from typing import Any, Dict + +from click.testing import CliRunner +from httpx import Request +from pytest_httpx import HTTPXMock +from utils.project_details import ProjectDetails +from utils.uipath_json import UiPathJson + +from uipath._cli.cli_push import push + + +def extract_agent_json_file_from_request(request: Request) -> dict[Any, str]: + boundary = re.search(rb"--([a-f0-9]+)", request.content).group(1).decode() + parts = request.content.split(f"--{boundary}".encode()) + + # Locate the agent.json file + agent_json_part = None + for part in parts: + if ( + b'Content-Disposition: form-data; name="file"; filename="agent.json"' + in part + ): + agent_json_part = part + break + + assert agent_json_part is not None, ( + "agent.json part not found in the multipart/form-data payload." + ) + + # Extract the agent.json content + agent_json_content = ( + agent_json_part.split(b"\r\n\r\n", 1)[1].split(b"\r\n--")[0].decode() + ) + + # Parse the agent.json payload + agent_json_data = json.loads(agent_json_content) + + return agent_json_data + + +class TestPush: + """Test push command.""" + + def test_push_without_uipath_json( + self, runner: CliRunner, temp_dir: str, project_details: ProjectDetails + ) -> None: + """Test push when uipath.json is missing.""" + with runner.isolated_filesystem(temp_dir=temp_dir): + with open("pyproject.toml", "w") as f: + f.write(project_details.to_toml()) + result = runner.invoke(push, ["./"]) + assert result.exit_code == 1 + assert ( + "uipath.json not found. Please run `uipath init` in the project directory." + in result.output + ) + + def test_push_without_project_id( + self, + runner: CliRunner, + temp_dir: str, + project_details: ProjectDetails, + uipath_json: UiPathJson, + ) -> None: + """Test push when UIPATH_PROJECT_ID is missing.""" + with runner.isolated_filesystem(temp_dir=temp_dir): + with open("uipath.json", "w") as f: + f.write(uipath_json.to_json()) + with open("pyproject.toml", "w") as f: + f.write(project_details.to_toml()) + + result = runner.invoke(push, ["./"]) + assert result.exit_code == 1 + assert "UIPATH_PROJECT_ID environment variable not found." in result.output + + def test_successful_push( + self, + runner: CliRunner, + temp_dir: str, + project_details: ProjectDetails, + uipath_json: UiPathJson, + mock_env_vars: Dict[str, str], + httpx_mock: HTTPXMock, + ) -> None: + """Test successful project push with various file operations.""" + base_url = "https://cloud.uipath.com/organization" + project_id = "test-project-id" + + # Mock the project structure response + mock_structure = { + "id": "root", + "name": "root", + "folders": [ + { + "id": "414af585-7e88-4774-ad94-cf6bd48f6c2d", + "name": "source_code", + "files": [ + { + "id": "123", + "name": "main.py", + "isMain": True, + "fileType": "1", + "isEntryPoint": True, + "ignoredFromPublish": False, + }, + { + "id": "456", + "name": "pyproject.toml", + "isMain": False, + "fileType": "1", + "isEntryPoint": False, + "ignoredFromPublish": False, + }, + { + "id": "789", + "name": "uipath.json", + "isMain": False, + "fileType": "1", + "isEntryPoint": False, + "ignoredFromPublish": False, + }, + ], + } + ], + "files": [ + { + "id": "246", + "name": "agent.json", + "isMain": False, + "fileType": "1", + "isEntryPoint": False, + "ignoredFromPublish": False, + }, + ], + "folderType": "0", + } + + httpx_mock.add_response( + url=f"{base_url}/studio_/backend/api/Project/{project_id}/FileOperations/Structure", + json=mock_structure, + ) + + # Mock agent.json download + httpx_mock.add_response( + method="GET", + url=f"{base_url}/studio_/backend/api/Project/{project_id}/FileOperations/File/246", + status_code=200, + json={"metadata": {"codeVersion": "0.1.0"}}, + ) + + # Mock file upload responses + # For main.py + httpx_mock.add_response( + method="PUT", + url=f"{base_url}/studio_/backend/api/Project/{project_id}/FileOperations/File/123", + status_code=200, + ) + + # For pyproject.toml + httpx_mock.add_response( + method="PUT", + url=f"{base_url}/studio_/backend/api/Project/{project_id}/FileOperations/File/456", + status_code=200, + ) + + # For uipath.json + httpx_mock.add_response( + method="PUT", + url=f"{base_url}/studio_/backend/api/Project/{project_id}/FileOperations/File/789", + status_code=200, + ) + + # For uv.lock + httpx_mock.add_response( + method="POST", + url=f"{base_url}/studio_/backend/api/Project/{project_id}/FileOperations/File", + status_code=200, + ) + + # For agent.json + httpx_mock.add_response( + method="PUT", + url=f"{base_url}/studio_/backend/api/Project/{project_id}/FileOperations/File/246", + status_code=200, + ) + + with runner.isolated_filesystem(temp_dir=temp_dir): + # Create necessary files + with open("uipath.json", "w") as f: + f.write(uipath_json.to_json()) + + with open("pyproject.toml", "w") as f: + f.write(project_details.to_toml()) + + with open("main.py", "w") as f: + f.write("print('Hello World')") + + with open("uv.lock", "w") as f: + f.write('version = 1 \n requires-python = ">=3.11"') + + # Set environment variables + os.environ.update(mock_env_vars) + os.environ["UIPATH_PROJECT_ID"] = project_id + + # Run push + result = runner.invoke(push, ["./"]) + assert result.exit_code == 0 + assert "Updated main.py" in result.output + assert "Updated pyproject.toml" in result.output + assert "Updated uipath.json" in result.output + assert "Uploaded uv.lock" in result.output + + # check incremented code version + agent_upload_request = None + for request in httpx_mock.get_requests( + method="PUT", + url=f"{base_url}/studio_/backend/api/Project/{project_id}/FileOperations/File/246", + ): + agent_upload_request = request + break + + agent_json_content = extract_agent_json_file_from_request( + agent_upload_request + ) + + # Validate `metadata["codeVersion"]` + expected_code_version = "0.1.1" + actual_code_version = agent_json_content.get("metadata", {}).get( + "codeVersion" + ) + assert actual_code_version == expected_code_version, ( + f"Unexpected codeVersion in metadata. Expected: {expected_code_version}, Got: {actual_code_version}" + ) + + def test_successful_push_new_project( + self, + runner: CliRunner, + temp_dir: str, + project_details: ProjectDetails, + uipath_json: UiPathJson, + mock_env_vars: Dict[str, str], + httpx_mock: HTTPXMock, + ) -> None: + """Test successful project push with various file operations.""" + base_url = "https://cloud.uipath.com/organization" + project_id = "test-project-id" + + # Mock the project structure response + mock_structure = { + "id": "root", + "name": "root", + "folders": [], + "files": [], + "folderType": "0", + } + # Create source_code folder + httpx_mock.add_response( + method="POST", + url=f"{base_url}/studio_/backend/api/Project/{project_id}/FileOperations/Folder", + status_code=200, + ) + + httpx_mock.add_response( + url=f"{base_url}/studio_/backend/api/Project/{project_id}/FileOperations/Structure", + json=mock_structure, + ) + + # Mock file upload responses + # For main.py + httpx_mock.add_response( + method="POST", + url=f"{base_url}/studio_/backend/api/Project/{project_id}/FileOperations/File", + status_code=200, + ) + + # For pyproject.toml + httpx_mock.add_response( + method="POST", + url=f"{base_url}/studio_/backend/api/Project/{project_id}/FileOperations/File", + status_code=200, + ) + + # For uipath.json + httpx_mock.add_response( + method="POST", + url=f"{base_url}/studio_/backend/api/Project/{project_id}/FileOperations/File", + status_code=200, + ) + + # For uv.lock + httpx_mock.add_response( + method="POST", + url=f"{base_url}/studio_/backend/api/Project/{project_id}/FileOperations/File", + status_code=200, + ) + + # For agent.json + httpx_mock.add_response( + method="POST", + url=f"{base_url}/studio_/backend/api/Project/{project_id}/FileOperations/File", + status_code=200, + ) + + with runner.isolated_filesystem(temp_dir=temp_dir): + # Create necessary files + with open("uipath.json", "w") as f: + f.write(uipath_json.to_json()) + + with open("pyproject.toml", "w") as f: + f.write(project_details.to_toml()) + + with open("main.py", "w") as f: + f.write("print('Hello World')") + + with open("uv.lock", "w") as f: + f.write('version = 1 \n requires-python = ">=3.11"') + + # Set environment variables + os.environ.update(mock_env_vars) + os.environ["UIPATH_PROJECT_ID"] = project_id + + # Run push + result = runner.invoke(push, ["./"]) + print(result.output) + assert result.exit_code == 0 + assert "Uploaded main.py" in result.output + assert "Uploaded pyproject.toml" in result.output + assert "Uploaded uipath.json" in result.output + assert "Uploaded uv.lock" in result.output + + # check expected agent.json fields + agent_upload_request = None + for request in httpx_mock.get_requests( + method="POST", + url=f"{base_url}/studio_/backend/api/Project/{project_id}/FileOperations/File", + ): + if ( + b'Content-Disposition: form-data; name="file"; filename="agent.json"' + in request.content + ): + agent_upload_request = request + break + + agent_json_content = extract_agent_json_file_from_request( + agent_upload_request + ) + + expected_code_version = "1.0.0" + actual_code_version = agent_json_content.get("metadata", {}).get( + "codeVersion" + ) + assert actual_code_version == expected_code_version, ( + f"Unexpected codeVersion in metadata. Expected: {expected_code_version}, Got: {actual_code_version}" + ) + assert "targetRuntime" in agent_json_content["metadata"] + assert agent_json_content["metadata"]["targetRuntime"] == "python" + assert "entryPoints" in agent_json_content + assert len(agent_json_content["entryPoints"]) == 2 + assert ( + agent_json_content["entryPoints"][0]["input"]["type"] + == uipath_json.entry_points[0].input.type + ) + assert ( + "agent_1_output" + in agent_json_content["entryPoints"][0]["output"]["properties"] + ) + + def test_push_with_api_error( + self, + runner: CliRunner, + temp_dir: str, + project_details: ProjectDetails, + uipath_json: UiPathJson, + mock_env_vars: Dict[str, str], + httpx_mock: HTTPXMock, + ) -> None: + """Test push when API request fails.""" + base_url = "https://cloud.uipath.com/organization" # Strip tenant part + project_id = "test-project-id" + + # Mock API error response + httpx_mock.add_response( + url=f"{base_url}/studio_/backend/api/Project/{project_id}/FileOperations/Structure", + status_code=401, + json={"message": "Unauthorized"}, + ) + + with runner.isolated_filesystem(temp_dir=temp_dir): + # Create necessary files + with open("uipath.json", "w") as f: + f.write(uipath_json.to_json()) + with open("pyproject.toml", "w") as f: + f.write(project_details.to_toml()) + with open("uv.lock", "w") as f: + f.write("") + + # Set environment variables + os.environ.update(mock_env_vars) + os.environ["UIPATH_PROJECT_ID"] = project_id + + result = runner.invoke(push, ["./"]) + assert result.exit_code == 1 + assert "Failed to get project structure" in result.output + + def test_push_with_nolock_flag( + self, + runner: CliRunner, + temp_dir: str, + project_details: ProjectDetails, + uipath_json: UiPathJson, + mock_env_vars: Dict[str, str], + httpx_mock: HTTPXMock, + ) -> None: + """Test push command with --nolock flag.""" + base_url = "https://cloud.uipath.com/organization" + project_id = "test-project-id" + + # Mock the project structure response + mock_structure = { + "id": "root", + "name": "root", + "folders": [ + { + "id": "123", + "name": "source_code", + "files": [ + { + "id": "123", + "name": "main.py", + "isMain": True, + "fileType": "1", + "isEntryPoint": True, + "ignoredFromPublish": False, + }, + { + "id": "789", + "name": "uipath.json", + "isMain": False, + "fileType": "1", + "isEntryPoint": False, + "ignoredFromPublish": False, + }, + ], + } + ], + "files": [], + "folderType": "0", + } + + httpx_mock.add_response( + url=f"{base_url}/studio_/backend/api/Project/{project_id}/FileOperations/Structure", + json=mock_structure, + ) + + # Mock file upload responses + # For main.py + httpx_mock.add_response( + method="PUT", + url=f"{base_url}/studio_/backend/api/Project/{project_id}/FileOperations/File/123", + status_code=200, + ) + + # For pyproject.toml + httpx_mock.add_response( + method="POST", + url=f"{base_url}/studio_/backend/api/Project/{project_id}/FileOperations/File", + status_code=200, + ) + + # For agent.json + httpx_mock.add_response( + method="POST", + url=f"{base_url}/studio_/backend/api/Project/{project_id}/FileOperations/File", + status_code=200, + ) + + # For uipath.json + httpx_mock.add_response( + method="PUT", + url=f"{base_url}/studio_/backend/api/Project/{project_id}/FileOperations/File/789", + status_code=200, + ) + + with runner.isolated_filesystem(temp_dir=temp_dir): + # Create necessary files + with open("uipath.json", "w") as f: + f.write(uipath_json.to_json()) + + with open("pyproject.toml", "w") as f: + f.write(project_details.to_toml()) + + with open("main.py", "w") as f: + f.write("print('Hello World')") + + with open("uv.lock", "w") as f: + f.write("") + # Set environment variables + os.environ.update(mock_env_vars) + os.environ["UIPATH_PROJECT_ID"] = project_id + + # Run push with --nolock flag + result = runner.invoke(push, ["./", "--nolock"]) + assert result.exit_code == 0 + assert "Updated main.py" in result.output + assert "Uploaded pyproject.toml" in result.output + assert "Updated uipath.json" in result.output + assert "Uploaded agent.json" not in result.output + assert "uv.lock" not in result.output From 736cc029cdd6805351b91501d464d85f62ef4bff Mon Sep 17 00:00:00 2001 From: radu-mocanu Date: Tue, 8 Jul 2025 14:39:19 +0300 Subject: [PATCH 12/18] feat: add SW scope --- src/uipath/_cli/_auth/auth_config.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/uipath/_cli/_auth/auth_config.json b/src/uipath/_cli/_auth/auth_config.json index d4852569..17d2b359 100644 --- a/src/uipath/_cli/_auth/auth_config.json +++ b/src/uipath/_cli/_auth/auth_config.json @@ -1,6 +1,6 @@ { "client_id": "36dea5b8-e8bb-423d-8e7b-c808df8f1c00", "redirect_uri": "http://localhost:__PY_REPLACE_PORT__/oidc/login", - "scope": "offline_access OrchestratorApiUserAccess IdentityServerApi ConnectionService DataService DocumentUnderstanding EnterpriseContextService Directory JamJamApi LLMGateway LLMOps OMS RCS.FolderAuthorization TM.Projects TM.TestCases TM.Requirements TM.TestSets", + "scope": "offline_access StudioWebBackend OrchestratorApiUserAccess IdentityServerApi ConnectionService DataService DocumentUnderstanding EnterpriseContextService Directory JamJamApi LLMGateway LLMOps OMS RCS.FolderAuthorization TM.Projects TM.TestCases TM.Requirements TM.TestSets", "port": 8104 -} \ No newline at end of file +} From e888bc7bdeb72284f94b62fe721f8a66f99f3d0f Mon Sep 17 00:00:00 2001 From: radu-mocanu Date: Mon, 14 Jul 2025 15:38:59 +0300 Subject: [PATCH 13/18] chore: run CI against release branches --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 10192fb7..2e7d0003 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,6 +9,7 @@ on: pull_request: branches: - main + - release/* jobs: commit-lint: From b8b154ec907baf41cdc6239730289fcd9c7e4f00 Mon Sep 17 00:00:00 2001 From: radu-mocanu Date: Mon, 7 Jul 2025 20:59:55 +0300 Subject: [PATCH 14/18] feat: cli add pull command --- src/uipath/_cli/__init__.py | 2 + src/uipath/_cli/_utils/_common.py | 21 +- src/uipath/_cli/_utils/_studio_project.py | 62 +++- src/uipath/_cli/cli_pull.py | 213 ++++++++++++ src/uipath/_cli/cli_push.py | 122 +++---- tests/cli/test_pull.py | 399 ++++++++++++++++++++++ tests/cli/test_push.py | 19 +- tests/cli/utils/common.py | 7 + 8 files changed, 761 insertions(+), 84 deletions(-) create mode 100644 src/uipath/_cli/cli_pull.py create mode 100644 tests/cli/test_pull.py create mode 100644 tests/cli/utils/common.py diff --git a/src/uipath/_cli/__init__.py b/src/uipath/_cli/__init__.py index ef13e365..7c24e1d6 100644 --- a/src/uipath/_cli/__init__.py +++ b/src/uipath/_cli/__init__.py @@ -10,6 +10,7 @@ from .cli_new import new as new # type: ignore from .cli_pack import pack as pack # type: ignore from .cli_publish import publish as publish # type: ignore +from .cli_pull import pull as pull # type: ignore from .cli_push import push as push # type: ignore from .cli_run import run as run # type: ignore @@ -65,3 +66,4 @@ def cli(lv: bool, v: bool) -> None: cli.add_command(auth) cli.add_command(invoke) cli.add_command(push) +cli.add_command(pull) diff --git a/src/uipath/_cli/_utils/_common.py b/src/uipath/_cli/_utils/_common.py index d423c200..75353f0e 100644 --- a/src/uipath/_cli/_utils/_common.py +++ b/src/uipath/_cli/_utils/_common.py @@ -1,5 +1,6 @@ import os from typing import Optional +from urllib.parse import urlparse import click @@ -29,7 +30,7 @@ def environment_options(function): return function -def get_env_vars(spinner: Optional[Spinner] = None) -> list[str | None]: +def get_env_vars(spinner: Optional[Spinner] = None) -> list[str]: base_url = os.environ.get("UIPATH_URL") token = os.environ.get("UIPATH_ACCESS_TOKEN") @@ -42,7 +43,8 @@ def get_env_vars(spinner: Optional[Spinner] = None) -> list[str | None]: click.echo("UIPATH_URL, UIPATH_ACCESS_TOKEN") click.get_current_context().exit(1) - return [base_url, token] + # at this step we know for sure that both base_url and token exist. type checking can be disabled + return [base_url, token] # type: ignore def serialize_object(obj): @@ -69,3 +71,18 @@ def serialize_object(obj): # Return primitive types as is else: return obj + + +def get_org_scoped_url(base_url: str) -> str: + """Get organization scoped URL from base URL. + + Args: + base_url: The base URL to scope + + Returns: + str: The organization scoped URL + """ + parsed = urlparse(base_url) + org_name, *_ = parsed.path.strip("/").split("/") + org_scoped_url = f"{parsed.scheme}://{parsed.netloc}/{org_name}" + return org_scoped_url diff --git a/src/uipath/_cli/_utils/_studio_project.py b/src/uipath/_cli/_utils/_studio_project.py index c277d1cf..f68c3088 100644 --- a/src/uipath/_cli/_utils/_studio_project.py +++ b/src/uipath/_cli/_utils/_studio_project.py @@ -1,5 +1,6 @@ -from typing import List, Optional, Union +from typing import Any, List, Optional, Union +import httpx from pydantic import BaseModel, ConfigDict, Field, field_validator @@ -136,3 +137,62 @@ def convert_folder_type(cls, v: Union[str, int, None]) -> Optional[str]: if isinstance(v, int): return str(v) return v + + +def get_project_structure( + project_id: str, + base_url: str, + token: str, + client: httpx.Client, +) -> ProjectStructure: + """Retrieve the project's file structure from UiPath Cloud. + + Makes an API call to fetch the complete file structure of a project, + including all files and folders. The response is validated against + the ProjectStructure model. + + Args: + project_id: The ID of the project + base_url: The base URL for the API + token: Authentication token + client: HTTP client to use for requests + + Returns: + ProjectStructure: The complete project structure + + Raises: + httpx.HTTPError: If the API request fails + """ + headers = {"Authorization": f"Bearer {token}"} + url = ( + f"{base_url}/studio_/backend/api/Project/{project_id}/FileOperations/Structure" + ) + + response = client.get(url, headers=headers) + response.raise_for_status() + return ProjectStructure.model_validate(response.json()) + + +def download_file(base_url: str, file_id: str, client: httpx.Client, token: str) -> Any: + file_url = f"{base_url}/File/{file_id}" + response = client.get(file_url, headers={"Authorization": f"Bearer {token}"}) + response.raise_for_status() + return response + + +def get_folder_by_name( + structure: ProjectStructure, folder_name: str +) -> Optional[ProjectFolder]: + """Get a folder from the project structure by name. + + Args: + structure: The project structure + folder_name: Name of the folder to find + + Returns: + Optional[ProjectFolder]: The found folder or None + """ + for folder in structure.folders: + if folder.name == folder_name: + return folder + return None diff --git a/src/uipath/_cli/cli_pull.py b/src/uipath/_cli/cli_pull.py new file mode 100644 index 00000000..ebd4b07b --- /dev/null +++ b/src/uipath/_cli/cli_pull.py @@ -0,0 +1,213 @@ +# type: ignore +"""CLI command for pulling remote project files from UiPath StudioWeb solution. + +This module provides functionality to pull remote project files from a UiPath StudioWeb solution. +It handles: +- File downloads from source_code and evals folders +- Maintaining folder structure locally +- File comparison using hashes +- Interactive confirmation for overwriting files +""" + +# type: ignore +import hashlib +import json +import os +from typing import Dict, Set + +import click +import httpx +from dotenv import load_dotenv + +from .._utils._ssl_context import get_httpx_client_kwargs +from ..telemetry import track +from ._utils._common import get_env_vars, get_org_scoped_url +from ._utils._console import ConsoleLogger +from ._utils._studio_project import ( + ProjectFile, + ProjectFolder, + download_file, + get_folder_by_name, + get_project_structure, +) + +console = ConsoleLogger() +load_dotenv(override=True) + + +def compute_normalized_hash(content: str) -> str: + """Compute hash of normalized content. + + Args: + content: Content to hash + + Returns: + str: SHA256 hash of the normalized content + """ + try: + # Try to parse as JSON to handle formatting + json_content = json.loads(content) + normalized = json.dumps(json_content, indent=2) + except json.JSONDecodeError: + # Not JSON, normalize line endings + normalized = content.replace("\r\n", "\n").replace("\r", "\n") + + return hashlib.sha256(normalized.encode("utf-8")).hexdigest() + + +def collect_files_from_folder( + folder: ProjectFolder, base_path: str, files_dict: Dict[str, ProjectFile] +) -> None: + """Recursively collect all files from a folder and its subfolders. + + Args: + folder: The folder to collect files from + base_path: Base path for file paths + files_dict: Dictionary to store collected files + """ + # Add files from current folder + for file in folder.files: + file_path = os.path.join(base_path, file.name) + files_dict[file_path] = file + + # Recursively process subfolders + for subfolder in folder.folders: + subfolder_path = os.path.join(base_path, subfolder.name) + collect_files_from_folder(subfolder, subfolder_path, files_dict) + + +def download_folder_files( + folder: ProjectFolder, + base_path: str, + base_url: str, + token: str, + client: httpx.Client, + processed_files: Set[str], +) -> None: + """Download files from a folder recursively. + + Args: + folder: The folder to download files from + base_path: Base path for local file storage + base_url: Base URL for API requests + token: Authentication token + client: HTTP client + processed_files: Set to track processed files + """ + files_dict: Dict[str, ProjectFile] = {} + collect_files_from_folder(folder, "", files_dict) + + for file_path, remote_file in files_dict.items(): + local_path = os.path.join(base_path, file_path) + local_dir = os.path.dirname(local_path) + + # Create directory if it doesn't exist + if not os.path.exists(local_dir): + os.makedirs(local_dir) + + # Download remote file + response = download_file(base_url, remote_file.id, client, token) + remote_content = response.read().decode("utf-8") + remote_hash = compute_normalized_hash(remote_content) + + if os.path.exists(local_path): + # Read and hash local file + with open(local_path, "r", encoding="utf-8") as f: + local_content = f.read() + local_hash = compute_normalized_hash(local_content) + + # Compare hashes + if local_hash != remote_hash: + styled_path = click.style(str(file_path), fg="cyan") + console.warning(f"File {styled_path}" + " differs from remote version.") + response = click.prompt("Do you want to override it? (y/n)", type=str) + if response.lower() == "y": + with open(local_path, "w", encoding="utf-8", newline="\n") as f: + f.write(remote_content) + console.success(f"Updated {click.style(str(file_path), fg='cyan')}") + else: + console.info(f"Skipped {click.style(str(file_path), fg='cyan')}") + else: + console.info( + f"File {click.style(str(file_path), fg='cyan')} is up to date" + ) + else: + # File doesn't exist locally, create it + with open(local_path, "w", encoding="utf-8", newline="\n") as f: + f.write(remote_content) + console.success(f"Downloaded {click.style(str(file_path), fg='cyan')}") + + processed_files.add(file_path) + + +@click.command() +@click.argument("root", type=str, default="./") +@track +def pull(root: str) -> None: + """Pull remote project files from Studio Web Project. + + This command pulls the remote project files from a UiPath Studio Web project. + It downloads files from the source_code and evals folders, maintaining the + folder structure locally. Files are compared using hashes before overwriting, + and user confirmation is required for differing files. + + Args: + root: The root directory to pull files into + + Environment Variables: + UIPATH_PROJECT_ID: Required. The ID of the UiPath Studio Web project + + Example: + $ uipath pull + $ uipath pull /path/to/project + """ + if not os.getenv("UIPATH_PROJECT_ID", False): + console.error("UIPATH_PROJECT_ID environment variable not found.") + + [base_url, token] = get_env_vars() + base_api_url = f"{get_org_scoped_url(base_url)}/studio_/backend/api/Project/{os.getenv('UIPATH_PROJECT_ID')}/FileOperations" + + with console.spinner("Pulling UiPath project files..."): + try: + with httpx.Client(**get_httpx_client_kwargs()) as client: + # Get project structure + structure = get_project_structure( + os.getenv("UIPATH_PROJECT_ID"), # type: ignore + get_org_scoped_url(base_url), + token, + client, + ) + + processed_files: Set[str] = set() + + # Process source_code folder + source_code_folder = get_folder_by_name(structure, "source_code") + if source_code_folder: + download_folder_files( + source_code_folder, + root, + base_api_url, + token, + client, + processed_files, + ) + else: + console.warning("No source_code folder found in remote project") + + # Process evals folder + evals_folder = get_folder_by_name(structure, "evals") + if evals_folder: + evals_path = os.path.join(root, "evals") + download_folder_files( + evals_folder, + evals_path, + base_api_url, + token, + client, + processed_files, + ) + else: + console.warning("No evals folder found in remote project") + + except Exception as e: + console.error(f"Failed to pull UiPath project: {str(e)}") diff --git a/src/uipath/_cli/cli_push.py b/src/uipath/_cli/cli_push.py index 84c3afe6..31c30c4d 100644 --- a/src/uipath/_cli/cli_push.py +++ b/src/uipath/_cli/cli_push.py @@ -18,8 +18,8 @@ import json import os from datetime import datetime, timezone +from pathlib import Path from typing import Any, Dict, Optional, Set, Tuple -from urllib.parse import urlparse import click import httpx @@ -27,7 +27,7 @@ from .._utils._ssl_context import get_httpx_client_kwargs from ..telemetry import track -from ._utils._common import get_env_vars +from ._utils._common import get_env_vars, get_org_scoped_url from ._utils._console import ConsoleLogger from ._utils._constants import ( AGENT_INITIAL_CODE_VERSION, @@ -42,55 +42,20 @@ read_toml_project, validate_config, ) -from ._utils._studio_project import ProjectFile, ProjectFolder, ProjectStructure +from ._utils._studio_project import ( + ProjectFile, + ProjectFolder, + ProjectStructure, + download_file, + get_folder_by_name, + get_project_structure, +) from ._utils._uv_helpers import handle_uv_operations console = ConsoleLogger() load_dotenv(override=True) -def get_org_scoped_url(base_url: str) -> str: - parsed = urlparse(base_url) - org_name, *_ = parsed.path.strip("/").split("/") - - # Construct the new scoped URL (scheme + domain + org_name) - org_scoped_url = f"{parsed.scheme}://{parsed.netloc}/{org_name}" - return org_scoped_url - - -def get_project_structure( - project_id: str, - base_url: str, - token: str, - client: httpx.Client, -) -> ProjectStructure: - """Retrieve the project's file structure from UiPath Cloud. - - Makes an API call to fetch the complete file structure of a project, - including all files and folders. The response is validated against - the ProjectStructure model. - - Args: - project_id: The ID of the project - base_url: The base URL for the API - token: Authentication token - client: HTTP client to use for requests - - Returns: - ProjectStructure: The complete project structure - - Raises: - httpx.HTTPError: If the API request fails - """ - url = get_org_scoped_url(base_url) - headers = {"Authorization": f"Bearer {token}"} - url = f"{url}/studio_/backend/api/Project/{project_id}/FileOperations/Structure" - - response = client.get(url, headers=headers) - response.raise_for_status() - return ProjectStructure.model_validate(response.json()) - - def collect_all_files( folder: ProjectFolder, files_dict: Dict[str, ProjectFile] ) -> None: @@ -108,24 +73,6 @@ def collect_all_files( collect_all_files(subfolder, files_dict) -def get_folder_by_name( - structure: ProjectStructure, folder_name: str -) -> Optional[ProjectFolder]: - """Get a folder from the project structure by name. - - Args: - structure: The project structure - folder_name: Name of the folder to find - - Returns: - Optional[ProjectFolder]: The found folder or None - """ - for folder in structure.folders: - if folder.name == folder_name: - return folder - return None - - def get_all_remote_files( structure: ProjectStructure, source_code_folder: Optional[ProjectFolder] = None ) -> Tuple[Dict[str, ProjectFile], Dict[str, ProjectFile]]: @@ -252,15 +199,19 @@ def update_agent_json( base_api_url = f"{url}/studio_/backend/api/Project/{project_id}/FileOperations" if agent_json_file: - # Download existing agent.json - file_url = f"{base_api_url}/File/{agent_json_file.id}" - response = client.get(file_url, headers=headers) - response.raise_for_status() - try: - existing_agent = response.json() + # Download existing agent.json + + existing_agent_json = ( + download_file( + base_url=base_api_url, + file_id=agent_json_file.id, + client=client, + token=token, + ) + ).json() # Get current version and increment patch version - version_parts = existing_agent["metadata"]["codeVersion"].split(".") + version_parts = existing_agent_json["metadata"]["codeVersion"].split(".") if len(version_parts) >= 3: version_parts[-1] = str(int(version_parts[-1]) + 1) agent_json["metadata"]["codeVersion"] = ".".join(version_parts) @@ -325,6 +276,22 @@ def create_project_folder( return ProjectFolder(name="source_code", id=response.content.decode("utf-8")) +def is_evals_path(file_path: str) -> bool: + """Check if a file path is within the evals directory. + + Args: + file_path: The file path to check + + Returns: + bool: True if the path is within evals directory, False otherwise + """ + path = Path(file_path) + try: + return "evals" in path.parts + except Exception: + return False + + def upload_source_files_to_project( project_id: str, config_data: dict[Any, str], @@ -340,10 +307,12 @@ def upload_source_files_to_project( - Uploads new files that don't exist remotely - Deletes remote files that no longer exist locally - Optionally includes the UV lock file + - Ignores files in the evals folder """ files = [ file.file_path.replace("./", "", 1) for file in files_to_include(config_data, directory) + if not is_evals_path(file.file_path) ] optional_files = ["pyproject.toml"] @@ -361,7 +330,9 @@ def upload_source_files_to_project( with httpx.Client(**get_httpx_client_kwargs()) as client: # get existing project structure try: - structure = get_project_structure(project_id, base_url, token, client) + structure = get_project_structure( + project_id, get_org_scoped_url(base_url), token, client + ) source_code_folder = get_folder_by_name(structure, "source_code") root_files, source_code_files = get_all_remote_files( structure, source_code_folder @@ -390,7 +361,6 @@ def upload_source_files_to_project( "Resource is locked. Unable to create 'source_code' folder." ) raise - except Exception as e: console.error(f"Failed to create 'source_code' folder: {str(e)}") raise @@ -502,13 +472,13 @@ def push(root: str, nolock: bool) -> None: $ uipath push $ uipath push --nolock """ - ensure_config_file(root) - config = get_project_config(root) - validate_config(config) - if not os.getenv("UIPATH_PROJECT_ID", False): console.error("UIPATH_PROJECT_ID environment variable not found.") + [base_url, token] = get_env_vars() + ensure_config_file(root) + config = get_project_config(root) + validate_config(config) with console.spinner("Pushing coded UiPath project to Studio Web..."): try: diff --git a/tests/cli/test_pull.py b/tests/cli/test_pull.py new file mode 100644 index 00000000..e943c93b --- /dev/null +++ b/tests/cli/test_pull.py @@ -0,0 +1,399 @@ +# type: ignore +import json +import os +from typing import Any, Dict + +from click.testing import CliRunner +from pytest_httpx import HTTPXMock +from utils.project_details import ProjectDetails +from utils.uipath_json import UiPathJson + +from tests.cli.utils.common import configure_env_vars +from uipath._cli.cli_pull import pull + + +class TestPull: + """Test pull command.""" + + def test_pull_without_project_id( + self, + runner: CliRunner, + temp_dir: str, + project_details: ProjectDetails, + uipath_json: UiPathJson, + ) -> None: + """Test pull when UIPATH_PROJECT_ID is missing.""" + with runner.isolated_filesystem(temp_dir=temp_dir): + result = runner.invoke(pull, ["./"]) + assert result.exit_code == 1 + assert "UIPATH_PROJECT_ID environment variable not found." in result.output + + def test_successful_pull( + self, + runner: CliRunner, + temp_dir: str, + project_details: ProjectDetails, + uipath_json: UiPathJson, + mock_env_vars: Dict[str, str], + httpx_mock: HTTPXMock, + ) -> None: + """Test successful project pull with various file operations.""" + base_url = "https://cloud.uipath.com/organization" + project_id = "test-project-id" + + # Mock the project structure response + mock_structure = { + "id": "root", + "name": "root", + "folders": [ + { + "id": "414af585-7e88-4774-ad94-cf6bd48f6c2d", + "name": "source_code", + "files": [ + { + "id": "123", + "name": "main.py", + "isMain": True, + "fileType": "1", + "isEntryPoint": True, + "ignoredFromPublish": False, + }, + { + "id": "456", + "name": "pyproject.toml", + "isMain": False, + "fileType": "1", + "isEntryPoint": False, + "ignoredFromPublish": False, + }, + { + "id": "789", + "name": "uipath.json", + "isMain": False, + "fileType": "1", + "isEntryPoint": False, + "ignoredFromPublish": False, + }, + ], + }, + { + "id": "evals-folder-id", + "name": "evals", + "folders": [ + { + "id": "eval-sets-id", + "name": "eval-sets", + "files": [ + { + "id": "eval-sets-file-id", + "name": "test-set.json", + "isMain": False, + "fileType": "1", + "isEntryPoint": False, + "ignoredFromPublish": False, + } + ], + "folders": [], + }, + { + "id": "evaluators-id", + "name": "evaluators", + "files": [ + { + "id": "evaluators-file-id", + "name": "test-evaluator.json", + "isMain": False, + "fileType": "1", + "isEntryPoint": False, + "ignoredFromPublish": False, + } + ], + "folders": [], + }, + ], + "files": [], + }, + ], + "files": [], + "folderType": "0", + } + + httpx_mock.add_response( + url=f"{base_url}/studio_/backend/api/Project/{project_id}/FileOperations/Structure", + json=mock_structure, + ) + + # Mock file download responses + # For main.py + httpx_mock.add_response( + method="GET", + url=f"{base_url}/studio_/backend/api/Project/{project_id}/FileOperations/File/123", + content=b"print('Hello World')", + ) + + # For pyproject.toml + httpx_mock.add_response( + method="GET", + url=f"{base_url}/studio_/backend/api/Project/{project_id}/FileOperations/File/456", + content=project_details.to_toml().encode(), + ) + + # For uipath.json + httpx_mock.add_response( + method="GET", + url=f"{base_url}/studio_/backend/api/Project/{project_id}/FileOperations/File/789", + content=uipath_json.to_json().encode(), + ) + + # For eval-sets/test-set.json + test_set_content = { + "id": "02424d08-f482-4777-ac4d-233add24ee06", + "fileName": "evaluation-set-1752568767335.json", + "evaluatorRefs": [ + "429d73a2-a748-4554-83d7-e32dec345931", + "bdb9f7c9-2d9e-4595-81c8-ef2a60216cb9", + ], + "evaluations": [], + "name": "Evaluation Set 2", + "batchSize": 10, + "timeoutMinutes": 20, + "modelSettings": [], + "createdAt": "2025-07-15T08:39:27.335Z", + "updatedAt": "2025-07-15T08:39:27.335Z", + } + httpx_mock.add_response( + method="GET", + url=f"{base_url}/studio_/backend/api/Project/{project_id}/FileOperations/File/eval-sets-file-id", + content=json.dumps(test_set_content, indent=2).encode(), + ) + + # For evaluators/test-evaluator.json + test_evaluator_content = { + "fileName": "evaluator-1752568815245.json", + "id": "52b716b1-c8ef-4da8-af31-fb218d0d5499", + "name": "Evaluator 3", + "description": "An evaluator that judges the agent based on its run history and expected behavior", + "type": 7, + "category": 3, + "prompt": "As an expert evaluator, determine how well the agent did on a scale of 0-100. Focus on if the simulation was successful and if the agent behaved according to the expected output accounting for alternative valid expressions, and reasonable variations in language while maintaining high standards for accuracy and completeness. Provide your score with a justification, explaining briefly and concisely why you gave that score.\n----\nUserOrSyntheticInputGivenToAgent:\n{{UserOrSyntheticInput}}\n----\nSimulationInstructions:\n{{SimulationInstructions}}\n----\nExpectedAgentBehavior:\n{{ExpectedAgentBehavior}}\n----\nAgentRunHistory:\n{{AgentRunHistory}}\n", + "targetOutputKey": "*", + "createdAt": "2025-07-15T08:40:15.245Z", + "updatedAt": "2025-07-15T08:40:15.245Z", + } + + httpx_mock.add_response( + method="GET", + url=f"{base_url}/studio_/backend/api/Project/{project_id}/FileOperations/File/evaluators-file-id", + content=json.dumps(test_evaluator_content, indent=2).encode(), + ) + + with runner.isolated_filesystem(temp_dir=temp_dir): + # Set environment variables + configure_env_vars(mock_env_vars) + os.environ["UIPATH_PROJECT_ID"] = project_id + + # Run pull + result = runner.invoke(pull, ["./"]) + assert result.exit_code == 0 + + # Verify source code files + assert "Downloaded main.py" in result.output + assert "Downloaded pyproject.toml" in result.output + assert "Downloaded uipath.json" in result.output + + # Verify source code file contents + with open("main.py", "r") as f: + assert f.read() == "print('Hello World')" + with open("pyproject.toml", "r") as f: + assert f.read() == project_details.to_toml() + with open("uipath.json", "r") as f: + assert f.read() == uipath_json.to_json() + + # Verify evals folder structure exists + assert os.path.isdir("evals") + assert os.path.isdir("evals/eval-sets") + assert os.path.isdir("evals/evaluators") + + # Verify eval files exist and have correct content + with open("evals/eval-sets/test-set.json", "r") as f: + assert json.load(f) == test_set_content + with open("evals/evaluators/test-evaluator.json", "r") as f: + assert json.load(f) == test_evaluator_content + + def test_pull_with_existing_files( + self, + runner: CliRunner, + temp_dir: str, + project_details: ProjectDetails, + uipath_json: UiPathJson, + mock_env_vars: Dict[str, str], + httpx_mock: HTTPXMock, + monkeypatch: Any, + ) -> None: + """Test pull when local files exist and differ from remote.""" + base_url = "https://cloud.uipath.com/organization" + project_id = "test-project-id" + + # Mock the project structure response + mock_structure = { + "id": "root", + "name": "root", + "folders": [ + { + "id": "414af585-7e88-4774-ad94-cf6bd48f6c2d", + "name": "source_code", + "files": [ + { + "id": "123", + "name": "main.py", + "isMain": True, + "fileType": "1", + "isEntryPoint": True, + "ignoredFromPublish": False, + } + ], + } + ], + "files": [], + "folderType": "0", + } + + httpx_mock.add_response( + url=f"{base_url}/studio_/backend/api/Project/{project_id}/FileOperations/Structure", + json=mock_structure, + ) + + # Mock file download response + remote_content = "print('Remote version')" + httpx_mock.add_response( + method="GET", + url=f"{base_url}/studio_/backend/api/Project/{project_id}/FileOperations/File/123", + content=remote_content.encode(), + ) + + with runner.isolated_filesystem(temp_dir=temp_dir): + # Create local file with different content + local_content = "print('Local version')" + with open("main.py", "w") as f: + f.write(local_content) + + # Set environment variables + configure_env_vars(mock_env_vars) + os.environ["UIPATH_PROJECT_ID"] = project_id + + # Mock user input to confirm override + monkeypatch.setattr("click.prompt", lambda *args, **kwargs: "y") + + # Run pull + result = runner.invoke(pull, ["./"]) + assert result.exit_code == 0 + assert "differs from remote version" in result.output + assert "Updated main.py" in result.output + + # Verify file was updated + with open("main.py", "r") as f: + assert f.read() == remote_content + + def test_pull_skip_override( + self, + runner: CliRunner, + temp_dir: str, + project_details: ProjectDetails, + uipath_json: UiPathJson, + mock_env_vars: Dict[str, str], + httpx_mock: HTTPXMock, + monkeypatch: Any, + ) -> None: + """Test pull when user chooses not to override local files.""" + base_url = "https://cloud.uipath.com/organization" + project_id = "test-project-id" + + # Mock the project structure response + mock_structure = { + "id": "root", + "name": "root", + "folders": [ + { + "id": "414af585-7e88-4774-ad94-cf6bd48f6c2d", + "name": "source_code", + "files": [ + { + "id": "123", + "name": "main.py", + "isMain": True, + "fileType": "1", + "isEntryPoint": True, + "ignoredFromPublish": False, + } + ], + } + ], + "files": [], + "folderType": "0", + } + + httpx_mock.add_response( + url=f"{base_url}/studio_/backend/api/Project/{project_id}/FileOperations/Structure", + json=mock_structure, + ) + + # Mock file download response + remote_content = "print('Remote version')" + httpx_mock.add_response( + method="GET", + url=f"{base_url}/studio_/backend/api/Project/{project_id}/FileOperations/File/123", + content=remote_content.encode(), + ) + + with runner.isolated_filesystem(temp_dir=temp_dir): + # Create local file with different content + local_content = "print('Local version')" + with open("main.py", "w") as f: + f.write(local_content) + + # Set environment variables + configure_env_vars(mock_env_vars) + os.environ["UIPATH_PROJECT_ID"] = project_id + + # Mock user input to reject override + monkeypatch.setattr("click.prompt", lambda *args, **kwargs: "n") + + # Run pull + result = runner.invoke(pull, ["./"]) + assert result.exit_code == 0 + assert "differs from remote version" in result.output + assert "Skipped main.py" in result.output + + # Verify file was not updated + with open("main.py", "r") as f: + assert f.read() == local_content + + def test_pull_with_api_error( + self, + runner: CliRunner, + temp_dir: str, + project_details: ProjectDetails, + uipath_json: UiPathJson, + mock_env_vars: Dict[str, str], + httpx_mock: HTTPXMock, + ) -> None: + """Test pull when API request fails.""" + base_url = "https://cloud.uipath.com/organization" + project_id = "test-project-id" + + # Mock API error response + httpx_mock.add_response( + url=f"{base_url}/studio_/backend/api/Project/{project_id}/FileOperations/Structure", + status_code=401, + json={"message": "Unauthorized"}, + ) + + with runner.isolated_filesystem(temp_dir=temp_dir): + # Set environment variables + configure_env_vars(mock_env_vars) + os.environ["UIPATH_PROJECT_ID"] = project_id + + result = runner.invoke(pull, ["./"]) + assert result.exit_code == 1 + assert "Failed to pull UiPath project" in result.output + assert "401 Unauthorized" in result.output diff --git a/tests/cli/test_push.py b/tests/cli/test_push.py index feab540d..3fba7f0e 100644 --- a/tests/cli/test_push.py +++ b/tests/cli/test_push.py @@ -10,6 +10,7 @@ from utils.project_details import ProjectDetails from utils.uipath_json import UiPathJson +from tests.cli.utils.common import configure_env_vars from uipath._cli.cli_push import push @@ -46,10 +47,16 @@ class TestPush: """Test push command.""" def test_push_without_uipath_json( - self, runner: CliRunner, temp_dir: str, project_details: ProjectDetails + self, + runner: CliRunner, + temp_dir: str, + project_details: ProjectDetails, + mock_env_vars: Dict[str, str], ) -> None: """Test push when uipath.json is missing.""" with runner.isolated_filesystem(temp_dir=temp_dir): + configure_env_vars(mock_env_vars) + os.environ["UIPATH_PROJECT_ID"] = "123" with open("pyproject.toml", "w") as f: f.write(project_details.to_toml()) result = runner.invoke(push, ["./"]) @@ -65,9 +72,11 @@ def test_push_without_project_id( temp_dir: str, project_details: ProjectDetails, uipath_json: UiPathJson, + mock_env_vars: Dict[str, str], ) -> None: """Test push when UIPATH_PROJECT_ID is missing.""" with runner.isolated_filesystem(temp_dir=temp_dir): + configure_env_vars(mock_env_vars) with open("uipath.json", "w") as f: f.write(uipath_json.to_json()) with open("pyproject.toml", "w") as f: @@ -203,7 +212,7 @@ def test_successful_push( f.write('version = 1 \n requires-python = ">=3.11"') # Set environment variables - os.environ.update(mock_env_vars) + configure_env_vars(mock_env_vars) os.environ["UIPATH_PROJECT_ID"] = project_id # Run push @@ -320,7 +329,7 @@ def test_successful_push_new_project( f.write('version = 1 \n requires-python = ">=3.11"') # Set environment variables - os.environ.update(mock_env_vars) + configure_env_vars(mock_env_vars) os.environ["UIPATH_PROJECT_ID"] = project_id # Run push @@ -399,7 +408,7 @@ def test_push_with_api_error( f.write("") # Set environment variables - os.environ.update(mock_env_vars) + configure_env_vars(mock_env_vars) os.environ["UIPATH_PROJECT_ID"] = project_id result = runner.invoke(push, ["./"]) @@ -499,7 +508,7 @@ def test_push_with_nolock_flag( with open("uv.lock", "w") as f: f.write("") # Set environment variables - os.environ.update(mock_env_vars) + configure_env_vars(mock_env_vars) os.environ["UIPATH_PROJECT_ID"] = project_id # Run push with --nolock flag diff --git a/tests/cli/utils/common.py b/tests/cli/utils/common.py new file mode 100644 index 00000000..cd1fcd92 --- /dev/null +++ b/tests/cli/utils/common.py @@ -0,0 +1,7 @@ +import os +from typing import Dict + + +def configure_env_vars(env_vars: Dict[str, str]): + os.environ.clear() + os.environ.update(env_vars) From bf8d3f29922c66875efdb923d5ec8222425f045f Mon Sep 17 00:00:00 2001 From: radu-mocanu Date: Thu, 17 Jul 2025 16:35:17 +0300 Subject: [PATCH 15/18] feat: add eval cli command --- src/uipath/_cli/__init__.py | 2 + src/uipath/_cli/_evals/evaluation_service.py | 236 ++++++++++++++++++ .../_cli/_evals/evaluators/llm_evaluator.py | 119 +++++++++ src/uipath/_cli/_evals/models.py | 58 +++++ src/uipath/_cli/_evals/random_sampler.py | 39 +++ src/uipath/_cli/cli_eval.py | 45 ++++ src/uipath/_utils/constants.py | 3 + 7 files changed, 502 insertions(+) create mode 100644 src/uipath/_cli/_evals/evaluation_service.py create mode 100644 src/uipath/_cli/_evals/evaluators/llm_evaluator.py create mode 100644 src/uipath/_cli/_evals/models.py create mode 100644 src/uipath/_cli/_evals/random_sampler.py create mode 100644 src/uipath/_cli/cli_eval.py diff --git a/src/uipath/_cli/__init__.py b/src/uipath/_cli/__init__.py index 7c24e1d6..d2b19137 100644 --- a/src/uipath/_cli/__init__.py +++ b/src/uipath/_cli/__init__.py @@ -13,6 +13,7 @@ from .cli_pull import pull as pull # type: ignore from .cli_push import push as push # type: ignore from .cli_run import run as run # type: ignore +from .cli_eval import eval as eval # type: ignore def _get_safe_version() -> str: @@ -67,3 +68,4 @@ def cli(lv: bool, v: bool) -> None: cli.add_command(invoke) cli.add_command(push) cli.add_command(pull) +cli.add_command(eval) diff --git a/src/uipath/_cli/_evals/evaluation_service.py b/src/uipath/_cli/_evals/evaluation_service.py new file mode 100644 index 00000000..86b7aa2f --- /dev/null +++ b/src/uipath/_cli/_evals/evaluation_service.py @@ -0,0 +1,236 @@ +import asyncio +import json +from datetime import UTC, datetime +from pathlib import Path +from typing import Any, Dict, List + +from uipath._cli._utils._console import ConsoleLogger + +from ..cli_run import run +from .evaluators.llm_evaluator import LLMEvaluator +from .models import EvaluationSetResult + +console = ConsoleLogger() + + +class EvaluationService: + """Service for running evaluations.""" + + def __init__(self, eval_set_path: str | Path): + """Initialize the evaluation service. + + Args: + eval_set_path: Path to the evaluation set file (can be string or Path) + """ + self.eval_set_path = Path(eval_set_path) + self.eval_set = self._load_eval_set() + self.evaluators = self._load_evaluators() + self.num_workers = 8 + self.results_lock = asyncio.Lock() + self._initialize_results() + + def _initialize_results(self) -> None: + """Initialize the results file and directory.""" + # Create results directory if it doesn't exist + results_dir = self.eval_set_path.parent.parent / "results" + results_dir.mkdir(exist_ok=True) + + # Create results file + timestamp = datetime.now(UTC).strftime("%M-%H-%d-%m-%Y") + eval_set_name = self.eval_set["name"] + self.result_file = results_dir / f"eval-{eval_set_name}-{timestamp}.json" + + # Initialize with empty results + initial_results = EvaluationSetResult( + eval_set_id=self.eval_set["id"], + eval_set_name=self.eval_set["name"], + results=[], + average_score=0.0, + ) + + with open(self.result_file, "w", encoding="utf-8") as f: + f.write(initial_results.model_dump_json(indent=2)) + + def _load_eval_set(self) -> Dict[str, Any]: + """Load the evaluation set from file. + + Returns: + The loaded evaluation set + """ + with open(self.eval_set_path, "r", encoding="utf-8") as f: + return json.load(f) + + def _load_evaluators(self) -> List[LLMEvaluator]: + """Load evaluators referenced by the evaluation set.""" + evaluators = [] + evaluators_dir = self.eval_set_path.parent.parent / "evaluators" + + for evaluator_id in self.eval_set["evaluatorRefs"]: + # Find evaluator file + evaluator_file = None + for file in evaluators_dir.glob("*.json"): + with open(file) as f: + data = json.load(f) + if data.get("id") == evaluator_id: + evaluator_file = data + break + + if not evaluator_file: + raise ValueError(f"Could not find evaluator with ID {evaluator_id}") + + evaluators.append(LLMEvaluator(evaluator_file)) + + return evaluators + + async def _write_results(self, results: List[Any]) -> None: + """Write evaluation results to file with async lock. + + Args: + results: List of evaluation results to write + """ + async with self.results_lock: + # Read current results + with open(self.result_file, "r", encoding="utf-8") as f: + current_results = EvaluationSetResult.model_validate_json(f.read()) + + # Add new results + current_results.results.extend(results) + + if current_results.results: + current_results.average_score = sum( + r.score for r in current_results.results + ) / len(current_results.results) + + # Write updated results + with open(self.result_file, "w", encoding="utf-8") as f: + f.write(current_results.model_dump_json(indent=2)) + + def _run_agent(self, input_json: str) -> Dict[str, Any]: + """Run the agent with the given input. + + Args: + input_json: JSON string containing input data + + Returns: + Agent output as dictionary + """ + try: + # Run the agent using the CLI run command + run.callback( + entrypoint=None, + input=input_json, + resume=False, + file=None, + debug=False, + debug_port=5678, + ) + + # Read the output file + output_file = Path("__uipath") / "output.json" + with open(output_file, "r", encoding="utf-8") as f: + result = json.load(f) + + # Extract and parse the output content + output_content = result.get("output", {}) + if isinstance(output_content, str): + try: + return json.loads(output_content) + except json.JSONDecodeError as e: + raise Exception(f"Error parsing output: {e}") from e + return output_content + + except Exception as e: + console.error(f"Error running agent: {str(e)}") + return {"error": str(e)} + + async def _process_evaluation(self, eval_item: Dict[str, Any]) -> None: + """Process a single evaluation item. + + Args: + eval_item: The evaluation item to process + """ + console.info(f"Running evaluation: {eval_item['name']}") + + # Run the agent using the evaluation input + input_json = json.dumps(eval_item["inputs"]) + + # Run _run_agent in a non-async context using run_in_executor + loop = asyncio.get_running_loop() + actual_output = await loop.run_in_executor(None, self._run_agent, input_json) + + # Run each evaluator + eval_results = [] + for evaluator in self.evaluators: + result = await evaluator.evaluate( + evaluation_id=eval_item["id"], + evaluation_name=eval_item["name"], + input_data=eval_item["inputs"], + expected_output=eval_item["expectedOutput"], + actual_output=actual_output, + ) + eval_results.append(result) + + # Write results immediately + await self._write_results(eval_results) + + # TODO: here we should send the event to the SW eval API + console.info(f"Evaluation {eval_item['name']} complete.") + + async def _producer_task(self, task_queue: asyncio.Queue) -> None: + """Producer task that adds all evaluations to the queue. + + Args: + task_queue: The asyncio queue to add tasks to + """ + for eval_item in self.eval_set["evaluations"]: + await task_queue.put(eval_item) + + # Add sentinel values to signal workers to stop + for _ in range(self.num_workers): + await task_queue.put(None) + + async def _consumer_task(self, task_queue: asyncio.Queue, worker_id: int) -> None: + """Consumer task that processes evaluations from the queue. + + Args: + task_queue: The asyncio queue to get tasks from + worker_id: ID of this worker for logging + """ + while True: + eval_item = await task_queue.get() + if eval_item is None: + # Sentinel value - worker should stop + task_queue.task_done() + return + + try: + await self._process_evaluation(eval_item) + task_queue.task_done() + except Exception as e: + # Log error and continue to next item + task_queue.task_done() + console.warning( + f"Worker {worker_id} failed evaluation {eval_item.get('name', 'Unknown')}: {str(e)}" + ) + + async def run_evaluation(self) -> None: + """Run the evaluation set using multiple worker tasks.""" + task_queue = asyncio.Queue() + + producer = asyncio.create_task(self._producer_task(task_queue)) + + consumers = [] + for worker_id in range(self.num_workers): + consumer = asyncio.create_task(self._consumer_task(task_queue, worker_id)) + consumers.append(consumer) + + await producer + + await task_queue.join() + + # Wait for all consumers to finish + await asyncio.gather(*consumers) + + console.success( + f"All evaluations complete. Results saved to {self.result_file}" + ) diff --git a/src/uipath/_cli/_evals/evaluators/llm_evaluator.py b/src/uipath/_cli/_evals/evaluators/llm_evaluator.py new file mode 100644 index 00000000..ad9050bc --- /dev/null +++ b/src/uipath/_cli/_evals/evaluators/llm_evaluator.py @@ -0,0 +1,119 @@ +import json +from typing import Any, Dict, Optional + +from ..._utils._debug import console +from ...._config import Config +from ...._execution_context import ExecutionContext +from ...._services.llm_gateway_service import UiPathLlmChatService +from ...._utils.constants import ( + ENV_BASE_URL, + ENV_UIPATH_ACCESS_TOKEN, + ENV_UNATTENDED_USER_ACCESS_TOKEN, COMMUNITY_AGENTS_SUFFIX, +) +from ..models import EvaluationResult, EvaluatorCategory, LLMResponse + + +class LLMEvaluator: + """Service for evaluating outputs using LLM.""" + + # TODO: find a better way to structure the output + format_instructions: dict[str, str] = { + "role": "system", + "content": 'Extract the data from the following text and model it like this in JSON format: {"similarity_score" = "", "score_justification" = "" . Similarity_score is a float between 0 and 100 and score_justification is a str. The output should be a plain json, nothing else. No markdown.', + } + + def __init__(self, evaluator_config: Dict[str, Any]): + """Initialize LLM evaluator. + + Args: + evaluator_config: Configuration for the evaluator from evaluator JSON file + """ + import os + + self.config = evaluator_config + base_url_value = os.getenv(ENV_BASE_URL) + secret_value = os.getenv(ENV_UNATTENDED_USER_ACCESS_TOKEN) or os.getenv( + ENV_UIPATH_ACCESS_TOKEN + ) + config = Config( + base_url=base_url_value, # type: ignore + secret=secret_value, # type: ignore + ) + self.llm = UiPathLlmChatService(config, ExecutionContext()) + + # Validate evaluator category + if self.config.get("category") != EvaluatorCategory.LlmAsAJudge: + raise ValueError("Evaluator must be of type LlmAsAJudge") + + async def evaluate( + self, + evaluation_id: str, + evaluation_name: str, + input_data: Dict[str, Any], + expected_output: Dict[str, Any], + actual_output: Dict[str, Any], + ) -> EvaluationResult: + """Evaluate the actual output against expected output using LLM. + + Args: + evaluation_id: ID of the evaluation + evaluation_name: Name of the evaluation + input_data: Input data used for the evaluation + expected_output: Expected output from the evaluation + actual_output: Actual output received + + Returns: + EvaluationResult containing the evaluation score and details + """ + # Prepare the prompt by replacing placeholders + prompt = self.config["prompt"] + prompt = prompt.replace( + "{{ExpectedOutput}}", json.dumps(expected_output, indent=2) + ) + content = prompt.replace( + "{{ActualOutput}}", json.dumps(actual_output, indent=2) + ) + + model: Optional[str] = self.config.get("model", None) + if not model: + console.error("Evaluator model cannot be extracted") + + # remove community-agents suffix from llm model name + if model.endswith(COMMUNITY_AGENTS_SUFFIX): + model = model.replace(COMMUNITY_AGENTS_SUFFIX, "") + + response = await self.llm.chat_completions( + messages=[{"role": "user", "content": content}], model=model + ) + structured_response = await self.llm.chat_completions( + messages=[ + self.format_instructions, + {"role": "user", "content": response.choices[-1].message.content}, + ], + model=model, + ) + try: + llm_response = LLMResponse( + **json.loads(structured_response.choices[-1].message.content) + ) + except Exception as e: + raise Exception(f"Error parsing LLM response: {e}") from e + # Leave those comments + # llm_response = LLMResponse(similarity_score=90, score_justification="test justification") + score = llm_response.similarity_score + details = llm_response.score_justification + + if score < 0 or score > 100: + raise ValueError(f"Score {score} is outside valid range 0-100") + + return EvaluationResult( + evaluation_id=evaluation_id, + evaluation_name=evaluation_name, + evaluator_id=self.config["id"], + evaluator_name=self.config["name"], + score=score, + input=input_data, + expected_output=expected_output, + actual_output=actual_output, + details=details, + ) diff --git a/src/uipath/_cli/_evals/models.py b/src/uipath/_cli/_evals/models.py new file mode 100644 index 00000000..328f4aac --- /dev/null +++ b/src/uipath/_cli/_evals/models.py @@ -0,0 +1,58 @@ +from datetime import datetime +from enum import IntEnum +from typing import Any, Dict, List, Optional + +from pydantic import BaseModel, Field + + +class LLMResponse(BaseModel): + similarity_score: float + score_justification: str + +class EvaluatorCategory(IntEnum): + """Types of evaluators.""" + + Deterministic = 0 + LlmAsAJudge = 1 + AgentScorer = 2 + Trajectory = 3 + + +class EvaluatorType(IntEnum): + """Subtypes of evaluators.""" + + Unknown = 0 + Equals = 1 + Contains = 2 + Regex = 3 + Factuality = 4 + Custom = 5 + JsonSimilarity = 6 + Trajectory = 7 + ContextPrecision = 8 + Faithfulness = 9 + + +class EvaluationResult(BaseModel): + """Result of a single evaluation.""" + + evaluation_id: str + evaluation_name: str + evaluator_id: str + evaluator_name: str + score: float + input: Dict[str, Any] + expected_output: Dict[str, Any] + actual_output: Dict[str, Any] + timestamp: datetime = Field(default_factory=datetime.utcnow) + details: Optional[str] = None + + +class EvaluationSetResult(BaseModel): + """Results of running an evaluation set.""" + + eval_set_id: str + eval_set_name: str + results: List[EvaluationResult] + average_score: float + timestamp: datetime = Field(default_factory=datetime.utcnow) diff --git a/src/uipath/_cli/_evals/random_sampler.py b/src/uipath/_cli/_evals/random_sampler.py new file mode 100644 index 00000000..f3e56f89 --- /dev/null +++ b/src/uipath/_cli/_evals/random_sampler.py @@ -0,0 +1,39 @@ +import random +from typing import Any, Generator, Sequence + + +class RandomChainSampler: + """Sampler that randomly chains multiple iterables together. + + This class takes a sequence of generators and yields items from them + in random order, removing generators as they become exhausted. + """ + + def __init__(self, iterables: Sequence[Generator[Any, None, None]]): + """Initialize the sampler with a sequence of iterables. + + Args: + iterables: Sequence of generators to sample from + """ + self.iterables = list(iterables) + + def __iter__(self): + """Return the iterator object.""" + return self + + def __next__(self): + """Get the next item from a randomly selected iterable. + + Returns: + The next item from one of the iterables + + Raises: + StopIteration: When all iterables are exhausted + """ + while len(self.iterables) > 0: + current_iterable = random.choice(self.iterables) + try: + return next(current_iterable) + except StopIteration: + self.iterables.remove(current_iterable) + raise StopIteration diff --git a/src/uipath/_cli/cli_eval.py b/src/uipath/_cli/cli_eval.py new file mode 100644 index 00000000..b11f265a --- /dev/null +++ b/src/uipath/_cli/cli_eval.py @@ -0,0 +1,45 @@ +# type: ignore +import asyncio +import os +from pathlib import Path +from typing import Optional + +import click +from dotenv import load_dotenv + +from .._utils.constants import ENV_JOB_ID +from ..telemetry import track +from ._evals.evaluation_service import EvaluationService +from ._utils._console import ConsoleLogger + +console = ConsoleLogger() +load_dotenv(override=True) + + +@click.command() +@click.argument("eval_set", required=True, type=click.Path(exists=True)) +@track(when=lambda *_a, **_kw: os.getenv(ENV_JOB_ID) is None) +def eval(eval_set: str) -> None: + """Run an evaluation set against the agent. + + Args: + eval_set: Path to the evaluation set JSON file + """ + try: + # Validate file path + eval_path = Path(eval_set) + if not eval_path.is_file() or eval_path.suffix != ".json": + console.error("Evaluation set must be a JSON file") + click.get_current_context().exit(1) + + # Run evaluation + service = EvaluationService(eval_set) + asyncio.run(service.run_evaluation()) + + except Exception as e: + console.error(f"Error running evaluation: {str(e)}") + click.get_current_context().exit(1) + + +if __name__ == "__main__": + eval() diff --git a/src/uipath/_utils/constants.py b/src/uipath/_utils/constants.py index 6050d4e2..08fe2cd5 100644 --- a/src/uipath/_utils/constants.py +++ b/src/uipath/_utils/constants.py @@ -25,3 +25,6 @@ # Local storage TEMP_ATTACHMENTS_FOLDER = "uipath_attachments" + +# LLM models +COMMUNITY_AGENTS_SUFFIX = "-community-agents" From 50a97b1894a00203be09e8af5c8abe090c78acc7 Mon Sep 17 00:00:00 2001 From: radu-mocanu Date: Thu, 24 Jul 2025 18:44:01 +0300 Subject: [PATCH 16/18] wip --- src/uipath/_cli/_evals/evaluation_service.py | 100 +++++++++--------- .../_cli/_evals/evaluators/evaluator_base.py | 6 ++ .../_cli/_evals/evaluators/llm_evaluator.py | 52 +++++---- src/uipath/_cli/_evals/models.py | 5 +- src/uipath/_cli/cli_eval.py | 18 +++- src/uipath/_cli/cli_run.py | 99 +++++++++++------ 6 files changed, 177 insertions(+), 103 deletions(-) create mode 100644 src/uipath/_cli/_evals/evaluators/evaluator_base.py diff --git a/src/uipath/_cli/_evals/evaluation_service.py b/src/uipath/_cli/_evals/evaluation_service.py index 86b7aa2f..6320ad91 100644 --- a/src/uipath/_cli/_evals/evaluation_service.py +++ b/src/uipath/_cli/_evals/evaluation_service.py @@ -1,12 +1,14 @@ import asyncio import json +import tempfile from datetime import UTC, datetime from pathlib import Path from typing import Any, Dict, List from uipath._cli._utils._console import ConsoleLogger +from .evaluators.evaluator_base import EvaluatorBase -from ..cli_run import run +from ..cli_run import run_core from .evaluators.llm_evaluator import LLMEvaluator from .models import EvaluationSetResult @@ -16,16 +18,17 @@ class EvaluationService: """Service for running evaluations.""" - def __init__(self, eval_set_path: str | Path): + def __init__(self, entrypoint: str, eval_set_path: str | Path, workers: int): """Initialize the evaluation service. Args: eval_set_path: Path to the evaluation set file (can be string or Path) """ + self.entrypoint = entrypoint self.eval_set_path = Path(eval_set_path) self.eval_set = self._load_eval_set() self.evaluators = self._load_evaluators() - self.num_workers = 8 + self.num_workers = workers self.results_lock = asyncio.Lock() self._initialize_results() @@ -60,7 +63,7 @@ def _load_eval_set(self) -> Dict[str, Any]: with open(self.eval_set_path, "r", encoding="utf-8") as f: return json.load(f) - def _load_evaluators(self) -> List[LLMEvaluator]: + def _load_evaluators(self) -> List[EvaluatorBase]: """Load evaluators referenced by the evaluation set.""" evaluators = [] evaluators_dir = self.eval_set_path.parent.parent / "evaluators" @@ -105,7 +108,7 @@ async def _write_results(self, results: List[Any]) -> None: with open(self.result_file, "w", encoding="utf-8") as f: f.write(current_results.model_dump_json(indent=2)) - def _run_agent(self, input_json: str) -> Dict[str, Any]: + def _run_agent(self, input_json: str) -> tuple[Dict[str, Any], bool]: """Run the agent with the given input. Args: @@ -114,34 +117,33 @@ def _run_agent(self, input_json: str) -> Dict[str, Any]: Returns: Agent output as dictionary """ - try: - # Run the agent using the CLI run command - run.callback( - entrypoint=None, - input=input_json, - resume=False, - file=None, - debug=False, - debug_port=5678, - ) - - # Read the output file - output_file = Path("__uipath") / "output.json" - with open(output_file, "r", encoding="utf-8") as f: - result = json.load(f) - - # Extract and parse the output content - output_content = result.get("output", {}) - if isinstance(output_content, str): - try: - return json.loads(output_content) - except json.JSONDecodeError as e: - raise Exception(f"Error parsing output: {e}") from e - return output_content - - except Exception as e: - console.error(f"Error running agent: {str(e)}") - return {"error": str(e)} + with tempfile.TemporaryDirectory() as tmpdir: + try: + output_file = Path(tmpdir) / "output.json" + success, error_message, info_message = run_core( + entrypoint=self.entrypoint, + input=input_json, + resume=False, + file=None, + output_file=output_file + ) + if not success: + console.warning(error_message) + return {}, False + else: + # Read the output file + with open(output_file, "r", encoding="utf-8") as f: + result = json.load(f) + if isinstance(result, str): + try: + return json.loads(result) + except json.JSONDecodeError as e: + raise Exception(f"Error parsing output: {e}") from e + return result, True + + except Exception as e: + console.error(f"Error running agent: {str(e)}") + return {"error": str(e)}, False async def _process_evaluation(self, eval_item: Dict[str, Any]) -> None: """Process a single evaluation item. @@ -156,22 +158,22 @@ async def _process_evaluation(self, eval_item: Dict[str, Any]) -> None: # Run _run_agent in a non-async context using run_in_executor loop = asyncio.get_running_loop() - actual_output = await loop.run_in_executor(None, self._run_agent, input_json) - - # Run each evaluator - eval_results = [] - for evaluator in self.evaluators: - result = await evaluator.evaluate( - evaluation_id=eval_item["id"], - evaluation_name=eval_item["name"], - input_data=eval_item["inputs"], - expected_output=eval_item["expectedOutput"], - actual_output=actual_output, - ) - eval_results.append(result) - - # Write results immediately - await self._write_results(eval_results) + actual_output, success = await loop.run_in_executor(None, self._run_agent, input_json) + if success: + # Run each evaluator + eval_results = [] + for evaluator in self.evaluators: + result = await evaluator.evaluate( + evaluation_id=eval_item["id"], + evaluation_name=eval_item["name"], + input_data=eval_item["inputs"], + expected_output=eval_item["expectedOutput"], + actual_output=actual_output, + ) + eval_results.append(result) + + # Write results immediately + await self._write_results(eval_results) # TODO: here we should send the event to the SW eval API console.info(f"Evaluation {eval_item['name']} complete.") diff --git a/src/uipath/_cli/_evals/evaluators/evaluator_base.py b/src/uipath/_cli/_evals/evaluators/evaluator_base.py new file mode 100644 index 00000000..4025e304 --- /dev/null +++ b/src/uipath/_cli/_evals/evaluators/evaluator_base.py @@ -0,0 +1,6 @@ +from abc import ABC + + +class EvaluatorBase(ABC): + async def evaluate(self, evaluation_id, evaluation_name, input_data, expected_output, actual_output): + pass diff --git a/src/uipath/_cli/_evals/evaluators/llm_evaluator.py b/src/uipath/_cli/_evals/evaluators/llm_evaluator.py index ad9050bc..1b3068d6 100644 --- a/src/uipath/_cli/_evals/evaluators/llm_evaluator.py +++ b/src/uipath/_cli/_evals/evaluators/llm_evaluator.py @@ -1,27 +1,23 @@ import json from typing import Any, Dict, Optional -from ..._utils._debug import console +from .evaluator_base import EvaluatorBase from ...._config import Config from ...._execution_context import ExecutionContext from ...._services.llm_gateway_service import UiPathLlmChatService from ...._utils.constants import ( + COMMUNITY_AGENTS_SUFFIX, ENV_BASE_URL, ENV_UIPATH_ACCESS_TOKEN, - ENV_UNATTENDED_USER_ACCESS_TOKEN, COMMUNITY_AGENTS_SUFFIX, + ENV_UNATTENDED_USER_ACCESS_TOKEN, ) +from ..._utils._debug import console from ..models import EvaluationResult, EvaluatorCategory, LLMResponse -class LLMEvaluator: +class LLMEvaluator(EvaluatorBase): """Service for evaluating outputs using LLM.""" - # TODO: find a better way to structure the output - format_instructions: dict[str, str] = { - "role": "system", - "content": 'Extract the data from the following text and model it like this in JSON format: {"similarity_score" = "", "score_justification" = "" . Similarity_score is a float between 0 and 100 and score_justification is a str. The output should be a plain json, nothing else. No markdown.', - } - def __init__(self, evaluator_config: Dict[str, Any]): """Initialize LLM evaluator. @@ -83,25 +79,43 @@ async def evaluate( model = model.replace(COMMUNITY_AGENTS_SUFFIX, "") response = await self.llm.chat_completions( - messages=[{"role": "user", "content": content}], model=model - ) - structured_response = await self.llm.chat_completions( - messages=[ - self.format_instructions, - {"role": "user", "content": response.choices[-1].message.content}, - ], + messages=[{"role": "user", "content": content}], model=model, + response_format={ + "type": "json_schema", + "json_schema": { + "name": "evaluation_result", + "strict": True, + "schema": { + "type": "object", + "properties": { + "score": { + "type": "number", + "minimum": 0, + "maximum": 100, + "description": "Similarity score between expected and actual output (0-100)", + }, + "justification": { + "type": "string", + "description": "Detailed explanation of why this score was given", + }, + }, + "required": ["score", "justification"], + "additionalProperties": False, + }, + }, + }, ) try: llm_response = LLMResponse( - **json.loads(structured_response.choices[-1].message.content) + **json.loads(response.choices[-1].message.content) ) except Exception as e: raise Exception(f"Error parsing LLM response: {e}") from e # Leave those comments # llm_response = LLMResponse(similarity_score=90, score_justification="test justification") - score = llm_response.similarity_score - details = llm_response.score_justification + score = llm_response.score + details = llm_response.justification if score < 0 or score > 100: raise ValueError(f"Score {score} is outside valid range 0-100") diff --git a/src/uipath/_cli/_evals/models.py b/src/uipath/_cli/_evals/models.py index 328f4aac..af0595c7 100644 --- a/src/uipath/_cli/_evals/models.py +++ b/src/uipath/_cli/_evals/models.py @@ -6,8 +6,9 @@ class LLMResponse(BaseModel): - similarity_score: float - score_justification: str + score: float + justification: str + class EvaluatorCategory(IntEnum): """Types of evaluators.""" diff --git a/src/uipath/_cli/cli_eval.py b/src/uipath/_cli/cli_eval.py index b11f265a..a3c322c3 100644 --- a/src/uipath/_cli/cli_eval.py +++ b/src/uipath/_cli/cli_eval.py @@ -17,13 +17,22 @@ @click.command() +@click.argument("entrypoint", required=True) @click.argument("eval_set", required=True, type=click.Path(exists=True)) +@click.option( + "--workers", + type=int, + default=8, + help="Number of parallel workers for running evaluations (default: 8)", +) @track(when=lambda *_a, **_kw: os.getenv(ENV_JOB_ID) is None) -def eval(eval_set: str) -> None: +def eval(entrypoint: str, eval_set: str, workers: int) -> None: """Run an evaluation set against the agent. Args: + entrypoint: Path to the agent script to evaluate eval_set: Path to the evaluation set JSON file + workers: Number of parallel workers for running evaluations """ try: # Validate file path @@ -32,8 +41,13 @@ def eval(eval_set: str) -> None: console.error("Evaluation set must be a JSON file") click.get_current_context().exit(1) + # Validate workers count + if workers < 1: + console.error("Number of workers must be at least 1") + click.get_current_context().exit(1) + # Run evaluation - service = EvaluationService(eval_set) + service = EvaluationService(entrypoint, eval_set, workers) asyncio.run(service.run_evaluation()) except Exception as e: diff --git a/src/uipath/_cli/cli_run.py b/src/uipath/_cli/cli_run.py index fb3298d7..d1e31295 100644 --- a/src/uipath/_cli/cli_run.py +++ b/src/uipath/_cli/cli_run.py @@ -3,7 +3,7 @@ import os import traceback from os import environ as env -from typing import Optional +from typing import Optional, Tuple from uuid import uuid4 import click @@ -110,6 +110,61 @@ async def execute(): ) +def run_core( + entrypoint: Optional[str], + resume: bool, + input: Optional[str] = None, + input_file: Optional[str] = None, + output_file: Optional[str] = None, + **kwargs, +) -> Tuple[bool, Optional[str], Optional[str]]: + """Core execution logic that can be called programmatically. + + Args: + entrypoint: Path to the Python script to execute + input: JSON string with input data + resume: Flag indicating if this is a resume execution + input_file: Path to input JSON file + output_file: Path where output will be written + **kwargs: Additional arguments to be forwarded to the middleware + + Returns: + Tuple containing: + - success: True if execution was successful + - error_message: Error message if any + - info_message: Info message if any + """ + # Process through middleware chain + result = Middlewares.next( + "run", + entrypoint, + input, + resume, + input_file=input_file, + execution_output_file=output_file, + **kwargs, + ) + + if result.should_continue: + result = python_run_middleware( + entrypoint=entrypoint, + input=input, + resume=resume, + input_file=input_file, + execution_output_file=output_file, + **kwargs, + ) + + if result.should_continue: + return False, "Could not process the request with any available handler.", None + + return ( + not bool(result.error_message), + result.error_message, + result.info_message, + ) + + @click.command() @click.argument("entrypoint", required=False) @click.argument("input", required=False, default="{}") @@ -161,44 +216,26 @@ def run( if not setup_debugging(debug, debug_port): console.error(f"Failed to start debug server on port {debug_port}") - # Process through middleware chain - result = Middlewares.next( - "run", - entrypoint, - input, - resume, + success, error_message, info_message = run_core( + entrypoint=entrypoint, + input=input, + resume=resume, + input_file=input_file, + output_file=output_file, debug=debug, debug_port=debug_port, - input_file=input_file, - execution_output_file=output_file, ) - if result.should_continue: - result = python_run_middleware( - entrypoint=entrypoint, - input=input, - resume=resume, - input_file=input_file, - execution_output_file=output_file, - ) - - # Handle result from middleware - if result.error_message: - console.error(result.error_message, include_traceback=True) - if result.should_include_stacktrace: + if error_message: + console.error(error_message, include_traceback=True) + if not success: # If there was an error and execution failed console.error(traceback.format_exc()) click.get_current_context().exit(1) - if result.info_message: - console.info(result.info_message) - - # If middleware chain completed but didn't handle the request - if result.should_continue: - console.error( - "Error: Could not process the request with any available handler." - ) + if info_message: + console.info(info_message) - if not result.should_continue and not result.error_message: + if success: console.success("Successful execution.") From f2ea6ef0db60d67f12b9538bf6a84f4bd261b37a Mon Sep 17 00:00:00 2001 From: radu-mocanu Date: Fri, 25 Jul 2025 11:06:50 +0300 Subject: [PATCH 17/18] wip --- src/uipath/_cli/_evals/evaluation_service.py | 41 ++++++++++++++------ src/uipath/_cli/_runtime/_contracts.py | 32 ++++++++++----- src/uipath/_cli/_runtime/_logging.py | 10 ++--- src/uipath/_cli/cli_run.py | 14 ++++--- 4 files changed, 66 insertions(+), 31 deletions(-) diff --git a/src/uipath/_cli/_evals/evaluation_service.py b/src/uipath/_cli/_evals/evaluation_service.py index 6320ad91..73f7a691 100644 --- a/src/uipath/_cli/_evals/evaluation_service.py +++ b/src/uipath/_cli/_evals/evaluation_service.py @@ -1,14 +1,15 @@ import asyncio import json import tempfile +import warnings from datetime import UTC, datetime from pathlib import Path from typing import Any, Dict, List from uipath._cli._utils._console import ConsoleLogger -from .evaluators.evaluator_base import EvaluatorBase from ..cli_run import run_core +from .evaluators.evaluator_base import EvaluatorBase from .evaluators.llm_evaluator import LLMEvaluator from .models import EvaluationSetResult @@ -120,13 +121,23 @@ def _run_agent(self, input_json: str) -> tuple[Dict[str, Any], bool]: with tempfile.TemporaryDirectory() as tmpdir: try: output_file = Path(tmpdir) / "output.json" - success, error_message, info_message = run_core( - entrypoint=self.entrypoint, - input=input_json, - resume=False, - file=None, - output_file=output_file - ) + logs_file = Path(tmpdir) / "execution.log" + + # Suppress LangChain deprecation warnings during agent execution + with warnings.catch_warnings(): + warnings.filterwarnings( + "ignore", category=UserWarning, module="langchain" + ) + success, error_message, info_message = run_core( + entrypoint=self.entrypoint, + input=input_json, + resume=False, + input_file=None, + execution_output_file=output_file, + logs_file=logs_file, + runtime_dir=tmpdir, + eval_run=True, + ) if not success: console.warning(error_message) return {}, False @@ -134,15 +145,19 @@ def _run_agent(self, input_json: str) -> tuple[Dict[str, Any], bool]: # Read the output file with open(output_file, "r", encoding="utf-8") as f: result = json.load(f) + + # uncomment the following lines to have access to the execution.logs (needed for some types of evals) + # with open(logs_file, "r", encoding="utf-8") as f: + # logs = f.read() if isinstance(result, str): try: - return json.loads(result) + return json.loads(result), True except json.JSONDecodeError as e: raise Exception(f"Error parsing output: {e}") from e return result, True except Exception as e: - console.error(f"Error running agent: {str(e)}") + console.warning(f"Error running agent: {str(e)}") return {"error": str(e)}, False async def _process_evaluation(self, eval_item: Dict[str, Any]) -> None: @@ -158,7 +173,9 @@ async def _process_evaluation(self, eval_item: Dict[str, Any]) -> None: # Run _run_agent in a non-async context using run_in_executor loop = asyncio.get_running_loop() - actual_output, success = await loop.run_in_executor(None, self._run_agent, input_json) + actual_output, success = await loop.run_in_executor( + None, self._run_agent, input_json + ) if success: # Run each evaluator eval_results = [] @@ -209,6 +226,8 @@ async def _consumer_task(self, task_queue: asyncio.Queue, worker_id: int) -> Non await self._process_evaluation(eval_item) task_queue.task_done() except Exception as e: + import click + # Log error and continue to next item task_queue.task_done() console.warning( diff --git a/src/uipath/_cli/_runtime/_contracts.py b/src/uipath/_cli/_runtime/_contracts.py index 24ee52af..aa5c94ab 100644 --- a/src/uipath/_cli/_runtime/_contracts.py +++ b/src/uipath/_cli/_runtime/_contracts.py @@ -158,15 +158,17 @@ class UiPathRuntimeContext(BaseModel): result: Optional[UiPathRuntimeResult] = None execution_output_file: Optional[str] = None input_file: Optional[str] = None + eval_run: bool = False model_config = {"arbitrary_types_allowed": True} @classmethod - def from_config(cls, config_path=None): + def from_config(cls, config_path=None, **kwargs) -> "UiPathRuntimeContext": """Load configuration from uipath.json file. Args: config_path: Path to the configuration file. If None, uses the default "uipath.json" + **kwargs: Additional keyword arguments to use as fallback for configuration values Returns: An instance of the class with fields populated from the config file @@ -184,20 +186,29 @@ def from_config(cls, config_path=None): instance = cls() + mapping = { + "dir": "runtime_dir", + "outputFile": "output_file", + "stateFile": "state_file", + "logsFile": "logs_file", + } + + attributes_set = set() + # set values from config file if available if "runtime" in config: runtime_config = config["runtime"] - - mapping = { - "dir": "runtime_dir", - "outputFile": "output_file", - "stateFile": "state_file", - "logsFile": "logs_file", - } - for config_key, attr_name in mapping.items(): if config_key in runtime_config and hasattr(instance, attr_name): + attributes_set.add(attr_name) setattr(instance, attr_name, runtime_config[config_key]) + # fallback to kwargs for any values not set from config file + for _, attr_name in mapping.items(): + if attr_name in kwargs and hasattr(instance, attr_name): + # Only set from kwargs if not already set from config file + if attr_name not in attributes_set: + setattr(instance, attr_name, kwargs[attr_name]) + return instance @@ -310,12 +321,13 @@ async def __aenter__(self): with open(self.context.input_file) as f: self.context.input = f.read() - # Intercept all stdout/stderr/logs and write them to a file (runtime), stdout (debug) + # Intercept all stdout/stderr/logs and write them to a file (runtime/evals), stdout (debug) self.logs_interceptor = LogsInterceptor( min_level=self.context.logs_min_level, dir=self.context.runtime_dir, file=self.context.logs_file, job_id=self.context.job_id, + eval_run=self.context.eval_run, ) self.logs_interceptor.setup() diff --git a/src/uipath/_cli/_runtime/_logging.py b/src/uipath/_cli/_runtime/_logging.py index b6c2b4ea..00256b42 100644 --- a/src/uipath/_cli/_runtime/_logging.py +++ b/src/uipath/_cli/_runtime/_logging.py @@ -29,6 +29,7 @@ def __init__( dir: Optional[str] = "__uipath", file: Optional[str] = "execution.log", job_id: Optional[str] = None, + eval_run: bool = False, ): """Initialize the log interceptor. @@ -37,9 +38,11 @@ def __init__( dir (str): The directory where logs should be stored. file (str): The log file name. job_id (str, optional): If provided, logs go to file; otherwise, to stdout. + eval_run (bool): If True, skip stdout/stderr redirection to prevent recursion. """ min_level = min_level or "INFO" self.job_id = job_id + self.eval_run = eval_run # Convert to numeric level for consistent comparison self.numeric_min_level = getattr(logging, min_level.upper(), logging.INFO) @@ -57,8 +60,8 @@ def __init__( self.log_handler: Union[PersistentLogsHandler, logging.StreamHandler[TextIO]] - # Create either file handler (runtime) or stdout handler (debug) - if self.job_id: + # Create either file handler (runtime/evals) or stdout handler (debug) + if self.job_id or self.eval_run: # Ensure directory exists for file logging dir = dir or "__uipath" file = file or "execution.log" @@ -103,9 +106,6 @@ def setup(self) -> None: self._clean_all_handlers(logger) self.patched_loggers.add(logger_name) - # Set up stdout/stderr redirection - self._redirect_stdout_stderr() - def _redirect_stdout_stderr(self) -> None: """Redirect stdout and stderr to the logging system.""" diff --git a/src/uipath/_cli/cli_run.py b/src/uipath/_cli/cli_run.py index d1e31295..f14e803f 100644 --- a/src/uipath/_cli/cli_run.py +++ b/src/uipath/_cli/cli_run.py @@ -64,7 +64,7 @@ def python_run_middleware( async def execute(): context = UiPathRuntimeContext.from_config( - env.get("UIPATH_CONFIG_PATH", "uipath.json") + env.get("UIPATH_CONFIG_PATH", "uipath.json"), **kwargs ) context.entrypoint = entrypoint context.input = input @@ -73,6 +73,7 @@ async def execute(): context.trace_id = env.get("UIPATH_TRACE_ID") context.input_file = kwargs.get("input_file", None) context.execution_output_file = kwargs.get("execution_output_file", None) + context.eval_run = kwargs.get("eval_run", False) context.tracing_enabled = env.get("UIPATH_TRACING_ENABLED", True) context.trace_context = UiPathTraceContext( trace_id=env.get("UIPATH_TRACE_ID"), @@ -115,7 +116,8 @@ def run_core( resume: bool, input: Optional[str] = None, input_file: Optional[str] = None, - output_file: Optional[str] = None, + execution_output_file: Optional[str] = None, + logs_file: Optional[str] = None, **kwargs, ) -> Tuple[bool, Optional[str], Optional[str]]: """Core execution logic that can be called programmatically. @@ -125,7 +127,7 @@ def run_core( input: JSON string with input data resume: Flag indicating if this is a resume execution input_file: Path to input JSON file - output_file: Path where output will be written + logs_file: Path where execution output will be written **kwargs: Additional arguments to be forwarded to the middleware Returns: @@ -141,7 +143,8 @@ def run_core( input, resume, input_file=input_file, - execution_output_file=output_file, + execution_output_file=execution_output_file, + logs_file=logs_file, **kwargs, ) @@ -151,7 +154,8 @@ def run_core( input=input, resume=resume, input_file=input_file, - execution_output_file=output_file, + execution_output_file=execution_output_file, + logs_file=logs_file, **kwargs, ) From da4ce189bd04ffef7def8c511521d56942c78e2d Mon Sep 17 00:00:00 2001 From: radu-mocanu Date: Fri, 25 Jul 2025 15:33:43 +0300 Subject: [PATCH 18/18] wip --- src/uipath/_cli/_evals/evaluation_service.py | 190 +++++++++++------- src/uipath/_cli/_evals/evaluators/__init__.py | 20 ++ .../evaluators/agent_scorer_evaluator.py | 61 ++++++ .../evaluators/deterministic_evaluator.py | 58 ++++++ .../_cli/_evals/evaluators/evaluator_base.py | 72 ++++++- .../_evals/evaluators/evaluator_factory.py | 105 ++++++++++ .../evaluators/llm_as_judge_evaluator.py | 180 +++++++++++++++++ .../_cli/_evals/evaluators/llm_evaluator.py | 133 ------------ .../_evals/evaluators/trajectory_evaluator.py | 61 ++++++ src/uipath/_cli/_evals/models/__init__.py | 13 ++ .../_cli/_evals/models/evaluation_set.py | 37 ++++ .../{models.py => models/evaluators.py} | 19 +- src/uipath/_cli/_evals/progress_reporter.py | 3 + src/uipath/_cli/_utils/_console.py | 112 ++++++++++- src/uipath/_cli/cli_eval.py | 64 ++++-- 15 files changed, 902 insertions(+), 226 deletions(-) create mode 100644 src/uipath/_cli/_evals/evaluators/__init__.py create mode 100644 src/uipath/_cli/_evals/evaluators/agent_scorer_evaluator.py create mode 100644 src/uipath/_cli/_evals/evaluators/deterministic_evaluator.py create mode 100644 src/uipath/_cli/_evals/evaluators/evaluator_factory.py create mode 100644 src/uipath/_cli/_evals/evaluators/llm_as_judge_evaluator.py delete mode 100644 src/uipath/_cli/_evals/evaluators/llm_evaluator.py create mode 100644 src/uipath/_cli/_evals/evaluators/trajectory_evaluator.py create mode 100644 src/uipath/_cli/_evals/models/__init__.py create mode 100644 src/uipath/_cli/_evals/models/evaluation_set.py rename src/uipath/_cli/_evals/{models.py => models/evaluators.py} (65%) create mode 100644 src/uipath/_cli/_evals/progress_reporter.py diff --git a/src/uipath/_cli/_evals/evaluation_service.py b/src/uipath/_cli/_evals/evaluation_service.py index 73f7a691..522678f6 100644 --- a/src/uipath/_cli/_evals/evaluation_service.py +++ b/src/uipath/_cli/_evals/evaluation_service.py @@ -6,12 +6,14 @@ from pathlib import Path from typing import Any, Dict, List +import click + from uipath._cli._utils._console import ConsoleLogger from ..cli_run import run_core from .evaluators.evaluator_base import EvaluatorBase -from .evaluators.llm_evaluator import LLMEvaluator -from .models import EvaluationSetResult +from .evaluators.evaluator_factory import EvaluatorFactory +from .models import EvaluationSet, EvaluationSetResult, EvaluatorCategory console = ConsoleLogger() @@ -23,7 +25,9 @@ def __init__(self, entrypoint: str, eval_set_path: str | Path, workers: int): """Initialize the evaluation service. Args: + entrypoint: Path to the agent script to evaluate eval_set_path: Path to the evaluation set file (can be string or Path) + workers: Number of parallel workers for running evaluations """ self.entrypoint = entrypoint self.eval_set_path = Path(eval_set_path) @@ -31,6 +35,7 @@ def __init__(self, entrypoint: str, eval_set_path: str | Path, workers: int): self.evaluators = self._load_evaluators() self.num_workers = workers self.results_lock = asyncio.Lock() + self._progress_manager = None self._initialize_results() def _initialize_results(self) -> None: @@ -41,13 +46,13 @@ def _initialize_results(self) -> None: # Create results file timestamp = datetime.now(UTC).strftime("%M-%H-%d-%m-%Y") - eval_set_name = self.eval_set["name"] + eval_set_name = self.eval_set.name self.result_file = results_dir / f"eval-{eval_set_name}-{timestamp}.json" # Initialize with empty results initial_results = EvaluationSetResult( - eval_set_id=self.eval_set["id"], - eval_set_name=self.eval_set["name"], + eval_set_id=self.eval_set.id, + eval_set_name=self.eval_set.name, results=[], average_score=0.0, ) @@ -55,34 +60,45 @@ def _initialize_results(self) -> None: with open(self.result_file, "w", encoding="utf-8") as f: f.write(initial_results.model_dump_json(indent=2)) - def _load_eval_set(self) -> Dict[str, Any]: + def _load_eval_set(self) -> EvaluationSet: """Load the evaluation set from file. Returns: - The loaded evaluation set + The loaded evaluation set as EvaluationSet model """ with open(self.eval_set_path, "r", encoding="utf-8") as f: - return json.load(f) + data = json.load(f) + return EvaluationSet(**data) def _load_evaluators(self) -> List[EvaluatorBase]: """Load evaluators referenced by the evaluation set.""" evaluators = [] evaluators_dir = self.eval_set_path.parent.parent / "evaluators" - - for evaluator_id in self.eval_set["evaluatorRefs"]: - # Find evaluator file - evaluator_file = None - for file in evaluators_dir.glob("*.json"): - with open(file) as f: - data = json.load(f) - if data.get("id") == evaluator_id: - evaluator_file = data - break - - if not evaluator_file: - raise ValueError(f"Could not find evaluator with ID {evaluator_id}") - - evaluators.append(LLMEvaluator(evaluator_file)) + evaluator_refs = set(self.eval_set.evaluatorRefs) + found_evaluator_ids = set() + + # Load evaluators from JSON files + for file in evaluators_dir.glob("*.json"): + with open(file, "r", encoding="utf-8") as f: + data = json.load(f) + evaluator_id = data.get("id") + + if evaluator_id in evaluator_refs: + try: + evaluator = EvaluatorFactory.create_evaluator(data) + evaluators.append(evaluator) + found_evaluator_ids.add(evaluator_id) + except Exception as e: + console.warning( + f"Failed to create evaluator {evaluator_id}: {str(e)}" + ) + + # Check if all referenced evaluators were found + missing_evaluators = evaluator_refs - found_evaluator_ids + if missing_evaluators: + raise ValueError( + f"Could not find evaluators with IDs: {missing_evaluators}" + ) return evaluators @@ -116,7 +132,7 @@ def _run_agent(self, input_json: str) -> tuple[Dict[str, Any], bool]: input_json: JSON string containing input data Returns: - Agent output as dictionary + Agent output as dictionary and success status """ with tempfile.TemporaryDirectory() as tmpdir: try: @@ -160,40 +176,64 @@ def _run_agent(self, input_json: str) -> tuple[Dict[str, Any], bool]: console.warning(f"Error running agent: {str(e)}") return {"error": str(e)}, False - async def _process_evaluation(self, eval_item: Dict[str, Any]) -> None: + async def _process_evaluation( + self, eval_item: Dict[str, Any], worker_id: int + ) -> None: """Process a single evaluation item. Args: eval_item: The evaluation item to process + worker_id: ID of the worker processing this evaluation """ - console.info(f"Running evaluation: {eval_item['name']}") - - # Run the agent using the evaluation input - input_json = json.dumps(eval_item["inputs"]) - - # Run _run_agent in a non-async context using run_in_executor - loop = asyncio.get_running_loop() - actual_output, success = await loop.run_in_executor( - None, self._run_agent, input_json - ) - if success: - # Run each evaluator - eval_results = [] - for evaluator in self.evaluators: - result = await evaluator.evaluate( - evaluation_id=eval_item["id"], - evaluation_name=eval_item["name"], - input_data=eval_item["inputs"], - expected_output=eval_item["expectedOutput"], - actual_output=actual_output, - ) - eval_results.append(result) + eval_id = eval_item["id"] + + # Update progress to running + if self._progress_manager: + self._progress_manager.start_evaluation(eval_id, worker_id) + + try: + # Run the agent using the evaluation input + input_json = json.dumps(eval_item["inputs"]) + + # Run _run_agent in a non-async context using run_in_executor + loop = asyncio.get_running_loop() + actual_output, success = await loop.run_in_executor( + None, self._run_agent, input_json + ) + + if success: + # Run each evaluator + eval_results = [] + for evaluator in self.evaluators: + result = await evaluator.evaluate( + evaluation_id=eval_item["id"], + evaluation_name=eval_item["name"], + input_data=eval_item["inputs"], + expected_output=eval_item["expectedOutput"], + actual_output=actual_output, + ) + eval_results.append(result) + + # Write results immediately + await self._write_results(eval_results) + + # Update progress to completed + if self._progress_manager: + self._progress_manager.complete_evaluation(eval_id) + else: + # Mark as failed if agent execution failed + if self._progress_manager: + self._progress_manager.fail_evaluation( + eval_id, "Agent execution failed" + ) - # Write results immediately - await self._write_results(eval_results) + except Exception as e: + # Mark as failed with error message + if self._progress_manager: + self._progress_manager.fail_evaluation(eval_id, str(e)) + raise # TODO: here we should send the event to the SW eval API - console.info(f"Evaluation {eval_item['name']} complete.") async def _producer_task(self, task_queue: asyncio.Queue) -> None: """Producer task that adds all evaluations to the queue. @@ -201,8 +241,8 @@ async def _producer_task(self, task_queue: asyncio.Queue) -> None: Args: task_queue: The asyncio queue to add tasks to """ - for eval_item in self.eval_set["evaluations"]: - await task_queue.put(eval_item) + for eval_item in self.eval_set.evaluations: + await task_queue.put(eval_item.model_dump()) # Add sentinel values to signal workers to stop for _ in range(self.num_workers): @@ -223,35 +263,47 @@ async def _consumer_task(self, task_queue: asyncio.Queue, worker_id: int) -> Non return try: - await self._process_evaluation(eval_item) + await self._process_evaluation(eval_item, worker_id) task_queue.task_done() except Exception as e: - import click - # Log error and continue to next item task_queue.task_done() console.warning( - f"Worker {worker_id} failed evaluation {eval_item.get('name', 'Unknown')}: {str(e)}" + f"Evaluation {eval_item.get('name', 'Unknown')} failed: {str(e)}" ) async def run_evaluation(self) -> None: """Run the evaluation set using multiple worker tasks.""" - task_queue = asyncio.Queue() + # Display starting message + console.info( + f"Starting evaluating {click.style(self.eval_set.name, fg='cyan')} evaluation set..." + ) - producer = asyncio.create_task(self._producer_task(task_queue)) + # Prepare items for progress tracker + progress_items = [ + {"id": eval_item.id, "name": eval_item.name} + for eval_item in self.eval_set.evaluations + ] - consumers = [] - for worker_id in range(self.num_workers): - consumer = asyncio.create_task(self._consumer_task(task_queue, worker_id)) - consumers.append(consumer) + # Use Rich Progress for evaluation tracking + with console.evaluation_progress(progress_items) as progress_manager: + self._progress_manager = progress_manager - await producer + task_queue = asyncio.Queue() - await task_queue.join() + producer = asyncio.create_task(self._producer_task(task_queue)) - # Wait for all consumers to finish - await asyncio.gather(*consumers) + consumers = [] + for worker_id in range(self.num_workers): + consumer = asyncio.create_task( + self._consumer_task(task_queue, worker_id) + ) + consumers.append(consumer) - console.success( - f"All evaluations complete. Results saved to {self.result_file}" - ) + await producer + await task_queue.join() + + # Wait for all consumers to finish + await asyncio.gather(*consumers) + + console.info(f"Results saved to {click.style(self.result_file, fg='cyan')}") diff --git a/src/uipath/_cli/_evals/evaluators/__init__.py b/src/uipath/_cli/_evals/evaluators/__init__.py new file mode 100644 index 00000000..03cbd970 --- /dev/null +++ b/src/uipath/_cli/_evals/evaluators/__init__.py @@ -0,0 +1,20 @@ +"""Evaluators package for the evaluation system. + +This package contains all evaluator types and the factory for creating them. +""" + +from .agent_scorer_evaluator import AgentScorerEvaluator +from .deterministic_evaluator import DeterministicEvaluator +from .evaluator_base import EvaluatorBase +from .evaluator_factory import EvaluatorFactory +from .llm_as_judge_evaluator import LlmAsAJudgeEvaluator +from .trajectory_evaluator import TrajectoryEvaluator + +__all__ = [ + "EvaluatorBase", + "EvaluatorFactory", + "DeterministicEvaluator", + "LlmAsAJudgeEvaluator", + "AgentScorerEvaluator", + "TrajectoryEvaluator", +] diff --git a/src/uipath/_cli/_evals/evaluators/agent_scorer_evaluator.py b/src/uipath/_cli/_evals/evaluators/agent_scorer_evaluator.py new file mode 100644 index 00000000..11bd1c51 --- /dev/null +++ b/src/uipath/_cli/_evals/evaluators/agent_scorer_evaluator.py @@ -0,0 +1,61 @@ +from datetime import datetime +from typing import Any, Dict, Optional + +from ..models import EvaluationResult, EvaluatorCategory, EvaluatorType +from .evaluator_base import EvaluatorBase, EvaluatorBaseParams + + +class AgentScorerEvaluator(EvaluatorBase): + """Evaluator that uses an agent to score outputs.""" + + def __init__( + self, + agent_config: Dict[str, Any] = None, + scoring_criteria: Dict[str, Any] = None, + target_output_key: str = "*", + ): + """Initialize the agent scorer evaluator. + + Args: + agent_config: Configuration for the scoring agent + scoring_criteria: Criteria used for scoring + target_output_key: Key in output to evaluate ("*" for entire output) + """ + super().__init__() + self.agent_config = agent_config or {} + self.scoring_criteria = scoring_criteria or {} + self.target_output_key = target_output_key + + async def evaluate( + self, + evaluation_id: str, + evaluation_name: str, + input_data: Dict[str, Any], + expected_output: Dict[str, Any], + actual_output: Dict[str, Any], + ) -> EvaluationResult: + """Evaluate using an agent scorer. + + Args: + evaluation_id: The ID of the evaluation being processed + evaluation_name: The name of the evaluation + input_data: The input data for the evaluation + expected_output: The expected output + actual_output: The actual output from the agent + + Returns: + EvaluationResult containing the score and details + """ + # TODO: Implement this + + return EvaluationResult( + evaluation_id=evaluation_id, + evaluation_name=evaluation_name, + evaluator_id=self.id, #type: ignore + evaluator_name="AgentScorer", + score=0.5, # Placeholder score + input=input_data, + expected_output=expected_output, + actual_output=actual_output, + details="Agent scorer evaluation not yet implemented", + ) diff --git a/src/uipath/_cli/_evals/evaluators/deterministic_evaluator.py b/src/uipath/_cli/_evals/evaluators/deterministic_evaluator.py new file mode 100644 index 00000000..d9814285 --- /dev/null +++ b/src/uipath/_cli/_evals/evaluators/deterministic_evaluator.py @@ -0,0 +1,58 @@ +import json +import re +from datetime import datetime +from typing import Any, Dict, Optional + +from ..models import EvaluationResult, EvaluatorCategory, EvaluatorType +from .evaluator_base import EvaluatorBase, EvaluatorBaseParams + + +class DeterministicEvaluator(EvaluatorBase): + """Evaluator for deterministic/rule-based evaluations.""" + + def __init__( + self, rule_config: Dict[str, Any] = None, target_output_key: str = "*" + ): + """Initialize the deterministic evaluator. + + Args: + rule_config: Configuration for the rule (expected_value, regex_pattern, etc.) + target_output_key: Key in output to evaluate ("*" for entire output) + """ + super().__init__() + self.rule_config = rule_config or {} + self.target_output_key = target_output_key + + async def evaluate( + self, + evaluation_id: str, + evaluation_name: str, + input_data: Dict[str, Any], + expected_output: Dict[str, Any], + actual_output: Dict[str, Any], + ) -> EvaluationResult: + """Evaluate using deterministic rules. + + Args: + evaluation_id: The ID of the evaluation being processed + evaluation_name: The name of the evaluation + input_data: The input data for the evaluation + expected_output: The expected output + actual_output: The actual output from the agent + + Returns: + EvaluationResult containing the score and details + """ + # TODO: implement this + + return EvaluationResult( + evaluation_id=evaluation_id, + evaluation_name=evaluation_name, + evaluator_id=self.id, #type: ignore + evaluator_name=self.name, #type: ignore + score=0.5, + input=input_data, + expected_output=expected_output, + actual_output=actual_output, + details="details", + ) diff --git a/src/uipath/_cli/_evals/evaluators/evaluator_base.py b/src/uipath/_cli/_evals/evaluators/evaluator_base.py index 4025e304..5c094e7f 100644 --- a/src/uipath/_cli/_evals/evaluators/evaluator_base.py +++ b/src/uipath/_cli/_evals/evaluators/evaluator_base.py @@ -1,6 +1,74 @@ -from abc import ABC +from abc import ABC, abstractmethod +from dataclasses import dataclass +from typing import Any, Dict + +from uipath._cli._evals.models import EvaluationResult, EvaluatorCategory, EvaluatorType + + +@dataclass +class EvaluatorBaseParams: + """Parameters for initializing the base evaluator.""" + + evaluator_id: str + category: EvaluatorCategory + evaluator_type: EvaluatorType + name: str + description: str + created_at: str + updated_at: str class EvaluatorBase(ABC): - async def evaluate(self, evaluation_id, evaluation_name, input_data, expected_output, actual_output): + """Abstract base class for all evaluators.""" + + def __init__(self): + # initialization done via 'from_params' function pass + + @classmethod + def from_params(cls, params: EvaluatorBaseParams, **kwargs): + """Initialize the base evaluator from parameters. + + Args: + params: EvaluatorBaseParams containing base configuration + **kwargs: Additional specific parameters for concrete evaluators + + Returns: + Initialized evaluator instance + """ + instance = cls(**kwargs) + instance.id = params.evaluator_id + instance.category = params.category + instance.type = params.evaluator_type + instance.name = params.name + instance.description = params.description + instance.created_at = params.created_at + instance.updated_at = params.updated_at + return instance + + @abstractmethod + async def evaluate( + self, + evaluation_id: str, + evaluation_name: str, + input_data: Dict[str, Any], + expected_output: Dict[str, Any], + actual_output: Dict[str, Any], + ) -> EvaluationResult: + """Evaluate the given data and return a result. + + Args: + evaluation_id: The ID of the evaluation being processed + evaluation_name: The name of the evaluation + input_data: The input data for the evaluation + expected_output: The expected output + actual_output: The actual output from the agent + + Returns: + EvaluationResult containing the score and details + """ + pass + + def __repr__(self) -> str: + """String representation of the evaluator.""" + return f"{self.__class__.__name__}(id='{self.id}', name='{self.name}', category={self.category.name})" diff --git a/src/uipath/_cli/_evals/evaluators/evaluator_factory.py b/src/uipath/_cli/_evals/evaluators/evaluator_factory.py new file mode 100644 index 00000000..1c84df19 --- /dev/null +++ b/src/uipath/_cli/_evals/evaluators/evaluator_factory.py @@ -0,0 +1,105 @@ +from datetime import datetime +from typing import Any, Dict + +from ..models import EvaluatorCategory, EvaluatorType +from .agent_scorer_evaluator import AgentScorerEvaluator +from .deterministic_evaluator import DeterministicEvaluator +from .evaluator_base import EvaluatorBase, EvaluatorBaseParams +from .llm_as_judge_evaluator import LlmAsAJudgeEvaluator +from .trajectory_evaluator import TrajectoryEvaluator + + +class EvaluatorFactory: + """Factory class for creating evaluator instances based on configuration.""" + + @staticmethod + def create_evaluator(data: Dict[str, Any]) -> EvaluatorBase: + """Create an evaluator instance from configuration data. + + Args: + data: Dictionary containing evaluator configuration from JSON file + + Returns: + Appropriate evaluator instance based on category + + Raises: + ValueError: If category is unknown or required fields are missing + """ + # Extract common fields + evaluator_id = data.get("id") + if not evaluator_id: + raise ValueError("Evaluator configuration must include 'id' field") + + category = EvaluatorCategory.from_int(data.get("category")) + evaluator_type = EvaluatorType.from_int(data.get("type", EvaluatorType.Unknown)) + name = data.get("name", "") + description = data.get("description", "") + created_at = data.get("createdAt", "") + updated_at = data.get("updatedAt", "") + + # Create base parameters + base_params = EvaluatorBaseParams( + evaluator_id=evaluator_id, + category=category, + evaluator_type=evaluator_type, + name=name, + description=description, + created_at=created_at, + updated_at=updated_at, + ) + + # Create evaluator based on category + if category == EvaluatorCategory.Deterministic: + return EvaluatorFactory._create_deterministic_evaluator(base_params, data) + elif category == EvaluatorCategory.LlmAsAJudge: + return EvaluatorFactory._create_llm_as_judge_evaluator(base_params, data) + elif category == EvaluatorCategory.AgentScorer: + return EvaluatorFactory._create_agent_scorer_evaluator(base_params, data) + elif category == EvaluatorCategory.Trajectory: + return EvaluatorFactory._create_trajectory_evaluator(base_params, data) + else: + raise ValueError(f"Unknown evaluator category: {category}") + + @staticmethod + def _create_deterministic_evaluator( + base_params: EvaluatorBaseParams, data: Dict[str, Any] + ) -> DeterministicEvaluator: + """Create a deterministic evaluator.""" + # TODO: implement this + pass + + @staticmethod + def _create_llm_as_judge_evaluator( + base_params: EvaluatorBaseParams, data: Dict[str, Any] + ) -> LlmAsAJudgeEvaluator: + """Create an LLM-as-a-judge evaluator.""" + prompt = data.get("prompt", "") + if not prompt: + raise ValueError("LLM evaluator must include 'prompt' field") + + model = data.get("model", "") + if not model: + raise ValueError("LLM evaluator must include 'model' field") + + return LlmAsAJudgeEvaluator.from_params( + base_params, + prompt=prompt, + model=model, + target_output_key=data.get("targetOutputKey", "*"), + ) + + @staticmethod + def _create_agent_scorer_evaluator( + base_params: EvaluatorBaseParams, data: Dict[str, Any] + ) -> AgentScorerEvaluator: + """Create an agent scorer evaluator.""" + #TODO: implement this + pass + + @staticmethod + def _create_trajectory_evaluator( + base_params: EvaluatorBaseParams, data: Dict[str, Any] + ) -> TrajectoryEvaluator: + """Create a trajectory evaluator.""" + #TODO: implement this + pass diff --git a/src/uipath/_cli/_evals/evaluators/llm_as_judge_evaluator.py b/src/uipath/_cli/_evals/evaluators/llm_as_judge_evaluator.py new file mode 100644 index 00000000..c9c2cb66 --- /dev/null +++ b/src/uipath/_cli/_evals/evaluators/llm_as_judge_evaluator.py @@ -0,0 +1,180 @@ +import json +from ...._services.llm_gateway_service import UiPathLlmChatService +from ...._execution_context import ExecutionContext +from typing import Any, Dict, Optional +from ...._utils.constants import ( + ENV_BASE_URL, + ENV_UIPATH_ACCESS_TOKEN, + ENV_UNATTENDED_USER_ACCESS_TOKEN, COMMUNITY_AGENTS_SUFFIX, +) +from ...._config import Config +from ..models import EvaluationResult, EvaluatorCategory, EvaluatorType, LLMResponse +from .evaluator_base import EvaluatorBase, EvaluatorBaseParams + + +class LlmAsAJudgeEvaluator(EvaluatorBase): + """Evaluator that uses an LLM to judge the quality of outputs.""" + + def __init__(self, prompt: str = "", model: str = "", target_output_key: str = "*"): + """Initialize the LLM-as-a-judge evaluator. + + Args: + prompt: The prompt template for the LLM + model: The model to use for evaluation + target_output_key: Key in output to evaluate ("*" for entire output) + """ + super().__init__() + self.actual_output_placeholder="{{ActualOutput}}" + self.expected_output_placeholder="{{ExpectedOutput}}" + self._initialize_llm() + self.prompt = prompt + self.model = model + self.target_output_key = target_output_key + + def _initialize_llm(self): + """Initialize the LLM used for evaluation.""" + import os + base_url_value = os.getenv(ENV_BASE_URL) + secret_value = os.getenv(ENV_UNATTENDED_USER_ACCESS_TOKEN) or os.getenv( + ENV_UIPATH_ACCESS_TOKEN + ) + config = Config( + base_url=base_url_value, # type: ignore + secret=secret_value, # type: ignore + ) + self.llm = UiPathLlmChatService(config, ExecutionContext()) + + async def evaluate( + self, + evaluation_id: str, + evaluation_name: str, + input_data: Dict[str, Any], + expected_output: Dict[str, Any], + actual_output: Dict[str, Any], + ) -> EvaluationResult: + """Evaluate using an LLM as a judge. + + Args: + evaluation_id: The ID of the evaluation being processed + evaluation_name: The name of the evaluation + input_data: The input data for the evaluation + expected_output: The expected output + actual_output: The actual output from the agent + + Returns: + EvaluationResult containing the score and details + """ + # Extract the target value to evaluate + target_value = self._extract_target_value(actual_output) + expected_value = self._extract_target_value(expected_output) + + # Create the evaluation prompt + evaluation_prompt = self._create_evaluation_prompt( + expected_value, target_value + ) + + llm_response = await self._get_llm_response(evaluation_prompt) + + return EvaluationResult( + evaluation_id=evaluation_id, + evaluation_name=evaluation_name, + evaluator_id=self.id, #type: ignore + evaluator_name=self.name, #type: ignore + score=llm_response.score, + input=input_data, + expected_output=expected_output, + actual_output=actual_output, + details=llm_response.justification, + ) + + def _extract_target_value(self, output: Dict[str, Any]) -> Any: + """Extract the target value from output based on target_output_key.""" + if self.target_output_key == "*": + return output + + # Handle nested keys like "result.data.value" + keys = self.target_output_key.split(".") + value = output + + try: + for key in keys: + if isinstance(value, dict): + value = value[key] + else: + return None + return value + except (KeyError, TypeError): + return None + + def _create_evaluation_prompt( + self, expected_output: Dict[str, Any], actual_output: Dict[str, Any] + ) -> str: + """Create the evaluation prompt for the LLM.""" + # Convert complex objects to JSON strings for the prompt + # Use the template prompt and substitute variables + formatted_prompt = self.prompt.replace( + self.actual_output_placeholder, json.dumps(actual_output, indent=2) + ) + formatted_prompt = formatted_prompt.replace( + self.expected_output_placeholder, json.dumps(expected_output, indent=2) + ) + + return formatted_prompt + + async def _get_llm_response(self, evaluation_prompt: str) -> LLMResponse: + """Get response from the LLM. + + Args: + prompt: The formatted prompt to send to the LLM + + Returns: + LLMResponse with score and justification + """ + try: + # remove community-agents suffix from llm model name + model = self.model + if model.endswith(COMMUNITY_AGENTS_SUFFIX): + model = model.replace(COMMUNITY_AGENTS_SUFFIX, "") + + # Prepare the request + request_data = { + "model": model, + "messages": [{"role": "user", "content": evaluation_prompt}], + "response_format": { + "type": "json_schema", + "json_schema": { + "name": "evaluation_response", + "schema": { + "type": "object", + "properties": { + "score": { + "type": "number", + "minimum": 0, + "maximum": 100, + "description": "Score between 0 and 100", + }, + "justification": { + "type": "string", + "description": "Explanation for the score", + }, + }, + "required": ["score", "justification"], + }, + }, + }, + } + + response = await self.llm.chat_completions(**request_data) + + try: + return LLMResponse(**json.loads(response.choices[-1].message.content)) + except (json.JSONDecodeError, ValueError) as e: + return LLMResponse( + score=0.0, justification=f"Error parsing LLM response: {str(e)}" + ) + + except Exception as e: + # Fallback in case of any errors + return LLMResponse( + score=0.0, justification=f"Error during LLM evaluation: {str(e)}" + ) diff --git a/src/uipath/_cli/_evals/evaluators/llm_evaluator.py b/src/uipath/_cli/_evals/evaluators/llm_evaluator.py deleted file mode 100644 index 1b3068d6..00000000 --- a/src/uipath/_cli/_evals/evaluators/llm_evaluator.py +++ /dev/null @@ -1,133 +0,0 @@ -import json -from typing import Any, Dict, Optional - -from .evaluator_base import EvaluatorBase -from ...._config import Config -from ...._execution_context import ExecutionContext -from ...._services.llm_gateway_service import UiPathLlmChatService -from ...._utils.constants import ( - COMMUNITY_AGENTS_SUFFIX, - ENV_BASE_URL, - ENV_UIPATH_ACCESS_TOKEN, - ENV_UNATTENDED_USER_ACCESS_TOKEN, -) -from ..._utils._debug import console -from ..models import EvaluationResult, EvaluatorCategory, LLMResponse - - -class LLMEvaluator(EvaluatorBase): - """Service for evaluating outputs using LLM.""" - - def __init__(self, evaluator_config: Dict[str, Any]): - """Initialize LLM evaluator. - - Args: - evaluator_config: Configuration for the evaluator from evaluator JSON file - """ - import os - - self.config = evaluator_config - base_url_value = os.getenv(ENV_BASE_URL) - secret_value = os.getenv(ENV_UNATTENDED_USER_ACCESS_TOKEN) or os.getenv( - ENV_UIPATH_ACCESS_TOKEN - ) - config = Config( - base_url=base_url_value, # type: ignore - secret=secret_value, # type: ignore - ) - self.llm = UiPathLlmChatService(config, ExecutionContext()) - - # Validate evaluator category - if self.config.get("category") != EvaluatorCategory.LlmAsAJudge: - raise ValueError("Evaluator must be of type LlmAsAJudge") - - async def evaluate( - self, - evaluation_id: str, - evaluation_name: str, - input_data: Dict[str, Any], - expected_output: Dict[str, Any], - actual_output: Dict[str, Any], - ) -> EvaluationResult: - """Evaluate the actual output against expected output using LLM. - - Args: - evaluation_id: ID of the evaluation - evaluation_name: Name of the evaluation - input_data: Input data used for the evaluation - expected_output: Expected output from the evaluation - actual_output: Actual output received - - Returns: - EvaluationResult containing the evaluation score and details - """ - # Prepare the prompt by replacing placeholders - prompt = self.config["prompt"] - prompt = prompt.replace( - "{{ExpectedOutput}}", json.dumps(expected_output, indent=2) - ) - content = prompt.replace( - "{{ActualOutput}}", json.dumps(actual_output, indent=2) - ) - - model: Optional[str] = self.config.get("model", None) - if not model: - console.error("Evaluator model cannot be extracted") - - # remove community-agents suffix from llm model name - if model.endswith(COMMUNITY_AGENTS_SUFFIX): - model = model.replace(COMMUNITY_AGENTS_SUFFIX, "") - - response = await self.llm.chat_completions( - messages=[{"role": "user", "content": content}], - model=model, - response_format={ - "type": "json_schema", - "json_schema": { - "name": "evaluation_result", - "strict": True, - "schema": { - "type": "object", - "properties": { - "score": { - "type": "number", - "minimum": 0, - "maximum": 100, - "description": "Similarity score between expected and actual output (0-100)", - }, - "justification": { - "type": "string", - "description": "Detailed explanation of why this score was given", - }, - }, - "required": ["score", "justification"], - "additionalProperties": False, - }, - }, - }, - ) - try: - llm_response = LLMResponse( - **json.loads(response.choices[-1].message.content) - ) - except Exception as e: - raise Exception(f"Error parsing LLM response: {e}") from e - # Leave those comments - # llm_response = LLMResponse(similarity_score=90, score_justification="test justification") - score = llm_response.score - details = llm_response.justification - - if score < 0 or score > 100: - raise ValueError(f"Score {score} is outside valid range 0-100") - - return EvaluationResult( - evaluation_id=evaluation_id, - evaluation_name=evaluation_name, - evaluator_id=self.config["id"], - evaluator_name=self.config["name"], - score=score, - input=input_data, - expected_output=expected_output, - actual_output=actual_output, - details=details, - ) diff --git a/src/uipath/_cli/_evals/evaluators/trajectory_evaluator.py b/src/uipath/_cli/_evals/evaluators/trajectory_evaluator.py new file mode 100644 index 00000000..95e19fdf --- /dev/null +++ b/src/uipath/_cli/_evals/evaluators/trajectory_evaluator.py @@ -0,0 +1,61 @@ +from datetime import datetime +from typing import Any, Dict, Optional + +from ..models import EvaluationResult, EvaluatorCategory, EvaluatorType +from .evaluator_base import EvaluatorBase, EvaluatorBaseParams + + +class TrajectoryEvaluator(EvaluatorBase): + """Evaluator that analyzes the trajectory/path taken to reach outputs.""" + + def __init__( + self, + trajectory_config: Dict[str, Any] = None, + step_weights: Dict[str, float] = None, + target_output_key: str = "*", + ): + """Initialize the trajectory evaluator. + + Args: + trajectory_config: Configuration for trajectory analysis + step_weights: Weights for different steps in the trajectory + target_output_key: Key in output to evaluate ("*" for entire output) + """ + super().__init__() + self.trajectory_config = trajectory_config or {} + self.step_weights = step_weights or {} + self.target_output_key = target_output_key + + async def evaluate( + self, + evaluation_id: str, + evaluation_name: str, + input_data: Dict[str, Any], + expected_output: Dict[str, Any], + actual_output: Dict[str, Any], + ) -> EvaluationResult: + """Evaluate using trajectory analysis. + + Args: + evaluation_id: The ID of the evaluation being processed + evaluation_name: The name of the evaluation + input_data: The input data for the evaluation + expected_output: The expected output + actual_output: The actual output from the agent + + Returns: + EvaluationResult containing the score and details + """ + # TODO: Implement this + + return EvaluationResult( + evaluation_id=evaluation_id, + evaluation_name=evaluation_name, + evaluator_id=self.id, #type: ignore + evaluator_name=self.name, #type: ignore + score=0.7, + input=input_data, + expected_output=expected_output, + actual_output=actual_output, + details="Trajectory evaluation not yet implemented", + ) diff --git a/src/uipath/_cli/_evals/models/__init__.py b/src/uipath/_cli/_evals/models/__init__.py new file mode 100644 index 00000000..5b88ac93 --- /dev/null +++ b/src/uipath/_cli/_evals/models/__init__.py @@ -0,0 +1,13 @@ +from uipath._cli._evals.models.evaluation_set import EvaluationItem, EvaluationSet +from uipath._cli._evals.models.evaluators import LLMResponse, EvaluatorCategory, EvaluatorType, EvaluationResult, \ + EvaluationSetResult + +__all__ = [ + "LLMResponse", + "EvaluatorCategory", + "EvaluatorType", + "EvaluationResult", + "EvaluationSetResult", + "EvaluationItem", + "EvaluationSet", +] diff --git a/src/uipath/_cli/_evals/models/evaluation_set.py b/src/uipath/_cli/_evals/models/evaluation_set.py new file mode 100644 index 00000000..05fb4209 --- /dev/null +++ b/src/uipath/_cli/_evals/models/evaluation_set.py @@ -0,0 +1,37 @@ +from datetime import datetime +from enum import IntEnum +from typing import Any, Dict, List, Optional + +from pydantic import BaseModel, Field + +class EvaluationItem(BaseModel): + """Individual evaluation item within an evaluation set.""" + + id: str + name: str + inputs: Dict[str, Any] + expectedOutput: Dict[str, Any] + expectedAgentBehavior: str = "" + simulationInstructions: str = "" + simulateInput: bool = False + inputGenerationInstructions: str = "" + simulateTools: bool = False + toolsToSimulate: List[str] = Field(default_factory=list) + evalSetId: str + createdAt: str + updatedAt: str + + +class EvaluationSet(BaseModel): + """Complete evaluation set model.""" + + id: str + fileName: str + evaluatorRefs: List[str] = Field(default_factory=list) + evaluations: List[EvaluationItem] = Field(default_factory=list) + name: str + batchSize: int = 10 + timeoutMinutes: int = 20 + modelSettings: List[Dict[str, Any]] = Field(default_factory=list) + createdAt: str + updatedAt: str diff --git a/src/uipath/_cli/_evals/models.py b/src/uipath/_cli/_evals/models/evaluators.py similarity index 65% rename from src/uipath/_cli/_evals/models.py rename to src/uipath/_cli/_evals/models/evaluators.py index af0595c7..8ed70222 100644 --- a/src/uipath/_cli/_evals/models.py +++ b/src/uipath/_cli/_evals/models/evaluators.py @@ -4,7 +4,6 @@ from pydantic import BaseModel, Field - class LLMResponse(BaseModel): score: float justification: str @@ -18,6 +17,14 @@ class EvaluatorCategory(IntEnum): AgentScorer = 2 Trajectory = 3 + @classmethod + def from_int(cls, value): + """Construct EvaluatorCategory from an int value.""" + if value in cls._value2member_map_: + return cls(value) + else: + raise ValueError(f"{value} is not a valid EvaluatorCategory value") + class EvaluatorType(IntEnum): """Subtypes of evaluators.""" @@ -33,6 +40,13 @@ class EvaluatorType(IntEnum): ContextPrecision = 8 Faithfulness = 9 + @classmethod + def from_int(cls, value): + """Construct EvaluatorCategory from an int value.""" + if value in cls._value2member_map_: + return cls(value) + else: + raise ValueError(f"{value} is not a valid EvaluatorType value") class EvaluationResult(BaseModel): """Result of a single evaluation.""" @@ -50,10 +64,9 @@ class EvaluationResult(BaseModel): class EvaluationSetResult(BaseModel): - """Results of running an evaluation set.""" + """Result of a complete evaluation set.""" eval_set_id: str eval_set_name: str results: List[EvaluationResult] average_score: float - timestamp: datetime = Field(default_factory=datetime.utcnow) diff --git a/src/uipath/_cli/_evals/progress_reporter.py b/src/uipath/_cli/_evals/progress_reporter.py new file mode 100644 index 00000000..19ca2a92 --- /dev/null +++ b/src/uipath/_cli/_evals/progress_reporter.py @@ -0,0 +1,3 @@ +class ProgressReporter: + def __init__(self): + pass diff --git a/src/uipath/_cli/_utils/_console.py b/src/uipath/_cli/_utils/_console.py index ee6ba481..4f2a5d42 100644 --- a/src/uipath/_cli/_utils/_console.py +++ b/src/uipath/_cli/_utils/_console.py @@ -1,10 +1,16 @@ from contextlib import contextmanager from enum import Enum -from typing import Any, Iterator, List, Optional, Type, TypeVar +from typing import Any, Dict, Iterator, List, Optional, Type, TypeVar import click from rich.console import Console from rich.live import Live +from rich.progress import ( + Progress, + SpinnerColumn, + TextColumn, + TimeElapsedColumn, +) from rich.spinner import Spinner as RichSpinner from rich.text import Text @@ -50,6 +56,8 @@ def __init__(self): self._console = Console() self._spinner_live: Optional[Live] = None self._spinner = RichSpinner("dots") + self._progress: Optional[Progress] = None + self._progress_tasks: Dict[str, int] = {} self._initialized = True def _stop_spinner_if_active(self) -> None: @@ -58,6 +66,13 @@ def _stop_spinner_if_active(self) -> None: self._spinner_live.stop() self._spinner_live = None + def _stop_progress_if_active(self) -> None: + """Internal method to stop the progress if it's active.""" + if self._progress: + self._progress.stop() + self._progress = None + self._progress_tasks.clear() + def log( self, message: str, level: LogLevel = LogLevel.INFO, fg: Optional[str] = None ) -> None: @@ -203,6 +218,44 @@ def update_spinner(self, message: str) -> None: if self._spinner_live and self._spinner_live.is_started: self._spinner.text = Text(message) + @contextmanager + def evaluation_progress( + self, evaluations: List[Dict[str, str]] + ) -> Iterator["EvaluationProgressManager"]: + """Context manager for evaluation progress tracking. + + Args: + evaluations: List of evaluation items with 'id' and 'name' keys + + Yields: + EvaluationProgressManager instance + """ + try: + # Stop any existing progress or spinner + self._stop_spinner_if_active() + self._stop_progress_if_active() + + # Create progress with custom columns + self._progress = Progress( + SpinnerColumn(), + TextColumn("[bold blue]{task.description}"), + TimeElapsedColumn(), + console=self._console, + transient=False, + ) + + # Add tasks for each evaluation + for eval_item in evaluations: + task_id = self._progress.add_task(eval_item["name"], total=1) + self._progress_tasks[eval_item["id"]] = task_id + + self._progress.start() + + yield EvaluationProgressManager(self._progress, self._progress_tasks) + + finally: + self._stop_progress_if_active() + @classmethod def get_instance(cls) -> "ConsoleLogger": """Get the singleton instance of ConsoleLogger. @@ -213,3 +266,60 @@ def get_instance(cls) -> "ConsoleLogger": if cls._instance is None: return cls() return cls._instance + + +class EvaluationProgressManager: + """Manager for evaluation progress updates.""" + + def __init__(self, progress: Progress, tasks: Dict[str, int]): + """Initialize the progress manager. + + Args: + progress: The Rich Progress instance + tasks: Mapping of evaluation IDs to task IDs + """ + self.progress = progress + self.tasks = tasks + + def start_evaluation(self, eval_id: str, worker_id: int) -> None: + """Mark an evaluation as started. + + Args: + eval_id: The evaluation ID + worker_id: The worker ID processing this evaluation + """ + # No need to update anything - spinner and timer will show it's running + pass + + def complete_evaluation(self, eval_id: str) -> None: + """Mark an evaluation as completed. + + Args: + eval_id: The evaluation ID + """ + if eval_id in self.tasks: + task_id = self.tasks[eval_id] + # Update description to show completion + current_desc = self.progress.tasks[task_id].description + self.progress.update( + task_id, completed=1, description=f"[green]✅ {current_desc}[/green]" + ) + + def fail_evaluation(self, eval_id: str, error_message: str) -> None: + """Mark an evaluation as failed. + + Args: + eval_id: The evaluation ID + error_message: The error message + """ + if eval_id in self.tasks: + task_id = self.tasks[eval_id] + # Truncate error message if too long + short_error = ( + error_message[:40] + "..." if len(error_message) > 40 else error_message + ) + # Update the description to show failure + current_desc = self.progress.tasks[task_id].description + self.progress.update( + task_id, description=f"[red]❌ {current_desc} - {short_error}[/red]" + ) diff --git a/src/uipath/_cli/cli_eval.py b/src/uipath/_cli/cli_eval.py index a3c322c3..d6a1333a 100644 --- a/src/uipath/_cli/cli_eval.py +++ b/src/uipath/_cli/cli_eval.py @@ -2,7 +2,7 @@ import asyncio import os from pathlib import Path -from typing import Optional +from typing import Optional, Tuple import click from dotenv import load_dotenv @@ -16,44 +16,72 @@ load_dotenv(override=True) -@click.command() -@click.argument("entrypoint", required=True) -@click.argument("eval_set", required=True, type=click.Path(exists=True)) -@click.option( - "--workers", - type=int, - default=8, - help="Number of parallel workers for running evaluations (default: 8)", -) -@track(when=lambda *_a, **_kw: os.getenv(ENV_JOB_ID) is None) -def eval(entrypoint: str, eval_set: str, workers: int) -> None: - """Run an evaluation set against the agent. +def eval_agent( + entrypoint: str, eval_set: str, workers: int = 8, **kwargs +) -> Tuple[bool, Optional[str], Optional[str]]: + """Core evaluation logic that can be called programmatically. Args: entrypoint: Path to the agent script to evaluate eval_set: Path to the evaluation set JSON file workers: Number of parallel workers for running evaluations + **kwargs: Additional arguments for future extensibility + + Returns: + Tuple containing: + - success: True if evaluation was successful + - error_message: Error message if any + - info_message: Info message if any """ try: # Validate file path eval_path = Path(eval_set) if not eval_path.is_file() or eval_path.suffix != ".json": - console.error("Evaluation set must be a JSON file") - click.get_current_context().exit(1) + return False, "Evaluation set must be a JSON file", None # Validate workers count if workers < 1: - console.error("Number of workers must be at least 1") - click.get_current_context().exit(1) + return False, "Number of workers must be at least 1", None # Run evaluation service = EvaluationService(entrypoint, eval_set, workers) asyncio.run(service.run_evaluation()) + return True, None, "Evaluation completed successfully" + except Exception as e: - console.error(f"Error running evaluation: {str(e)}") + return False, f"Error running evaluation: {str(e)}", None + + +@click.command() +@click.argument("entrypoint", required=True) +@click.argument("eval_set", required=True, type=click.Path(exists=True)) +@click.option( + "--workers", + type=int, + default=8, + help="Number of parallel workers for running evaluations (default: 8)", +) +@track(when=lambda *_a, **_kw: os.getenv(ENV_JOB_ID) is None) +def eval(entrypoint: str, eval_set: str, workers: int) -> None: + """Run an evaluation set against the agent. + + Args: + entrypoint: Path to the agent script to evaluate + eval_set: Path to the evaluation set JSON file + workers: Number of parallel workers for running evaluations + """ + success, error_message, info_message = eval_agent( + entrypoint=entrypoint, eval_set=eval_set, workers=workers + ) + + if error_message: + console.error(error_message) click.get_current_context().exit(1) + if info_message: + console.success(info_message) + if __name__ == "__main__": eval()