From 3ac6e7afbb92cc3520b0c8f82390e7a659849001 Mon Sep 17 00:00:00 2001 From: DAVID BOAZ Date: Tue, 2 Dec 2025 20:08:40 +0200 Subject: [PATCH 01/42] merge into altk --- altk/pre_tool/toolguard/README.md | 135 ++--- altk/pre_tool/toolguard/__init__.py | 3 + altk/pre_tool/toolguard/core/__init__.py | 18 - altk/pre_tool/toolguard/core/types.py | 64 --- altk/pre_tool/toolguard/examples/.gitignore | 1 - .../calculator_example/example_tools.py | 92 ---- .../calculator_example/run_example.py | 106 ---- .../toolguard/examples/full_test_calc.py | 110 ----- .../toolguard/examples/tool_guard_example.py | 52 -- .../toolguard/pre_tool_guard/__init__.py | 3 - .../pre_tool_guard/pre_tool_guard.py | 91 ---- altk/pre_tool/toolguard/toolguard/__init__.py | 10 +- altk/pre_tool/toolguard/toolguard/__main__.py | 8 + .../pre_tool/toolguard/toolguard/buildtime.py | 132 +++++ altk/pre_tool/toolguard/toolguard/cli.py | 64 +++ .../toolguard/toolguard/common/langchain.py | 53 ++ .../toolguard/common/multi_process.py | 48 ++ .../toolguard/toolguard/common/open_api.py | 9 +- .../pre_tool/toolguard/toolguard/common/py.py | 27 +- .../toolguard/toolguard/common/ref.py | 3 +- .../toolguard/toolguard/common/safe_py.py | 11 + .../toolguard/toolguard/common/str.py | 21 +- altk/pre_tool/toolguard/toolguard/core.py | 115 ----- .../toolguard/toolguard/data_types.py | 79 ++- .../toolguard/gen_py/api_extractor.py | 44 +- .../toolguard/toolguard/gen_py/consts.py | 20 +- .../toolguard/gen_py/domain_from_funcs.py | 14 +- .../toolguard/gen_py/domain_from_openapi.py | 35 +- .../toolguard/gen_py/gen_toolguards.py | 86 +--- .../toolguard/gen_py/prompts/gen_tests.py | 14 +- .../toolguard/gen_py/prompts/improve_guard.py | 6 +- .../toolguard/gen_py/prompts/pseudo_code.py | 6 +- .../toolguard/gen_py/prompts/~python_code.py | 24 - .../toolguard/gen_py/templates/__init__.py | 13 +- .../toolguard/gen_py/templates/api_impl.j2 | 15 +- .../toolguard/gen_py/templates/tool_guard.j2 | 4 +- .../toolguard/gen_py/tool_dependencies.py | 10 +- .../toolguard/gen_py/tool_guard_generator.py | 156 +++--- .../toolguard/gen_py/utils/pyright.py | 8 +- .../toolguard/gen_py/utils/pytest.py | 98 ++-- .../toolguard/toolguard/gen_py/utils/venv.py | 20 - .../gen_spec}/__init__.py | 0 .../oas_summary.py} | 118 ++--- .../prompts/add_examples.txt | 2 +- .../prompts/add_policies.txt | 1 + .../prompts/add_references.txt | 2 +- .../prompts/create_examples.txt | 0 .../prompts/create_policy.txt | 0 .../prompts/examples_reviewer.txt | 2 +- .../prompts/fix_example.txt | 0 .../prompts/functions.txt | 0 .../prompts/merge.txt | 1 + .../prompts/merge_and_split.txt | 1 + .../prompts/merge_examples.txt | 0 .../prompts/policy_reviewer.txt | 0 .../prompts/split.txt | 1 + .../toolguard/gen_spec/spec_generator.py | 322 ++++++++++++ .../toolguard/toolguard/gen_spec/utils.py | 85 ++++ .../toolguard/toolguard/llm/tg_litellm.py | 176 +++++++ .../toolguard/toolguard/llm/tg_llmevalkit.py | 20 - .../toolguard/toolguard/logging_utils.py | 52 +- altk/pre_tool/toolguard/toolguard/runtime.py | 108 ++-- .../tool_policy_extractor/__init__.py | 0 .../text_tool_policy_generator.py | 467 ------------------ .../toolguard/tool_policy_extractor/utils.py | 96 ---- .../toolguard/toolguard_code_component.py | 101 ++++ .../toolguard/toolguard_spec_component.py | 61 +++ tests/pre_tool/toolguard/.gitignore | 1 + .../pre_tool/toolguard/inputs}/__init__.py | 0 tests/pre_tool/toolguard/inputs/oas.json | 284 +++++++++++ .../pre_tool/toolguard/inputs/policy_doc.md | 18 +- .../toolguard/inputs/step1/add_tool.json | 24 + .../toolguard/inputs/step1/divide_tool.json | 22 + .../inputs/step1/map_kdi_number.json | 3 + .../toolguard/inputs/step1/multiply_tool.json | 22 + .../toolguard/inputs/step1/subtract_tool.json | 3 + .../toolguard/inputs/tool_functions.py | 66 +++ .../toolguard/inputs/tool_langchain.py | 70 +++ .../pre_tool/toolguard/inputs/tool_methods.py | 66 +++ .../test_tool_guard_calculator_policy.py | 108 ---- .../pre_tool/toolguard/test_toolguard_code.py | 205 ++++++++ .../toolguard/test_toolguard_specs.py | 143 ++++++ 82 files changed, 2473 insertions(+), 2006 deletions(-) delete mode 100644 altk/pre_tool/toolguard/core/__init__.py delete mode 100644 altk/pre_tool/toolguard/core/types.py delete mode 100644 altk/pre_tool/toolguard/examples/.gitignore delete mode 100644 altk/pre_tool/toolguard/examples/calculator_example/example_tools.py delete mode 100644 altk/pre_tool/toolguard/examples/calculator_example/run_example.py delete mode 100644 altk/pre_tool/toolguard/examples/full_test_calc.py delete mode 100644 altk/pre_tool/toolguard/examples/tool_guard_example.py delete mode 100644 altk/pre_tool/toolguard/pre_tool_guard/__init__.py delete mode 100644 altk/pre_tool/toolguard/pre_tool_guard/pre_tool_guard.py create mode 100644 altk/pre_tool/toolguard/toolguard/__main__.py create mode 100644 altk/pre_tool/toolguard/toolguard/buildtime.py create mode 100644 altk/pre_tool/toolguard/toolguard/cli.py create mode 100644 altk/pre_tool/toolguard/toolguard/common/langchain.py create mode 100644 altk/pre_tool/toolguard/toolguard/common/multi_process.py create mode 100644 altk/pre_tool/toolguard/toolguard/common/safe_py.py delete mode 100644 altk/pre_tool/toolguard/toolguard/core.py delete mode 100644 altk/pre_tool/toolguard/toolguard/gen_py/prompts/~python_code.py delete mode 100644 altk/pre_tool/toolguard/toolguard/gen_py/utils/venv.py rename altk/pre_tool/toolguard/{examples => toolguard/gen_spec}/__init__.py (100%) rename altk/pre_tool/toolguard/toolguard/{tool_policy_extractor/create_oas_summary.py => gen_spec/oas_summary.py} (73%) rename altk/pre_tool/toolguard/toolguard/{tool_policy_extractor => gen_spec}/prompts/add_examples.txt (99%) rename altk/pre_tool/toolguard/toolguard/{tool_policy_extractor => gen_spec}/prompts/add_policies.txt (99%) rename altk/pre_tool/toolguard/toolguard/{tool_policy_extractor => gen_spec}/prompts/add_references.txt (99%) rename altk/pre_tool/toolguard/toolguard/{tool_policy_extractor => gen_spec}/prompts/create_examples.txt (100%) rename altk/pre_tool/toolguard/toolguard/{tool_policy_extractor => gen_spec}/prompts/create_policy.txt (100%) rename altk/pre_tool/toolguard/toolguard/{tool_policy_extractor => gen_spec}/prompts/examples_reviewer.txt (99%) rename altk/pre_tool/toolguard/toolguard/{tool_policy_extractor => gen_spec}/prompts/fix_example.txt (100%) rename altk/pre_tool/toolguard/toolguard/{tool_policy_extractor => gen_spec}/prompts/functions.txt (100%) rename altk/pre_tool/toolguard/toolguard/{tool_policy_extractor => gen_spec}/prompts/merge.txt (99%) rename altk/pre_tool/toolguard/toolguard/{tool_policy_extractor => gen_spec}/prompts/merge_and_split.txt (99%) rename altk/pre_tool/toolguard/toolguard/{tool_policy_extractor => gen_spec}/prompts/merge_examples.txt (100%) rename altk/pre_tool/toolguard/toolguard/{tool_policy_extractor => gen_spec}/prompts/policy_reviewer.txt (100%) rename altk/pre_tool/toolguard/toolguard/{tool_policy_extractor => gen_spec}/prompts/split.txt (99%) create mode 100644 altk/pre_tool/toolguard/toolguard/gen_spec/spec_generator.py create mode 100644 altk/pre_tool/toolguard/toolguard/gen_spec/utils.py create mode 100644 altk/pre_tool/toolguard/toolguard/llm/tg_litellm.py delete mode 100644 altk/pre_tool/toolguard/toolguard/llm/tg_llmevalkit.py delete mode 100644 altk/pre_tool/toolguard/toolguard/tool_policy_extractor/__init__.py delete mode 100644 altk/pre_tool/toolguard/toolguard/tool_policy_extractor/text_tool_policy_generator.py delete mode 100644 altk/pre_tool/toolguard/toolguard/tool_policy_extractor/utils.py create mode 100644 altk/pre_tool/toolguard/toolguard_code_component.py create mode 100644 altk/pre_tool/toolguard/toolguard_spec_component.py create mode 100644 tests/pre_tool/toolguard/.gitignore rename {altk/pre_tool/toolguard/examples/calculator_example => tests/pre_tool/toolguard/inputs}/__init__.py (100%) create mode 100644 tests/pre_tool/toolguard/inputs/oas.json rename altk/pre_tool/toolguard/examples/calculator_example/policy_document.md => tests/pre_tool/toolguard/inputs/policy_doc.md (80%) create mode 100644 tests/pre_tool/toolguard/inputs/step1/add_tool.json create mode 100644 tests/pre_tool/toolguard/inputs/step1/divide_tool.json create mode 100644 tests/pre_tool/toolguard/inputs/step1/map_kdi_number.json create mode 100644 tests/pre_tool/toolguard/inputs/step1/multiply_tool.json create mode 100644 tests/pre_tool/toolguard/inputs/step1/subtract_tool.json create mode 100644 tests/pre_tool/toolguard/inputs/tool_functions.py create mode 100644 tests/pre_tool/toolguard/inputs/tool_langchain.py create mode 100644 tests/pre_tool/toolguard/inputs/tool_methods.py delete mode 100644 tests/pre_tool/toolguard/test_tool_guard_calculator_policy.py create mode 100644 tests/pre_tool/toolguard/test_toolguard_code.py create mode 100644 tests/pre_tool/toolguard/test_toolguard_specs.py diff --git a/altk/pre_tool/toolguard/README.md b/altk/pre_tool/toolguard/README.md index a86e050..0048e08 100644 --- a/altk/pre_tool/toolguard/README.md +++ b/altk/pre_tool/toolguard/README.md @@ -18,35 +18,32 @@ An agent lifecycle solution for enforcing business policy adherence in agentic w Business policies (or guidelines) are normally detailed in company documents, and have traditionally been hard-coded into automatic assistant platforms. Contemporary agentic approaches take the "best-effort" strategy, where the policies are appended to the agent's system prompt, an inherently non-deterministic approach, that does not scale effectively. Here we propose a deterministic, predictable and interpretable two-phase solution for agentic policy adherence at the tool-level: guards are executed prior to function invocation and raise alerts in case a tool-related policy deem violated. -### Key Components - -The solution enforces policy adherence through a two-phase process: - -(1) **Buildtime**: an offline two-step pipeline that automatically maps policy fragments to the relevant tools and generates policy validation code - ToolGuards. - -(2) **Runtime**: ToolGuards are deployed within the agent's ReAct flow, and are executed after "reason" and just before "act" (agent's tool invocation). If a planned action violates a policy, the agent is prompted to self-reflect and revise its plan before proceeding. Ultimately, the deployed ToolGuards will prevent the agent from taking an action violating a policy. - - +This component enforces **pre‑tool activation policy constraints**, ensuring that agent decisions comply with business rules **before** modifying system state. This prevents policy violations such as unauthorized tool calls or unsafe parameter values. +## ToolGuardSpecComponent +This component gets a set of tools and a policy document and generated multiple ToolGuard specifications, known as `ToolGuardSpec`s. Each specification is attached to a tool, and it declares a precondition that must apply before invoking the tool. The specification has a `name`, `description`, list of `refernces` to the original policy document, a set of declerative `compliance_examples`, describing test cases that the toolGuard should allow the tool invocation, and `violation_examples`, where the toolGuard should raise an exception. -## When it is Recommended to Use This Component -This component enforces **pre‑tool activation policy constraints**, ensuring that agent decisions comply with business rules **before** modifying system state. This prevents policy violations such as unauthorized tool calls or unsafe parameter values. +This componenet supports only a `build` phase. The generate specifications are returned as output, and are also saved to a specified file system directory. +The specifications are aimed to be used as input into our next component - the `ToolGuardCodeComponent` described below. -## LLM Configuration Requirements -The **build phase** uses **two LLMs**: +The two components are not concatenated by design. As the geneartion involves a non-deterministic language model, the results need to be reviewed by a human. Hence, the output specification files should be reviewed and optionaly edited. For example, removing a wrong compliance example. -### 1. Reasoning LLM (Build Step 1) -Used to interpret, restructure, and classify policy text. +### Usage example +see [simple calculator test](../../tests/pre_tool_guard_toolkit/test_toolguard_specs.py) -This model can be any LLM registered through: +### Component Configuarion +This component expects an LLM client configuarion: ```python -from altk.core.llm import get_llm # def get_llm(name: str) -> Type["LLMClient"] - -OPENAILiteLLMClientOutputVal = get_llm("litellm.output_val") +from altk.toolkit_core.llm import get_llm +LLMClient = get_llm("litellm.output_val") +llm_client = LLMClient(...) +toolguard_component = ToolGuardSpecComponent( + ToolGuardSpecComponentConfig(llm_client=llm_client) +) ``` -#### Azure example for gpt-4o: +Here is a concerete example with `litellm` and `azure`: Environment variables: ```bash export AZURE_OPENAI_API_KEY="" @@ -55,18 +52,31 @@ export AZURE_API_VERSION="2024-08-01-preview" ``` code: ```python -from altk.core.llm import get_llm # def get_llm(name: str) -> Type["LLMClient"] +from altk.toolkit_core.llm import get_llm -OPENAILiteLLMClientOutputVal = get_llm("litellm.output_val") -validating_llm_client = OPENAILiteLLMClientOutputVal( +LLMClient = get_llm("litellm.output_val") +llm_client = LLMClient( model_name="gpt-4o-2024-08-06", custom_llm_provider="azure", ) ``` +## ToolGuardCodeComponent + +This components enfoorces policy adherence through a two-phase process: + +(1) **Buildtime**: Given a set of `ToolGuardSpec`s, generates policy validation code - `ToolGuard`s. +Similar to ToolGuard Specifications, generated `ToolGuards` are a good start, but they may contain errors. Hence, they should be also reviewed by a human. + +(2) **Runtime**: ToolGuards are deployed within the agent's flow, and are triggered before agent's tool invocation. They can be deployed into the agent loop, or in an MCP Gateway. +The ToolGuards checks if a planned action complies with the policy. If it violates, the agent is prompted to self-reflect and revise its plan before proceeding. + + +### Usage example +see [simple calculator test](../../tests/pre_tool_guard_toolkit/test_toolguard_code.py) + +### Component Configuarion -### 2. Code Generation LLM (Build Step 2) -Used only in the code generation phase to produce Python enforcement logic. Backed by Mellea, which requires parameters aligning to: ```python mellea.MelleaSession.start_session( @@ -75,8 +85,7 @@ mellea.MelleaSession.start_session( backend_kwargs=... # any additional arguments ) ``` - -These map directly to environment variables: +The `melea` session parameters can be provided explicitely, or loaded from environment variables: | Environment Variable | Mellea Parameter | Description | | ------------------------------ | ---------------- | ------------------------------------------------------------------ | @@ -90,75 +99,3 @@ export TOOLGUARD_GENPY_BACKEND_NAME="openai" export TOOLGUARD_GENPY_MODEL_ID="GCP/claude-4-sonnet" export TOOLGUARD_GENPY_ARGS='{"base_url":"https://your-litellm-endpoint","api_key":""}' ``` - -## Quick Start -See runnable example: -``` -pre-tool-guard-toolkit/examples/calculator_example -``` - -```python -import asyncio -from altk.pre_tool.toolguard.core import ( - ToolGuardBuildInput, ToolGuardBuildInputMetaData, - ToolGuardRunInput, ToolGuardRunInputMetaData, -) -from altk.pre_tool.toolguard.pre_tool_guard import PreToolGuardComponent - -class ToolGuardExample: - def __init__(self, tools, workdir, policy_text, validating_llm_client, short=True): - self.middleware = PreToolGuardComponent(tools=tools, workdir=workdir, app_name="calculator") - build_input = ToolGuardBuildInput(metadata=ToolGuardBuildInputMetaData( - policy_text=policy_text, - short1=short, - validating_llm_client=validating_llm_client, - )) - asyncio.run(self.middleware._build(build_input)) - - def run_example(self, tool_name, tool_params): - run_input = ToolGuardRunInput( - metadata=ToolGuardRunInputMetaData(tool_name=tool_name, tool_parms=tool_params), - ) - return self.middleware._run(run_input) -``` - - -## Parameters - -### Constructor Parameters -```python -PreToolGuardComponent(tools, workdir) -``` - -| Parameter | Type | Description | -|----------|------------------|-------------| -| `tools` | `list[Callable]` | List of functions or LangChain tools to safeguard. -| `workdir` | `str` or `Path` | Writable working directory for storing build artifacts. - -### Build Phase Input Format -```python -ToolGuardBuildInput( - metadata=ToolGuardBuildInputMetaData( - policy_text="", - short1=True, - validating_llm_client= - ) -) -``` - -### Run Phase Input Format -```python -ToolGuardRunInput( - metadata=ToolGuardRunInputMetaData( - tool_name="divide_tool", - tool_parms={"g": 3, "h": 4}, - ), - messages=[{"role": "user", "content": "Calculate 3/4"}] -) -``` - -### Run Phase Output Format -```python -ToolGuardRunOutput(output=ToolGuardRunOutputMetaData(error_message=False)) -``` -`error_message` is either `False` (valid) or a descriptive violation message. diff --git a/altk/pre_tool/toolguard/__init__.py b/altk/pre_tool/toolguard/__init__.py index e69de29..98eefdd 100644 --- a/altk/pre_tool/toolguard/__init__.py +++ b/altk/pre_tool/toolguard/__init__.py @@ -0,0 +1,3 @@ +from .toolguard_code_component import * +from .toolguard_spec_component import * +from .toolguard import * \ No newline at end of file diff --git a/altk/pre_tool/toolguard/core/__init__.py b/altk/pre_tool/toolguard/core/__init__.py deleted file mode 100644 index 97fd319..0000000 --- a/altk/pre_tool/toolguard/core/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -from .types import ( - ToolGuardBuildInput, - ToolGuardRunInput, - ToolGuardRunOutput, - ToolGuardBuildInputMetaData, - ToolGuardRunInputMetaData, - ToolGuardRunOutputMetaData, -) - - -__all__ = [ - "ToolGuardBuildInput", - "ToolGuardRunInput", - "ToolGuardRunOutput", - "ToolGuardBuildInputMetaData", - "ToolGuardRunInputMetaData", - "ToolGuardRunOutputMetaData", -] diff --git a/altk/pre_tool/toolguard/core/types.py b/altk/pre_tool/toolguard/core/types.py deleted file mode 100644 index 1397350..0000000 --- a/altk/pre_tool/toolguard/core/types.py +++ /dev/null @@ -1,64 +0,0 @@ -from typing import Union, List - -from altk.pre_tool.toolguard.toolguard.data_types import ToolPolicy -from altk.pre_tool.toolguard.toolguard.runtime import ToolGuardsCodeGenerationResult -from altk.core.toolkit import ComponentInput, ComponentOutput -from altk.core.llm import BaseLLMClient -from pydantic import BaseModel, Field, ConfigDict - - -class ToolGuardBuildInputMetaData(BaseModel): - model_config = ConfigDict(arbitrary_types_allowed=True) - policy_text: str = Field(description="Text of the policy document file") - short1: bool = Field(default=True, description="Run build short or long version. ") - validating_llm_client: BaseLLMClient = Field( - description="ValidatingLLMClient for build time" - ) - - -class ToolGuardBuildInput(ComponentInput): - metadata: ToolGuardBuildInputMetaData = Field( - default_factory=lambda: ToolGuardBuildInputMetaData() - ) - - -class ToolGuardRunInputMetaData(BaseModel): - model_config = ConfigDict(arbitrary_types_allowed=True) - tool_name: str = Field(description="Tool name") - tool_parms: dict = Field(default={}, description="Tool parameters") - # llm_client: LLMClient = Field(description="LLMClient for build time") - - -class ToolGuardRunInput(ComponentInput): - metadata: ToolGuardRunInputMetaData = Field( - default_factory=lambda: ToolGuardRunInputMetaData() - ) - - -class ToolGuardBuildOutputMetaData(BaseModel): - tool_policies: List[ToolPolicy] = ( - Field( - description="List of policies specs for each tool extracted from the policy document" - ), - ) - generated_code_object: ToolGuardsCodeGenerationResult = Field( - description="root_dir of the generated code object, runtime domain and code for each tool guard" - ) - - -class ToolGuardBuildOutput(ComponentOutput): - output: ToolGuardBuildOutputMetaData = Field( - default_factory=lambda: ToolGuardBuildOutputMetaData() - ) - - -class ToolGuardRunOutputMetaData(BaseModel): - error_message: Union[str, bool] = Field( - description="Error string or False if no error occurred" - ) - - -class ToolGuardRunOutput(ComponentOutput): - output: ToolGuardRunOutputMetaData = Field( - default_factory=lambda: ToolGuardRunOutputMetaData() - ) diff --git a/altk/pre_tool/toolguard/examples/.gitignore b/altk/pre_tool/toolguard/examples/.gitignore deleted file mode 100644 index d9862e1..0000000 --- a/altk/pre_tool/toolguard/examples/.gitignore +++ /dev/null @@ -1 +0,0 @@ -work_dir_wx diff --git a/altk/pre_tool/toolguard/examples/calculator_example/example_tools.py b/altk/pre_tool/toolguard/examples/calculator_example/example_tools.py deleted file mode 100644 index 46f73f1..0000000 --- a/altk/pre_tool/toolguard/examples/calculator_example/example_tools.py +++ /dev/null @@ -1,92 +0,0 @@ -def add_tool(a: float, b: float) -> float: - """ - Add two numbers. - - Parameters - ---------- - a : float - The first number to add. - b : float - The second number to add. - - Returns - ------- - float - The sum of a and b. - """ - return a + b - - -def subtract_tool(c: float, d: float) -> float: - """ - Subtract one number from another. - - Parameters - ---------- - c : float - The number to subtract from. - d : float - The number to subtract. - - Returns - ------- - float - The result of a minus b. - """ - return c - d - - -def multiply_tool(e: float, f: float) -> float: - """ - Multiply two numbers. - - Parameters - ---------- - e : float - The first number. - f : float - The second number. - - Returns - ------- - float - The product of a and b. - """ - return e * f - - -def divide_tool(g: float, h: float) -> float: - """ - Divide one number by another. - - Parameters - ---------- - g : float - The dividend. - h : float - The divisor (must not be zero). - - Returns - ------- - float - The result of a divided by b. - """ - return g / h - - -def map_kdi_number(i: float) -> float: - """ - return the mapping of the numer i to it's kdi value - - Parameters - ---------- - i : float - The number to map. - - - Returns - ------- - float - The value of the dki of the given number. - """ - return 3.14 * i diff --git a/altk/pre_tool/toolguard/examples/calculator_example/run_example.py b/altk/pre_tool/toolguard/examples/calculator_example/run_example.py deleted file mode 100644 index 79ada58..0000000 --- a/altk/pre_tool/toolguard/examples/calculator_example/run_example.py +++ /dev/null @@ -1,106 +0,0 @@ -import os -from pathlib import Path - -import markdown - -from altk.pre_tool.toolguard.examples.calculator_example.example_tools import ( - add_tool, - subtract_tool, - multiply_tool, - divide_tool, - map_kdi_number, -) -from altk.pre_tool.toolguard.examples.tool_guard_example import ToolGuardExample -from altk.core.llm import get_llm - - -subdir_name = "work_dir_wx" -script_path = os.path.abspath(__file__) -script_directory = os.path.dirname(script_path) -work_dir = Path(os.path.join(script_directory, subdir_name)) -policy_doc_path = os.path.join(script_directory, "policy_document.md") -work_dir.mkdir(exist_ok=True) - -OPENAILiteLLMClientOutputVal = get_llm("litellm.output_val") -validating_llm_client = OPENAILiteLLMClientOutputVal( - model_name="gpt-4o-2024-08-06", - custom_llm_provider="azure", -) - - -tool_funcs = [add_tool, subtract_tool, multiply_tool, divide_tool, map_kdi_number] -policy_text = open(policy_doc_path, "r", encoding="utf-8").read() -policy_text = markdown.markdown(policy_text) - -tool_guard_example = ToolGuardExample( - tools=tool_funcs, - workdir=work_dir, - policy_text=policy_text, - validating_llm_client=validating_llm_client, - app_name="calculator", -) -run_output = tool_guard_example.run_example( - "divide_tool", - {"g": 3, "h": 4}, -) -print(run_output) -passed = not run_output.output.error_message -if passed: - print("success!") -else: - print("failure!") - -run_output = tool_guard_example.run_example( - "divide_tool", - {"g": 5, "h": 0}, -) -print(run_output) -passed = not run_output.output.error_message -if not passed: - print("success!") -else: - print("failure!") - -run_output = tool_guard_example.run_example( - "add_tool", - {"a": 5, "b": 44}, -) -print(run_output) -passed = not run_output.output.error_message -if passed: - print("success!") -else: - print("failure!") - -run_output = tool_guard_example.run_example( - "add_tool", - {"a": 5, "b": 73}, -) -print(run_output) -passed = not run_output.output.error_message -if not passed: - print("success!") -else: - print("failure!") - -run_output = tool_guard_example.run_example( - "multiply_tool", - {"e": 3, "f": 44}, -) -print(run_output) -passed = not run_output.output.error_message -if passed: - print("success!") -else: - print("failure!") - -run_output = tool_guard_example.run_example( - "multiply_tool", - {"e": 2, "f": 73}, -) -print(run_output) -passed = not run_output.output.error_message -if not passed: - print("success!") -else: - print("failure!") diff --git a/altk/pre_tool/toolguard/examples/full_test_calc.py b/altk/pre_tool/toolguard/examples/full_test_calc.py deleted file mode 100644 index 29d1f5f..0000000 --- a/altk/pre_tool/toolguard/examples/full_test_calc.py +++ /dev/null @@ -1,110 +0,0 @@ -import asyncio -import os -from typing import List, Dict - -import dotenv -import markdown - -from altk.pre_tool.toolguard.examples.calculator_example.example_tools import ( - divide_tool, -) -from altk.pre_tool.toolguard.toolguard.llm.tg_llmevalkit import TG_LLMEval -from altk.pre_tool.toolguard.toolguard.tool_policy_extractor.text_tool_policy_generator import ( - ToolInfo, - extract_policies, -) -from altk.pre_tool.toolguard.toolguard.core import ( - generate_guards_from_tool_policies, -) - -from altk.core.llm import get_llm - - -class FullAgent: - def __init__( - self, - app_name, - tools, - workdir, - policy_doc_path, - llm_model="gpt-4o-2024-08-06", - tools2run: List[str] | None = None, - short1=False, - ): - self.model = llm_model - self.tools = tools - self.workdir = workdir - self.policy_doc = open(policy_doc_path, "r", encoding="utf-8").read() - self.policy_doc = markdown.markdown(self.policy_doc) - self.tools2run = tools2run - self.short1 = short1 - self.app_name = app_name - self.step1_out_dir = os.path.join(self.workdir, "step1") - self.step2_out_dir = os.path.join(self.workdir, "step2") - # self.tool_registry = {tool.name: tool for tool in tools} - self.tool_registry = {tool.__name__: tool for tool in tools} - - async def build_time(self): - OPENAILiteLLMClientOutputVal = get_llm("litellm.output_val") - validating_llm_client = OPENAILiteLLMClientOutputVal( - model_name="watsonx/gpt-4o-2024-08-06", - custom_llm_provider="azure", - ) - llm = TG_LLMEval(validating_llm_client) - tools_info = [ToolInfo.from_function(tool) for tool in self.tools] - - tool_policies = await extract_policies( - self.policy_doc, tools_info, self.step1_out_dir, llm, short=True - ) - self.gen_result = await generate_guards_from_tool_policies( - self.tools, - tool_policies, - to_step2_path=self.step2_out_dir, - app_name=self.app_name, - ) - - def guard_tool(self, tool_name: str, tool_params: Dict) -> str: - print("validate_tool_node") - import sys - - code_root_dir = self.gen_result.root_dir - sys.path.insert(0, code_root_dir) - from rt_toolguard import load_toolguards - - toolguards = load_toolguards(code_root_dir) - - try: - # app_guards.check_tool_call(tool_name, tool_parms, state["messages"]) - toolguards.check_toolcall( - tool_name, tool_params, list(self.tool_registry.values()) - ) - print("ok to invoke tool") - except Exception as e: - error_message = ( - "it is against the policy to invoke tool: " - + tool_name - + " Error: " - + str(e) - ) - print(error_message) - - -if __name__ == "__main__": - dotenv.load_dotenv() - work_dir = "examples/calculator_example/output" - policy_doc_path = "examples/calculator_example/policy_document.md" - policy_doc_path = os.path.abspath(policy_doc_path) - work_dir = os.path.abspath(work_dir) - - tools = [divide_tool] # [add_tool, subtract_tool, multiply_tool, divide_tool] - fa = FullAgent( - "calculator", - tools, - work_dir, - policy_doc_path, - llm_model="gpt-4o-2024-08-06", - short1=True, - ) - asyncio.run(fa.build_time()) - fa.guard_tool("divide_tool", {"g": 5, "h": 0}) - fa.guard_tool("divide_tool", {"g": 5, "h": 4}) diff --git a/altk/pre_tool/toolguard/examples/tool_guard_example.py b/altk/pre_tool/toolguard/examples/tool_guard_example.py deleted file mode 100644 index 085516d..0000000 --- a/altk/pre_tool/toolguard/examples/tool_guard_example.py +++ /dev/null @@ -1,52 +0,0 @@ -import asyncio -import dotenv - - -from altk.pre_tool.toolguard.core import ( - ToolGuardBuildInput, - ToolGuardBuildInputMetaData, - ToolGuardRunInput, - ToolGuardRunInputMetaData, -) -from altk.pre_tool.toolguard.pre_tool_guard import PreToolGuardComponent - -# Load environment variables -dotenv.load_dotenv() - - -class ToolGuardExample: - """ - Runs examples with a ToolGuard component and validates tool invocation against policy. - """ - - def __init__( - self, tools, workdir, policy_text, validating_llm_client, app_name, short=True - ): - self.tools = tools - self.middleware = PreToolGuardComponent( - tools=tools, workdir=workdir, app_name=app_name - ) - - build_input = ToolGuardBuildInput( - metadata=ToolGuardBuildInputMetaData( - policy_text=policy_text, - short1=short, - validating_llm_client=validating_llm_client, - ) - ) - self.output = asyncio.run(self.middleware._build(build_input)) - - def run_example(self, tool_name: str, tool_params: dict): - """ - Runs a single example through ToolGuard and checks if the result matches the expectation. - """ - - run_input = ToolGuardRunInput( - metadata=ToolGuardRunInputMetaData( - tool_name=tool_name, - tool_parms=tool_params, - ), - ) - - run_output = self.middleware._run(run_input) - return run_output diff --git a/altk/pre_tool/toolguard/pre_tool_guard/__init__.py b/altk/pre_tool/toolguard/pre_tool_guard/__init__.py deleted file mode 100644 index ff307ab..0000000 --- a/altk/pre_tool/toolguard/pre_tool_guard/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .pre_tool_guard import PreToolGuardComponent - -__all__ = ["PreToolGuardComponent"] diff --git a/altk/pre_tool/toolguard/pre_tool_guard/pre_tool_guard.py b/altk/pre_tool/toolguard/pre_tool_guard/pre_tool_guard.py deleted file mode 100644 index b98ced8..0000000 --- a/altk/pre_tool/toolguard/pre_tool_guard/pre_tool_guard.py +++ /dev/null @@ -1,91 +0,0 @@ -import json -import logging -import os -from typing import Set - - -from altk.pre_tool.toolguard.toolguard.core import ( - generate_guards_from_tool_policies, -) -from altk.pre_tool.toolguard.toolguard.llm.tg_llmevalkit import TG_LLMEval -from altk.pre_tool.toolguard.toolguard.runtime import ToolFunctionsInvoker -from altk.pre_tool.toolguard.toolguard.tool_policy_extractor.text_tool_policy_generator import ( - ToolInfo, - extract_policies, -) - -from altk.core.toolkit import AgentPhase, ComponentBase - - -from altk.pre_tool.toolguard.core.types import ( - ToolGuardBuildInput, - ToolGuardRunInput, - ToolGuardRunOutput, - ToolGuardRunOutputMetaData, - ToolGuardBuildOutput, - ToolGuardBuildOutputMetaData, -) - -logger = logging.getLogger(__name__) - - -class PreToolGuardComponent(ComponentBase): - def __init__(self, tools, workdir, app_name): - super().__init__() - self._tools = tools - self._tool_registry = {tool.__name__: tool for tool in self._tools} - self._workdir = workdir - self._step1_dir = os.path.join(self._workdir, "Step_1") - self._step2_dir = os.path.join(self._workdir, "Step_2") - self._app_name = app_name - self._tool_policies = None - self._gen_result = None - - @classmethod - def supported_phases(cls) -> Set[AgentPhase]: - """Return the supported agent phases.""" - return {AgentPhase.BUILDTIME, AgentPhase.RUNTIME} - - async def _build(self, data: ToolGuardBuildInput) -> ToolGuardBuildOutput: - llm = TG_LLMEval(data.metadata.validating_llm_client) - tools_info = [ToolInfo.from_function(tool) for tool in self._tools] - - self._tool_policies = await extract_policies( - data.metadata.policy_text, tools_info, self._step1_dir, llm, short=True - ) - self._gen_result = await generate_guards_from_tool_policies( - self._tools, - self._tool_policies, - to_step2_path=self._step2_dir, - app_name=self._app_name, - ) - output = ToolGuardBuildOutputMetaData( - tool_policies=self._tool_policies, generated_code_object=self._gen_result - ) - return ToolGuardBuildOutput(output=output) - - def _run(self, data: ToolGuardRunInput) -> ToolGuardRunOutput: - import sys - - code_root_dir = self._gen_result.root_dir - sys.path.insert(0, code_root_dir) - tool_name = data.metadata.tool_name - tool_params = data.metadata.tool_parms - from rt_toolguard import load_toolguards - - app_guards = load_toolguards(code_root_dir) - - try: - app_guards.check_toolcall( - tool_name, - tool_params, - ToolFunctionsInvoker(list(self._tool_registry.values())), - ) - error_message = False - except Exception as e: - error_message = ( - f"It is against the policy to invoke tool: {tool_name}({json.dumps(tool_params)}) Error: " - + str(e) - ) - output = ToolGuardRunOutputMetaData(error_message=error_message) - return ToolGuardRunOutput(output=output) diff --git a/altk/pre_tool/toolguard/toolguard/__init__.py b/altk/pre_tool/toolguard/toolguard/__init__.py index 59b7732..3528d66 100644 --- a/altk/pre_tool/toolguard/toolguard/__init__.py +++ b/altk/pre_tool/toolguard/toolguard/__init__.py @@ -1,7 +1,5 @@ -# from .core import +from .buildtime import generate_guard_specs, generate_guards_from_specs, build_toolguards +from .llm.tg_litellm import LitellmModel, I_TG_LLM +from .data_types import * -from altk.pre_tool.toolguard.toolguard.core import ( - build_toolguards, - extract_policies, - generate_guards_from_tool_policies, -) +from .runtime import IToolInvoker, ToolFunctionsInvoker, ToolGuardsCodeGenerationResult, ToolMethodsInvoker, load_toolguard_code_result, load_toolguards, LangchainToolInvoker \ No newline at end of file diff --git a/altk/pre_tool/toolguard/toolguard/__main__.py b/altk/pre_tool/toolguard/toolguard/__main__.py new file mode 100644 index 0000000..595126e --- /dev/null +++ b/altk/pre_tool/toolguard/toolguard/__main__.py @@ -0,0 +1,8 @@ +#important to load the env variables BEFORE policy_adherence library (so programmatic_ai configuration will take place) +import dotenv +dotenv.load_dotenv() + +from .cli import main + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/altk/pre_tool/toolguard/toolguard/buildtime.py b/altk/pre_tool/toolguard/toolguard/buildtime.py new file mode 100644 index 0000000..a46bab9 --- /dev/null +++ b/altk/pre_tool/toolguard/toolguard/buildtime.py @@ -0,0 +1,132 @@ +import os +from os.path import join +from typing import Callable, List, Optional, cast +import json +import logging + +from langchain.tools import BaseTool + + +from .llm.i_tg_llm import I_TG_LLM +from .runtime import ToolGuardsCodeGenerationResult +from .data_types import MeleaSessionData, ToolGuardSpec, load_tool_policy, ToolInfo +from .gen_py.gen_toolguards import generate_toolguards_from_functions, generate_toolguards_from_openapi +from .gen_spec.oas_summary import OASSummarizer +from .gen_spec.spec_generator import extract_toolguard_specs +from .common.langchain import langchain_tools_to_openapi + +logger = logging.getLogger(__name__) + + +#Step1 and immediatly step2 +async def build_toolguards( + policy_text:str, + tools: List[Callable]|str, + step1_out_dir:str, + step2_out_dir:str, + step1_llm:I_TG_LLM, + step2_llm: MeleaSessionData, + app_name:str= "my_app", + tools2run: List[str] | None=None, + short1=True)->ToolGuardsCodeGenerationResult: + + os.makedirs(step1_out_dir, exist_ok=True) + os.makedirs(step2_out_dir, exist_ok=True) + + # case1: path to OpenAPI spec + oas_path = tools if isinstance(tools, str) else None + + # case2: List of Langchain tools + if isinstance(tools, list) and all([isinstance(tool, BaseTool) for tool in tools]): + oas = langchain_tools_to_openapi(tools) # type: ignore + oas_path = f"{step1_out_dir}/oas.json" + oas.save(oas_path) + + if oas_path: # for cases 1 and 2 + with open(oas_path, 'r', encoding='utf-8') as file: + oas = json.load(file) + summarizer = OASSummarizer(oas) + tools_info = summarizer.summarize() + tool_policies = await extract_toolguard_specs(policy_text, tools_info, step1_out_dir, step1_llm, tools2run, short1) + return await generate_guards_from_specs(oas_path, tool_policies, step2_out_dir, llm_data=step2_llm, app_name=app_name, lib_names=None, tool_names=tools2run) + + # Case 3: List of functions + case 4: List of methods + if isinstance(tools, list) and not oas_path: + tools_info = [ToolInfo.from_function(tool) for tool in tools] + tool_policies = await extract_toolguard_specs(policy_text, tools_info, step1_out_dir, step1_llm, tools2run, short1) + return await generate_guards_from_specs(tools, tool_policies, step2_out_dir, llm_data=step2_llm, app_name=app_name, lib_names=None, tool_names=tools2run) + + raise Exception("Unknown tools") + + +#Step1 only +async def generate_guard_specs( + policy_text: str, + tools: List[Callable] | List[BaseTool] | str, + llm: I_TG_LLM, + work_dir: str, +)->List[ToolGuardSpec]: + # case1: path to OpenAPI spec + oas_path = tools if isinstance(tools, str) else None + + # case2: List of Langchain tools + if isinstance(tools, list) and all([isinstance(tool, BaseTool) for tool in tools]): + oas = langchain_tools_to_openapi(tools) # type: ignore + oas_path = join(work_dir, "oas.json") + oas.save(oas_path) + + if oas_path: # for cases 1 and 2 + with open(oas_path, 'r', encoding='utf-8') as file: + oas = json.load(file) + summarizer = OASSummarizer(oas) + tools_info = summarizer.summarize() + return await extract_toolguard_specs(policy_text, tools_info, work_dir, llm, None, True) + + # Case 3: List of functions + case 4: List of methods + if isinstance(tools, list): + tools_info = [ToolInfo.from_function(tool) for tool in tools] + return await extract_toolguard_specs(policy_text, tools_info, work_dir, llm, None, True) + + raise NotImplementedError() + + +#Step2 only +async def generate_guards_from_specs( + tools: List[Callable] | List[BaseTool] | str, + tool_specs: List[ToolGuardSpec], + work_dir: str, + llm_data: MeleaSessionData, + app_name: str = "myapp", + lib_names: Optional[List[str]] = None, + tool_names: Optional[List[str]] = None) -> ToolGuardsCodeGenerationResult: + + tool_specs = [policy for policy in tool_specs if (not tool_names) or (policy.tool_name in tool_names)] + + # case1: path to OpenAPI spec + oas_path = tools if isinstance(tools, str) else None + + # case2: List of Langchain tools + if isinstance(tools, list) and all([isinstance(tool, BaseTool) for tool in tools]): + oas = langchain_tools_to_openapi(tools) # type: ignore + oas_path = join(work_dir, "oas.json") + oas.save(oas_path) + + if oas_path: # for cases 1 and 2 + return await generate_toolguards_from_openapi(app_name, tool_specs, work_dir, oas_path, llm_data) + + # Case 3: List of functions + case 4: List of methods + funcs = [cast(Callable, tool) for tool in tools] + return await generate_toolguards_from_functions(app_name, tool_specs, work_dir, funcs=funcs, llm_data=llm_data, module_roots=lib_names) + + +#load step1 results +def load_policies_in_folder(folder:str)->List[ToolGuardSpec]: + files = [f for f in os.listdir(folder) + if os.path.isfile(join(folder, f)) and f.endswith(".json")] + tool_policies = [] + for file in files: + tool_name = file[:-len(".json")] + policy = load_tool_policy(join(folder, file), tool_name) + if policy.policy_items: + tool_policies.append(policy) + return tool_policies diff --git a/altk/pre_tool/toolguard/toolguard/cli.py b/altk/pre_tool/toolguard/toolguard/cli.py new file mode 100644 index 0000000..f327959 --- /dev/null +++ b/altk/pre_tool/toolguard/toolguard/cli.py @@ -0,0 +1,64 @@ + +import asyncio +import inspect +import os +from os.path import join +from typing import Callable, List + +import markdown +import logging + +from .buildtime import build_toolguards +from .common import py +from .llm.tg_litellm import LitellmModel + +logger = logging.getLogger(__name__) + +import argparse + +def main(): + parser = argparse.ArgumentParser(description='parser') + parser.add_argument('--policy-path', type=str, help='Path to the policy file. Currently, in `markdown` syntax. eg: `/Users/me/airline/wiki.md`') + parser.add_argument('--app-root', type=str, default="../ToolGuardAgent/src/appointment_app") + parser.add_argument('--app-tools-py-file', type=str, default="lg_tools.py") + parser.add_argument('--out-dir', type=str, help='Path to an output folder where the generated artifacts will be written. eg: `/Users/me/airline/outdir2') + parser.add_argument('--step1-dir-name', type=str, default='Step1', help='Step1 folder name under the output folder') + parser.add_argument('--step2-dir-name', type=str, default='Step2', help='Step2 folder name under the output folder') + parser.add_argument('--step1-model-name', type=str, default='gpt-4o-2024-08-06', help='Model to use for generating in step 1') + parser.add_argument('--tools2run', nargs='+', default=None, help='Optional list of tool names. These are a subset of the tools in the openAPI operation ids.') + parser.add_argument('--short-step1', action='store_true', default=False, help='run short version of step 1') + + args = parser.parse_args() + policy_path = args.policy_path + + policy_text = open(policy_path, 'r', encoding='utf-8').read() + policy_text = markdown.markdown(policy_text) + + llm = LitellmModel(args.step1_model_name, "azure") #FIXME from args + tools = load_functions_in_file(args.app_root, args.app_tools_py_file) + os.makedirs(args.out_dir, exist_ok=True) + step1_out_dir = join(args.out_dir, args.step1_dir_name) + step2_out_dir = join(args.out_dir, args.step2_dir_name) + asyncio.run( + build_toolguards( + policy_text = policy_text, + tools = tools, + step1_out_dir = step1_out_dir, + step2_out_dir = step2_out_dir, + step1_llm = llm, + tools2run = args.tools2run, + short1 = args.short_step1 + ) + ) + +from importlib import import_module +def load_functions_in_file(py_root:str, file_path: str) -> List[Callable]: + with py.temp_python_path(py_root): + module = import_module(py.path_to_module(file_path)) + funcs = [] + for name, obj in inspect.getmembers(module): + # if isinstance(obj, BaseTool): + # funcs.append(py.unwrap_fn(obj)) + if callable(obj) and not (name=='tool' and obj.__module__ =='langchain_core.tools.convert'): + funcs.append(obj) + return funcs \ No newline at end of file diff --git a/altk/pre_tool/toolguard/toolguard/common/langchain.py b/altk/pre_tool/toolguard/toolguard/common/langchain.py new file mode 100644 index 0000000..638f399 --- /dev/null +++ b/altk/pre_tool/toolguard/toolguard/common/langchain.py @@ -0,0 +1,53 @@ + + +from typing import List +from langchain.tools import BaseTool + +from .open_api import OpenAPI + +def langchain_tools_to_openapi( + tools: List[BaseTool], + title: str = "LangChain Tools API", + version: str = "1.0.0", +)->OpenAPI: + paths = {} + components = {"schemas": {}} + + for tool in tools: + # Get JSON schema from the args model + if tool.get_input_schema(): + components["schemas"][tool.name + "Args"] = tool.get_input_schema().model_json_schema() + + request_body = { + "description": tool.description, + "required": True, + "content": { + "application/json": { + "schema": {"$ref": f"#/components/schemas/{tool.name}Args"} + } + }, + } + else: + # Tools without args → empty schema + request_body = None + + paths[f"/tools/{tool.name}"] = { + "post": { + "summary": tool.description, + "operationId": tool.name, + "requestBody": request_body, + "responses": { + "200": { + "description": "Tool result", + "content": {"application/json": tool.get_output_jsonschema()}, + } + }, + } + } + + return OpenAPI.model_validate({ + "openapi": "3.1.0", + "info": {"title": title, "version": version}, + "paths": paths, + "components": components, + }) diff --git a/altk/pre_tool/toolguard/toolguard/common/multi_process.py b/altk/pre_tool/toolguard/toolguard/common/multi_process.py new file mode 100644 index 0000000..f31de7a --- /dev/null +++ b/altk/pre_tool/toolguard/toolguard/common/multi_process.py @@ -0,0 +1,48 @@ +import sys +import importlib +from pathlib import Path +import multiprocessing + +def process_target(queue, func, args, kwargs, extra_paths): + try: + # Add dynamic python paths + if extra_paths: + for p in extra_paths: + p = str(Path(p).resolve()) + if p not in sys.path: + sys.path.insert(0, p) + + # Force reload of modules from those paths (optional) + if extra_paths: + for modname, module in list(sys.modules.items()): + file = getattr(module, "__file__", None) + if not file: + continue + + for p in extra_paths: + p = Path(p).resolve() + try: + if Path(file).resolve().is_relative_to(p): + importlib.reload(module) + except Exception: + pass + + # Run the function + result = func(*args, **kwargs) + queue.put(("ok", result)) + except Exception as e: + queue.put(("error", e)) + +def run_in_process(func, *args, extra_paths=None, **kwargs): + queue = multiprocessing.Queue() + p = multiprocessing.Process( + target=process_target, + args=(queue, func, args, kwargs, extra_paths) + ) + p.start() + p.join() + + status, value = queue.get() + if status == "error": + raise value + return value diff --git a/altk/pre_tool/toolguard/toolguard/common/open_api.py b/altk/pre_tool/toolguard/toolguard/common/open_api.py index 7e5cad1..b72f7be 100644 --- a/altk/pre_tool/toolguard/toolguard/common/open_api.py +++ b/altk/pre_tool/toolguard/toolguard/common/open_api.py @@ -4,10 +4,11 @@ import json import yaml -from altk.pre_tool.toolguard.toolguard.common.dict import find_ref -from altk.pre_tool.toolguard.toolguard.common.http import MEDIA_TYPE_APP_JSON -from altk.pre_tool.toolguard.toolguard.common.jschema import JSchema -from altk.pre_tool.toolguard.toolguard.common.ref import Reference +from .array import not_none +from .dict import find_ref +from .http import MEDIA_TYPE_APP_JSON +from .jschema import JSchema +from .ref import Reference class Contact(BaseModel): diff --git a/altk/pre_tool/toolguard/toolguard/common/py.py b/altk/pre_tool/toolguard/toolguard/common/py.py index 880ffb8..50c31de 100644 --- a/altk/pre_tool/toolguard/toolguard/common/py.py +++ b/altk/pre_tool/toolguard/toolguard/common/py.py @@ -1,3 +1,4 @@ + import os import inspect from typing import Callable @@ -5,8 +6,7 @@ from pathlib import Path from contextlib import contextmanager -from altk.pre_tool.toolguard.toolguard.common.str import to_snake_case - +from .str import to_snake_case def py_extension(filename: str) -> str: return filename if filename.endswith(".py") else filename + ".py" @@ -28,11 +28,6 @@ def module_to_path(module: str) -> str: parts = module.split(".") return os.path.join(*parts[:-1], py_extension(parts[-1])) - -def unwrap_fn(fn: Callable) -> Callable: - return fn.func if hasattr(fn, "func") else fn - - @contextmanager def temp_python_path(path: str): path = str(Path(path).resolve()) @@ -46,8 +41,7 @@ def temp_python_path(path: str): # Already in sys.path, no need to remove yield - -def extract_docstr_args(func: Callable) -> str: +def extract_docstr_args(func:Callable) -> str: doc = inspect.getdoc(func) if not doc: return "" @@ -86,3 +80,18 @@ def extract_docstr_args(func: Callable) -> str: return "" return "\n".join(args_lines) + +def get_func_signature(obj)->str: + if inspect.isfunction(obj): + return inspect.signature(obj) + if hasattr(obj, "func") and inspect.isfunction(obj.func): # a @tool + return inspect.signature(obj.func) + if hasattr(obj, "args_schema"): + schema = obj.args_schema + fields = schema.model_fields + params = ", ".join( + f"{name}: {field.annotation.__name__ if hasattr(field.annotation, '__name__') else field.annotation}" + for name, field in fields.items() + ) + return f"({params})" + return None \ No newline at end of file diff --git a/altk/pre_tool/toolguard/toolguard/common/ref.py b/altk/pre_tool/toolguard/toolguard/common/ref.py index 00308e6..ad25a80 100644 --- a/altk/pre_tool/toolguard/toolguard/common/ref.py +++ b/altk/pre_tool/toolguard/toolguard/common/ref.py @@ -1,7 +1,8 @@ + from typing import TypeVar from pydantic import BaseModel, Field -from altk.pre_tool.toolguard.toolguard.common.dict import find_ref +from .dict import find_ref class Reference(BaseModel): diff --git a/altk/pre_tool/toolguard/toolguard/common/safe_py.py b/altk/pre_tool/toolguard/toolguard/common/safe_py.py new file mode 100644 index 0000000..90b690c --- /dev/null +++ b/altk/pre_tool/toolguard/toolguard/common/safe_py.py @@ -0,0 +1,11 @@ +from typing import List + +def run_safe_python(code: str, libs: List[str] = []): + from smolagents.local_python_executor import LocalPythonExecutor + exec = LocalPythonExecutor( + additional_authorized_imports = libs, + max_print_outputs_length= None, + additional_functions = None, + ) + out = exec(code) + return out \ No newline at end of file diff --git a/altk/pre_tool/toolguard/toolguard/common/str.py b/altk/pre_tool/toolguard/toolguard/common/str.py index d9a8a9b..665efbb 100644 --- a/altk/pre_tool/toolguard/toolguard/common/str.py +++ b/altk/pre_tool/toolguard/toolguard/common/str.py @@ -1,3 +1,6 @@ +import re + + def to_camel_case(snake_str: str) -> str: return ( snake_str.replace("_", " ") @@ -6,7 +9,7 @@ def to_camel_case(snake_str: str) -> str: .replace("-", "_") .replace("'", "_") .replace(",", "_") - .replace(".", "_") + .replace("’","_") ) @@ -17,5 +20,19 @@ def to_snake_case(human_name: str) -> str: .replace("-", "_") .replace("'", "_") .replace(",", "_") - .replace(".", "_") + .replace("’","_") ) + +def to_pascal_case(name: str) -> str: + # Split by underscores first + parts = name.split('_') + + result_parts = [] + for part in parts: + # Split camelCase or mixedCase segments into words + subparts = re.findall(r'[A-Z]?[a-z]+|[A-Z]+(?![a-z])|\d+', part) + # Capitalize each segment + cap = ''.join(s[0].upper() + s[1:] if s else '' for s in subparts) + result_parts.append(cap) + + return ''.join(result_parts) \ No newline at end of file diff --git a/altk/pre_tool/toolguard/toolguard/core.py b/altk/pre_tool/toolguard/toolguard/core.py deleted file mode 100644 index ebf168a..0000000 --- a/altk/pre_tool/toolguard/toolguard/core.py +++ /dev/null @@ -1,115 +0,0 @@ -import os -from os.path import join -from typing import Callable, List, Optional - -import json -import logging - -from altk.pre_tool.toolguard.toolguard.gen_py.gen_toolguards import ( - generate_toolguards_from_openapi, - generate_toolguards_from_functions, -) -from altk.pre_tool.toolguard.toolguard.llm.i_tg_llm import I_TG_LLM -from altk.pre_tool.toolguard.toolguard.runtime import ToolGuardsCodeGenerationResult -from altk.pre_tool.toolguard.toolguard.data_types import ToolPolicy, load_tool_policy -from altk.pre_tool.toolguard.toolguard.tool_policy_extractor.create_oas_summary import ( - OASSummarizer, -) -from altk.pre_tool.toolguard.toolguard.tool_policy_extractor.text_tool_policy_generator import ( - ToolInfo, - extract_policies, -) - - -logger = logging.getLogger(__name__) - - -async def build_toolguards( - policy_text: str, - tools: List[Callable] | str, - step1_out_dir: str, - step2_out_dir: str, - step1_llm: I_TG_LLM, - app_name: str = "my_app", - tools2run: List[str] | None = None, - short1=True, -) -> ToolGuardsCodeGenerationResult: - if isinstance(tools, list): # supports list of functions or list of langgraph tools - tools_info = [ToolInfo.from_function(tool) for tool in tools] - tool_policies = await extract_policies( - policy_text, tools_info, step1_out_dir, step1_llm, tools2run, short1 - ) - return await generate_guards_from_tool_policies( - tools, tool_policies, step2_out_dir, app_name, None, tools2run - ) - - if isinstance(tools, str): # Backward compatibility to support OpenAPI specs - oas_path = tools - with open(oas_path, "r", encoding="utf-8") as file: - oas = json.load(file) - summarizer = OASSummarizer(oas) - tools_info = summarizer.summarize() - tool_policies = await extract_policies( - policy_text, tools_info, step1_out_dir, step1_llm, tools2run, short1 - ) - return await generate_guards_from_tool_policies_oas( - oas_path, tool_policies, step2_out_dir, app_name, tools2run - ) - - raise ValueError("Unknown tools") - - -async def generate_guards_from_tool_policies( - funcs: List[Callable], - tool_policies: List[ToolPolicy], - to_step2_path: str, - app_name: str, - lib_names: Optional[List[str]] = None, - tool_names: Optional[List[str]] = None, -) -> ToolGuardsCodeGenerationResult: - os.makedirs(to_step2_path, exist_ok=True) - - tool_policies = [ - policy - for policy in tool_policies - if (not tool_names) or (policy.tool_name in tool_names) - ] - return await generate_toolguards_from_functions( - app_name, tool_policies, to_step2_path, funcs=funcs, module_roots=lib_names - ) - - -async def generate_guards_from_tool_policies_oas( - oas_path: str, - tool_policies: List[ToolPolicy], - to_step2_path: str, - app_name: str, - tool_names: Optional[List[str]] = None, -) -> ToolGuardsCodeGenerationResult: - os.makedirs(to_step2_path, exist_ok=True) - - tool_policies = [ - policy - for policy in tool_policies - if (not tool_names) or (policy.tool_name in tool_names) - ] - return await generate_toolguards_from_openapi( - app_name, tool_policies, to_step2_path, oas_path - ) - - -def load_policies_in_folder( - folder: str, -) -> List[ToolPolicy]: - files = [ - f - for f in os.listdir(folder) - if os.path.isfile(join(folder, f)) and f.endswith(".json") - ] - tool_policies = [] - for file in files: - tool_name = file[: -len(".json")] - policy = load_tool_policy(join(folder, file), tool_name) - if policy.policy_items: - tool_policies.append(policy) - return tool_policies diff --git a/altk/pre_tool/toolguard/toolguard/data_types.py b/altk/pre_tool/toolguard/toolguard/data_types.py index ff37a0d..e5b30f5 100644 --- a/altk/pre_tool/toolguard/toolguard/data_types.py +++ b/altk/pre_tool/toolguard/toolguard/data_types.py @@ -1,14 +1,75 @@ +import inspect import json +import logging import os from pathlib import Path from pydantic import BaseModel, Field -from typing import List, Optional +from typing import Any, Callable, Dict, List, Literal, Optional, cast + +logger = logging.getLogger(__name__) DEBUG_DIR = "debug" TESTS_DIR = "tests" RESULTS_FILENAME = "result.json" API_PARAM = "api" +ENV_GENPY_BACKEND_NAME = "TOOLGUARD_GENPY_BACKEND_NAME" +ENV_GENPY_MODEL_ID = "TOOLGUARD_GENPY_MODEL_ID" +ENV_GENPY_ARGS = "TOOLGUARD_GENPY_ARGS" +MeleaBackendName = Literal["ollama", "hf", "openai", "watsonx", "litellm"] + +class MeleaSessionData(BaseModel): + backend_name: MeleaBackendName | None = None + model_id: str| None = None + kw_args: Dict[str, Any]| None = None + + def model_post_init(self, __context): + if not self.backend_name: + self.backend_name = cast(MeleaBackendName, + os.getenv(ENV_GENPY_BACKEND_NAME, "openai"), + ) + + if not self.model_id: + self.model_id = os.getenv(ENV_GENPY_MODEL_ID) + assert self.model_id, f"'{ENV_GENPY_MODEL_ID}' environment variable not set" + + if not self.kw_args: + kw_args_val = os.getenv(ENV_GENPY_ARGS) + self.kw_args = {} + if kw_args_val: + try: + self.kw_args = json.loads(kw_args_val) + except Exception as e: + logger.warning( + f"Failed to parse {ENV_GENPY_ARGS}: {e}. Using empty dict instead." + ) + +class ToolInfo(BaseModel): + name: str + description: str + parameters: Any + signature: str + full_description:str + + @classmethod + def from_function(cls, fn: Callable) -> "ToolInfo": + # Assumes @tool decorator from langchain https://python.langchain.com/docs/how_to/custom_tools/ + # or a plain function with doc string + def doc_summary(doc:str): + paragraphs = [p.strip() for p in doc.split("\n\n") if p.strip()] + return paragraphs[0] if paragraphs else "" + + fn_name = fn.name if hasattr(fn, 'name') else fn.__name__ + sig =fn_name + str(inspect.signature(fn)) + full_desc = fn.description if hasattr(fn,'description') else fn.__doc__.strip() if fn.__doc__ else (inspect.getdoc(fn) or "") + return cls( + name=fn_name, + description=doc_summary(full_desc), + full_description = full_desc, + parameters=fn.args_schema.model_json_schema() if hasattr(fn, 'args_schema') else inspect.getdoc(fn), + signature=sig, + ) + class FileTwin(BaseModel): file_name: str @@ -33,9 +94,9 @@ def load_from(folder: str, file_path: str) -> "FileTwin": with open(os.path.join(folder, file_path), "r") as file: data = file.read() return FileTwin(file_name=file_path, content=data) - - -class ToolPolicyItem(BaseModel): + + +class ToolGuardSpecItem(BaseModel): name: str = Field(..., description="Policy item name") description: str = Field(..., description="Policy item description") references: List[str] = Field(..., description="original texts") @@ -62,20 +123,20 @@ def __str__(self) -> str: return s -class ToolPolicy(BaseModel): +class ToolGuardSpec(BaseModel): tool_name: str = Field(..., description="Name of the tool") - policy_items: List[ToolPolicyItem] = Field( + policy_items: List[ToolGuardSpecItem] = Field( ..., description="Policy items. All (And logic) policy items must hold whehn invoking the tool.", ) -def load_tool_policy(file_path: str, tool_name: str) -> ToolPolicy: +def load_tool_policy(file_path: str, tool_name: str) -> ToolGuardSpec: with open(file_path, "r") as file: d = json.load(file) items = [ - ToolPolicyItem( + ToolGuardSpecItem( name=item.get("policy_name"), description=item.get("description"), references=item.get("references"), @@ -85,7 +146,7 @@ def load_tool_policy(file_path: str, tool_name: str) -> ToolPolicy: for item in d.get("policies", []) if not item.get("skip") ] - return ToolPolicy(tool_name=tool_name, policy_items=items) + return ToolGuardSpec(tool_name=tool_name, policy_items=items) class Domain(BaseModel): diff --git a/altk/pre_tool/toolguard/toolguard/gen_py/api_extractor.py b/altk/pre_tool/toolguard/toolguard/gen_py/api_extractor.py index 5feb9a4..ecda453 100644 --- a/altk/pre_tool/toolguard/toolguard/gen_py/api_extractor.py +++ b/altk/pre_tool/toolguard/toolguard/gen_py/api_extractor.py @@ -21,16 +21,13 @@ from typing import Annotated, Union from collections import defaultdict, deque import typing -from altk.pre_tool.toolguard.toolguard.common.py import module_to_path, unwrap_fn -from altk.pre_tool.toolguard.toolguard.data_types import FileTwin +from ..common.py import module_to_path +from ..data_types import FileTwin Dependencies = DefaultDict[type, Set[type]] - class APIExtractor: - def __init__(self, py_path: str, include_module_roots: Optional[List[str]] = None): - if not include_module_roots: - include_module_roots = [] + def __init__(self, py_path:str, include_module_roots:List[str] = []): self.py_path = py_path self.include_module_roots = include_module_roots @@ -43,7 +40,6 @@ def extract_from_functions( impl_module_name: str, impl_class_name: str, ) -> Tuple[FileTwin, FileTwin, FileTwin]: - funcs = [unwrap_fn(func) for func in funcs] assert all([_is_global_or_class_function(func) for func in funcs]) os.makedirs(self.py_path, exist_ok=True) @@ -748,37 +744,3 @@ def _is_global_or_class_function(func): return False # instance method return False - - -# Example class for testing -if __name__ == "__main__": - # from airline2.domains.airline.tools import AirlineTools - # extractor = APIExtractor("output", include_module_roots = ["airline2"]) - # # interface_file, types_file = extractor.extract_from_class(AirlineTools, output_dir="output") - # t = AirlineTools({}) - # interface_file, types_file = extractor.extract_from_functions([AirlineTools.book_reservation], "I_Tau", "my_app.i_tau", "my_app.tau_types") - - from appointment_app import lg_tools - - funcs = [ - lg_tools.add_user, - lg_tools.add_payment_method, - lg_tools.get_user_payment_methods, - lg_tools.get_available_dr_specialties, - lg_tools.search_doctors, - lg_tools.search_available_appointments, - ] - extractor = APIExtractor("output", include_module_roots=["appointment_app"]) - interface, types_, impl = extractor.extract_from_functions( - funcs, - "I_Clinic", - "clinic.i_clinic", - "clinic.clinc_types", - "clinic.clinic_impl", - "ClinicImpl", - ) - - print(f"Interface saved to: {interface.file_name}") - print(f"Types saved to: {types_.file_name}") - print(f"Impl saved to: {impl.file_name}") - print("Done") diff --git a/altk/pre_tool/toolguard/toolguard/gen_py/consts.py b/altk/pre_tool/toolguard/toolguard/gen_py/consts.py index fa14d69..1ab6a75 100644 --- a/altk/pre_tool/toolguard/toolguard/gen_py/consts.py +++ b/altk/pre_tool/toolguard/toolguard/gen_py/consts.py @@ -1,5 +1,5 @@ -from altk.pre_tool.toolguard.toolguard.common.str import to_snake_case -from altk.pre_tool.toolguard.toolguard.data_types import ToolPolicy, ToolPolicyItem +from ..common.str import to_snake_case +from ..data_types import ToolGuardSpec, ToolGuardSpecItem RUNTIME_PACKAGE_NAME = "rt_toolguard" @@ -7,29 +7,25 @@ RUNTIME_TYPES_PY = "data_types.py" RUNTIME_APP_TYPES_PY = "domain_types.py" -PY_ENV = "my_env" -PY_PACKAGES = ["pydantic", "pytest"] # , "litellm"] - - -def guard_fn_name(tool_policy: ToolPolicy) -> str: +def guard_fn_name(tool_policy: ToolGuardSpec) -> str: return to_snake_case(f"guard_{tool_policy.tool_name}") -def guard_fn_module_name(tool_policy: ToolPolicy) -> str: +def guard_fn_module_name(tool_policy: ToolGuardSpec) -> str: return to_snake_case(f"guard_{tool_policy.tool_name}") -def guard_item_fn_name(tool_item: ToolPolicyItem) -> str: +def guard_item_fn_name(tool_item: ToolGuardSpecItem) -> str: return to_snake_case(f"guard_{tool_item.name}") -def guard_item_fn_module_name(tool_item: ToolPolicyItem) -> str: +def guard_item_fn_module_name(tool_item: ToolGuardSpecItem) -> str: return to_snake_case(f"guard_{tool_item.name}") -def test_fn_name(tool_item: ToolPolicyItem) -> str: +def test_fn_name(tool_item: ToolGuardSpecItem) -> str: return to_snake_case(f"test_guard_{tool_item.name}") -def test_fn_module_name(tool_item: ToolPolicyItem) -> str: +def test_fn_module_name(tool_item: ToolGuardSpecItem) -> str: return to_snake_case(f"test_guard_{tool_item.name}") diff --git a/altk/pre_tool/toolguard/toolguard/gen_py/domain_from_funcs.py b/altk/pre_tool/toolguard/toolguard/gen_py/domain_from_funcs.py index f815108..0a170e2 100644 --- a/altk/pre_tool/toolguard/toolguard/gen_py/domain_from_funcs.py +++ b/altk/pre_tool/toolguard/toolguard/gen_py/domain_from_funcs.py @@ -3,10 +3,10 @@ from typing import Callable, List from os.path import join -from altk.pre_tool.toolguard.toolguard.gen_py.api_extractor import APIExtractor -from altk.pre_tool.toolguard.toolguard.common.str import to_camel_case, to_snake_case -from altk.pre_tool.toolguard.toolguard.data_types import FileTwin, RuntimeDomain -import altk.pre_tool.toolguard.toolguard.gen_py.consts as consts +from .api_extractor import APIExtractor +from ..common.str import to_camel_case, to_snake_case +from ..data_types import FileTwin, RuntimeDomain +from . import consts def generate_domain_from_functions( @@ -20,9 +20,9 @@ def generate_domain_from_functions( py_path, join(consts.RUNTIME_PACKAGE_NAME, consts.RUNTIME_TYPES_PY) ) runtime = FileTwin.load_from(root, "runtime.py") - runtime.content = runtime.content.replace( - "toolguard.", f"{consts.RUNTIME_PACKAGE_NAME}." - ) + # runtime.content = runtime.content.replace( + # "toolguard.", f"{consts.RUNTIME_PACKAGE_NAME}." + # ) runtime.save_as(py_path, join(consts.RUNTIME_PACKAGE_NAME, consts.RUNTIME_INIT_PY)) # APP init and Types diff --git a/altk/pre_tool/toolguard/toolguard/gen_py/domain_from_openapi.py b/altk/pre_tool/toolguard/toolguard/gen_py/domain_from_openapi.py index f19e930..223385b 100644 --- a/altk/pre_tool/toolguard/toolguard/gen_py/domain_from_openapi.py +++ b/altk/pre_tool/toolguard/toolguard/gen_py/domain_from_openapi.py @@ -3,15 +3,15 @@ from typing import List, Optional, Tuple, Union from os.path import join -from altk.pre_tool.toolguard.toolguard.common.array import find -from altk.pre_tool.toolguard.toolguard.common.py import module_to_path -from altk.pre_tool.toolguard.toolguard.common.str import to_camel_case, to_snake_case -import altk.pre_tool.toolguard.toolguard.gen_py.consts as consts -from altk.pre_tool.toolguard.toolguard.gen_py.templates import load_template -from altk.pre_tool.toolguard.toolguard.gen_py.utils.datamodel_codegen import ( +from ..common.array import find +from ..common.py import module_to_path +from ..common.str import to_camel_case, to_pascal_case, to_snake_case +from . import consts +from .templates import load_template +from .utils.datamodel_codegen import ( run as dm_codegen, ) -from altk.pre_tool.toolguard.toolguard.common.open_api import ( +from ..common.open_api import ( OpenAPI, Operation, Parameter, @@ -23,8 +23,9 @@ JSchema, read_openapi, ) -from altk.pre_tool.toolguard.toolguard.data_types import FileTwin, RuntimeDomain +from ..data_types import FileTwin, RuntimeDomain +ARGS = "args" def generate_domain_from_openapi( py_path: str, app_name: str, openapi_file: str @@ -37,9 +38,9 @@ def generate_domain_from_openapi( py_path, join(consts.RUNTIME_PACKAGE_NAME, consts.RUNTIME_TYPES_PY) ) runtime = FileTwin.load_from(root, "runtime.py") - runtime.content = runtime.content.replace( - "toolguard.", f"{consts.RUNTIME_PACKAGE_NAME}." - ) + # runtime.content = runtime.content.replace( + # "toolguard.", f"{consts.RUNTIME_PACKAGE_NAME}." + # ) runtime.save_as(py_path, join(consts.RUNTIME_PACKAGE_NAME, consts.RUNTIME_INIT_PY)) # APP Types @@ -104,7 +105,7 @@ def _get_oas_methods(oas: OpenAPI): args_str = ", ".join(["self"] + [f"{arg}:{type}" for arg, type in args]) sig = f"({args_str})->{ret}" - body = "pass" + body = f"return self._delegate.invoke('{to_snake_case(op.operationId)}', {ARGS}.model_dump(), {ret})" # if orign_funcs: # func = find(orign_funcs or [], lambda fn: fn.__name__ == op.operationId) # type: ignore # if func: @@ -185,7 +186,7 @@ def _make_signature( if find(params, lambda p: p.in_ == ParameterIn.query): query_type = f"{fn_name}ParametersQuery" - args.append(("args", query_type)) + args.append((ARGS, query_type)) req_body = oas.resolve_ref(op.requestBody, RequestBody) if req_body: @@ -193,7 +194,7 @@ def _make_signature( body_type = _oas_to_py_type(scm_or_ref, oas) if body_type is None: body_type = f"{fn_name}Request" - args.append(("args", body_type)) + args.append((ARGS, body_type)) rsp_or_ref = op.responses.get("200") rsp = oas.resolve_ref(rsp_or_ref, Response) @@ -203,13 +204,15 @@ def _make_signature( rsp_type = _oas_to_py_type(scm_or_ref, oas) if rsp_type is None: rsp_type = f"{fn_name}Response" - + else: + rsp_type = "Any" return args, rsp_type def _oas_to_py_type(scm_or_ref: Union[Reference, JSchema], oas: OpenAPI) -> str | None: if isinstance(scm_or_ref, Reference): - return scm_or_ref.ref.split("/")[-1] + typ = scm_or_ref.ref.split("/")[-1] + return to_pascal_case(typ) scm = oas.resolve_ref(scm_or_ref, JSchema) if scm: diff --git a/altk/pre_tool/toolguard/toolguard/gen_py/gen_toolguards.py b/altk/pre_tool/toolguard/toolguard/gen_py/gen_toolguards.py index 921d65f..34d85dc 100644 --- a/altk/pre_tool/toolguard/toolguard/gen_py/gen_toolguards.py +++ b/altk/pre_tool/toolguard/toolguard/gen_py/gen_toolguards.py @@ -1,41 +1,25 @@ import asyncio -import json import logging -import os -from os.path import join -from typing import Callable, List, Literal, Optional, cast +from typing import Callable, List, Optional import mellea -import altk.pre_tool.toolguard.toolguard.gen_py.consts as consts -from altk.pre_tool.toolguard.toolguard.gen_py.domain_from_funcs import ( - generate_domain_from_functions, -) -from altk.pre_tool.toolguard.toolguard.data_types import RuntimeDomain, ToolPolicy -from altk.pre_tool.toolguard.toolguard.gen_py.domain_from_openapi import ( - generate_domain_from_openapi, -) -from altk.pre_tool.toolguard.toolguard.runtime import ToolGuardsCodeGenerationResult -from altk.pre_tool.toolguard.toolguard.gen_py.tool_guard_generator import ( - ToolGuardGenerator, -) -import altk.pre_tool.toolguard.toolguard.gen_py.utils.pytest as pytest -import altk.pre_tool.toolguard.toolguard.gen_py.utils.venv as venv -import altk.pre_tool.toolguard.toolguard.gen_py.utils.pyright as pyright -from altk.pre_tool.toolguard.toolguard.common.py import unwrap_fn +from .domain_from_funcs import generate_domain_from_functions +from ..data_types import RuntimeDomain, ToolGuardSpec +from .domain_from_openapi import generate_domain_from_openapi +from ..runtime import ToolGuardsCodeGenerationResult +from .tool_guard_generator import ToolGuardGenerator +from .utils import pytest +from .utils import pyright logger = logging.getLogger(__name__) -ENV_GENPY_BACKEND_NAME = "TOOLGUARD_GENPY_BACKEND_NAME" -ENV_GENPY_MODEL_ID = "TOOLGUARD_GENPY_MODEL_ID" -ENV_GENPY_ARGS = "TOOLGUARD_GENPY_ARGS" - - async def generate_toolguards_from_functions( app_name: str, - tool_policies: List[ToolPolicy], + tool_policies: List[ToolGuardSpec], py_root: str, funcs: List[Callable], + llm_data: MeleaSessionData, module_roots: Optional[List[str]] = None, ) -> ToolGuardsCodeGenerationResult: assert funcs, "Funcs cannot be empty" @@ -44,62 +28,46 @@ async def generate_toolguards_from_functions( if not module_roots: if len(funcs) > 0: module_roots = list( - {unwrap_fn(func).__module__.split(".")[0] for func in funcs} + {func.__module__.split(".")[0] for func in funcs} ) assert module_roots # Domain from functions domain = generate_domain_from_functions(py_root, app_name, funcs, module_roots) return await generate_toolguards_from_domain( - app_name, tool_policies, py_root, domain + app_name, tool_policies, py_root, domain, llm_data ) async def generate_toolguards_from_openapi( - app_name: str, tool_policies: List[ToolPolicy], py_root: str, openapi_file: str + app_name: str, tool_policies: List[ToolGuardSpec], py_root: str, openapi_file: str, llm_data: MeleaSessionData ) -> ToolGuardsCodeGenerationResult: logger.debug(f"Starting... will save into {py_root}") # Domain from OpenAPI domain = generate_domain_from_openapi(py_root, app_name, openapi_file) return await generate_toolguards_from_domain( - app_name, tool_policies, py_root, domain - ) - - -def start_melea_session() -> mellea.MelleaSession: - backend_name = cast( - Literal["ollama", "hf", "openai", "watsonx", "litellm"], - os.getenv(ENV_GENPY_BACKEND_NAME, "openai"), + app_name, tool_policies, py_root, domain, llm_data ) - model_id = os.getenv(ENV_GENPY_MODEL_ID) - assert model_id, f"'{ENV_GENPY_MODEL_ID}' environment variable not set" - - kw_args_val = os.getenv(ENV_GENPY_ARGS) - kw_args = {} - if kw_args_val: - try: - kw_args = json.loads(kw_args_val) - except Exception as e: - logger.warning( - f"Failed to parse {ENV_GENPY_ARGS}: {e}. Using empty dict instead." - ) - - print(kw_args) - - return mellea.start_session(backend_name=backend_name, model_id=model_id, **kw_args) - - async def generate_toolguards_from_domain( - app_name: str, tool_policies: List[ToolPolicy], py_root: str, domain: RuntimeDomain + app_name: str, tool_policies: List[ToolGuardSpec], py_root: str, domain: RuntimeDomain, + llm_data: MeleaSessionData ) -> ToolGuardsCodeGenerationResult: # Setup env - venv.run(join(py_root, consts.PY_ENV), consts.PY_PACKAGES) + # venv.run(join(py_root, consts.PY_ENV), consts.PY_PACKAGES) pyright.config(py_root) pytest.configure(py_root) - with start_melea_session(): + for tool_policy in tool_policies: + for policy in tool_policy.policy_items: + policy.name = policy.name.replace(".","_") + + with mellea.start_session( + backend_name = llm_data.backend_name, + model_id=llm_data.model_id, + **llm_data.kw_args + ): # tools tools_w_poilicies = [ tool_policy @@ -109,7 +77,7 @@ async def generate_toolguards_from_domain( tool_results = await asyncio.gather( *[ ToolGuardGenerator( - app_name, tool_policy, py_root, domain, consts.PY_ENV + app_name, tool_policy, py_root, domain ).generate() for tool_policy in tools_w_poilicies ] diff --git a/altk/pre_tool/toolguard/toolguard/gen_py/prompts/gen_tests.py b/altk/pre_tool/toolguard/toolguard/gen_py/prompts/gen_tests.py index 80a1724..20b79be 100644 --- a/altk/pre_tool/toolguard/toolguard/gen_py/prompts/gen_tests.py +++ b/altk/pre_tool/toolguard/toolguard/gen_py/prompts/gen_tests.py @@ -1,9 +1,5 @@ from typing import List -from altk.pre_tool.toolguard.toolguard.data_types import ( - Domain, - FileTwin, - ToolPolicyItem, -) +from ...data_types import Domain, FileTwin, ToolGuardSpecItem from mellea import generative # from toolguard.gen_py.prompts.python_code import PythonCodeModel @@ -12,7 +8,7 @@ @generative def generate_init_tests( fn_src: FileTwin, - policy_item: ToolPolicyItem, + policy_item: ToolGuardSpecItem, domain: Domain, dependent_tool_names: List[str], ) -> str: @@ -21,7 +17,7 @@ def generate_init_tests( Args: fn_src (FileTwin): Source code containing the function-under-test signature. - policy_item (ToolPolicyItem): Specification of the function-under-test, including positive and negative examples. + policy_item (ToolGuardSpecItem): Specification of the function-under-test, including positive and negative examples. domain (Domain): available data types and interfaces the test can use. dependent_tool_names (List[str]): other tool names that this tool depends on. @@ -115,7 +111,7 @@ def test_violation_book_room_in_the_past(): def improve_tests( prev_impl: str, domain: Domain, - policy_item: ToolPolicyItem, + policy_item: ToolGuardSpecItem, review_comments: List[str], dependent_tool_names: List[str], ) -> str: @@ -125,7 +121,7 @@ def improve_tests( Args: prev_impl (str): previous implementation of a Python function. domain (Domain): Python source code defining available data types and APIs that the test can use. - tool (ToolPolicyItem): Requirements for this tool. + tool (ToolGuardSpecItem): Requirements for this tool. review_comments (List[str]): Review comments on the current implementation. For example, pylint errors or Failed unit-tests. dependent_tool_names (List[str]): other tool names that this tool depends on. diff --git a/altk/pre_tool/toolguard/toolguard/gen_py/prompts/improve_guard.py b/altk/pre_tool/toolguard/toolguard/gen_py/prompts/improve_guard.py index 7af2f88..5070fd3 100644 --- a/altk/pre_tool/toolguard/toolguard/gen_py/prompts/improve_guard.py +++ b/altk/pre_tool/toolguard/toolguard/gen_py/prompts/improve_guard.py @@ -1,5 +1,5 @@ from typing import List -from altk.pre_tool.toolguard.toolguard.data_types import Domain, ToolPolicyItem +from ...data_types import Domain, ToolGuardSpecItem from mellea import generative # from toolguard.gen_py.prompts.python_code import PythonCodeModel @@ -9,7 +9,7 @@ def improve_tool_guard( prev_impl: str, domain: Domain, - policy_item: ToolPolicyItem, + policy_item: ToolGuardSpecItem, dependent_tool_names: List[str], review_comments: List[str], ) -> str: @@ -19,7 +19,7 @@ def improve_tool_guard( Args: prev_impl (str): The previous implementation of the tool-call check. domain (Domain): Python code defining available data types and other tool interfaces. - policy_item (ToolPolicyItem): Requirements for this tool. + policy_item (ToolGuardSpecItem): Requirements for this tool. dependent_tool_names (List[str]): Names of other tools that this tool may call to obtain required information. review_comments (List[str]): Review feedback on the current implementation (e.g., pylint errors, failed unit tests). diff --git a/altk/pre_tool/toolguard/toolguard/gen_py/prompts/pseudo_code.py b/altk/pre_tool/toolguard/toolguard/gen_py/prompts/pseudo_code.py index 02f98b1..5f9e324 100644 --- a/altk/pre_tool/toolguard/toolguard/gen_py/prompts/pseudo_code.py +++ b/altk/pre_tool/toolguard/toolguard/gen_py/prompts/pseudo_code.py @@ -1,16 +1,16 @@ -from altk.pre_tool.toolguard.toolguard.data_types import Domain, ToolPolicyItem +from ...data_types import Domain, ToolGuardSpecItem from mellea import generative @generative def tool_policy_pseudo_code( - policy_item: ToolPolicyItem, fn_to_analyze: str, domain: Domain + policy_item: ToolGuardSpecItem, fn_to_analyze: str, domain: Domain ) -> str: """ Returns a pseudo code to check business constraints on a tool cool using an API Args: - policy_item (ToolPolicyItem): Business policy, in natural language, specifying a constraint on a process involving the tool under analysis. + policy_item (ToolGuardSpecItem): Business policy, in natural language, specifying a constraint on a process involving the tool under analysis. fn_to_analyze (str): The function signature of the tool under analysis. domain (Domain): Python code defining available data types and APIs for invoking other tools. diff --git a/altk/pre_tool/toolguard/toolguard/gen_py/prompts/~python_code.py b/altk/pre_tool/toolguard/toolguard/gen_py/prompts/~python_code.py deleted file mode 100644 index f3feba2..0000000 --- a/altk/pre_tool/toolguard/toolguard/gen_py/prompts/~python_code.py +++ /dev/null @@ -1,24 +0,0 @@ -from pydantic import BaseModel, Field -import re - -PYTHON_PATTERN = r"^```python\s*\n([\s\S]*)\n```" - - -class PythonCodeModel(BaseModel): - python_code: str = Field( - ..., - ) - - def get_code_content(self) -> str: - code = self.python_code.replace("\\n", "\n") - match = re.match(PYTHON_PATTERN, code) - if match: - return match.group(1) - - return code - - @classmethod - def create(cls, python_code: str) -> "PythonCodeModel": - return PythonCodeModel.model_construct( - python_code=f"```python\n{python_code}\n```" - ) diff --git a/altk/pre_tool/toolguard/toolguard/gen_py/templates/__init__.py b/altk/pre_tool/toolguard/toolguard/gen_py/templates/__init__.py index e26b90d..049d3ae 100644 --- a/altk/pre_tool/toolguard/toolguard/gen_py/templates/__init__.py +++ b/altk/pre_tool/toolguard/toolguard/gen_py/templates/__init__.py @@ -1,10 +1,13 @@ -from jinja2 import Environment, PackageLoader, select_autoescape +from pathlib import Path +from jinja2 import Environment, FileSystemLoader, select_autoescape -from altk.pre_tool.toolguard.toolguard.common.py import path_to_module -from altk.pre_tool.toolguard.toolguard.common.str import to_snake_case +from ...common.py import path_to_module +from ...common.str import to_snake_case + +TEMPLATES_DIR = Path(__file__).parent env = Environment( - loader=PackageLoader("altk.pre_tool.toolguard.toolguard.gen_py", "templates"), + loader=FileSystemLoader(str(TEMPLATES_DIR)), autoescape=select_autoescape(), ) env.globals["path_to_module"] = path_to_module @@ -12,4 +15,4 @@ def load_template(template_name: str): - return env.get_template(template_name) + return env.get_template(template_name) \ No newline at end of file diff --git a/altk/pre_tool/toolguard/toolguard/gen_py/templates/api_impl.j2 b/altk/pre_tool/toolguard/toolguard/gen_py/templates/api_impl.j2 index ab114b8..d8d8414 100644 --- a/altk/pre_tool/toolguard/toolguard/gen_py/templates/api_impl.j2 +++ b/altk/pre_tool/toolguard/toolguard/gen_py/templates/api_impl.j2 @@ -1,11 +1,22 @@ -from typing import Any, Dict, List +from abc import ABC, abstractmethod from datetime import date, datetime from {{ types_module }} import * from {{ api_module }} import * +class IToolInvoker(ABC): + T = TypeVar("T") + @abstractmethod + def invoke(self, toolname: str, arguments: Dict[str, Any], return_type: Type[T])->T: + ... + + class {{ class_name }}({{ api_cls_name }}): + + def __init__(self, delegate: IToolInvoker): + self._delegate = delegate + {% for method in methods %} def {{ method.name }}{{ method.signature }}: {% if method.doc %}"""{{ method.doc }}"""{% endif %} {{ method.body }} -{% endfor %} +{% endfor %} \ No newline at end of file diff --git a/altk/pre_tool/toolguard/toolguard/gen_py/templates/tool_guard.j2 b/altk/pre_tool/toolguard/toolguard/gen_py/templates/tool_guard.j2 index d4b0e35..c432a2a 100644 --- a/altk/pre_tool/toolguard/toolguard/gen_py/templates/tool_guard.j2 +++ b/altk/pre_tool/toolguard/toolguard/gen_py/templates/tool_guard.j2 @@ -15,11 +15,11 @@ def {{ method.name }}(api: {{ domain.app_api_class_name }}, {{ method.signature} Args: api ({{ domain.app_api_class_name }}): api to access other tools. -{{method.args_doc_str}} +{{method.args_doc_str}} Raises: PolicyViolationException: If the tool call does not comply to the policy. """ - + {% for item in items %} {{ item.guard_fn }}(api, {{method.args_call}}) {% endfor %} diff --git a/altk/pre_tool/toolguard/toolguard/gen_py/tool_dependencies.py b/altk/pre_tool/toolguard/toolguard/gen_py/tool_dependencies.py index 5149083..0cb4013 100644 --- a/altk/pre_tool/toolguard/toolguard/gen_py/tool_dependencies.py +++ b/altk/pre_tool/toolguard/toolguard/gen_py/tool_dependencies.py @@ -1,17 +1,15 @@ import asyncio import re from typing import Set -from altk.pre_tool.toolguard.toolguard.data_types import Domain, ToolPolicyItem from mellea.backends.types import ModelOption -from altk.pre_tool.toolguard.toolguard.gen_py.prompts.pseudo_code import ( - tool_policy_pseudo_code, -) +from ..data_types import Domain, ToolGuardSpecItem +from prompts.pseudo_code import tool_policy_pseudo_code MAX_TRIALS = 3 async def tool_dependencies( - policy_item: ToolPolicyItem, tool_signature: str, domain: Domain, trial=0 + policy_item: ToolGuardSpecItem, tool_signature: str, domain: Domain, trial=0 ) -> Set[str]: model_options = {ModelOption.TEMPERATURE: 0.8} pseudo_code = await asyncio.to_thread( # FIXME when melea will support aysnc @@ -33,4 +31,4 @@ async def tool_dependencies( def _extract_api_calls(code: str) -> Set[str]: pattern = re.compile(r"\bapi\.(\w+)\s*\(") - return set(pattern.findall(code)) + return set(pattern.findall(code)) \ No newline at end of file diff --git a/altk/pre_tool/toolguard/toolguard/gen_py/tool_guard_generator.py b/altk/pre_tool/toolguard/toolguard/gen_py/tool_guard_generator.py index 02e4e0f..7a10923 100644 --- a/altk/pre_tool/toolguard/toolguard/gen_py/tool_guard_generator.py +++ b/altk/pre_tool/toolguard/toolguard/gen_py/tool_guard_generator.py @@ -1,3 +1,4 @@ +from importlib import import_module import inspect import os import asyncio @@ -6,68 +7,56 @@ import re from typing import Callable, List, Tuple -from altk.pre_tool.toolguard.toolguard.common import py -from altk.pre_tool.toolguard.toolguard.common.llm_py import get_code_content -from altk.pre_tool.toolguard.toolguard.common.py_doc_str import extract_docstr_args -from altk.pre_tool.toolguard.toolguard.common.str import to_snake_case -from altk.pre_tool.toolguard.toolguard.data_types import ( +from ..common import py +from ..common.llm_py import get_code_content +from ..common.py_doc_str import extract_docstr_args +from ..common.str import to_snake_case +from ..data_types import ( DEBUG_DIR, TESTS_DIR, FileTwin, RuntimeDomain, - ToolPolicy, - ToolPolicyItem, + ToolGuardSpec, + ToolGuardSpecItem, ) -from altk.pre_tool.toolguard.toolguard.gen_py.consts import ( +from ..gen_py.consts import ( guard_fn_module_name, guard_fn_name, guard_item_fn_module_name, guard_item_fn_name, test_fn_module_name, ) -from altk.pre_tool.toolguard.toolguard.gen_py.tool_dependencies import tool_dependencies -from altk.pre_tool.toolguard.toolguard.runtime import ( - ToolGuardCodeResult, - find_class_in_module, - load_module_from_path, -) -import altk.pre_tool.toolguard.toolguard.gen_py.utils.pytest as pytest -import altk.pre_tool.toolguard.toolguard.gen_py.utils.pyright as pyright -from altk.pre_tool.toolguard.toolguard.gen_py.prompts.gen_tests import ( - generate_init_tests, - improve_tests, -) -from altk.pre_tool.toolguard.toolguard.gen_py.prompts.improve_guard import ( - improve_tool_guard, -) -from altk.pre_tool.toolguard.toolguard.gen_py.templates import load_template +from ..runtime import ToolGuardCodeResult, find_class_in_module +from ..gen_py.tool_dependencies import tool_dependencies +from ..gen_py.prompts.gen_tests import generate_init_tests, improve_tests +from ..gen_py.prompts.improve_guard import improve_tool_guard +from ..gen_py.templates import load_template +from .utils import pytest +from .utils import pyright logger = logging.getLogger(__name__) MAX_TOOL_IMPROVEMENTS = 5 MAX_TEST_GEN_TRIALS = 3 - class ToolGuardGenerator: app_name: str py_path: str - tool_policy: ToolPolicy + tool_policy: ToolGuardSpec domain: RuntimeDomain common: FileTwin def __init__( self, app_name: str, - tool_policy: ToolPolicy, + tool_policy: ToolGuardSpec, py_path: str, - domain: RuntimeDomain, - py_env: str, + domain: RuntimeDomain ) -> None: self.py_path = py_path self.app_name = app_name self.tool_policy = tool_policy self.domain = domain - self.py_env = py_env def start(self): app_path = join(self.py_path, to_snake_case(self.app_name)) @@ -120,7 +109,7 @@ async def generate(self) -> ToolGuardCodeResult: ) async def _generate_item_tests_and_guard( - self, item: ToolPolicyItem, init_guard: FileTwin + self, item: ToolGuardSpecItem, init_guard: FileTwin ) -> Tuple[FileTwin | None, FileTwin]: # Dependencies of this tool tool_fn_name = to_snake_case(self.tool_policy.tool_name) @@ -142,6 +131,7 @@ async def _generate_item_tests_and_guard( guard = await self._improve_guard(item, init_guard, [], dep_tools) return None, guard except Exception as ex: + logger.exception(ex) logger.warning( "guard generation failed. returning initial guard: %s", str(ex) ) @@ -157,19 +147,20 @@ async def _generate_item_tests_and_guard( ) # 😄🎉 Happy path return guard_tests, guard except Exception as ex: + logger.exception(ex) logger.warning( "guard generation failed. returning initial guard: %s", str(ex) ) return None, init_guard - # async def tool_dependencies(self, policy_item: ToolPolicyItem, tool_signature: str) -> Set[str]: + # async def tool_dependencies(self, policy_item: ToolGuardSpecItem, tool_signature: str) -> Set[str]: # domain = self.domain.get_definitions_only() #remove runtime fields # pseudo_code = await tool_policy_pseudo_code(policy_item, tool_signature, domain) # dep_tools = await extract_api_dependencies_from_pseudo_code(pseudo_code, domain) # return set(dep_tools) async def _generate_tests( - self, item: ToolPolicyItem, guard: FileTwin, dep_tools: List[str] + self, item: ToolGuardSpecItem, guard: FileTwin, dep_tools: List[str] ) -> FileTwin: test_file_name = join( TESTS_DIR, self.tool_policy.tool_name, f"{test_fn_module_name(item)}.py" @@ -184,26 +175,20 @@ async def _generate_tests( domain = self.domain.get_definitions_only() # remove runtime fields first_time = trial_no == "a" if first_time: - # FIXME when melea will support aysnc - res = await asyncio.to_thread( - lambda: generate_init_tests( - fn_src=guard, - policy_item=item, - domain=domain, # noqa: B023 - dependent_tool_names=dep_tools, - ) + res = generate_init_tests( + fn_src=guard, + policy_item=item, + domain=domain, # noqa: B023 + dependent_tool_names=dep_tools, ) else: assert test_file - # FIXME when melea will support aysnc - res = await asyncio.to_thread( - lambda: improve_tests( - prev_impl=test_file.content, # noqa: B023 - domain=domain, # noqa: B023 - policy_item=item, - review_comments=errors, # noqa: B023 - dependent_tool_names=dep_tools, - ) + res = improve_tests( + prev_impl=test_file.content, # noqa: B023 + domain=domain, # noqa: B023 + policy_item=item, + review_comments=errors, # noqa: B023 + dependent_tool_names=dep_tools, ) test_file = FileTwin( @@ -211,16 +196,14 @@ async def _generate_tests( ).save(self.py_path) test_file.save_as(self.py_path, self.debug_dir(item, f"test_{trial_no}.py")) - syntax_report = pyright.run(self.py_path, test_file.file_name, self.py_env) + syntax_report = pyright.run(self.py_path, test_file.file_name) FileTwin( - file_name=self.debug_dir(item, f"test_{trial_no}_pyright.json"), - content=syntax_report.model_dump_json(indent=2), - ).save(self.py_path) - - if syntax_report.summary.errorCount > 0: - logger.warning( - f"{syntax_report.summary.errorCount} syntax errors in tests iteration '{trial_no}' in item '{item.name}'." - ) + file_name= self.debug_dir(item, f"test_{trial_no}_pyright.json"), + content=syntax_report.model_dump_json(indent=2) + ).save(self.py_path) + + if syntax_report.summary.errorCount>0: + logger.warning(f"{syntax_report.summary.errorCount} syntax errors in tests iteration '{trial_no}' in item '{item.name}'.") errors = syntax_report.list_error_messages(test_file.content) continue @@ -228,25 +211,26 @@ async def _generate_tests( logger.debug( f"Generated Tests for tool '{self.tool_policy.tool_name}' '{item.name}'(trial='{trial_no}')" ) - report_file_name = self.debug_dir(item, f"test_{trial_no}_pytest.json") - pytest_report = pytest.run( - self.py_path, test_file.file_name, report_file_name - ) - if ( - pytest_report.all_tests_collected_successfully() - and pytest_report.non_empty_tests() - ): - return test_file - if not pytest_report.non_empty_tests(): # empty test set - errors = ["empty set of generated unit tests is not allowed"] - else: - errors = pytest_report.list_errors() + return test_file + # report_file_name = self.debug_dir(item, f"test_{trial_no}_pytest.json") + # pytest_report = pytest.run( + # self.py_path, test_file.file_name, report_file_name + # ) + # if ( + # pytest_report.all_tests_collected_successfully() + # and pytest_report.non_empty_tests() + # ): + # return test_file + # if not pytest_report.non_empty_tests(): # empty test set + # errors = ["empty set of generated unit tests is not allowed"] + # else: + # errors = pytest_report.list_errors() raise Exception("Generated tests contain syntax errors") async def _improve_guard_green_loop( self, - item: ToolPolicyItem, + item: ToolGuardSpecItem, guard: FileTwin, tests: FileTwin, dep_tools: List[str], @@ -279,7 +263,7 @@ async def _improve_guard_green_loop( async def _improve_guard( self, - item: ToolPolicyItem, + item: ToolGuardSpecItem, prev_guard: FileTwin, review_comments: List[str], dep_tools: List[str], @@ -294,15 +278,12 @@ async def _improve_guard( ) domain = self.domain.get_definitions_only() # omit runtime fields prev_python = get_code_content(prev_guard.content) - # FIXME when melea will support aysnc - res = await asyncio.to_thread( - lambda: improve_tool_guard( - prev_impl=prev_python, # noqa: B023 - domain=domain, # noqa: B023 - policy_item=item, - dependent_tool_names=dep_tools, - review_comments=review_comments + errors, # noqa: B023 - ) + res = improve_tool_guard( + prev_impl=prev_python, # noqa: B023 + domain=domain, # noqa: B023 + policy_item=item, + dependent_tool_names=dep_tools, + review_comments=review_comments + errors, # noqa: B023 ) guard = FileTwin( @@ -312,7 +293,7 @@ async def _improve_guard( self.py_path, self.debug_dir(item, f"guard_{round}_{trial}.py") ) - syntax_report = pyright.run(self.py_path, guard.file_name, self.py_env) + syntax_report = pyright.run(self.py_path, guard.file_name) FileTwin( file_name=self.debug_dir(item, f"guard_{round}_{trial}.pyright.json"), content=syntax_report.model_dump_json(indent=2), @@ -335,8 +316,7 @@ async def _improve_guard( raise Exception(f"Syntax error generating for tool '{item.name}'.") def _find_api_function(self, tool_fn_name: str): - with py.temp_python_path(self.py_path): - module = load_module_from_path(self.domain.app_api.file_name, self.py_path) + module = import_module(py.path_to_module(self.domain.app_api.file_name)) assert module, f"File not found {self.domain.app_api.file_name}" cls = find_class_in_module(module, self.domain.app_api_class_name) return getattr(cls, tool_fn_name) @@ -411,7 +391,7 @@ def _signature_str(self, sig: inspect.Signature): return clean_sig_str def _create_item_module( - self, tool_item: ToolPolicyItem, tool_fn: Callable + self, tool_item: ToolGuardSpecItem, tool_fn: Callable ) -> FileTwin: file_name = join( to_snake_case(self.app_name), @@ -437,7 +417,7 @@ def _create_item_module( ), ).save(self.py_path) - def debug_dir(self, policy_item: ToolPolicyItem, dir: str): + def debug_dir(self, policy_item: ToolGuardSpecItem, dir: str): return join( DEBUG_DIR, to_snake_case(self.tool_policy.tool_name), diff --git a/altk/pre_tool/toolguard/toolguard/gen_py/utils/pyright.py b/altk/pre_tool/toolguard/toolguard/gen_py/utils/pyright.py index fd92d81..71a2917 100644 --- a/altk/pre_tool/toolguard/toolguard/gen_py/utils/pyright.py +++ b/altk/pre_tool/toolguard/toolguard/gen_py/utils/pyright.py @@ -1,10 +1,10 @@ import json -import os +import sys import subprocess from pydantic import BaseModel from typing import List, Optional -from altk.pre_tool.toolguard.toolguard.data_types import FileTwin +from ...data_types import FileTwin ERROR = "error" WARNING = "warning" @@ -73,8 +73,8 @@ def get_text_by_range(file_content: str, rng: Range) -> str: return "\n".join(selected_lines) -def run(folder: str, py_file: str, venv_name: str) -> DiagnosticsReport: - py_path = os.path.join(venv_name, "bin", "python3") +def run(folder: str, py_file: str) -> DiagnosticsReport: + py_path = sys.executable res = subprocess.run( [ "pyright", diff --git a/altk/pre_tool/toolguard/toolguard/gen_py/utils/pytest.py b/altk/pre_tool/toolguard/toolguard/gen_py/utils/pytest.py index 9113c19..a0367ae 100644 --- a/altk/pre_tool/toolguard/toolguard/gen_py/utils/pytest.py +++ b/altk/pre_tool/toolguard/toolguard/gen_py/utils/pytest.py @@ -1,20 +1,16 @@ -from enum import Enum + +from enum import StrEnum import json import os from os.path import join -import subprocess -import sys from typing import Any, List, Dict, Optional from pydantic import BaseModel, Field -from contextlib import contextmanager - -from altk.pre_tool.toolguard.toolguard.data_types import FileTwin +from ...common.multi_process import run_in_process +from ...common.safe_py import run_safe_python +from ...data_types import FileTwin -class StrEnum(str, Enum): - """An abstract base class for string-based enums.""" - pass class TestOutcome(StrEnum): @@ -107,7 +103,9 @@ def list_errors(self) -> List[str]: # applicative test failure for test in self.tests: if test.outcome == TestOutcome.failed: - error = test.call.crash.message + error = test.call.longrepr + if test.call.crash: + error = test.call.crash.message if test.user_properties: case_desc = test.user_properties[0].get("docstring") if case_desc: @@ -115,63 +113,41 @@ def list_errors(self) -> List[str]: errors.add(error) return list(errors) - -def run(folder: str, test_file: str, report_file: str) -> TestReport: - # _run_in_subprocess(folder, test_file, report_file) - _run_safe_python(folder, test_file, report_file) - +def run(folder:str, test_file:str, report_file:str)->TestReport: + run_tests_in_safe_python_separate_process(folder, test_file, report_file) + report = read_test_report(os.path.join(folder, report_file)) - # overwrite it with indented version + + #overwrite it with indented version with open(os.path.join(folder, report_file), "w", encoding="utf-8") as f: json.dump(report.model_dump(), f, indent=2) return report - -@contextmanager -def temp_sys_path(path): - """Temporarily insert a path into sys.path.""" - sys.path.insert(0, path) - try: - yield - finally: - try: - sys.path.remove(path) - except ValueError: - pass - - -def _run_safe_python(folder: str, test_file: str, report_file: str): - from smolagents.local_python_executor import LocalPythonExecutor - - exec = LocalPythonExecutor( - additional_authorized_imports=["pytest"], - max_print_outputs_length=None, - additional_functions=None, - ) - exec.static_tools = {"temp_sys_path": temp_sys_path} +# Run the tests in this environment. +# run the tests in safe mode, so network and os operations are blocked. only specified libraries can be used. +# run the tests in a separate process. so python modules are isolated. as the code is evolving in the filesystem, we need a way to avoid python module caching. otherwise, it will not see that the code in the file has changed. +def run_tests_in_safe_python_separate_process(folder:str, test_file:str, report_file:str): code = f""" import pytest -with temp_sys_path("{folder}") - pytest.main(["{join(folder, test_file)}", "--quiet", "--json-report", "--json-report-file={join(folder, report_file)}"]) -""" - out = exec(code) - return out - - -def _run_in_subprocess(folder: str, test_file: str, report_file: str): - subprocess.run( - [ - "pytest", - test_file, - # "--verbose", - "--quiet", - "--json-report", - f"--json-report-file={report_file}", - ], - env={**os.environ, "PYTHONPATH": "."}, - cwd=folder, - ) +pytest.main(["{join(folder, test_file)}", "--quiet", "--json-report", "--json-report-file={join(folder, report_file)}"]) +""" + return run_in_process(run_safe_python, code, ["pytest"]) + +# def _run_in_subprocess(folder:str, test_file:str, report_file:str): +# subprocess.run([ +# "pytest", +# test_file, +# # "--verbose", +# "--quiet", +# "--json-report", +# f"--json-report-file={report_file}" +# ], +# env={ +# **os.environ, +# "PYTHONPATH": "." +# }, +# cwd=folder) def configure(folder: str): @@ -192,7 +168,3 @@ def read_test_report(file_path: str) -> TestReport: with open(file_path, "r") as file: data = json.load(file) return TestReport.model_validate(data, strict=False) - - -# report = read_test_report("/Users/davidboaz/Documents/GitHub/gen_policy_validator/tau_airline/output/2025-03-12 08:54:16/pytest_report.json") -# print(report.summary.failed) diff --git a/altk/pre_tool/toolguard/toolguard/gen_py/utils/venv.py b/altk/pre_tool/toolguard/toolguard/gen_py/utils/venv.py deleted file mode 100644 index 3220602..0000000 --- a/altk/pre_tool/toolguard/toolguard/gen_py/utils/venv.py +++ /dev/null @@ -1,20 +0,0 @@ -import subprocess -import sys -import ensurepip - - -def run(venv_dir: str, packages: list[str]): - # # Bootstrap pip if not present - try: - import pip # noqa: F401 - except ImportError: - ensurepip.bootstrap(upgrade=True) - - subprocess.run([sys.executable, "-m", "pip", "install"] + packages, check=True) - - # # Create the virtual environment - # venv.create(venv_dir, with_pip=True) - # # - # # #install packages - # pip_executable = os.path.join(venv_dir, "bin", "pip") - # subprocess.run([pip_executable, "install"] + packages, check=True) diff --git a/altk/pre_tool/toolguard/examples/__init__.py b/altk/pre_tool/toolguard/toolguard/gen_spec/__init__.py similarity index 100% rename from altk/pre_tool/toolguard/examples/__init__.py rename to altk/pre_tool/toolguard/toolguard/gen_spec/__init__.py diff --git a/altk/pre_tool/toolguard/toolguard/tool_policy_extractor/create_oas_summary.py b/altk/pre_tool/toolguard/toolguard/gen_spec/oas_summary.py similarity index 73% rename from altk/pre_tool/toolguard/toolguard/tool_policy_extractor/create_oas_summary.py rename to altk/pre_tool/toolguard/toolguard/gen_spec/oas_summary.py index 68bd025..49c4154 100644 --- a/altk/pre_tool/toolguard/toolguard/tool_policy_extractor/create_oas_summary.py +++ b/altk/pre_tool/toolguard/toolguard/gen_spec/oas_summary.py @@ -1,10 +1,7 @@ import json from typing import Any, Dict, List -from altk.pre_tool.toolguard.toolguard.tool_policy_extractor.text_tool_policy_generator import ( - ToolInfo, -) - +from ..data_types import ToolInfo class OASSummarizer: def __init__(self, oas: Dict[str, Any]): @@ -16,36 +13,25 @@ def summarize(self) -> List[ToolInfo]: for path, methods in self.oas.get("paths", {}).items(): for method, operation in methods.items(): summary = self._format_operation(path, method, operation) - tool_info = ToolInfo( - name=summary.get("name"), + tool_info = ToolInfo(name=summary.get("name"), description=summary.get("description"), signature=summary.get("signature"), parameters=summary.get("params"), - full_description=json.dumps(summary, indent=4), + full_description=json.dumps(summary, indent=4) ) operations.append(tool_info) - operations.append(tool_info) return operations - def _format_operation( - self, path: str, method: str, operation: Dict[str, Any] - ) -> Dict[str, Any]: - operation_id = operation.get( - "operationId", f"{method}_{path.strip('/').replace('/', '_')}" - ) + def _format_operation(self, path: str, method: str, operation: Dict[str, Any]) -> Dict[str, Any]: + operation_id = operation.get("operationId", f"{method}_{path.strip('/').replace('/', '_')}") class_name = operation_id description = operation.get("description", "") request_body = operation.get("requestBody", {}) params = self._parse_request_body(request_body) if request_body else {} - signature = self._generate_signature( - class_name, params, operation.get("responses", {}) - ) - example = operation.get( - "x-input-examples", [] - ) # self._generate_example(class_name, params) - output_examples = operation.get( - "x-output-examples", [] - ) # self._parse_response_examples(operation.get("responses", {})) + signature = self._generate_signature(class_name, params,operation.get("responses", {})) + example = operation.get("x-input-examples",[])#self._generate_example(class_name, params) + output_examples = operation.get("x-output-examples",[])#self._parse_response_examples(operation.get("responses", {})) + return { "name": class_name, @@ -53,7 +39,7 @@ def _format_operation( "description": description, "params": params, "examples": [example], - "output_examples": output_examples, + "output_examples": output_examples } def _parse_request_body(self, request_body: Dict[str, Any]) -> Dict[str, Any]: @@ -69,7 +55,7 @@ def _parse_request_body(self, request_body: Dict[str, Any]) -> Dict[str, Any]: params[param_name] = { "type": param_type, "description": param_desc, - "required": param_name in required, + "required": param_name in required } return params @@ -93,20 +79,12 @@ def _parse_request_body(self, request_body: Dict[str, Any]) -> Dict[str, Any]: # "boolean": "bool", # "object": "Dict[str, Any]", # }.get(schema.get("type", "Any"), "Any") - + def _resolve_schema_type(self, schema: Dict[str, Any]) -> str: if "anyOf" in schema: - return ( - "Union[" - + ", ".join(self._resolve_schema_type(s) for s in schema["anyOf"]) - + "]" - ) + return "Union[" + ", ".join(self._resolve_schema_type(s) for s in schema["anyOf"]) + "]" if "oneOf" in schema: - return ( - "Union[" - + ", ".join(self._resolve_schema_type(s) for s in schema["oneOf"]) - + "]" - ) + return "Union[" + ", ".join(self._resolve_schema_type(s) for s in schema["oneOf"]) + "]" if "$ref" in schema: return self._resolve_schema_type(self._resolve_ref(schema)) if schema.get("type") == "array": @@ -114,7 +92,7 @@ def _resolve_schema_type(self, schema: Dict[str, Any]) -> str: return f"List[{item_type}]" if schema.get("type") == "object": return "Dict[str, Any]" - + type_value = schema.get("type", "Any") if isinstance(type_value, list): # Filter out "null" and resolve remaining types @@ -122,19 +100,11 @@ def _resolve_schema_type(self, schema: Dict[str, Any]) -> str: if not non_null_types: return "Optional[Any]" if len(non_null_types) == 1: - base_type = self._resolve_schema_type( - {**schema, "type": non_null_types[0]} - ) + base_type = self._resolve_schema_type({**schema, "type": non_null_types[0]}) else: - base_type = ( - "Union[" - + ", ".join( - self._resolve_schema_type({"type": t}) for t in non_null_types - ) - + "]" - ) + base_type = "Union[" + ", ".join(self._resolve_schema_type({"type": t}) for t in non_null_types) + "]" return f"Optional[{base_type}]" if "null" in type_value else base_type - + return { "string": "str", "integer": "int", @@ -142,7 +112,7 @@ def _resolve_schema_type(self, schema: Dict[str, Any]) -> str: "boolean": "bool", "object": "Dict[str, Any]", }.get(type_value, "Any") - + def _resolve_ref(self, schema: Dict[str, Any]) -> Dict[str, Any]: if isinstance(schema, Dict): if "$ref" in schema: @@ -153,9 +123,7 @@ def _resolve_ref(self, schema: Dict[str, Any]) -> Dict[str, Any]: return schema return schema - def _generate_signature( - self, class_name: str, params: Dict[str, Any], responses: Dict[str, Any] - ) -> str: + def _generate_signature(self, class_name: str, params: Dict[str, Any],responses: Dict[str, Any]) -> str: args = ", ".join(f"{name}: {meta['type']}" for name, meta in params.items()) out = "str" if responses and "200" in responses: @@ -164,6 +132,7 @@ def _generate_signature( schema = self._resolve_ref(app_json.get("schema", {})) out = self._resolve_schema_type(schema) return f"{class_name}({args}) -> {out}" + def _generate_example(self, class_name: str, params: Dict[str, Any]) -> str: args = ", ".join( @@ -171,7 +140,9 @@ def _generate_example(self, class_name: str, params: Dict[str, Any]) -> str: for _, meta in params.items() ) return f"{class_name}({args})" - + + + def _parse_response_examples(self, responses: Dict[str, Any]) -> List[str]: examples = [] for response in responses.values(): @@ -182,9 +153,7 @@ def _parse_response_examples(self, responses: Dict[str, Any]) -> List[str]: if "example" in app_json: example_data = app_json["example"] elif "examples" in app_json: - example_data = next(iter(app_json["examples"].values()), {}).get( - "value" - ) + example_data = next(iter(app_json["examples"].values()), {}).get("value") else: example_data = self._construct_example_from_schema(schema) @@ -200,13 +169,11 @@ def _construct_example_from_schema(self, schema: Dict[str, Any]) -> Any: if not isinstance(schema, Dict): return schema schema_type = schema.get("type") - + if schema_type == "object": if "additionalProperties" in schema: value_schema = self._resolve_ref(schema["additionalProperties"]) - return { - "example_key": self._construct_example_from_schema(value_schema) - } + return {"example_key": self._construct_example_from_schema(value_schema)} props = schema.get("properties", {}) return { key: self._construct_example_from_schema(self._resolve_ref(subschema)) @@ -235,20 +202,17 @@ def _construct_example_from_schema(self, schema: Dict[str, Any]) -> Any: return "example_value" - -if __name__ == "__main__": - oas_file = ( - "/Users/naamazwerdling/Documents/OASB/policy_validation/airline/oas2.json" - ) - # oas_file = "/Users/naamazwerdling/Documents/OASB/policy_validation/orca/bank/oas.json" - shortfile = "/Users/naamazwerdling/Documents/OASB/policy_validation/airline/s1.json" - # shortfile = "/Users/naamazwerdling/Documents/OASB/policy_validation/orca/bank/short.json" - with open(oas_file) as f: - oas_data = json.load(f) - - summarizer = OASSummarizer(oas_data) - summary = summarizer.summarize() - with open(shortfile, "w") as outfile: - json.dump(summary, outfile, indent=4) - - print(json.dumps(summary, indent=2)) +# if __name__ == '__main__': +# oas_file = "/Users/naamazwerdling/Documents/OASB/policy_validation/airline/oas2.json" +# #oas_file = "/Users/naamazwerdling/Documents/OASB/policy_validation/orca/bank/oas.json" +# shortfile = "/Users/naamazwerdling/Documents/OASB/policy_validation/airline/s1.json" +# #shortfile = "/Users/naamazwerdling/Documents/OASB/policy_validation/orca/bank/short.json" +# with open(oas_file) as f: +# oas_data = json.load(f) + +# summarizer = OASSummarizer(oas_data) +# summary = summarizer.summarize() +# with open(shortfile, "w") as outfile: +# json.dump(summary, outfile, indent=4) + +# print(json.dumps(summary, indent=2)) \ No newline at end of file diff --git a/altk/pre_tool/toolguard/toolguard/tool_policy_extractor/prompts/add_examples.txt b/altk/pre_tool/toolguard/toolguard/gen_spec/prompts/add_examples.txt similarity index 99% rename from altk/pre_tool/toolguard/toolguard/tool_policy_extractor/prompts/add_examples.txt rename to altk/pre_tool/toolguard/toolguard/gen_spec/prompts/add_examples.txt index 7ed7c92..dd7a068 100644 --- a/altk/pre_tool/toolguard/toolguard/tool_policy_extractor/prompts/add_examples.txt +++ b/altk/pre_tool/toolguard/toolguard/gen_spec/prompts/add_examples.txt @@ -46,4 +46,4 @@ Output Format (JSON): If no new examples are needed, return an empty object: -{} +{} \ No newline at end of file diff --git a/altk/pre_tool/toolguard/toolguard/tool_policy_extractor/prompts/add_policies.txt b/altk/pre_tool/toolguard/toolguard/gen_spec/prompts/add_policies.txt similarity index 99% rename from altk/pre_tool/toolguard/toolguard/tool_policy_extractor/prompts/add_policies.txt rename to altk/pre_tool/toolguard/toolguard/gen_spec/prompts/add_policies.txt index 917abd4..dc9686e 100644 --- a/altk/pre_tool/toolguard/toolguard/tool_policy_extractor/prompts/add_policies.txt +++ b/altk/pre_tool/toolguard/toolguard/gen_spec/prompts/add_policies.txt @@ -55,3 +55,4 @@ If no additional relevant policies exist, return: { "policies": [] } + diff --git a/altk/pre_tool/toolguard/toolguard/tool_policy_extractor/prompts/add_references.txt b/altk/pre_tool/toolguard/toolguard/gen_spec/prompts/add_references.txt similarity index 99% rename from altk/pre_tool/toolguard/toolguard/tool_policy_extractor/prompts/add_references.txt rename to altk/pre_tool/toolguard/toolguard/gen_spec/prompts/add_references.txt index dc98cd4..14f6031 100644 --- a/altk/pre_tool/toolguard/toolguard/tool_policy_extractor/prompts/add_references.txt +++ b/altk/pre_tool/toolguard/toolguard/gen_spec/prompts/add_references.txt @@ -35,4 +35,4 @@ Output Format (JSON): If no relevant references are found, return: { "references": [] -} +} \ No newline at end of file diff --git a/altk/pre_tool/toolguard/toolguard/tool_policy_extractor/prompts/create_examples.txt b/altk/pre_tool/toolguard/toolguard/gen_spec/prompts/create_examples.txt similarity index 100% rename from altk/pre_tool/toolguard/toolguard/tool_policy_extractor/prompts/create_examples.txt rename to altk/pre_tool/toolguard/toolguard/gen_spec/prompts/create_examples.txt diff --git a/altk/pre_tool/toolguard/toolguard/tool_policy_extractor/prompts/create_policy.txt b/altk/pre_tool/toolguard/toolguard/gen_spec/prompts/create_policy.txt similarity index 100% rename from altk/pre_tool/toolguard/toolguard/tool_policy_extractor/prompts/create_policy.txt rename to altk/pre_tool/toolguard/toolguard/gen_spec/prompts/create_policy.txt diff --git a/altk/pre_tool/toolguard/toolguard/tool_policy_extractor/prompts/examples_reviewer.txt b/altk/pre_tool/toolguard/toolguard/gen_spec/prompts/examples_reviewer.txt similarity index 99% rename from altk/pre_tool/toolguard/toolguard/tool_policy_extractor/prompts/examples_reviewer.txt rename to altk/pre_tool/toolguard/toolguard/gen_spec/prompts/examples_reviewer.txt index 69957b7..1fa0d59 100644 --- a/altk/pre_tool/toolguard/toolguard/tool_policy_extractor/prompts/examples_reviewer.txt +++ b/altk/pre_tool/toolguard/toolguard/gen_spec/prompts/examples_reviewer.txt @@ -38,4 +38,4 @@ Format the Output in JSON: "value": true or false } } -``` +``` \ No newline at end of file diff --git a/altk/pre_tool/toolguard/toolguard/tool_policy_extractor/prompts/fix_example.txt b/altk/pre_tool/toolguard/toolguard/gen_spec/prompts/fix_example.txt similarity index 100% rename from altk/pre_tool/toolguard/toolguard/tool_policy_extractor/prompts/fix_example.txt rename to altk/pre_tool/toolguard/toolguard/gen_spec/prompts/fix_example.txt diff --git a/altk/pre_tool/toolguard/toolguard/tool_policy_extractor/prompts/functions.txt b/altk/pre_tool/toolguard/toolguard/gen_spec/prompts/functions.txt similarity index 100% rename from altk/pre_tool/toolguard/toolguard/tool_policy_extractor/prompts/functions.txt rename to altk/pre_tool/toolguard/toolguard/gen_spec/prompts/functions.txt diff --git a/altk/pre_tool/toolguard/toolguard/tool_policy_extractor/prompts/merge.txt b/altk/pre_tool/toolguard/toolguard/gen_spec/prompts/merge.txt similarity index 99% rename from altk/pre_tool/toolguard/toolguard/tool_policy_extractor/prompts/merge.txt rename to altk/pre_tool/toolguard/toolguard/gen_spec/prompts/merge.txt index 8756b7b..e41b8ab 100644 --- a/altk/pre_tool/toolguard/toolguard/tool_policy_extractor/prompts/merge.txt +++ b/altk/pre_tool/toolguard/toolguard/gen_spec/prompts/merge.txt @@ -49,3 +49,4 @@ If no additional relevant policies exist, return: { "policies": [] } + diff --git a/altk/pre_tool/toolguard/toolguard/tool_policy_extractor/prompts/merge_and_split.txt b/altk/pre_tool/toolguard/toolguard/gen_spec/prompts/merge_and_split.txt similarity index 99% rename from altk/pre_tool/toolguard/toolguard/tool_policy_extractor/prompts/merge_and_split.txt rename to altk/pre_tool/toolguard/toolguard/gen_spec/prompts/merge_and_split.txt index 0ceb541..3728338 100644 --- a/altk/pre_tool/toolguard/toolguard/tool_policy_extractor/prompts/merge_and_split.txt +++ b/altk/pre_tool/toolguard/toolguard/gen_spec/prompts/merge_and_split.txt @@ -50,3 +50,4 @@ If no additional relevant policies exist, return: { "policies": [] } + diff --git a/altk/pre_tool/toolguard/toolguard/tool_policy_extractor/prompts/merge_examples.txt b/altk/pre_tool/toolguard/toolguard/gen_spec/prompts/merge_examples.txt similarity index 100% rename from altk/pre_tool/toolguard/toolguard/tool_policy_extractor/prompts/merge_examples.txt rename to altk/pre_tool/toolguard/toolguard/gen_spec/prompts/merge_examples.txt diff --git a/altk/pre_tool/toolguard/toolguard/tool_policy_extractor/prompts/policy_reviewer.txt b/altk/pre_tool/toolguard/toolguard/gen_spec/prompts/policy_reviewer.txt similarity index 100% rename from altk/pre_tool/toolguard/toolguard/tool_policy_extractor/prompts/policy_reviewer.txt rename to altk/pre_tool/toolguard/toolguard/gen_spec/prompts/policy_reviewer.txt diff --git a/altk/pre_tool/toolguard/toolguard/tool_policy_extractor/prompts/split.txt b/altk/pre_tool/toolguard/toolguard/gen_spec/prompts/split.txt similarity index 99% rename from altk/pre_tool/toolguard/toolguard/tool_policy_extractor/prompts/split.txt rename to altk/pre_tool/toolguard/toolguard/gen_spec/prompts/split.txt index 199ee8c..5bcf82d 100644 --- a/altk/pre_tool/toolguard/toolguard/tool_policy_extractor/prompts/split.txt +++ b/altk/pre_tool/toolguard/toolguard/gen_spec/prompts/split.txt @@ -46,3 +46,4 @@ If no additional relevant policies exist, return: { "policies": [] } + diff --git a/altk/pre_tool/toolguard/toolguard/gen_spec/spec_generator.py b/altk/pre_tool/toolguard/toolguard/gen_spec/spec_generator.py new file mode 100644 index 0000000..b7704f6 --- /dev/null +++ b/altk/pre_tool/toolguard/toolguard/gen_spec/spec_generator.py @@ -0,0 +1,322 @@ + +import asyncio +import json +import os +from typing import List, Optional + +from ..data_types import ToolInfo, ToolGuardSpec, load_tool_policy +from ..llm.i_tg_llm import I_TG_LLM +from .utils import read_prompt_file, generate_messages, save_output, find_mismatched_references + +class ToolGuardSpecGenerator: + def __init__(self, llm:I_TG_LLM, policy_document:str, tools:List[ToolInfo], out_dir:str) -> None: + self.llm = llm + self.policy_document = policy_document + self.tools_descriptions = {tool.name: tool.description for tool in tools} + self.tools_details = {tool.name: tool for tool in tools} + self.out_dir = out_dir + + async def generate_minimal_policy(self, tool_name:str)->dict: + tptd = await self.create_policy(tool_name) + tptd = await self.example_creator(tool_name,tptd) + return tptd + + async def generate_policy(self, tool_name: str)->dict: + tptd = await self.create_policy(tool_name) + for i in range(3): + tptd = await self.add_policies(tool_name, tptd, i) + tptd = await self.split(tool_name, tptd) + tptd = await self.merge(tool_name, tptd) + tptd = await self.review_policy(tool_name, tptd) + tptd = await self.add_references(tool_name, tptd) + tptd = await self.reference_correctness(tool_name, tptd) + tptd = await self.example_creator(tool_name, tptd) + for i in range(5): #FIXME + tptd = await self.add_examples(tool_name, tptd, i) + tptd = await self.merge_examples(tool_name, tptd) + #tptd = self.fix_examples(tool_name, tptd) + tptd = await self.review_examples(tool_name, tptd) + return tptd + + + async def create_policy(self, tool_name:str) -> dict: + print("policy_creator_node") + system_prompt = read_prompt_file("create_policy") + system_prompt = system_prompt.replace("ToolX",tool_name) + user_content = f"Policy Document:{self.policy_document}\nTools Descriptions:{json.dumps(self.tools_descriptions)}\nTarget Tool:{self.tools_details[tool_name].model_dump_json()}\n" + tptd = await self.llm.chat_json(generate_messages(system_prompt, user_content)) + save_output(self.out_dir, f"{tool_name}.json", tptd) + return tptd + + + + async def add_policies(self, tool_name:str, tptd:dict, iteration:int=0) -> dict: + print("add_policy") + system_prompt = read_prompt_file("add_policies") + user_content = f"Policy Document:{self.policy_document}\nTools Descriptions:{json.dumps(self.tools_descriptions)}\nTarget Tool:{self.tools_details[tool_name].model_dump_json()}\nTPTD: {json.dumps(tptd)}" + response = await self.llm.chat_json(generate_messages(system_prompt, user_content)) + + policies = response["additionalProperties"]["policies"] \ + if "additionalProperties" in response and "policies" not in response \ + else response["policies"] + + for policy in policies: + #for policy in response["policies"]: + policy["iteration"] = iteration + tptd["policies"].append(policy) + + save_output(self.out_dir, f"{tool_name}_ADD_{iteration}.json", tptd) + return tptd + + + async def split(self, tool_name,tptd:dict) -> dict: + # todo: consider addition step to split policy by policy and not overall + print("split") + system_prompt = read_prompt_file("split") + user_content = f"Policy Document:{self.policy_document}\nTools Descriptions:{json.dumps(self.tools_descriptions)}\nTarget Tool:{self.tools_details[tool_name].model_dump_json()}\nTPTD: {json.dumps(tptd)}" + tptd = await self.llm.chat_json(generate_messages(system_prompt, user_content)) + save_output(self.out_dir, f"{tool_name}_split.json", tptd) + return tptd + + async def merge(self, tool_name,tptd:dict) -> dict: + # todo: consider addition step to split policy by policy and not overall + print("merge") + system_prompt = read_prompt_file("merge") + user_content = f"Policy Document:{self.policy_document}\nTools Descriptions:{json.dumps(self.tools_descriptions)}\nTarget Tool:{self.tools_details[tool_name].model_dump_json()}\nTPTD: {json.dumps(tptd)}" + tptd = await self.llm.chat_json(generate_messages(system_prompt, user_content)) + + save_output(self.out_dir, f"{tool_name}_merge.json", tptd) + return tptd + + + def move2archive(self, reviews) -> (bool, str): + comments = "" + num = len(reviews) + if num == 0: + return False + counts = { + "is_relevant": 0, + "is_tool_specific": 0, + "can_be_validated": 0, + "is_actionable": 0 + } + + for r in reviews: + print(f"{r['is_relevant'] if 'is_relevant' in r else ''}\t{r['is_tool_specific'] if 'is_tool_specific' in r else ''}\t{r['can_be_validated'] if 'can_be_validated' in r else ''}\t{r['is_actionable'] if 'is_actionable' in r else ''}\t{r['is_self_contained'] if 'is_self_contained' in r else ''}\t{r['score'] if 'score' in r else ''}\t") + + counts["is_relevant"] += (r["is_relevant"] if 'is_relevant' in r else 0) + counts["is_tool_specific"] += (r["is_tool_specific"] if 'is_tool_specific' in r else 0) + counts["can_be_validated"] += (r["can_be_validated"] if "can_be_validated" in r else 0) + counts["is_actionable"] += (r["is_actionable"] if "is_actionable" in r else 0) + + if not all(e in r for e in ['is_relevant', 'is_tool_specific', 'can_be_validated', 'is_actionable']) or \ + not(r['is_relevant'] and r['is_tool_specific'] and r['can_be_validated'] and r['is_actionable']): + comments+= r["comments"]+"\n" + + return not(all(float(counts[key]) / num > 0.5 for key in counts)),comments + + + + async def review_policy(self, tool_name,tptd) -> dict: + print("review_policy") + system_prompt = read_prompt_file("policy_reviewer") + newTPTD = {"policies":[]} + + if 'policies' not in tptd: + tptd['policies'] = [] + + for policy in tptd["policies"]: + reviews = [] + for iteration in range(5): + user_content = f"Policy Document:{self.policy_document}\nTools Descriptions:{json.dumps(self.tools_descriptions)}\nTarget Tool:{json.dumps(self.tools_descriptions[tool_name])}\npolicy: {json.dumps(policy)}" + response = await self.llm.chat_json(generate_messages(system_prompt, user_content)) + if "is_self_contained" in response: + is_self_contained = response["is_self_contained"] + if not is_self_contained: + if "alternative_description" in response: + policy["description"] = response["alternative_description"] + else: + print("Error: review is_self_contained is false but no alternative_description.") + else: + print("Error: review did not provide is_self_contained.") + reviews.append(response) + archive,comments = self.move2archive(reviews) + print(archive) + if archive: + if "archive" not in newTPTD: + newTPTD["archive"] = [] + policy["comments"] = comments + newTPTD["archive"].append(policy) + else: + newTPTD["policies"].append(policy) + save_output(self.out_dir, f"{tool_name}_rev.json", newTPTD) + return newTPTD + + async def add_references(self, tool_name:str,tptd:dict) -> dict: + print("add_ref") + system_prompt = read_prompt_file("add_references") + #remove old refs (used to help avoid duplications) + for policy in tptd["policies"]: + policy["references"] = [] + user_content = f"Policy Document:{self.policy_document}\nTools Descriptions:{json.dumps(self.tools_descriptions)}\nTarget Tool:{self.tools_details[tool_name].model_dump_json()}\npolicy: {json.dumps(policy)}" + response = await self.llm.chat_json(generate_messages(system_prompt, user_content)) + if "references" in response: + policy["references"] = response["references"] + else: + print("Error! no references in response") + print(response) + + + save_output(self.out_dir, f"{tool_name}_ref.json", tptd) + return tptd + + async def reference_correctness(self, tool_name:str,tptd:dict) -> dict: + print("reference_correctness") + tptd, unmatched_policies = find_mismatched_references(self.policy_document,tptd) + save_output(self.out_dir, f"{tool_name}_ref_orig_.json", unmatched_policies) + save_output(self.out_dir, f"{tool_name}_ref_correction_.json", tptd) + return tptd + + async def example_creator(self, tool_name:str,tptd:dict) -> dict: + print("example_creator") + system_prompt = read_prompt_file("create_examples") + system_prompt = system_prompt.replace("ToolX",tool_name) + + for policy in tptd["policies"]: + #user_content = f"Policy Document:{state['policy_text']}\nTools Descriptions:{json.dumps(state['tools'])}\nTarget Tool:{json.dumps(state['target_tool_description'])}\nPolicy:{policy}" + user_content = f"Tools Descriptions:{json.dumps(self.tools_descriptions)}\nTarget Tool:{self.tools_details[tool_name].model_dump_json()}\nPolicy:{policy}" + + response = await self.llm.chat_json(generate_messages(system_prompt, user_content)) + if 'violating_examples' in response: + policy["violating_examples"] = response["violating_examples"] + + if 'compliance_examples' in response: + policy["compliance_examples"] = response["compliance_examples"] + + save_output(self.out_dir, f"{tool_name}_examples.json", tptd) + return tptd + + async def add_examples(self, tool_name:str,tptd:dict,iteration:int) -> dict: + print("add_examples") + system_prompt = read_prompt_file("add_examples") + system_prompt = system_prompt.replace("ToolX", tool_name) + for policy in tptd["policies"]: + #user_content = f"Policy Document:{state['policy_text']}\nTools Descriptions:{json.dumps(state['tools'])}\nTarget Tool:{json.dumps(state['target_tool_description'])}\nPolicy:{policy}" + user_content = f"Tools Descriptions:{json.dumps(self.tools_descriptions)}\nTarget Tool:{self.tools_details[tool_name].model_dump_json()}\nPolicy:{policy}" + response = await self.llm.chat_json(generate_messages(system_prompt, user_content)) + if 'violating_examples' in response: + for vexample in response["violating_examples"]: + #vexample["iteration"] = state["iteration"] + if "violating_examples" not in policy: + policy["violating_examples"] = [] + policy["violating_examples"].append(vexample) + if 'compliance_examples' in response: + for cexample in response["compliance_examples"]: + if "compliance_examples" not in policy: + policy["compliance_examples"] = [] + #cexample["iteration"] = state["iteration"] + policy["compliance_examples"].append(cexample) + + save_output(self.out_dir, f"{tool_name}_ADD_examples{iteration}.json", tptd) + return tptd + + async def merge_examples(self,tool_name:str,tptd:dict) -> dict: + print("merge_examples") + system_prompt = read_prompt_file("merge_examples") + system_prompt = system_prompt.replace("ToolX", tool_name) + for policy in tptd["policies"]: + #user_content = f"Policy Document:{state['policy_text']}\nTools Descriptions:{json.dumps(state['tools'])}\nTarget Tool:{json.dumps(state['target_tool_description'])}\nPolicy Name:{policy['policy_name']}\nPolicy Description:{policy['description']}" + user_content = f"Tools Descriptions:{json.dumps(self.tools_descriptions)}\nTarget Tool:{self.tools_details[tool_name].model_dump_json()}\nPolicy Name:{policy['policy_name']}\nPolicy Description:{policy['description']}" + user_content+= f"\n\nViolating Examples: {policy['violating_examples']}" + user_content+= f"\n\nCompliance Examples: {policy['compliance_examples']}" + response = await self.llm.chat_json(generate_messages(system_prompt, user_content)) + policy["violating_examples"] = response["violating_examples"] + policy["compliance_examples"] = response["compliance_examples"] + + + save_output(self.out_dir, f"{tool_name}_merge_examples.json", tptd) + return tptd + + async def fix_examples(self, tool_name:str,tptd:dict) -> dict: + print("fix_examples") + orig_prompt = read_prompt_file("fix_example") + for policy in tptd["policies"]: + for etype in ["violating","compliance"]: + fixed_examples = [] + for example in policy[etype + "_examples"]: + system_prompt = orig_prompt.replace("ToolX", tool_name) + system_prompt = system_prompt.replace("__EXAMPLE_TYPE__", "") + + #user_content = f"Policy Document:{state['policy_text']}\nTools Descriptions:{json.dumps(state['tools'])}\nTarget Tool:{json.dumps(state['target_tool_description'])}\nPolicy Name:{policy['policy_name']}\nPolicy Description:{policy['description']}\nExample:{example}" + user_content = f"Tools Descriptions:{json.dumps(self.tools_descriptions)}\nTarget Tool:{self.tools_details[tool_name].model_dump_json()}\nPolicy Name:{policy['policy_name']}\nPolicy Description:{policy['description']}\nExample:{example}" + + response = await self.llm.chat_json(generate_messages(system_prompt, user_content)) + fixed_examples.append(response["revised_example"]) + policy[etype + "_examples"] = fixed_examples + + + save_output(self.out_dir, f"{tool_name}_fix_examples.json", tptd) + return tptd + + #todo: change to revew examples, write prompts + async def review_examples(self, tool_name:str,tptd:dict) -> dict: + print("review_examples") + system_prompt = read_prompt_file("examples_reviewer") + for policy in tptd["policies"]: + print(policy['policy_name']) + for etype in ["violating","compliance"]: + print(etype) + passed_examples = [] + for example in policy[etype + "_examples"]: + print(example) + reviews = [] + for iteration in range(5): + #user_content = f"Policy Document:{state['policy_text']}\nTools Descriptions:{json.dumps(state['tools'])}\nTarget Tool:{json.dumps(state['target_tool_description'])}\nPolicy Name:{policy['policy_name']}\nPolicy Description:{policy['description']}\nExample:{example}" + user_content = f"Tools Descriptions:{json.dumps(self.tools_descriptions)}\nTarget Tool:{self.tools_details[tool_name].model_dump_json()}\nPolicy Name:{policy['policy_name']}\nPolicy Description:{policy['description']}\nExample:{example}" + response = await self.llm.chat_json(generate_messages(system_prompt, user_content)) + reviews.append(response) + keep = self.keep_example(reviews) + if keep: + passed_examples.append(example) + + policy[etype + "_examples"] = passed_examples + + + save_output(self.out_dir, f"{tool_name}_example_rev.json", tptd) + return tptd + + def keep_example(self, reviews) -> bool: + bads = 0 + totals = 0 + for r in reviews: + for vals in r.values(): + totals+=1 + if "value" not in vals: + print(reviews) + elif not vals["value"]: + bads += 1 + if bads/totals > 0.8: + return False + return True + +async def extract_toolguard_specs(policy_text:str, tools:List[ToolInfo], step1_output_dir:str, llm:I_TG_LLM, tools_shortlist: Optional[List[str]]=None, short=False)->List[ToolGuardSpec]: + process_dir = os.path.join(step1_output_dir, "process") + if not os.path.isdir(process_dir): + os.makedirs(process_dir) + output_tool_policies = [] + tpg = ToolGuardSpecGenerator(llm, policy_text, tools, process_dir) + async def do_one_tool(tool_name): + if short: + final_output = await tpg.generate_minimal_policy(tool_name) + else: + final_output = await tpg.generate_policy(tool_name) + + with open(os.path.join(step1_output_dir, tool_name + ".json"), "w") as outfile1: + outfile1.write(json.dumps(final_output, indent=2)) + output_tool_policies.append(load_tool_policy(os.path.join(step1_output_dir, tool_name + ".json"), tool_name)) + + await asyncio.gather(*[do_one_tool(tool.name) for tool in tools if ((tools_shortlist is None) or (tool.name in tools_shortlist))]) + print("All tools done") + return output_tool_policies + + diff --git a/altk/pre_tool/toolguard/toolguard/gen_spec/utils.py b/altk/pre_tool/toolguard/toolguard/gen_spec/utils.py new file mode 100644 index 0000000..dfdc9be --- /dev/null +++ b/altk/pre_tool/toolguard/toolguard/gen_spec/utils.py @@ -0,0 +1,85 @@ +from typing import Dict, Any +import os + +import json +from typing import List, Optional + + +def read_prompt_file(filename: str) -> str: + with open(os.path.join(os.path.dirname(__file__), "prompts", filename + ".txt"), "r") as f: + return f.read() + + +def generate_messages(system_prompt: str, user_content: str) -> List[Dict[str, str]]: + return [ + {"role": "system", "content": system_prompt}, + {"role": "user", "content": user_content} + ] + +def save_output(outdir: str, filename: str, content: Any): + with open(os.path.join(outdir, filename), "w") as outfile: + json.dump(content, outfile, indent=4) + + +def normalize_text(text): + """Normalize text by removing punctuation, converting to lowercase, and standardizing spaces.""" + #return re.sub(r'\s+', ' ', re.sub(r'[^a-zA-Z0-9\s]', '', text)).strip().lower() + return text.lower() + +def split_reference_if_both_parts_exist(reference, policy_text): + words = reference.split() + for split_point in range(1, len(words)): + part1 = ' '.join(words[:split_point]) + part2 = ' '.join(words[split_point:]) + + normalized_part1 = normalize_text(part1) + normalized_part2 = normalize_text(part2) + normalized_policy_text = normalize_text(policy_text) + + if normalized_part1 in normalized_policy_text and normalized_part2 in normalized_policy_text: + start_idx1 = normalized_policy_text.find(normalized_part1) + end_idx1 = start_idx1 + len(part1) + start_idx2 = normalized_policy_text.find(normalized_part2) + end_idx2 = start_idx2 + len(part2) + return [policy_text[start_idx1:end_idx1], policy_text[start_idx2:end_idx2]] + return None + + +def find_mismatched_references(policy_text, policy_json): + corrections = json.loads(json.dumps(policy_json)) + unmatched_policies = [] + if isinstance(corrections["policies"], str): + return corrections, unmatched_policies + + normalized_policy_text = normalize_text(policy_text) + + for policy in corrections["policies"]: + corrected_references = [] + has_unmatched = False + + for reference in policy["references"]: + normalized_ref = normalize_text(reference) + # if normalized ref in policy doc- just copy the original + if normalized_ref in normalized_policy_text: + start_idx = normalized_policy_text.find(normalized_ref) + end_idx = start_idx + len(reference) + corrected_references.append(policy_text[start_idx:end_idx]) + else: + # close_match = get_close_matches(normalized_ref, [normalized_policy_text], n=1, cutoff=0.9) + # if close_match: + # start_idx = normalized_policy_text.find(close_match[0]) + # end_idx = start_idx + len(close_match[0]) + # corrected_references.append(policy_text[start_idx:end_idx]) + # else: + split_segments = split_reference_if_both_parts_exist(reference, policy_text) + if split_segments: + corrected_references.extend(split_segments) + else: + corrected_references.append(reference) # Keep original if no match found + has_unmatched = True + + policy["references"] = corrected_references + if has_unmatched: + unmatched_policies.append(policy["policy_name"]) + + return corrections, unmatched_policies \ No newline at end of file diff --git a/altk/pre_tool/toolguard/toolguard/llm/tg_litellm.py b/altk/pre_tool/toolguard/toolguard/llm/tg_litellm.py new file mode 100644 index 0000000..625a928 --- /dev/null +++ b/altk/pre_tool/toolguard/toolguard/llm/tg_litellm.py @@ -0,0 +1,176 @@ +import asyncio +import json +import os +import re +from typing import List, Dict +import time +from litellm import acompletion +from litellm.types.utils import ModelResponse +from litellm.exceptions import RateLimitError +import json +from jsonschema import validate, ValidationError + +import dotenv + +from ..llm.i_tg_llm import I_TG_LLM + +rits_model_name_to_endpoint_list=[ +# {"endpoint":"https://ete-litellm.bx.cloud9.ibm.com", "model_name":"claude-3-7-sonnet"}, + {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/avengers-jamba-9b","model_name":"ibm-fms/avengers-jamba-9b"}, + {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/codellama-34b-instruct-hf","model_name":"codellama/CodeLlama-34b-Instruct-hf"}, + {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/deepseek-coder-33b-instruct","model_name":"deepseek-ai/DeepSeek-V2.5"}, + {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/deepseek-v2-5/v1","model_name":"deepseek-ai/DeepSeek-V2.5"}, + {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/deepseek-v3/v1","model_name":"deepseek-ai/DeepSeek-V3"}, + {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/gemma-2-9b-it","model_name":"google/gemma-2-9b-it"}, + {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/granite-13b-chat-v2","model_name":"ibm-granite/granite-13b-chat-v2"}, + {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/granite-13b-instruct-v2","model_name":"ibm-granite/granite-13b-instruct-v2"}, + {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/granite-20b-cbcl","model_name":"ibm-granite/granite-20b-code-base-content-linking"}, + {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/granite-20b-cbsl","model_name":"ibm/granite-20b-schema-sqlinstruct-granite-fine-vs"}, + {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/granite-20b-cbsql","model_name":"ibm-granite/granite-20b-code-base-sql-gen"}, + {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/granite-20b-ci-r1-1","model_name":"ibm-granite/granite-20b-code-instruct-r1.1"}, + {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/granite-20b-code-8k-ansible","model_name":"ibm/granite-20b-code-8k-ansible"}, + {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/granite-20b-code-instruct","model_name":"ibm/granite-20b-code-instruct"}, + {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/granite-20b-code-instruct-8k","model_name":"ibm-granite/granite-20b-code-instruct-8k"}, + {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/granite-20b-code-instruct-uapi","model_name":"ibm-granite/granite-20b-code-instruct-unified-api"}, + {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/granite-20b-functioncalling","model_name":"ibm-granite/granite-20b-functioncalling"}, + {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/granite-3-0-8b-instruct","model_name":"ibm-granite/granite-3.0-8b-instruct"}, + {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/granite-3-1-8b-content-linking","model_name":"ibm/granite-3-1-8b-content-linking"}, + {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/granite-3-1-8b-instruct","model_name":"ibm-granite/granite-3.1-8b-instruct"}, + {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/granite-3-1-8b-question-gen","model_name":"ibm/granite-3-1-8b-question-gen"}, + {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/granite-3-1-8b-schema-linking","model_name":"ibm/granite-3-1-8b-schema-linking"}, + {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/granite-3-1-8b-sql-gen","model_name":"ibm/granite-3-1-8b-sql-gen"}, + {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/granite-3-2-8b-instruct","model_name":"ibm-granite/granite-3.2-8b-instruct"}, + {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/granite-3-3-8b-instruct/v1/","model_name":"ibm-granite/granite-3.3-8b-instruct"}, + {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/granite-34b-code-instruct-8k","model_name":"ibm-granite/granite-34b-code-instruct-8k"}, + {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/granite-34b-content-linking","model_name":"ibm/granite-34b-content-linking"}, + {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/granite-34b-question-gen","model_name":"ibm/granite-34b-question-gen"}, + {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/granite-34b-schema-linking","model_name":"ibm/granite-34b-schema-linking"}, + {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/granite-34b-sql-gen","model_name":"ibm/granite-34b-sql-gen"}, + {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/granite-7b-lab","model_name":"ibm/granite-7b-lab"}, + {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/granite-8b-code-instruct-128k","model_name":"ibm-granite/granite-8b-code-instruct-128k"}, + {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/granite-8b-code-instruct-4k","model_name":"ibm-granite/granite-8b-code-instruct-4k"}, + {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/granite-8b-instruct-preview-4k","model_name":"ibm-granite/granite-8b-instruct-preview-4k"}, + {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/granite-8b-japanese-instruct","model_name":"ibm-granite/granite-8b-japanese-instruct"}, + {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/granite-vision-3-2-2b","model_name":"ibm-granite/granite-vision-3.2-2b"}, + {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/internvl2-llama3-76b","model_name":"OpenGVLab/InternVL2-Llama3-76B"}, + {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/llama-3-1-405b-instruct-fp8","model_name":"meta-llama/llama-3-1-405b-instruct-fp8"}, + {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/llama-3-1-70b-instruct","model_name":"meta-llama/llama-3-1-70b-instruct"}, + {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/llama-3-1-8b-instruct","model_name":"meta-llama/Llama-3.1-8B-Instruct"}, + {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/llama-3-1-test","model_name":"meta-llama/llama-3-1-405b-instruct-fp8"}, + {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/llama-3-2-11b-instruct","model_name":"meta-llama/Llama-3.2-11B-Vision-Instruct"}, + {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/llama-3-2-90b-instruct","model_name":"meta-llama/Llama-3.2-90B-Vision-Instruct"}, + {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/llama-3-3-70b-instruct/v1","model_name":"meta-llama/llama-3-3-70b-instruct"}, + {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/microsoft-phi-4/v1","model_name":"microsoft/phi-4"}, + {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/mistralai-pixtral-12b-2409","model_name":"mistralai/pixtral-12b-2409"}, + {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/mixtral-8x22b-instruct-v01/v1","model_name":"mistralai/mixtral-8x22B-instruct-v0.1"}, + {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/mixtral-8x7b-instruct-v01/v1","model_name":"mistralai/mixtral-8x7B-instruct-v0.1"}, + {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/mixtral-8x7b-instruct-v01-test","model_name":"mistralai/mixtral-8x7B-instruct-v0.1-test"}, + {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/pixtral-large-instruct-2411","model_name":"mistralai/Pixtral-Large-Instruct-2411"}, + {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/qwen2-5-72b-instruct/v1","model_name":"Qwen/Qwen2.5-72B-Instruct"}, + {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/qwen2-vl-72b-instruct","model_name":"Qwen/Qwen2-VL-72B-Instruct"}, + {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/slate-125m-english-rtrvr-v2","model_name":"ibm/slate-125m-english-rtrvr-v2"}] + +rits_model_to_endpoint = { + entry["model_name"]: entry["endpoint"] + for entry in rits_model_name_to_endpoint_list +} + +anthropic_models = ['claude-3-5-sonnet-latest', 'claude-3-5-haiku'] +RITS = "RITS" + +class LitellmModel(I_TG_LLM): + def __init__(self, model_name:str, provider: str): + self.model_name = model_name + self.provider = provider + + async def generate(self, messages: List[Dict])->str: + if self.provider and self.provider.upper() == RITS: + response = await acompletion( + messages=messages, + model=self.model_name, + custom_llm_provider="openai", + base_url=rits_model_to_endpoint[self.model_name], + extra_headers={ + 'RITS_API_KEY': os.getenv("RITS_API_KEY"), + 'Content-Type': 'application/json' + }, + ) + return response.choices[0].message.content + + response = await acompletion( + messages=messages, + model=self.model_name, + custom_llm_provider=self.provider, + extra_headers={ + 'Content-Type': 'application/json' + }, + ) + return response["choices"][0]["message"]["content"] + + + async def chat_json(self, messages: List[Dict], max_retries: int = 5, backoff_factor: float = 1.5) -> Dict: + retries = 0 + while retries < max_retries: + try: + response = await self.generate(messages) + res = self.extract_json_from_string(response) + if res is None: + wait_time = backoff_factor ** retries + print(f"Error: not json format. Retrying in {wait_time:.1f} seconds... (attempt {retries + 1}/{max_retries})") + time.sleep(wait_time) + retries += 1 + else: + return res + except RateLimitError as e: + wait_time = backoff_factor ** retries + print(f"Rate limit hit. Retrying in {wait_time:.1f} seconds... (attempt {retries + 1}/{max_retries})") + time.sleep(wait_time) + retries += 1 + except Exception as e: + raise RuntimeError(f"Unexpected error during chat completion: {e}") + raise RuntimeError("Exceeded maximum retries due to rate limits.") + + def extract_json_from_string(self,s): + # Use regex to extract the JSON part from the string + match = re.search(r'```json\s*(\{.*?\})\s*```', s, re.DOTALL) + if match: + json_str = match.group(1) + try: + return json.loads(json_str) + except json.JSONDecodeError as e: + print(f"Failed to decode JSON: {e}") + return None + else: + ## for rits? + match = re.search(r'(\{[\s\S]*\})', s) + if match: + json_str = match.group(1) + try: + return json.loads(json_str) + except json.JSONDecodeError as e: + print("response:") + print(s) + print("match:") + print(match.group(1)) + print("Failed to parse JSON:", e) + return None + + print("No JSON found in the string.") + print(s) + return None + + +if __name__ == '__main__': + dotenv.load_dotenv() + model = "gpt-4o-2024-08-06" + #model = "claude-3-5-sonnet-20240620" + #model = "meta-llama/llama-3-3-70b-instruct" + aw = LitellmModel(model, "azure") + + async def my_test(): + resp = await aw.generate([{"role": "user", "content": "what is the weather?"}]) + print(resp) + resp = await aw.chat_json([{"role": "user", "content": "what is the weather? please answer in json format"}]) + print(resp) + asyncio.run(my_test()) + diff --git a/altk/pre_tool/toolguard/toolguard/llm/tg_llmevalkit.py b/altk/pre_tool/toolguard/toolguard/llm/tg_llmevalkit.py deleted file mode 100644 index c983016..0000000 --- a/altk/pre_tool/toolguard/toolguard/llm/tg_llmevalkit.py +++ /dev/null @@ -1,20 +0,0 @@ -from altk.pre_tool.toolguard.toolguard.llm.i_tg_llm import I_TG_LLM -from altk.core.llm import ValidatingLLMClient - - -class TG_LLMEval(I_TG_LLM): - def __init__(self, llm_client: ValidatingLLMClient): - if not isinstance(llm_client, ValidatingLLMClient): - print("llm_client is a ValidatingLLMClient") - exit(1) - self.llm_client = llm_client - - async def chat_json(self, messages: list[dict], schema=dict) -> dict: - return self.llm_client.generate( - prompt=messages, schema=schema, retries=5, schema_field=None - ) - - async def generate(self, messages: list[dict]) -> str: - return self.llm_client.generate( - prompt=messages, schema=str, retries=5, schema_field=None - ) diff --git a/altk/pre_tool/toolguard/toolguard/logging_utils.py b/altk/pre_tool/toolguard/toolguard/logging_utils.py index 941edc4..250f847 100644 --- a/altk/pre_tool/toolguard/toolguard/logging_utils.py +++ b/altk/pre_tool/toolguard/toolguard/logging_utils.py @@ -1,32 +1,34 @@ -import logging +import logging -def add_log_file_handler(log_file: str): - formatter = logging.Formatter( - fmt="%(asctime)s | %(levelname)s | %(name)s | %(message)s", - datefmt="%Y-%m-%d %H:%M:%S", - ) - file_handler = logging.FileHandler(log_file, encoding="utf-8") - file_handler.setFormatter(formatter) - logging.getLogger().addHandler(file_handler) +def add_log_file_handler(log_file:str): + formatter = logging.Formatter( + fmt="%(asctime)s | %(levelname)s | %(name)s | %(message)s", + datefmt="%Y-%m-%d %H:%M:%S" + ) + file_handler = logging.FileHandler(log_file, encoding="utf-8") + file_handler.setFormatter(formatter) + logging.getLogger().addHandler(file_handler) def init_logging(): - logging.getLogger().setLevel(logging.INFO) # Default for other libraries - logging.getLogger("toolguard").setLevel(logging.DEBUG) # debug for our library - logging.getLogger("mellea").setLevel(logging.DEBUG) - init_log_console_handler() - + logging.getLogger().setLevel(logging.INFO) #Default for other libraries + logging.getLogger("toolguard").setLevel(logging.DEBUG) # debug for our library + logging.getLogger("mellea").setLevel(logging.DEBUG) + init_log_console_handler() def init_log_console_handler(): - formatter = logging.Formatter( - fmt="%(asctime)s | %(levelname)s | %(name)s | %(message)s", - datefmt="%Y-%m-%d %H:%M:%S", - ) - - # Set up console handler - console_handler = logging.StreamHandler() - console_handler.setFormatter(formatter) - - # Set up the root logger - logging.basicConfig(level=logging.INFO, handlers=[console_handler]) + formatter = logging.Formatter( + fmt="%(asctime)s | %(levelname)s | %(name)s | %(message)s", + datefmt="%Y-%m-%d %H:%M:%S" + ) + + # Set up console handler + console_handler = logging.StreamHandler() + console_handler.setFormatter(formatter) + + # Set up the root logger + logging.basicConfig( + level=logging.INFO, + handlers=[console_handler] + ) diff --git a/altk/pre_tool/toolguard/toolguard/runtime.py b/altk/pre_tool/toolguard/toolguard/runtime.py index 60b2c6b..876067e 100644 --- a/altk/pre_tool/toolguard/toolguard/runtime.py +++ b/altk/pre_tool/toolguard/toolguard/runtime.py @@ -1,13 +1,14 @@ import inspect import json import os +import sys from types import ModuleType from typing import Any, Dict, List, Optional, Type, Callable, TypeVar from pydantic import BaseModel -import importlib.util +from langchain_core.tools import BaseTool import functools -from .data_types import API_PARAM, RESULTS_FILENAME, FileTwin, RuntimeDomain, ToolPolicy +from .data_types import API_PARAM, RESULTS_FILENAME, FileTwin, RuntimeDomain, ToolGuardSpec from abc import ABC, abstractmethod @@ -20,19 +21,17 @@ def invoke( self, toolname: str, arguments: Dict[str, Any], return_type: Type[T] ) -> T: ... - -def load_toolguards( - directory: str, filename: str = RESULTS_FILENAME -) -> "ToolguardRuntime": +def load_toolguard_code_result(directory: str, filename: str = RESULTS_FILENAME): full_path = os.path.join(directory, filename) with open(full_path, "r", encoding="utf-8") as f: data = json.load(f) - result = ToolGuardsCodeGenerationResult(**data) - return ToolguardRuntime(result, directory) + return ToolGuardsCodeGenerationResult(**data) +def load_toolguards(directory: str, filename: str = RESULTS_FILENAME) -> "ToolguardRuntime": + return ToolguardRuntime(load_toolguard_code_result(directory, filename), directory) class ToolGuardCodeResult(BaseModel): - tool: ToolPolicy + tool: ToolGuardSpec guard_fn_name: str guard_file: FileTwin item_guard_files: List[FileTwin | None] @@ -54,41 +53,50 @@ def save( class ToolguardRuntime: + _original_pypath:list[str] = [] + def __init__(self, result: ToolGuardsCodeGenerationResult, ctx_dir: str) -> None: self._ctx_dir = ctx_dir self._result = result + + def __enter__(self): + #add folder to python path + self._original_pypath = list(sys.path) #remember old path + sys.path.insert(0, os.path.abspath(self._ctx_dir)) + + #cache the tool guards self._guards = {} - for tool_name, tool_result in result.tools.items(): - module = load_module_from_path(tool_result.guard_file.file_name, ctx_dir) + for tool_name, tool_result in self._result.tools.items(): + mod_name = file_to_module_name(tool_result.guard_file.file_name) + module = importlib.import_module(mod_name) guard_fn = find_function_in_module(module, tool_result.guard_fn_name) assert guard_fn, "Guard not found" self._guards[tool_name] = guard_fn - def _make_args( - self, guard_fn: Callable, args: dict, delegate: IToolInvoker - ) -> Dict[str, Any]: + return self + + def __exit__(self, exc_type, exc, tb): + del self._guards + #back to original python path + sys.path[:] = self._original_pypath + return False + + def _make_args(self, guard_fn:Callable, args: dict, delegate: IToolInvoker)->Dict[str, Any]: sig = inspect.signature(guard_fn) guard_args = {} for p_name, param in sig.parameters.items(): if p_name == API_PARAM: - module = load_module_from_path( - self._result.domain.app_api_impl.file_name, self._ctx_dir - ) - clazz = find_class_in_module( - module, self._result.domain.app_api_impl_class_name - ) - assert clazz, ( - f"class {self._result.domain.app_api_impl_class_name} not found in {self._result.domain.app_api_impl.file_name}" - ) + mod_name = file_to_module_name(self._result.domain.app_api_impl.file_name) + module = importlib.import_module(mod_name) + clazz = find_class_in_module(module, self._result.domain.app_api_impl_class_name) + assert clazz, f"class {self._result.domain.app_api_impl_class_name} not found in {self._result.domain.app_api_impl.file_name}" guard_args[p_name] = clazz(delegate) else: - arg = args.get(p_name) - if inspect.isclass(param.annotation) and issubclass( - param.annotation, BaseModel - ): - guard_args[p_name] = param.annotation.model_construct(**arg) + arg_val = args.get(p_name) + if inspect.isclass(param.annotation) and issubclass(param.annotation, BaseModel): + guard_args[p_name] = param.annotation.model_construct(**arg_val) else: - guard_args[p_name] = arg + guard_args[p_name] = arg_val return guard_args def check_toolcall(self, tool_name: str, args: dict, delegate: IToolInvoker): @@ -97,30 +105,8 @@ def check_toolcall(self, tool_name: str, args: dict, delegate: IToolInvoker): return guard_fn(**self._make_args(guard_fn, args, delegate)) - -def file_to_module(file_path: str): - return file_path.removesuffix(".py").replace("/", ".") - - -def load_module_from_path(file_path: str, py_root: str) -> ModuleType: - full_path = os.path.abspath(os.path.join(py_root, file_path)) - if not os.path.exists(full_path): - raise ImportError(f"Module file does not exist: {full_path}") - - module_name = file_to_module(file_path) - - spec = importlib.util.spec_from_file_location(module_name, full_path) - if spec is None or spec.loader is None: - raise ImportError(f"Could not load module spec from {full_path}") - - module = importlib.util.module_from_spec(spec) - try: - spec.loader.exec_module(module) # type: ignore - except Exception as e: - raise ImportError(f"Failed to execute module '{module_name}': {e}") from e - - return module - +def file_to_module_name(file_path:str): + return file_path.removesuffix('.py').replace('/', '.') def find_function_in_module(module: ModuleType, function_name: str): func = getattr(module, function_name, None) @@ -157,7 +143,7 @@ class ToolMethodsInvoker(IToolInvoker): def __init__(self, object: object) -> None: self._obj = object - def invoke(self, toolname: str, arguments: Dict[str, Any], model: Type[T]) -> T: + def invoke(self, toolname: str, arguments: Dict[str, Any], return_type: Type[T]) -> T: mtd = getattr(self._obj, toolname) assert callable(mtd), f"Tool {toolname} was not found" return mtd(**arguments) @@ -167,12 +153,24 @@ class ToolFunctionsInvoker(IToolInvoker): def __init__(self, funcs: List[Callable]) -> None: self._funcs_by_name = {func.__name__: func for func in funcs} - def invoke(self, toolname: str, arguments: Dict[str, Any], model: Type[T]) -> T: + def invoke(self, toolname: str, arguments: Dict[str, Any], return_type: Type[T]) -> T: func = self._funcs_by_name.get(toolname) assert callable(func), f"Tool {toolname} was not found" return func(**arguments) +class LangchainToolInvoker(IToolInvoker): + T = TypeVar("T") + _tools: Dict[str, BaseTool] + + def __init__(self, tools: List[BaseTool]) -> None: + self._tools = {tool.name: tool for tool in tools} + def invoke(self, toolname: str, arguments: Dict[str, Any], return_type: Type[T])->T: + tool = self._tools.get(toolname) + if tool: + return tool.invoke(arguments) + raise ValueError(f"unknown tool {toolname}") + def guard_before_call(guards_folder: str) -> Callable[[Callable], Callable]: """Decorator factory that logs function calls to the given logfile.""" toolguards = load_toolguards(guards_folder) diff --git a/altk/pre_tool/toolguard/toolguard/tool_policy_extractor/__init__.py b/altk/pre_tool/toolguard/toolguard/tool_policy_extractor/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/altk/pre_tool/toolguard/toolguard/tool_policy_extractor/text_tool_policy_generator.py b/altk/pre_tool/toolguard/toolguard/tool_policy_extractor/text_tool_policy_generator.py deleted file mode 100644 index de28c0f..0000000 --- a/altk/pre_tool/toolguard/toolguard/tool_policy_extractor/text_tool_policy_generator.py +++ /dev/null @@ -1,467 +0,0 @@ -import asyncio -import json -import os -import inspect -import sys -from typing import Any, Callable, List, Optional - -from langchain_core.tools import BaseTool -from pydantic import BaseModel - -from altk.pre_tool.toolguard.toolguard.data_types import load_tool_policy, ToolPolicy -from altk.pre_tool.toolguard.toolguard.llm.i_tg_llm import I_TG_LLM -from altk.pre_tool.toolguard.toolguard.tool_policy_extractor.utils import ( - read_prompt_file, - generate_messages, - save_output, - find_mismatched_references, -) - - -class ToolInfo(BaseModel): - name: str - description: str - parameters: Any - signature: str - full_description: str - - @classmethod - def from_function(cls, fn: Callable) -> "ToolInfo": - # Assumes @tool decorator from langchain https://python.langchain.com/docs/how_to/custom_tools/ - # or a plain function with doc string - def doc_summary(doc: str): - paragraphs = [p.strip() for p in doc.split("\n\n") if p.strip()] - return paragraphs[0] if paragraphs else "" - - fn_name = fn.name if hasattr(fn, "name") else fn.__name__ - sig = fn_name + str(get_tool_signature(fn)) - full_desc = ( - fn.description - if hasattr(fn, "description") - else fn.__doc__.strip() - if fn.__doc__ - else (inspect.getdoc(fn) or "") - ) - return cls( - name=fn_name, - description=doc_summary(full_desc), - full_description=full_desc, - parameters=fn.args_schema.model_json_schema() - if hasattr(fn, "args_schema") - else inspect.getdoc(fn), - signature=sig, - ) - - -def get_tool_signature(obj): - if inspect.isfunction(obj): - return inspect.signature(obj) - if hasattr(obj, "func") and inspect.isfunction(obj.func): - return inspect.signature(obj.func) - if hasattr(obj, "args_schema"): - schema = obj.args_schema - fields = schema.model_fields - params = ", ".join( - f"{name}: {field.annotation.__name__ if hasattr(field.annotation, '__name__') else field.annotation}" - for name, field in fields.items() - ) - return f"({params})" - return None - - -def extract_functions(file_path: str) -> List[Callable]: - import importlib.util - import inspect - - module_name = os.path.splitext(os.path.basename(file_path))[0] - - # Add project root to sys.path - project_root = os.path.abspath( - os.path.join(file_path, "..", "..") - ) # Adjust as needed - if project_root not in sys.path: - sys.path.insert(0, project_root) - - spec = importlib.util.spec_from_file_location(module_name, file_path) - if not spec or not spec.loader: - raise ImportError(f"Could not load module from {file_path}") - - module = importlib.util.module_from_spec(spec) - spec.loader.exec_module(module) - tools = [] - for name, obj in inspect.getmembers(module): # noqa: B007 - if isinstance(obj, BaseTool): - if hasattr(obj, "name") and hasattr(obj, "args_schema"): - tools.append(obj) - else: - if callable(obj) and obj.__name__ != "tool": - tools.append(obj) - - return tools - - -class TextToolPolicyGenerator: - def __init__( - self, llm: I_TG_LLM, policy_document: str, tools: List[ToolInfo], out_dir: str - ) -> None: - self.llm = llm - self.policy_document = policy_document - self.tools_descriptions = {tool.name: tool.description for tool in tools} - self.tools_details = {tool.name: tool for tool in tools} - self.out_dir = out_dir - - async def generate_minimal_policy(self, tool_name: str) -> dict: - tptd = await self.create_policy(tool_name) - tptd = await self.example_creator(tool_name, tptd) - return tptd - - async def generate_policy(self, tool_name: str) -> dict: - tptd = await self.create_policy(tool_name) - for i in range(3): - tptd = await self.add_policies(tool_name, tptd, i) - tptd = await self.split(tool_name, tptd) - tptd = await self.merge(tool_name, tptd) - tptd = await self.review_policy(tool_name, tptd) - tptd = await self.add_references(tool_name, tptd) - tptd = await self.reference_correctness(tool_name, tptd) - tptd = await self.example_creator(tool_name, tptd) - for i in range(5): # FIXME - tptd = await self.add_examples(tool_name, tptd, i) - tptd = await self.merge_examples(tool_name, tptd) - # tptd = self.fix_examples(tool_name, tptd) - tptd = await self.review_examples(tool_name, tptd) - return tptd - - async def create_policy(self, tool_name: str) -> dict: - print("policy_creator_node") - system_prompt = read_prompt_file("create_policy") - system_prompt = system_prompt.replace("ToolX", tool_name) - user_content = f"Policy Document:{self.policy_document}\nTools Descriptions:{json.dumps(self.tools_descriptions)}\nTarget Tool:{self.tools_details[tool_name].model_dump_json()}\n" - tptd = await self.llm.chat_json(generate_messages(system_prompt, user_content)) - save_output(self.out_dir, f"{tool_name}.json", tptd) - return tptd - - async def add_policies( - self, tool_name: str, tptd: dict, iteration: int = 0 - ) -> dict: - print("add_policy") - system_prompt = read_prompt_file("add_policies") - user_content = f"Policy Document:{self.policy_document}\nTools Descriptions:{json.dumps(self.tools_descriptions)}\nTarget Tool:{self.tools_details[tool_name].model_dump_json()}\nTPTD: {json.dumps(tptd)}" - response = await self.llm.chat_json( - generate_messages(system_prompt, user_content) - ) - - policies = ( - response["additionalProperties"]["policies"] - if "additionalProperties" in response and "policies" not in response - else response["policies"] - ) - - for policy in policies: - # for policy in response["policies"]: - policy["iteration"] = iteration - tptd["policies"].append(policy) - - save_output(self.out_dir, f"{tool_name}_ADD_{iteration}.json", tptd) - return tptd - - async def split(self, tool_name, tptd: dict) -> dict: - # todo: consider addition step to split policy by policy and not overall - print("split") - system_prompt = read_prompt_file("split") - user_content = f"Policy Document:{self.policy_document}\nTools Descriptions:{json.dumps(self.tools_descriptions)}\nTarget Tool:{self.tools_details[tool_name].model_dump_json()}\nTPTD: {json.dumps(tptd)}" - tptd = await self.llm.chat_json(generate_messages(system_prompt, user_content)) - save_output(self.out_dir, f"{tool_name}_split.json", tptd) - return tptd - - async def merge(self, tool_name, tptd: dict) -> dict: - # todo: consider addition step to split policy by policy and not overall - print("merge") - system_prompt = read_prompt_file("merge") - user_content = f"Policy Document:{self.policy_document}\nTools Descriptions:{json.dumps(self.tools_descriptions)}\nTarget Tool:{self.tools_details[tool_name].model_dump_json()}\nTPTD: {json.dumps(tptd)}" - tptd = await self.llm.chat_json(generate_messages(system_prompt, user_content)) - - save_output(self.out_dir, f"{tool_name}_merge.json", tptd) - return tptd - - def move2archive(self, reviews) -> (bool, str): - comments = "" - num = len(reviews) - if num == 0: - return False - counts = { - "is_relevant": 0, - "is_tool_specific": 0, - "can_be_validated": 0, - "is_actionable": 0, - } - - for r in reviews: - print( - f"{r['is_relevant'] if 'is_relevant' in r else ''}\t{r['is_tool_specific'] if 'is_tool_specific' in r else ''}\t{r['can_be_validated'] if 'can_be_validated' in r else ''}\t{r['is_actionable'] if 'is_actionable' in r else ''}\t{r['is_self_contained'] if 'is_self_contained' in r else ''}\t{r['score'] if 'score' in r else ''}\t" - ) - - counts["is_relevant"] += r["is_relevant"] if "is_relevant" in r else 0 - counts["is_tool_specific"] += ( - r["is_tool_specific"] if "is_tool_specific" in r else 0 - ) - counts["can_be_validated"] += ( - r["can_be_validated"] if "can_be_validated" in r else 0 - ) - counts["is_actionable"] += r["is_actionable"] if "is_actionable" in r else 0 - - if not all( - e in r - for e in [ - "is_relevant", - "is_tool_specific", - "can_be_validated", - "is_actionable", - ] - ) or not ( - r["is_relevant"] - and r["is_tool_specific"] - and r["can_be_validated"] - and r["is_actionable"] - ): - comments += r["comments"] + "\n" - - return not (all(float(counts[key]) / num > 0.5 for key in counts)), comments - - async def review_policy(self, tool_name, tptd) -> dict: - print("review_policy") - system_prompt = read_prompt_file("policy_reviewer") - newTPTD = {"policies": []} - - if "policies" not in tptd: - tptd["policies"] = [] - - for policy in tptd["policies"]: - reviews = [] - for _iteration in range(5): - user_content = f"Policy Document:{self.policy_document}\nTools Descriptions:{json.dumps(self.tools_descriptions)}\nTarget Tool:{json.dumps(self.tools_descriptions[tool_name])}\npolicy: {json.dumps(policy)}" - response = await self.llm.chat_json( - generate_messages(system_prompt, user_content) - ) - if "is_self_contained" in response: - is_self_contained = response["is_self_contained"] - if not is_self_contained: - if "alternative_description" in response: - policy["description"] = response["alternative_description"] - else: - print( - "Error: review is_self_contained is false but no alternative_description." - ) - else: - print("Error: review did not provide is_self_contained.") - reviews.append(response) - archive, comments = self.move2archive(reviews) - print(archive) - if archive: - if "archive" not in newTPTD: - newTPTD["archive"] = [] - policy["comments"] = comments - newTPTD["archive"].append(policy) - else: - newTPTD["policies"].append(policy) - save_output(self.out_dir, f"{tool_name}_rev.json", newTPTD) - return newTPTD - - async def add_references(self, tool_name: str, tptd: dict) -> dict: - print("add_ref") - system_prompt = read_prompt_file("add_references") - # remove old refs (used to help avoid duplications) - for policy in tptd["policies"]: - policy["references"] = [] - user_content = f"Policy Document:{self.policy_document}\nTools Descriptions:{json.dumps(self.tools_descriptions)}\nTarget Tool:{self.tools_details[tool_name].model_dump_json()}\npolicy: {json.dumps(policy)}" - response = await self.llm.chat_json( - generate_messages(system_prompt, user_content) - ) - if "references" in response: - policy["references"] = response["references"] - else: - print("Error! no references in response") - print(response) - - save_output(self.out_dir, f"{tool_name}_ref.json", tptd) - return tptd - - async def reference_correctness(self, tool_name: str, tptd: dict) -> dict: - print("reference_correctness") - tptd, unmatched_policies = find_mismatched_references( - self.policy_document, tptd - ) - save_output(self.out_dir, f"{tool_name}_ref_orig_.json", unmatched_policies) - save_output(self.out_dir, f"{tool_name}_ref_correction_.json", tptd) - return tptd - - async def example_creator(self, tool_name: str, tptd: dict) -> dict: - print("example_creator") - system_prompt = read_prompt_file("create_examples") - system_prompt = system_prompt.replace("ToolX", tool_name) - - for policy in tptd["policies"]: - # user_content = f"Policy Document:{state['policy_text']}\nTools Descriptions:{json.dumps(state['tools'])}\nTarget Tool:{json.dumps(state['target_tool_description'])}\nPolicy:{policy}" - user_content = f"Tools Descriptions:{json.dumps(self.tools_descriptions)}\nTarget Tool:{self.tools_details[tool_name].model_dump_json()}\nPolicy:{policy}" - - response = await self.llm.chat_json( - generate_messages(system_prompt, user_content) - ) - if "violating_examples" in response: - policy["violating_examples"] = response["violating_examples"] - - if "compliance_examples" in response: - policy["compliance_examples"] = response["compliance_examples"] - - save_output(self.out_dir, f"{tool_name}_examples.json", tptd) - return tptd - - async def add_examples(self, tool_name: str, tptd: dict, iteration: int) -> dict: - print("add_examples") - system_prompt = read_prompt_file("add_examples") - system_prompt = system_prompt.replace("ToolX", tool_name) - for policy in tptd["policies"]: - # user_content = f"Policy Document:{state['policy_text']}\nTools Descriptions:{json.dumps(state['tools'])}\nTarget Tool:{json.dumps(state['target_tool_description'])}\nPolicy:{policy}" - user_content = f"Tools Descriptions:{json.dumps(self.tools_descriptions)}\nTarget Tool:{self.tools_details[tool_name].model_dump_json()}\nPolicy:{policy}" - response = await self.llm.chat_json( - generate_messages(system_prompt, user_content) - ) - if "violating_examples" in response: - for vexample in response["violating_examples"]: - # vexample["iteration"] = state["iteration"] - if "violating_examples" not in policy: - policy["violating_examples"] = [] - policy["violating_examples"].append(vexample) - if "compliance_examples" in response: - for cexample in response["compliance_examples"]: - if "compliance_examples" not in policy: - policy["compliance_examples"] = [] - # cexample["iteration"] = state["iteration"] - policy["compliance_examples"].append(cexample) - - save_output(self.out_dir, f"{tool_name}_ADD_examples{iteration}.json", tptd) - return tptd - - async def merge_examples(self, tool_name: str, tptd: dict) -> dict: - print("merge_examples") - system_prompt = read_prompt_file("merge_examples") - system_prompt = system_prompt.replace("ToolX", tool_name) - for policy in tptd["policies"]: - # user_content = f"Policy Document:{state['policy_text']}\nTools Descriptions:{json.dumps(state['tools'])}\nTarget Tool:{json.dumps(state['target_tool_description'])}\nPolicy Name:{policy['policy_name']}\nPolicy Description:{policy['description']}" - user_content = f"Tools Descriptions:{json.dumps(self.tools_descriptions)}\nTarget Tool:{self.tools_details[tool_name].model_dump_json()}\nPolicy Name:{policy['policy_name']}\nPolicy Description:{policy['description']}" - user_content += f"\n\nViolating Examples: {policy['violating_examples']}" - user_content += f"\n\nCompliance Examples: {policy['compliance_examples']}" - response = await self.llm.chat_json( - generate_messages(system_prompt, user_content) - ) - policy["violating_examples"] = response["violating_examples"] - policy["compliance_examples"] = response["compliance_examples"] - - save_output(self.out_dir, f"{tool_name}_merge_examples.json", tptd) - return tptd - - async def fix_examples(self, tool_name: str, tptd: dict) -> dict: - print("fix_examples") - orig_prompt = read_prompt_file("fix_example") - for policy in tptd["policies"]: - for etype in ["violating", "compliance"]: - fixed_examples = [] - for example in policy[etype + "_examples"]: - system_prompt = orig_prompt.replace("ToolX", tool_name) - system_prompt = system_prompt.replace("__EXAMPLE_TYPE__", "") - - # user_content = f"Policy Document:{state['policy_text']}\nTools Descriptions:{json.dumps(state['tools'])}\nTarget Tool:{json.dumps(state['target_tool_description'])}\nPolicy Name:{policy['policy_name']}\nPolicy Description:{policy['description']}\nExample:{example}" - user_content = f"Tools Descriptions:{json.dumps(self.tools_descriptions)}\nTarget Tool:{self.tools_details[tool_name].model_dump_json()}\nPolicy Name:{policy['policy_name']}\nPolicy Description:{policy['description']}\nExample:{example}" - - response = await self.llm.chat_json( - generate_messages(system_prompt, user_content) - ) - fixed_examples.append(response["revised_example"]) - policy[etype + "_examples"] = fixed_examples - - save_output(self.out_dir, f"{tool_name}_fix_examples.json", tptd) - return tptd - - # todo: change to revew examples, write prompts - async def review_examples(self, tool_name: str, tptd: dict) -> dict: - print("review_examples") - system_prompt = read_prompt_file("examples_reviewer") - for policy in tptd["policies"]: - print(policy["policy_name"]) - for etype in ["violating", "compliance"]: - print(etype) - passed_examples = [] - for example in policy[etype + "_examples"]: - print(example) - reviews = [] - for _iteration in range(5): - # user_content = f"Policy Document:{state['policy_text']}\nTools Descriptions:{json.dumps(state['tools'])}\nTarget Tool:{json.dumps(state['target_tool_description'])}\nPolicy Name:{policy['policy_name']}\nPolicy Description:{policy['description']}\nExample:{example}" - user_content = f"Tools Descriptions:{json.dumps(self.tools_descriptions)}\nTarget Tool:{self.tools_details[tool_name].model_dump_json()}\nPolicy Name:{policy['policy_name']}\nPolicy Description:{policy['description']}\nExample:{example}" - response = await self.llm.chat_json( - generate_messages(system_prompt, user_content) - ) - reviews.append(response) - keep = self.keep_example(reviews) - if keep: - passed_examples.append(example) - - policy[etype + "_examples"] = passed_examples - - save_output(self.out_dir, f"{tool_name}_example_rev.json", tptd) - return tptd - - def keep_example(self, reviews) -> bool: - bads = 0 - totals = 0 - for r in reviews: - for vals in r.values(): - totals += 1 - if "value" not in vals: - print(reviews) - elif not vals["value"]: - bads += 1 - if bads / totals > 0.8: - return False - return True - - -async def extract_policies( - policy_text: str, - tools: List[ToolInfo], - step1_output_dir: str, - llm: I_TG_LLM, - tools_shortlist: Optional[List[str]] = None, - short=False, -) -> List[ToolPolicy]: - if not os.path.isdir(step1_output_dir): - os.makedirs(step1_output_dir) - - process_dir = os.path.join(step1_output_dir, "process") - if not os.path.isdir(process_dir): - os.makedirs(process_dir) - output_tool_policies = [] - tpg = TextToolPolicyGenerator(llm, policy_text, tools, process_dir) - - async def do_one_tool(tool_name): - if short: - final_output = await tpg.generate_minimal_policy(tool_name) - else: - final_output = await tpg.generate_policy(tool_name) - - with open(os.path.join(step1_output_dir, tool_name + ".json"), "w") as outfile1: - outfile1.write(json.dumps(final_output, indent=2)) - output_tool_policies.append( - load_tool_policy( - os.path.join(step1_output_dir, tool_name + ".json"), tool_name - ) - ) - - await asyncio.gather( - *[ - do_one_tool(tool.name) - for tool in tools - if ((tools_shortlist is None) or (tool.name in tools_shortlist)) - ] - ) - print("All tools done") - return output_tool_policies diff --git a/altk/pre_tool/toolguard/toolguard/tool_policy_extractor/utils.py b/altk/pre_tool/toolguard/toolguard/tool_policy_extractor/utils.py deleted file mode 100644 index b2bd518..0000000 --- a/altk/pre_tool/toolguard/toolguard/tool_policy_extractor/utils.py +++ /dev/null @@ -1,96 +0,0 @@ -from typing import Dict, Any -import os - -import json -from typing import List - - -def read_prompt_file(filename: str) -> str: - with open( - os.path.join(os.path.dirname(__file__), "prompts", filename + ".txt"), "r" - ) as f: - return f.read() - - -def generate_messages(system_prompt: str, user_content: str) -> List[Dict[str, str]]: - return [ - {"role": "system", "content": system_prompt}, - {"role": "user", "content": user_content}, - ] - - -def save_output(outdir: str, filename: str, content: Any): - with open(os.path.join(outdir, filename), "w") as outfile: - json.dump(content, outfile, indent=4) - - -def normalize_text(text): - """Normalize text by removing punctuation, converting to lowercase, and standardizing spaces.""" - # return re.sub(r'\s+', ' ', re.sub(r'[^a-zA-Z0-9\s]', '', text)).strip().lower() - return text.lower() - - -def split_reference_if_both_parts_exist(reference, policy_text): - words = reference.split() - for split_point in range(1, len(words)): - part1 = " ".join(words[:split_point]) - part2 = " ".join(words[split_point:]) - - normalized_part1 = normalize_text(part1) - normalized_part2 = normalize_text(part2) - normalized_policy_text = normalize_text(policy_text) - - if ( - normalized_part1 in normalized_policy_text - and normalized_part2 in normalized_policy_text - ): - start_idx1 = normalized_policy_text.find(normalized_part1) - end_idx1 = start_idx1 + len(part1) - start_idx2 = normalized_policy_text.find(normalized_part2) - end_idx2 = start_idx2 + len(part2) - return [policy_text[start_idx1:end_idx1], policy_text[start_idx2:end_idx2]] - return None - - -def find_mismatched_references(policy_text, policy_json): - corrections = json.loads(json.dumps(policy_json)) - unmatched_policies = [] - if isinstance(corrections["policies"], str): - return corrections, unmatched_policies - - normalized_policy_text = normalize_text(policy_text) - - for policy in corrections["policies"]: - corrected_references = [] - has_unmatched = False - - for reference in policy["references"]: - normalized_ref = normalize_text(reference) - # if normalized ref in policy doc- just copy the original - if normalized_ref in normalized_policy_text: - start_idx = normalized_policy_text.find(normalized_ref) - end_idx = start_idx + len(reference) - corrected_references.append(policy_text[start_idx:end_idx]) - else: - # close_match = get_close_matches(normalized_ref, [normalized_policy_text], n=1, cutoff=0.9) - # if close_match: - # start_idx = normalized_policy_text.find(close_match[0]) - # end_idx = start_idx + len(close_match[0]) - # corrected_references.append(policy_text[start_idx:end_idx]) - # else: - split_segments = split_reference_if_both_parts_exist( - reference, policy_text - ) - if split_segments: - corrected_references.extend(split_segments) - else: - corrected_references.append( - reference - ) # Keep original if no match found - has_unmatched = True - - policy["references"] = corrected_references - if has_unmatched: - unmatched_policies.append(policy["policy_name"]) - - return corrections, unmatched_policies diff --git a/altk/pre_tool/toolguard/toolguard_code_component.py b/altk/pre_tool/toolguard/toolguard_code_component.py new file mode 100644 index 0000000..c5a819e --- /dev/null +++ b/altk/pre_tool/toolguard/toolguard_code_component.py @@ -0,0 +1,101 @@ +import logging +from typing import Callable, List, cast +from enum import Enum +from pydantic import BaseModel, Field +from typing import Set + +from .toolguard.data_types import MeleaSessionData +from altk.toolkit_core.core.toolkit import ComponentConfig, ComponentInput, AgentPhase, ComponentBase +from .toolguard import generate_guards_from_specs, ToolGuardSpec, ToolGuardsCodeGenerationResult, load_toolguards +from .toolguard.runtime import IToolInvoker, ToolGuardsCodeGenerationResult + +logger = logging.getLogger(__name__) + +class ToolGuardCodeComponentConfig(ComponentConfig): + llm_config: MeleaSessionData + +class ToolGuardCodeBuildInput(ComponentInput): + tools: List[Callable] | str + toolguard_specs: List[ToolGuardSpec] + out_dir: str + +ToolGuardBuildOutput = ToolGuardsCodeGenerationResult + +class ToolGuardCodeRunInput(ComponentInput): + generated_guard_dir: str + tool_name: str = Field(description="Tool name") + tool_args: dict = Field(default={}, description="Tool arguments") + tool_invoker: IToolInvoker + + model_config = { + "arbitrary_types_allowed": True + } + +class ViolationLevel(Enum): + """Severity level of a safety violation. + + :cvar INFO: Informational level violation that does not require action + :cvar WARN: Warning level violation that suggests caution but allows continuation + :cvar ERROR: Error level violation that requires blocking or intervention + """ + + INFO = "info" + WARN = "warn" + ERROR = "error" + +class PolicyViolation(BaseModel): + """Details of a safety violation detected by content moderation. + + :param violation_level: Severity level of the violation + :param user_message: (Optional) Message to convey to the user about the violation + """ + + violation_level: ViolationLevel + + # what message should you convey to the user + user_message: str | None = None + +class ToolGuardCodeRunOutput(BaseModel): + violation: PolicyViolation | None = None + + +class ToolGuardCodeComponent(ComponentBase): + + def __init__(self, config:ToolGuardCodeComponentConfig): + super().__init__(config=config) + + @classmethod + def supported_phases(cls) -> Set[AgentPhase]: + """Return the supported agent phases.""" + return {AgentPhase.BUILDTIME, AgentPhase.RUNTIME} + + def _build(self, data: ToolGuardCodeBuildInput) -> ToolGuardsCodeGenerationResult: + raise NotImplementedError("Please use the aprocess() function in an async context") + + async def _abuild(self, data: ToolGuardCodeBuildInput) -> ToolGuardsCodeGenerationResult: + config = cast(ToolGuardCodeComponentConfig, self.config) + return await generate_guards_from_specs( + tools=data.tools, + tool_specs=data.toolguard_specs, + work_dir=data.out_dir, + llm_data=config.llm_config + ) + + def _run(self, data: ToolGuardCodeRunInput) -> ToolGuardCodeRunOutput: + code_root_dir = data.generated_guard_dir + tool_name = data.tool_name + tool_params = data.tool_args + with load_toolguards(code_root_dir) as toolguards: + from rt_toolguard.data_types import PolicyViolationException + try: + toolguards.check_toolcall(tool_name, tool_params, data.tool_invoker) + return ToolGuardCodeRunOutput() + except PolicyViolationException as e: + return ToolGuardCodeRunOutput(violation=PolicyViolation( + violation_level=ViolationLevel.ERROR, + user_message=str(e) + )) + + def _arun(self, data: ToolGuardCodeRunInput) -> ToolGuardCodeRunOutput: + return self._run(data) + diff --git a/altk/pre_tool/toolguard/toolguard_spec_component.py b/altk/pre_tool/toolguard/toolguard_spec_component.py new file mode 100644 index 0000000..ccae219 --- /dev/null +++ b/altk/pre_tool/toolguard/toolguard_spec_component.py @@ -0,0 +1,61 @@ +import logging +import os +from typing import Callable, List, Set +from langchain.tools import BaseTool +from pydantic import Field + +from ..toolkit_core.llm import ValidatingLLMClient +from ..toolkit_core.core.toolkit import AgentPhase, ComponentBase, ComponentConfig, ComponentInput +from .toolguard import ToolGuardSpec, generate_guard_specs +from .toolguard.llm.i_tg_llm import I_TG_LLM + +logger = logging.getLogger(__name__) + +class ToolGuardSpecComponentConfig(ComponentConfig): + pass + +class ToolGuardSpecBuildInput(ComponentInput): + policy_text: str = Field(description="Text of the policy document file") + tools: List[Callable] | List[BaseTool] | str + work_dir: str + +ToolGuardSpecs=List[ToolGuardSpec] + +class ToolGuardSpecComponent(ComponentBase): + + def __init__(self, config:ToolGuardSpecComponentConfig): + super().__init__(config=config) + + @classmethod + def supported_phases(cls) -> Set[AgentPhase]: + return {AgentPhase.BUILDTIME, AgentPhase.RUNTIME} + + def _build(self, data: ToolGuardSpecBuildInput) -> ToolGuardSpecs: + raise NotImplementedError("Please use the aprocess() function in an async context") + + async def _abuild(self, data: ToolGuardSpecBuildInput) -> ToolGuardSpecs: + os.makedirs(data.work_dir, exist_ok=True) + return await generate_guard_specs( + policy_text=data.policy_text, + tools=data.tools, + work_dir=data.work_dir, + llm=TG_LLMEval(self.config.llm_client) + ) + + +class TG_LLMEval(I_TG_LLM): + def __init__(self, llm_client: ValidatingLLMClient): + if not isinstance(llm_client, ValidatingLLMClient): + print("llm_client is a ValidatingLLMClient") + exit(1) + self.llm_client = llm_client + + async def chat_json(self, messages: list[dict], schema=dict) -> dict: + return self.llm_client.generate( + prompt=messages, schema=schema, retries=5, schema_field=None + ) + + async def generate(self, messages: list[dict]) -> str: + return self.llm_client.generate( + prompt=messages, schema=str, retries=5, schema_field=None + ) diff --git a/tests/pre_tool/toolguard/.gitignore b/tests/pre_tool/toolguard/.gitignore new file mode 100644 index 0000000..e6d35e7 --- /dev/null +++ b/tests/pre_tool/toolguard/.gitignore @@ -0,0 +1 @@ +outputs \ No newline at end of file diff --git a/altk/pre_tool/toolguard/examples/calculator_example/__init__.py b/tests/pre_tool/toolguard/inputs/__init__.py similarity index 100% rename from altk/pre_tool/toolguard/examples/calculator_example/__init__.py rename to tests/pre_tool/toolguard/inputs/__init__.py diff --git a/tests/pre_tool/toolguard/inputs/oas.json b/tests/pre_tool/toolguard/inputs/oas.json new file mode 100644 index 0000000..f6da957 --- /dev/null +++ b/tests/pre_tool/toolguard/inputs/oas.json @@ -0,0 +1,284 @@ +{ + "openapi": "3.0.3", + "info": { + "title": "Math Tools API", + "version": "1.0.0", + "description": "A simple API providing basic mathematical operations: addition, subtraction, multiplication, and division." + }, + "paths": { + "/tools/add": { + "post": { + "operationId": "add_tool", + "summary": "Add two numbers", + "description": "Add two numbers and return their sum.", + "x-input-examples": [ + "add_tool(1.0, 2.0)", + "add_tool(-5.5, 3.2)", + "add_tool(0, 100.75)", + "add_tool(1e3, 2e3)" + ], + "x-output-examples": [3.0, -2.3, 100.75, 3000.0], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "a": { + "type": "number", + "description": "The first number.", + "example": 5.0 + }, + "b": { + "type": "number", + "description": "The second number.", + "example": 3.0 + } + }, + "required": ["a", "b"] + } + } + } + }, + "responses": { + "200": { + "description": "The sum of a and b.", + "content": { + "application/json": { + "schema": { + "type": "number", + "example": 8.0 + } + } + } + } + } + } + }, + "/tools/subtract": { + "post": { + "operationId": "subtract_tool", + "summary": "Subtract one number from another", + "description": "Subtract one number from another and return the result.", + "x-input-examples": [ + "subtract_tool(10.0, 4.0)", + "subtract_tool(0.0, 1.5)", + "subtract_tool(-3.2, -5.8)", + "subtract_tool(1000, 1)" + ], + "x-output-examples": [6.0, -1.5, 2.6, 999.0], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "a": { + "type": "number", + "description": "The number to subtract from.", + "example": 10.0 + }, + "b": { + "type": "number", + "description": "The number to subtract.", + "example": 4.0 + } + }, + "required": ["a", "b"] + } + } + } + }, + "responses": { + "200": { + "description": "The result of a minus b.", + "content": { + "application/json": { + "schema": { + "type": "number", + "example": 6.0 + } + } + } + } + } + } + }, + "/tools/multiply": { + "post": { + "operationId": "multiply_tool", + "summary": "Multiply two numbers", + "description": "Multiply two numbers and return their product.", + "x-input-examples": [ + "multiply_tool(2.0, 3.5)", + "multiply_tool(-1.2, 4.0)", + "multiply_tool(0, 999.99)", + "multiply_tool(100, 0.01)" + ], + "x-output-examples": [7.0, -4.8, 0.0, 1.0], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "a": { + "type": "number", + "description": "The first number.", + "example": 2.0 + }, + "b": { + "type": "number", + "description": "The second number.", + "example": 3.5 + } + }, + "required": ["a", "b"] + } + } + } + }, + "responses": { + "200": { + "description": "The product of a and b.", + "content": { + "application/json": { + "schema": { + "type": "number", + "example": 7.0 + } + } + } + } + } + } + }, + "/tools/divide": { + "post": { + "operationId": "divide_tool", + "summary": "Divide one number by another", + "description": "Divide one number by another. The divisor must not be zero.", + "x-input-examples": [ + "divide_tool(10.0, 2.0)", + "divide_tool(3.0, 0.5)", + "divide_tool(-9.0, 3.0)", + "divide_tool(5.0, -2.5)" + ], + "x-output-examples": [5.0, 6.0, -3.0, -2.0], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "g": { + "type": "number", + "description": "The dividend.", + "example": 10.0 + }, + "h": { + "type": "number", + "description": "The divisor (must not be zero).", + "example": 2.0 + } + }, + "required": ["g", "h"] + } + } + } + }, + "responses": { + "200": { + "description": "The result of g divided by h.", + "content": { + "application/json": { + "schema": { + "type": "number", + "example": 5.0 + } + } + } + }, + "400": { + "description": "Division by zero is not allowed.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string", + "example": "Division by zero is not allowed." + } + } + } + } + } + } + } + } + }, + "/tools/map_kdi_number": { + "post": { + "operationId": "map_kdi_number", + "summary": "Compute the kdi number", + "description": "Compute the kdi number", + "x-input-examples": [ + "map_kdi_number(2.0)" + ], + "x-output-examples": [-2.0], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "i": { + "type": "number", + "description": "The number to compute.", + "example": 10.0 + } + }, + "required": ["g", "h"] + } + } + } + }, + "responses": { + "200": { + "description": "The kdi index.", + "content": { + "application/json": { + "schema": { + "type": "number", + "example": 5.0 + } + } + } + }, + "400": { + "description": "Bad request.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string", + "example": "not a number" + } + } + } + } + } + } + } + } + } + } +} diff --git a/altk/pre_tool/toolguard/examples/calculator_example/policy_document.md b/tests/pre_tool/toolguard/inputs/policy_doc.md similarity index 80% rename from altk/pre_tool/toolguard/examples/calculator_example/policy_document.md rename to tests/pre_tool/toolguard/inputs/policy_doc.md index ce70740..f476eb8 100644 --- a/altk/pre_tool/toolguard/examples/calculator_example/policy_document.md +++ b/tests/pre_tool/toolguard/inputs/policy_doc.md @@ -15,22 +15,22 @@ The calculator supports the following operations: - Subtraction (`-`) - Multiplication (`*`) - Division (`/`) -- Map KDI +- Map KDI ## Operation Constraints -- **Division by Zero is Not Allowed** - The calculator **must not** allow division by zero. - If a user attempts to divide by zero, the operation must be rejected and an appropriate error message should be shown +- **Division by Zero is Not Allowed** + The calculator **must not** allow division by zero. + If a user attempts to divide by zero, the operation must be rejected and an appropriate error message should be shown (e.g., `"Error: Division by zero is not allowed."`). -- **Summing Numbers Whose Product is 365 is Not Allowed** - The calculator **must not** allow addition of two or more numbers if their multiplication result equals `365`. - For example, adding `5 + 73` should be disallowed, because `5 * 73 = 365` . - In such cases, the operation must be rejected with an error like: +- **Summing Numbers Whose Product is 365 is Not Allowed** + The calculator **must not** allow addition of two or more numbers if their multiplication result equals `365`. + For example, adding `5 + 73` should be disallowed, because `5 * 73 = 365` . + In such cases, the operation must be rejected with an error like: `"Error: Addition of numbers whose product equals 365 is not allowed."` - - **Multiplying Numbers When Any Operand’s KDI Value Equals 6.28 is Not Allowed** + - **Multiplying Numbers When Any Operand’s KDI Value Equals 6.28 is Not Allowed** The calculator must not allow multiplication if any of the operands correspond to a number whose KDI value equals 6.28. For example, if a number x has KDI(x) = 6.28, then an operation such as x * 2 must be rejected. In such cases, the calculator should display an error message such as: diff --git a/tests/pre_tool/toolguard/inputs/step1/add_tool.json b/tests/pre_tool/toolguard/inputs/step1/add_tool.json new file mode 100644 index 0000000..81fd8b9 --- /dev/null +++ b/tests/pre_tool/toolguard/inputs/step1/add_tool.json @@ -0,0 +1,24 @@ +{ + "policies": [ + { + "policy_name": "Prohibition of Adding Numbers Whose Product Equals 365", + "description": "The add_tool must not allow addition of two or more numbers if their multiplication result equals 365. For example, adding 5 + 73 should be disallowed, because 5 * 73 = 365. In such cases, the operation must be rejected with an error like: 'Error: Addition of numbers whose product equals 365 is not allowed.'", + "references": [ + "The calculator must not allow addition of two or more numbers if their multiplication result equals 365. For example, adding 5 + 73 should be disallowed, because 5 * 73 = 365. In such cases, the operation must be rejected with an error like: 'Error: Addition of numbers whose product equals 365 is not allowed.'" + ], + "iteration_added": 0, + "violating_examples": [ + "A user tries to add 5 and 73. Since 5 * 73 equals 365, this operation should be rejected with an error message: 'Error: Addition of numbers whose product equals 365 is not allowed.'", + "The user attempts to add two numbers, 1 and 365, using the add_tool. This is a violation because 1 * 365 is also 365 and should be met with an error message.", + "Adding together the numbers 10 and 36.5 should be disallowed because their product is 365. An error message should be shown to prevent this operation.", + "A user inputs 365 and 1 into the add_tool. This action violates the policy as 365 multiplied by 1 is 365, warranting an error message rejection.", + "The system tries to perform the addition of -5 and -73 using the add_tool. Despite the negative signs, their product still equals 365 and thus contravenes the policy, requiring an error message to be displayed." + ], + "compliance_examples": [ + "Performing an addition of 10 and 20, where their product is 200, complies with the policy since it does not equal 365, and the addition proceeds without errors.", + "A user adds together the numbers 30 and 40. Since their product is 1200, which is not 365, this operation adheres to the policy without issue.", + "Addition of numbers 123 and 3 is compliant as they result in 369 when multiplied and hence clear the policy condition allowing progressive operation." + ] + } + ] +} \ No newline at end of file diff --git a/tests/pre_tool/toolguard/inputs/step1/divide_tool.json b/tests/pre_tool/toolguard/inputs/step1/divide_tool.json new file mode 100644 index 0000000..ae64b76 --- /dev/null +++ b/tests/pre_tool/toolguard/inputs/step1/divide_tool.json @@ -0,0 +1,22 @@ +{ + "policies": [ + { + "policy_name": "Division by Zero is Not Allowed", + "description": "The calculator must not allow division by zero. If a user attempts to divide by zero, the operation must be rejected and an appropriate error message should be shown (e.g., 'Error: Division by zero is not allowed.')", + "references": [ + "The calculator must not allow division by zero. If a user attempts to divide by zero, the operation must be rejected and an appropriate error message should be shown (e.g., 'Error: Division by zero is not allowed.')" + ], + "iteration_added": 0, + "violating_examples": [ + "A user attempts to divide 15 by 0, but the program proceeds with the operation, resulting in either an undefined behavior or a system error instead of an appropriate error message being shown.", + "The system calculates the result of 42 divided by 0 without any checks, ignoring the divisor being zero and directly providing an undefined or erroneous result.", + "A user input of 80 divided by 0 is processed and the system returns a zero or a nonsensical number instead of stopping the operation and displaying an error message about division by zero.", + "A user attempts to divide 25 by 0 and the application immediately rejects the operation, displaying the message: 'Error: Division by zero is not allowed.'" + ], + "compliance_examples": [ + "Upon entering inputs of 0 divided by 15, the tool successfully executes the operation and returns 0, complying with the division rules since the divisor (15) is non-zero.", + "A calculation involving 132 divided by 11 is processed correctly, returning a result of 12 without any error since it adheres to all division rules." + ] + } + ] +} \ No newline at end of file diff --git a/tests/pre_tool/toolguard/inputs/step1/map_kdi_number.json b/tests/pre_tool/toolguard/inputs/step1/map_kdi_number.json new file mode 100644 index 0000000..7189256 --- /dev/null +++ b/tests/pre_tool/toolguard/inputs/step1/map_kdi_number.json @@ -0,0 +1,3 @@ +{ + "policies": [] +} \ No newline at end of file diff --git a/tests/pre_tool/toolguard/inputs/step1/multiply_tool.json b/tests/pre_tool/toolguard/inputs/step1/multiply_tool.json new file mode 100644 index 0000000..b9ef8bf --- /dev/null +++ b/tests/pre_tool/toolguard/inputs/step1/multiply_tool.json @@ -0,0 +1,22 @@ +{ + "policies": [ + { + "policy_name": "Multiplication KDI Constraint", + "description": "Multiplying numbers when any operand\u2019s KDI value equals 6.28 is not allowed. The calculator must not allow multiplication if any of the operands correspond to a number whose KDI value equals 6.28. For example, if a number x has KDI(x) = 6.28, then an operation such as x * 2 must be rejected.", + "references": [ + "Multiplying Numbers When Any Operand\u2019s KDI Value Equals 6.28 is Not Allowed\nThe calculator must not allow multiplication if any of the operands correspond to a number whose KDI value equals 6.28. For example, if a number x has KDI(x) = 6.28, then an operation such as x * 2 must be rejected. In such cases, the calculator should display an error message such as: \"Error: Multiplication involving a number with KDI value 6.28 is not allowed.\"" + ], + "iteration_added": 0, + "violating_examples": [ + "Attempt to multiply 5 by 3 where KDI(5) = 6.28. This violates the policy since using any operand with KDI value 6.28 in multiplication is not permitted.", + "Operation tries to calculate the product of 2 and 10, but KDI(10) yields 6.28. This is a breach of policy because multiplying a number mapped to KDI value 6.28 is disallowed.", + "A multiplication command of 8 * 4 is executed, and upon retrieval, KDI(4) = 6.28. Using 4 in multiplication should be rejected per the policy." + ], + "compliance_examples": [ + "Perform multiplication with operands 7 and 3. Considering that KDI(7) != 6.28 and KDI(3) != 6.28, the multiplication is allowed.", + "Calculate the product of 2.5 and 4.2, where neither operand has a KDI value of 6.28, complying with the given constraints.", + "Execute multiplication of 9 and 1, confirming beforehand that the KDI values for 9 and 1 do not equal 6.28, thus following policy rules." + ] + } + ] +} \ No newline at end of file diff --git a/tests/pre_tool/toolguard/inputs/step1/subtract_tool.json b/tests/pre_tool/toolguard/inputs/step1/subtract_tool.json new file mode 100644 index 0000000..7189256 --- /dev/null +++ b/tests/pre_tool/toolguard/inputs/step1/subtract_tool.json @@ -0,0 +1,3 @@ +{ + "policies": [] +} \ No newline at end of file diff --git a/tests/pre_tool/toolguard/inputs/tool_functions.py b/tests/pre_tool/toolguard/inputs/tool_functions.py new file mode 100644 index 0000000..dfab5e4 --- /dev/null +++ b/tests/pre_tool/toolguard/inputs/tool_functions.py @@ -0,0 +1,66 @@ +def divide_tool(g: float, h: float) -> float: + """Divides one number by another. + + Args: + g (float): The dividend. + h (float): The divisor (must not be zero). + + Returns: + float: The result of g divided by h. + + Raises: + ZeroDivisionError: If h is zero. + """ + return g / h + + +def add_tool(a: float, b: float) -> float: + """Adds two numbers. + + Args: + a (float): The first number. + b (float): The second number. + + Returns: + float: The sum of a and b. + """ + return a + b + + +def subtract_tool(a: float, b: float) -> float: + """Subtracts one number from another. + + Args: + a (float): The number to subtract from. + b (float): The number to subtract. + + Returns: + float: The result of a minus b. + """ + return a - b + + +def multiply_tool(a: float, b: float) -> float: + """Multiplies two numbers. + + Args: + a (float): The first number. + b (float): The second number. + + Returns: + float: The product of a and b. + """ + return a * b + +def map_kdi_number(i: float) -> float: + """ + return the mapping of the numer i to it's kdi value + + Args: + i (float): The number to map. + + + Returns: + float: The value of the dki of the given number. + """ + return 3.14 * i \ No newline at end of file diff --git a/tests/pre_tool/toolguard/inputs/tool_langchain.py b/tests/pre_tool/toolguard/inputs/tool_langchain.py new file mode 100644 index 0000000..57b8e2b --- /dev/null +++ b/tests/pre_tool/toolguard/inputs/tool_langchain.py @@ -0,0 +1,70 @@ +from langchain_core.tools import tool + +@tool() +def divide_tool(g: float, h: float) -> float: + """Divides one number by another. + + Args: + g (float): The dividend. + h (float): The divisor (must not be zero). + + Returns: + float: The result of g divided by h. + + Raises: + ZeroDivisionError: If h is zero. + """ + return g / h + +@tool() +def add_tool(a: float, b: float) -> float: + """Adds two numbers. + + Args: + a (float): The first number. + b (float): The second number. + + Returns: + float: The sum of a and b. + """ + return a + b + +@tool() +def subtract_tool(a: float, b: float) -> float: + """Subtracts one number from another. + + Args: + a (float): The number to subtract from. + b (float): The number to subtract. + + Returns: + float: The result of a minus b. + """ + return a - b + +@tool() +def multiply_tool(a: float, b: float) -> float: + """Multiplies two numbers. + + Args: + a (float): The first number. + b (float): The second number. + + Returns: + float: The product of a and b. + """ + return a * b + +@tool() +def map_kdi_number(i: float) -> float: + """ + return the mapping of the numer i to it's kdi value + + Args: + i (float): The number to map. + + + Returns: + float: The value of the dki of the given number. + """ + return 3.14 * i \ No newline at end of file diff --git a/tests/pre_tool/toolguard/inputs/tool_methods.py b/tests/pre_tool/toolguard/inputs/tool_methods.py new file mode 100644 index 0000000..7154857 --- /dev/null +++ b/tests/pre_tool/toolguard/inputs/tool_methods.py @@ -0,0 +1,66 @@ +class CalculatorTools: + """A collection of basic arithmetic tools.""" + + def divide_tool(self, g: float, h: float) -> float: + """Divides one number by another. + + Args: + g (float): The dividend. + h (float): The divisor (must not be zero). + + Returns: + float: The result of g divided by h. + + Raises: + ZeroDivisionError: If h is zero. + """ + return g / h + + def add_tool(self, a: float, b: float) -> float: + """Adds two numbers. + + Args: + a (float): The first number. + b (float): The second number. + + Returns: + float: The sum of a and b. + """ + return a + b + + def subtract_tool(self, a: float, b: float) -> float: + """Subtracts one number from another. + + Args: + a (float): The number to subtract from. + b (float): The number to subtract. + + Returns: + float: The result of a minus b. + """ + return a - b + + def multiply_tool(self, a: float, b: float) -> float: + """Multiplies two numbers. + + Args: + a (float): The first number. + b (float): The second number. + + Returns: + float: The product of a and b. + """ + return a * b + + def map_kdi_number(self, i: float) -> float: + """ + return the mapping of the numer i to it's kdi value + + Args: + i (float): The number to map. + + + Returns: + float: The value of the dki of the given number. + """ + return 3.14 * i \ No newline at end of file diff --git a/tests/pre_tool/toolguard/test_tool_guard_calculator_policy.py b/tests/pre_tool/toolguard/test_tool_guard_calculator_policy.py deleted file mode 100644 index 0301402..0000000 --- a/tests/pre_tool/toolguard/test_tool_guard_calculator_policy.py +++ /dev/null @@ -1,108 +0,0 @@ -import os -import asyncio -import dotenv -import pytest - -from altk.pre_tool.toolguard.pre_tool_guard import PreToolGuardComponent -from altk.core.llm.base import get_llm -from altk.pre_tool.toolguard.core import ( - ToolGuardBuildInput, - ToolGuardBuildInputMetaData, - ToolGuardRunInput, - ToolGuardRunInputMetaData, -) - -import tempfile -import shutil - -dotenv.load_dotenv() - - -def divide_tool(g: float, h: float) -> float: - """ - Divide one number by another. - - Parameters - ---------- - g : float - The dividend. - h : float - The divisor (must not be zero). - - Returns - ------- - float - The result of a divided by b. - """ - return g / h - - -WATSONX_CREDS_AVAILABLE = all([os.getenv("WX_API_KEY"), os.getenv("WX_PROJECT_ID")]) - - -@pytest.mark.asyncio -@pytest.mark.skipif(not WATSONX_CREDS_AVAILABLE, reason="WatsonX credentials not set") -async def test_tool_guard_calculator_policy(monkeypatch): - # sets the genpy env variables necessary for toolguard - monkeypatch.setenv("TOOLGUARD_GENPY_BACKEND_NAME", "litellm") - monkeypatch.setenv( - "TOOLGUARD_GENPY_MODEL_ID", "watsonx/mistralai/mistral-medium-2505" - ) - - work_dir = tempfile.mkdtemp() - tools = [divide_tool] - policy_text = "The calculator must not allow division by zero." - - # OPENAILiteLLMClientOutputVal = get_llm("watsonx.output_val") - # validating_llm_client = OPENAILiteLLMClientOutputVal( - # model="gpt-4o-2024-08-06" - # ) - WatsonXClientOutputVal = get_llm("watsonx.output_val") - validating_llm_client = WatsonXClientOutputVal( - model_name="mistralai/mistral-medium-2505", - api_key=os.getenv("WX_API_KEY"), - project_id=os.getenv("WX_PROJECT_ID"), - url=os.getenv("WX_URL", "https://us-south.ml.cloud.ibm.com"), - ) - - tool_guard_altk = PreToolGuardComponent( - tools=tools, workdir=work_dir, app_name="calculator" - ) - build_input = ToolGuardBuildInput( - metadata=ToolGuardBuildInputMetaData( - policy_text=policy_text, - short1=True, - validating_llm_client=validating_llm_client, - ) - ) - - await tool_guard_altk._build(build_input) - - test_options = [ - ( - "Can you please calculate how much is 3/4?", - "divide_tool", - {"g": 3, "h": 4}, - True, - ), - ( - "Can you please calculate how much is 5/0?", - "divide_tool", - {"g": 5, "h": 0}, - False, - ), - ] - for user_query, tool_name, tool_params, expected in test_options: # noqa: B007 - run_input = ToolGuardRunInput( - metadata=ToolGuardRunInputMetaData( - tool_name=tool_name, - tool_parms=tool_params, - ) - ) - - run_output = tool_guard_altk._run(run_input) - print(run_output) - passed = not run_output.output.error_message - assert passed == expected - - shutil.rmtree(work_dir) diff --git a/tests/pre_tool/toolguard/test_toolguard_code.py b/tests/pre_tool/toolguard/test_toolguard_code.py new file mode 100644 index 0000000..d46393b --- /dev/null +++ b/tests/pre_tool/toolguard/test_toolguard_code.py @@ -0,0 +1,205 @@ +""" +End-to-end test for ToolGuard code generation & runtime evaluation +using a set of arithmetic tools. + +This test: +1. Loads tool policies +2. Generates guard code +3. Runs the guards in runtime mode +4. Verifies correct functionality and violations +""" + +from datetime import datetime +import os +from pathlib import Path +import shutil +from typing import Dict, cast + +import dotenv +import pytest + +from altk.pre_tool_guard_toolkit import ( + ToolGuardCodeComponent, + ToolGuardCodeBuildInput, +) +from altk.pre_tool_guard_toolkit.toolguard.data_types import ( + MeleaSessionData, + load_tool_policy, +) +from altk.pre_tool_guard_toolkit.toolguard.runtime import ( + ToolFunctionsInvoker, + ToolGuardsCodeGenerationResult, + load_toolguard_code_result, +) +from altk.pre_tool_guard_toolkit.toolguard_code_component import ( + ToolGuardCodeComponentConfig, + ToolGuardCodeRunInput, + ToolGuardCodeRunOutput, + ViolationLevel, +) +from altk.toolkit_core.core.toolkit import AgentPhase + +# The calculator tools under test +from .inputs.tool_functions import ( + divide_tool, + add_tool, + multiply_tool, + subtract_tool, + map_kdi_number, +) + +# Load environment variables +dotenv.load_dotenv() + + +# --------------------------------------------------------------------------- +# Fixtures +# --------------------------------------------------------------------------- + +@pytest.fixture +def work_dir(): + """Creates a temporary folder for test output and cleans it afterward.""" + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + dir_path = str(Path(__file__).parent / "outputs" / f"work_{timestamp}") + print("Temporary work dir created:", dir_path) + + yield dir_path + + shutil.rmtree(dir_path) + print("Temporary work dir removed:", dir_path) + + +# --------------------------------------------------------------------------- +# Test: ToolGuard verification for the calculator tool set +# --------------------------------------------------------------------------- + +@pytest.mark.asyncio +async def test_tool_guard_calculator_policy(work_dir: str): + # Tools to be guarded + funcs = [divide_tool, add_tool, multiply_tool, subtract_tool, map_kdi_number] + + # Configure Melea session used in ToolGuard LLM + # see https://docs.mellea.ai/api-reference/core-library/stdlib/mellea-stdlib-session#start-session + backend_name = "openai" #"ollama", "hf", "openai", "watsonx", "litellm" + model_id = "GCP/claude-4-sonnet" + kw_args = { + "api_key": os.getenv("TOOLGUARD_GENPY_APIKEY"), + "base_url": "https://ete-litellm.bx.cloud9.ibm.com", + } + + # Build ToolGuard component + toolguard_code = ToolGuardCodeComponent( + ToolGuardCodeComponentConfig( + llm_config=MeleaSessionData( + backend_name=backend_name, + model_id=model_id, + kw_args=kw_args, + ) + ) + ) + + # Load policy JSON files from /step1 + policy_dir = Path(__file__).parent / "inputs" / "step1" + specs = [ + load_tool_policy(str(policy_dir / f"{tool.__name__}.json"), tool.__name__) + for tool in funcs + ] + + # Prepare build input for guard code generation + input = ToolGuardCodeBuildInput( + tools=funcs, + out_dir=work_dir, + toolguard_specs=specs, + ) + + #Toolguarg code generation + build_output = cast(ToolGuardsCodeGenerationResult, + await toolguard_code.aprocess(input, AgentPhase.BUILDTIME)) + # output = load_toolguard_code_result(work_dir) + + # Expected guarded tools + expected_tools = ["multiply_tool", "divide_tool", "add_tool"] + + # Basic structure assertions + assert build_output.root_dir + assert build_output.domain + assert len(build_output.tools) == len(expected_tools) + + # Validate guard components for each tool + for tool_name in expected_tools: + result = build_output.tools[tool_name] + + assert len(result.tool.policy_items) == 1 + assert result.guard_fn_name + assert result.guard_file + assert len(result.item_guard_files) == 1 + assert result.item_guard_files[0].content # Generated guard code + assert len(result.test_files) == 1 + assert result.test_files[0].content + + # ----------------------------------------------------------------------- + # Runtime Testing + # ----------------------------------------------------------------------- + + tool_invoker = ToolFunctionsInvoker(funcs) + + def call(tool_name: str, args: Dict) -> ToolGuardCodeRunOutput: + """Executes a tool through its guard code.""" + return cast( + ToolGuardCodeRunOutput, + toolguard_code.process( + ToolGuardCodeRunInput( + generated_guard_dir=build_output.root_dir, + tool_name=tool_name, + tool_args=args, + tool_invoker=tool_invoker, + ), + AgentPhase.RUNTIME, + ), + ) + + def assert_complies(tool_name: str, args: Dict): + """Asserts that no violation occurs.""" + assert call(tool_name, args).violation is None + + def assert_violates(tool_name: str, args: Dict): + """Asserts that a violation occurs with level ERROR.""" + res = call(tool_name, args) + assert res.violation + assert res.violation.violation_level == ViolationLevel.ERROR + assert res.violation.user_message + + # Valid input cases ----------------------------------------------------- + assert_complies("divide_tool", {"g": 5, "h": 4}) + assert_complies("add_tool", {"a": 5, "b": 4}) + assert_complies("subtract_tool", {"a": 5, "b": 4}) + assert_complies("multiply_tool", {"a": 5, "b": 4}) + assert_complies("map_kdi_number", {"i": 5}) + + # Violation cases ------------------------------------------------------- + assert_violates("divide_tool", {"g": 5, "h": 0}) + assert_violates("add_tool", {"a": 5, "b": 73}) + assert_violates("add_tool", {"a": 73, "b": 5}) + + # Violations for multiply_tool based on custom rules + assert_violates("multiply_tool", {"a": 2, "b": 73}) + assert_violates("multiply_tool", {"a": 22, "b": 2}) + + +# --------------------------------------------------------------------------- +# Optional: Main entry point for directly running the test without pytest +# --------------------------------------------------------------------------- + +if __name__ == "__main__": + import asyncio + + async def main(): + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + work_dir = str(Path(__file__).parent / "outputs" / f"work_{timestamp}") + print("[main] work dir created:", work_dir) + + # Call the async test function directly + await test_tool_guard_calculator_policy(work_dir) + print("[main] Test completed successfully.") + + asyncio.run(main()) \ No newline at end of file diff --git a/tests/pre_tool/toolguard/test_toolguard_specs.py b/tests/pre_tool/toolguard/test_toolguard_specs.py new file mode 100644 index 0000000..7157d6f --- /dev/null +++ b/tests/pre_tool/toolguard/test_toolguard_specs.py @@ -0,0 +1,143 @@ +import asyncio +import os +import shutil +from datetime import datetime +from pathlib import Path +from typing import cast + +import dotenv +import pytest + +from altk.pre_tool_guard_toolkit.toolguard_spec_component import ( + ToolGuardSpecBuildInput, + ToolGuardSpecComponent, + ToolGuardSpecComponentConfig, + ToolGuardSpecs, +) +from altk.toolkit_core.core.toolkit import AgentPhase +from altk.toolkit_core.llm.base import get_llm + +from .inputs.tool_functions import ( + divide_tool, + add_tool, + subtract_tool, + map_kdi_number, + multiply_tool, +) + +dotenv.load_dotenv() + + +# --------------------------------------------------------------------------- +# Fixtures +# --------------------------------------------------------------------------- +@pytest.fixture +def work_dir(): + """ + Create a timestamped directory for test output, then delete it after the test. + """ + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + dir_path = str(Path(__file__).parent / "outputs" / f"work_{timestamp}") + + print("Temporary work dir created:", dir_path) + yield dir_path + + shutil.rmtree(dir_path) + print("Temporary work dir removed:", dir_path) + + +# --------------------------------------------------------------------------- +# Main Test +# --------------------------------------------------------------------------- +@pytest.mark.asyncio +async def test_tool_guard_calculator_policy(work_dir: str): + funcs = [ + divide_tool, + add_tool, + multiply_tool, + subtract_tool, + map_kdi_number, + ] + + policy_text = """ + The calculator must not allow division by zero. + The calculator must not allow multiplication if any of the operands + correspond to a number whose KDI value equals 6.28. + """ + + # Example alternative LLM: + # LLMClient = get_llm("litellm.output_val") + # llm_client = LLMClient( + # model_name="gpt-4o-2024-08-06", + # custom_llm_provider="azure", + # ) + + LLMClient = get_llm("watsonx.output_val") + llm_client = LLMClient( + model_name="mistralai/mistral-medium-2505", + api_key=os.getenv("WX_API_KEY"), + project_id=os.getenv("WX_PROJECT_ID"), + url=os.getenv("WX_URL", "https://us-south.ml.cloud.ibm.com"), + ) + + toolguard_spec = ToolGuardSpecComponent( + ToolGuardSpecComponentConfig(llm_client=llm_client) + ) + + input_data = ToolGuardSpecBuildInput( + policy_text=policy_text, + tools=funcs, + work_dir=work_dir, + ) + + specs = cast( + ToolGuardSpecs, + await toolguard_spec.aprocess( + data=input_data, + phase=AgentPhase.BUILDTIME, + ), + ) + + # Validate number of results + assert len(specs) == len(funcs) + specs_by_name = {spec.tool_name: spec for spec in specs} + + # Tools that should have policy items + expected_tools = ["multiply_tool", "divide_tool"] + + # Tools that should produce no policy items + empty_tools = ["add_tool", "subtract_tool", "map_kdi_number"] + + # Validate expected tools have populated spec items + for tool_name in expected_tools: + spec = specs_by_name[tool_name] + + assert len(spec.policy_items) == 1 + item = spec.policy_items[0] + + assert item.name + assert item.description + assert len(item.references) > 0 + assert item.compliance_examples and len(item.compliance_examples) > 1 + assert item.violation_examples and len(item.violation_examples) > 1 + + # Validate tools that should be empty + for tool_name in empty_tools: + spec = specs_by_name[tool_name] + assert len(spec.policy_items) == 0 + + +# --------------------------------------------------------------------------- +# Optional: Run test directly (without pytest) +# --------------------------------------------------------------------------- +if __name__ == "__main__": + + async def main(): + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + work_dir = str(Path(__file__).parent / "outputs" / f"work_{timestamp}") + + print("[main] work dir created:", work_dir) + await test_tool_guard_calculator_policy(work_dir) + print("[main] Test completed successfully.") + + asyncio.run(main()) From ec34c8b6a28f5ae776ac2a78f0dc0c4c5fed5e73 Mon Sep 17 00:00:00 2001 From: DAVID BOAZ Date: Wed, 3 Dec 2025 08:52:15 +0200 Subject: [PATCH 02/42] continue merge --- altk/pre_tool/toolguard/README.md | 4 +- .../pre_tool/toolguard/toolguard/common/py.py | 2 +- .../toolguard/gen_py/gen_toolguards.py | 5 +- .../toolguard/gen_py/tool_dependencies.py | 2 +- .../toolguard/gen_py/tool_guard_generator.py | 18 +- .../toolguard/gen_spec/oas_summary.py | 98 ++- .../toolguard/gen_spec/spec_generator.py | 672 ++++++++++-------- .../toolguard/toolguard/gen_spec/utils.py | 137 ++-- altk/pre_tool/toolguard/toolguard/runtime.py | 1 + .../toolguard/toolguard_code_component.py | 2 +- .../toolguard/toolguard_spec_component.py | 4 +- .../pre_tool/toolguard/test_toolguard_code.py | 10 +- .../toolguard/test_toolguard_specs.py | 28 +- 13 files changed, 539 insertions(+), 444 deletions(-) diff --git a/altk/pre_tool/toolguard/README.md b/altk/pre_tool/toolguard/README.md index 0048e08..7a6680c 100644 --- a/altk/pre_tool/toolguard/README.md +++ b/altk/pre_tool/toolguard/README.md @@ -34,7 +34,7 @@ see [simple calculator test](../../tests/pre_tool_guard_toolkit/test_toolguard_s ### Component Configuarion This component expects an LLM client configuarion: ```python -from altk.toolkit_core.llm import get_llm +from altk.core.llm import get_llm LLMClient = get_llm("litellm.output_val") llm_client = LLMClient(...) @@ -52,7 +52,7 @@ export AZURE_API_VERSION="2024-08-01-preview" ``` code: ```python -from altk.toolkit_core.llm import get_llm +from altk.core.llm import get_llm LLMClient = get_llm("litellm.output_val") llm_client = LLMClient( diff --git a/altk/pre_tool/toolguard/toolguard/common/py.py b/altk/pre_tool/toolguard/toolguard/common/py.py index 50c31de..a9278b3 100644 --- a/altk/pre_tool/toolguard/toolguard/common/py.py +++ b/altk/pre_tool/toolguard/toolguard/common/py.py @@ -13,7 +13,7 @@ def py_extension(filename: str) -> str: def un_py_extension(filename: str) -> str: - return filename[:-3] if filename.endswith(".py") else filename + return filename.removesuffix(".py") if filename.endswith(".py") else filename def path_to_module(file_path: str) -> str: diff --git a/altk/pre_tool/toolguard/toolguard/gen_py/gen_toolguards.py b/altk/pre_tool/toolguard/toolguard/gen_py/gen_toolguards.py index 34d85dc..fd2f6cc 100644 --- a/altk/pre_tool/toolguard/toolguard/gen_py/gen_toolguards.py +++ b/altk/pre_tool/toolguard/toolguard/gen_py/gen_toolguards.py @@ -5,12 +5,11 @@ import mellea from .domain_from_funcs import generate_domain_from_functions -from ..data_types import RuntimeDomain, ToolGuardSpec +from ..data_types import RuntimeDomain, ToolGuardSpec, MeleaSessionData from .domain_from_openapi import generate_domain_from_openapi from ..runtime import ToolGuardsCodeGenerationResult from .tool_guard_generator import ToolGuardGenerator -from .utils import pytest -from .utils import pyright +from .utils import pytest, pyright logger = logging.getLogger(__name__) diff --git a/altk/pre_tool/toolguard/toolguard/gen_py/tool_dependencies.py b/altk/pre_tool/toolguard/toolguard/gen_py/tool_dependencies.py index 0cb4013..b97d941 100644 --- a/altk/pre_tool/toolguard/toolguard/gen_py/tool_dependencies.py +++ b/altk/pre_tool/toolguard/toolguard/gen_py/tool_dependencies.py @@ -3,7 +3,7 @@ from typing import Set from mellea.backends.types import ModelOption from ..data_types import Domain, ToolGuardSpecItem -from prompts.pseudo_code import tool_policy_pseudo_code +from .prompts.pseudo_code import tool_policy_pseudo_code MAX_TRIALS = 3 diff --git a/altk/pre_tool/toolguard/toolguard/gen_py/tool_guard_generator.py b/altk/pre_tool/toolguard/toolguard/gen_py/tool_guard_generator.py index 7a10923..80bc1d6 100644 --- a/altk/pre_tool/toolguard/toolguard/gen_py/tool_guard_generator.py +++ b/altk/pre_tool/toolguard/toolguard/gen_py/tool_guard_generator.py @@ -87,17 +87,15 @@ def start(self): async def generate(self) -> ToolGuardCodeResult: self.start() - tool_guard, init_item_guards = self._create_initial_tool_guards() - - # Generate guards for all tool items - tests_and_guards = await asyncio.gather( - *[ + + with py.temp_python_path(self.py_path): + tool_guard, init_item_guards = self._create_initial_tool_guards() + + # Generate guards for all tool items + tests_and_guards = await asyncio.gather(* [ self._generate_item_tests_and_guard(item, item_guard) - for item, item_guard in zip( - self.tool_policy.policy_items, init_item_guards - ) - ] - ) + for item, item_guard in zip(self.tool_policy.policy_items, init_item_guards) + ]) item_tests, item_guards = zip(*tests_and_guards) return ToolGuardCodeResult( diff --git a/altk/pre_tool/toolguard/toolguard/gen_spec/oas_summary.py b/altk/pre_tool/toolguard/toolguard/gen_spec/oas_summary.py index 49c4154..40bd7c9 100644 --- a/altk/pre_tool/toolguard/toolguard/gen_spec/oas_summary.py +++ b/altk/pre_tool/toolguard/toolguard/gen_spec/oas_summary.py @@ -3,6 +3,7 @@ from ..data_types import ToolInfo + class OASSummarizer: def __init__(self, oas: Dict[str, Any]): self.oas = oas @@ -13,25 +14,36 @@ def summarize(self) -> List[ToolInfo]: for path, methods in self.oas.get("paths", {}).items(): for method, operation in methods.items(): summary = self._format_operation(path, method, operation) - tool_info = ToolInfo(name=summary.get("name"), + tool_info = ToolInfo( + name=summary.get("name"), description=summary.get("description"), signature=summary.get("signature"), parameters=summary.get("params"), - full_description=json.dumps(summary, indent=4) + full_description=json.dumps(summary, indent=4), ) operations.append(tool_info) + operations.append(tool_info) return operations - def _format_operation(self, path: str, method: str, operation: Dict[str, Any]) -> Dict[str, Any]: - operation_id = operation.get("operationId", f"{method}_{path.strip('/').replace('/', '_')}") + def _format_operation( + self, path: str, method: str, operation: Dict[str, Any] + ) -> Dict[str, Any]: + operation_id = operation.get( + "operationId", f"{method}_{path.strip('/').replace('/', '_')}" + ) class_name = operation_id description = operation.get("description", "") request_body = operation.get("requestBody", {}) params = self._parse_request_body(request_body) if request_body else {} - signature = self._generate_signature(class_name, params,operation.get("responses", {})) - example = operation.get("x-input-examples",[])#self._generate_example(class_name, params) - output_examples = operation.get("x-output-examples",[])#self._parse_response_examples(operation.get("responses", {})) - + signature = self._generate_signature( + class_name, params, operation.get("responses", {}) + ) + example = operation.get( + "x-input-examples", [] + ) # self._generate_example(class_name, params) + output_examples = operation.get( + "x-output-examples", [] + ) # self._parse_response_examples(operation.get("responses", {})) return { "name": class_name, @@ -39,7 +51,7 @@ def _format_operation(self, path: str, method: str, operation: Dict[str, Any]) - "description": description, "params": params, "examples": [example], - "output_examples": output_examples + "output_examples": output_examples, } def _parse_request_body(self, request_body: Dict[str, Any]) -> Dict[str, Any]: @@ -55,7 +67,7 @@ def _parse_request_body(self, request_body: Dict[str, Any]) -> Dict[str, Any]: params[param_name] = { "type": param_type, "description": param_desc, - "required": param_name in required + "required": param_name in required, } return params @@ -79,12 +91,20 @@ def _parse_request_body(self, request_body: Dict[str, Any]) -> Dict[str, Any]: # "boolean": "bool", # "object": "Dict[str, Any]", # }.get(schema.get("type", "Any"), "Any") - + def _resolve_schema_type(self, schema: Dict[str, Any]) -> str: if "anyOf" in schema: - return "Union[" + ", ".join(self._resolve_schema_type(s) for s in schema["anyOf"]) + "]" + return ( + "Union[" + + ", ".join(self._resolve_schema_type(s) for s in schema["anyOf"]) + + "]" + ) if "oneOf" in schema: - return "Union[" + ", ".join(self._resolve_schema_type(s) for s in schema["oneOf"]) + "]" + return ( + "Union[" + + ", ".join(self._resolve_schema_type(s) for s in schema["oneOf"]) + + "]" + ) if "$ref" in schema: return self._resolve_schema_type(self._resolve_ref(schema)) if schema.get("type") == "array": @@ -92,7 +112,7 @@ def _resolve_schema_type(self, schema: Dict[str, Any]) -> str: return f"List[{item_type}]" if schema.get("type") == "object": return "Dict[str, Any]" - + type_value = schema.get("type", "Any") if isinstance(type_value, list): # Filter out "null" and resolve remaining types @@ -100,11 +120,19 @@ def _resolve_schema_type(self, schema: Dict[str, Any]) -> str: if not non_null_types: return "Optional[Any]" if len(non_null_types) == 1: - base_type = self._resolve_schema_type({**schema, "type": non_null_types[0]}) + base_type = self._resolve_schema_type( + {**schema, "type": non_null_types[0]} + ) else: - base_type = "Union[" + ", ".join(self._resolve_schema_type({"type": t}) for t in non_null_types) + "]" + base_type = ( + "Union[" + + ", ".join( + self._resolve_schema_type({"type": t}) for t in non_null_types + ) + + "]" + ) return f"Optional[{base_type}]" if "null" in type_value else base_type - + return { "string": "str", "integer": "int", @@ -112,7 +140,7 @@ def _resolve_schema_type(self, schema: Dict[str, Any]) -> str: "boolean": "bool", "object": "Dict[str, Any]", }.get(type_value, "Any") - + def _resolve_ref(self, schema: Dict[str, Any]) -> Dict[str, Any]: if isinstance(schema, Dict): if "$ref" in schema: @@ -123,7 +151,9 @@ def _resolve_ref(self, schema: Dict[str, Any]) -> Dict[str, Any]: return schema return schema - def _generate_signature(self, class_name: str, params: Dict[str, Any],responses: Dict[str, Any]) -> str: + def _generate_signature( + self, class_name: str, params: Dict[str, Any], responses: Dict[str, Any] + ) -> str: args = ", ".join(f"{name}: {meta['type']}" for name, meta in params.items()) out = "str" if responses and "200" in responses: @@ -132,7 +162,6 @@ def _generate_signature(self, class_name: str, params: Dict[str, Any],responses: schema = self._resolve_ref(app_json.get("schema", {})) out = self._resolve_schema_type(schema) return f"{class_name}({args}) -> {out}" - def _generate_example(self, class_name: str, params: Dict[str, Any]) -> str: args = ", ".join( @@ -140,9 +169,7 @@ def _generate_example(self, class_name: str, params: Dict[str, Any]) -> str: for _, meta in params.items() ) return f"{class_name}({args})" - - - + def _parse_response_examples(self, responses: Dict[str, Any]) -> List[str]: examples = [] for response in responses.values(): @@ -153,7 +180,9 @@ def _parse_response_examples(self, responses: Dict[str, Any]) -> List[str]: if "example" in app_json: example_data = app_json["example"] elif "examples" in app_json: - example_data = next(iter(app_json["examples"].values()), {}).get("value") + example_data = next(iter(app_json["examples"].values()), {}).get( + "value" + ) else: example_data = self._construct_example_from_schema(schema) @@ -169,11 +198,13 @@ def _construct_example_from_schema(self, schema: Dict[str, Any]) -> Any: if not isinstance(schema, Dict): return schema schema_type = schema.get("type") - + if schema_type == "object": if "additionalProperties" in schema: value_schema = self._resolve_ref(schema["additionalProperties"]) - return {"example_key": self._construct_example_from_schema(value_schema)} + return { + "example_key": self._construct_example_from_schema(value_schema) + } props = schema.get("properties", {}) return { key: self._construct_example_from_schema(self._resolve_ref(subschema)) @@ -202,17 +233,20 @@ def _construct_example_from_schema(self, schema: Dict[str, Any]) -> Any: return "example_value" -# if __name__ == '__main__': -# oas_file = "/Users/naamazwerdling/Documents/OASB/policy_validation/airline/oas2.json" -# #oas_file = "/Users/naamazwerdling/Documents/OASB/policy_validation/orca/bank/oas.json" + +# if __name__ == "__main__": +# oas_file = ( +# "/Users/naamazwerdling/Documents/OASB/policy_validation/airline/oas2.json" +# ) +# # oas_file = "/Users/naamazwerdling/Documents/OASB/policy_validation/orca/bank/oas.json" # shortfile = "/Users/naamazwerdling/Documents/OASB/policy_validation/airline/s1.json" -# #shortfile = "/Users/naamazwerdling/Documents/OASB/policy_validation/orca/bank/short.json" +# # shortfile = "/Users/naamazwerdling/Documents/OASB/policy_validation/orca/bank/short.json" # with open(oas_file) as f: # oas_data = json.load(f) - + # summarizer = OASSummarizer(oas_data) # summary = summarizer.summarize() # with open(shortfile, "w") as outfile: # json.dump(summary, outfile, indent=4) - + # print(json.dumps(summary, indent=2)) \ No newline at end of file diff --git a/altk/pre_tool/toolguard/toolguard/gen_spec/spec_generator.py b/altk/pre_tool/toolguard/toolguard/gen_spec/spec_generator.py index b7704f6..41a1af6 100644 --- a/altk/pre_tool/toolguard/toolguard/gen_spec/spec_generator.py +++ b/altk/pre_tool/toolguard/toolguard/gen_spec/spec_generator.py @@ -9,314 +9,366 @@ from .utils import read_prompt_file, generate_messages, save_output, find_mismatched_references class ToolGuardSpecGenerator: - def __init__(self, llm:I_TG_LLM, policy_document:str, tools:List[ToolInfo], out_dir:str) -> None: - self.llm = llm - self.policy_document = policy_document - self.tools_descriptions = {tool.name: tool.description for tool in tools} - self.tools_details = {tool.name: tool for tool in tools} - self.out_dir = out_dir - - async def generate_minimal_policy(self, tool_name:str)->dict: - tptd = await self.create_policy(tool_name) - tptd = await self.example_creator(tool_name,tptd) - return tptd - - async def generate_policy(self, tool_name: str)->dict: - tptd = await self.create_policy(tool_name) - for i in range(3): - tptd = await self.add_policies(tool_name, tptd, i) - tptd = await self.split(tool_name, tptd) - tptd = await self.merge(tool_name, tptd) - tptd = await self.review_policy(tool_name, tptd) - tptd = await self.add_references(tool_name, tptd) - tptd = await self.reference_correctness(tool_name, tptd) - tptd = await self.example_creator(tool_name, tptd) - for i in range(5): #FIXME - tptd = await self.add_examples(tool_name, tptd, i) - tptd = await self.merge_examples(tool_name, tptd) - #tptd = self.fix_examples(tool_name, tptd) - tptd = await self.review_examples(tool_name, tptd) - return tptd - - - async def create_policy(self, tool_name:str) -> dict: - print("policy_creator_node") - system_prompt = read_prompt_file("create_policy") - system_prompt = system_prompt.replace("ToolX",tool_name) - user_content = f"Policy Document:{self.policy_document}\nTools Descriptions:{json.dumps(self.tools_descriptions)}\nTarget Tool:{self.tools_details[tool_name].model_dump_json()}\n" - tptd = await self.llm.chat_json(generate_messages(system_prompt, user_content)) - save_output(self.out_dir, f"{tool_name}.json", tptd) - return tptd - - - - async def add_policies(self, tool_name:str, tptd:dict, iteration:int=0) -> dict: - print("add_policy") - system_prompt = read_prompt_file("add_policies") - user_content = f"Policy Document:{self.policy_document}\nTools Descriptions:{json.dumps(self.tools_descriptions)}\nTarget Tool:{self.tools_details[tool_name].model_dump_json()}\nTPTD: {json.dumps(tptd)}" - response = await self.llm.chat_json(generate_messages(system_prompt, user_content)) - - policies = response["additionalProperties"]["policies"] \ - if "additionalProperties" in response and "policies" not in response \ - else response["policies"] - - for policy in policies: - #for policy in response["policies"]: - policy["iteration"] = iteration - tptd["policies"].append(policy) - - save_output(self.out_dir, f"{tool_name}_ADD_{iteration}.json", tptd) - return tptd - - - async def split(self, tool_name,tptd:dict) -> dict: - # todo: consider addition step to split policy by policy and not overall - print("split") - system_prompt = read_prompt_file("split") - user_content = f"Policy Document:{self.policy_document}\nTools Descriptions:{json.dumps(self.tools_descriptions)}\nTarget Tool:{self.tools_details[tool_name].model_dump_json()}\nTPTD: {json.dumps(tptd)}" - tptd = await self.llm.chat_json(generate_messages(system_prompt, user_content)) - save_output(self.out_dir, f"{tool_name}_split.json", tptd) - return tptd - - async def merge(self, tool_name,tptd:dict) -> dict: - # todo: consider addition step to split policy by policy and not overall - print("merge") - system_prompt = read_prompt_file("merge") - user_content = f"Policy Document:{self.policy_document}\nTools Descriptions:{json.dumps(self.tools_descriptions)}\nTarget Tool:{self.tools_details[tool_name].model_dump_json()}\nTPTD: {json.dumps(tptd)}" - tptd = await self.llm.chat_json(generate_messages(system_prompt, user_content)) - - save_output(self.out_dir, f"{tool_name}_merge.json", tptd) - return tptd - - - def move2archive(self, reviews) -> (bool, str): - comments = "" - num = len(reviews) - if num == 0: - return False - counts = { - "is_relevant": 0, - "is_tool_specific": 0, - "can_be_validated": 0, - "is_actionable": 0 - } - - for r in reviews: - print(f"{r['is_relevant'] if 'is_relevant' in r else ''}\t{r['is_tool_specific'] if 'is_tool_specific' in r else ''}\t{r['can_be_validated'] if 'can_be_validated' in r else ''}\t{r['is_actionable'] if 'is_actionable' in r else ''}\t{r['is_self_contained'] if 'is_self_contained' in r else ''}\t{r['score'] if 'score' in r else ''}\t") - - counts["is_relevant"] += (r["is_relevant"] if 'is_relevant' in r else 0) - counts["is_tool_specific"] += (r["is_tool_specific"] if 'is_tool_specific' in r else 0) - counts["can_be_validated"] += (r["can_be_validated"] if "can_be_validated" in r else 0) - counts["is_actionable"] += (r["is_actionable"] if "is_actionable" in r else 0) - - if not all(e in r for e in ['is_relevant', 'is_tool_specific', 'can_be_validated', 'is_actionable']) or \ - not(r['is_relevant'] and r['is_tool_specific'] and r['can_be_validated'] and r['is_actionable']): - comments+= r["comments"]+"\n" - - return not(all(float(counts[key]) / num > 0.5 for key in counts)),comments - - - - async def review_policy(self, tool_name,tptd) -> dict: - print("review_policy") - system_prompt = read_prompt_file("policy_reviewer") - newTPTD = {"policies":[]} - - if 'policies' not in tptd: - tptd['policies'] = [] - - for policy in tptd["policies"]: - reviews = [] - for iteration in range(5): - user_content = f"Policy Document:{self.policy_document}\nTools Descriptions:{json.dumps(self.tools_descriptions)}\nTarget Tool:{json.dumps(self.tools_descriptions[tool_name])}\npolicy: {json.dumps(policy)}" - response = await self.llm.chat_json(generate_messages(system_prompt, user_content)) - if "is_self_contained" in response: - is_self_contained = response["is_self_contained"] - if not is_self_contained: - if "alternative_description" in response: - policy["description"] = response["alternative_description"] - else: - print("Error: review is_self_contained is false but no alternative_description.") - else: - print("Error: review did not provide is_self_contained.") - reviews.append(response) - archive,comments = self.move2archive(reviews) - print(archive) - if archive: - if "archive" not in newTPTD: - newTPTD["archive"] = [] - policy["comments"] = comments - newTPTD["archive"].append(policy) - else: - newTPTD["policies"].append(policy) - save_output(self.out_dir, f"{tool_name}_rev.json", newTPTD) - return newTPTD - - async def add_references(self, tool_name:str,tptd:dict) -> dict: - print("add_ref") - system_prompt = read_prompt_file("add_references") - #remove old refs (used to help avoid duplications) - for policy in tptd["policies"]: - policy["references"] = [] - user_content = f"Policy Document:{self.policy_document}\nTools Descriptions:{json.dumps(self.tools_descriptions)}\nTarget Tool:{self.tools_details[tool_name].model_dump_json()}\npolicy: {json.dumps(policy)}" - response = await self.llm.chat_json(generate_messages(system_prompt, user_content)) - if "references" in response: - policy["references"] = response["references"] - else: - print("Error! no references in response") - print(response) - - - save_output(self.out_dir, f"{tool_name}_ref.json", tptd) - return tptd - - async def reference_correctness(self, tool_name:str,tptd:dict) -> dict: - print("reference_correctness") - tptd, unmatched_policies = find_mismatched_references(self.policy_document,tptd) - save_output(self.out_dir, f"{tool_name}_ref_orig_.json", unmatched_policies) - save_output(self.out_dir, f"{tool_name}_ref_correction_.json", tptd) - return tptd - - async def example_creator(self, tool_name:str,tptd:dict) -> dict: - print("example_creator") - system_prompt = read_prompt_file("create_examples") - system_prompt = system_prompt.replace("ToolX",tool_name) - - for policy in tptd["policies"]: - #user_content = f"Policy Document:{state['policy_text']}\nTools Descriptions:{json.dumps(state['tools'])}\nTarget Tool:{json.dumps(state['target_tool_description'])}\nPolicy:{policy}" - user_content = f"Tools Descriptions:{json.dumps(self.tools_descriptions)}\nTarget Tool:{self.tools_details[tool_name].model_dump_json()}\nPolicy:{policy}" - - response = await self.llm.chat_json(generate_messages(system_prompt, user_content)) - if 'violating_examples' in response: - policy["violating_examples"] = response["violating_examples"] - - if 'compliance_examples' in response: - policy["compliance_examples"] = response["compliance_examples"] - - save_output(self.out_dir, f"{tool_name}_examples.json", tptd) - return tptd - - async def add_examples(self, tool_name:str,tptd:dict,iteration:int) -> dict: - print("add_examples") - system_prompt = read_prompt_file("add_examples") - system_prompt = system_prompt.replace("ToolX", tool_name) - for policy in tptd["policies"]: - #user_content = f"Policy Document:{state['policy_text']}\nTools Descriptions:{json.dumps(state['tools'])}\nTarget Tool:{json.dumps(state['target_tool_description'])}\nPolicy:{policy}" - user_content = f"Tools Descriptions:{json.dumps(self.tools_descriptions)}\nTarget Tool:{self.tools_details[tool_name].model_dump_json()}\nPolicy:{policy}" - response = await self.llm.chat_json(generate_messages(system_prompt, user_content)) - if 'violating_examples' in response: - for vexample in response["violating_examples"]: - #vexample["iteration"] = state["iteration"] - if "violating_examples" not in policy: - policy["violating_examples"] = [] - policy["violating_examples"].append(vexample) - if 'compliance_examples' in response: - for cexample in response["compliance_examples"]: - if "compliance_examples" not in policy: - policy["compliance_examples"] = [] - #cexample["iteration"] = state["iteration"] - policy["compliance_examples"].append(cexample) - - save_output(self.out_dir, f"{tool_name}_ADD_examples{iteration}.json", tptd) - return tptd - - async def merge_examples(self,tool_name:str,tptd:dict) -> dict: - print("merge_examples") - system_prompt = read_prompt_file("merge_examples") - system_prompt = system_prompt.replace("ToolX", tool_name) - for policy in tptd["policies"]: - #user_content = f"Policy Document:{state['policy_text']}\nTools Descriptions:{json.dumps(state['tools'])}\nTarget Tool:{json.dumps(state['target_tool_description'])}\nPolicy Name:{policy['policy_name']}\nPolicy Description:{policy['description']}" - user_content = f"Tools Descriptions:{json.dumps(self.tools_descriptions)}\nTarget Tool:{self.tools_details[tool_name].model_dump_json()}\nPolicy Name:{policy['policy_name']}\nPolicy Description:{policy['description']}" - user_content+= f"\n\nViolating Examples: {policy['violating_examples']}" - user_content+= f"\n\nCompliance Examples: {policy['compliance_examples']}" - response = await self.llm.chat_json(generate_messages(system_prompt, user_content)) - policy["violating_examples"] = response["violating_examples"] - policy["compliance_examples"] = response["compliance_examples"] - - - save_output(self.out_dir, f"{tool_name}_merge_examples.json", tptd) - return tptd - - async def fix_examples(self, tool_name:str,tptd:dict) -> dict: - print("fix_examples") - orig_prompt = read_prompt_file("fix_example") - for policy in tptd["policies"]: - for etype in ["violating","compliance"]: - fixed_examples = [] - for example in policy[etype + "_examples"]: - system_prompt = orig_prompt.replace("ToolX", tool_name) - system_prompt = system_prompt.replace("__EXAMPLE_TYPE__", "") - - #user_content = f"Policy Document:{state['policy_text']}\nTools Descriptions:{json.dumps(state['tools'])}\nTarget Tool:{json.dumps(state['target_tool_description'])}\nPolicy Name:{policy['policy_name']}\nPolicy Description:{policy['description']}\nExample:{example}" - user_content = f"Tools Descriptions:{json.dumps(self.tools_descriptions)}\nTarget Tool:{self.tools_details[tool_name].model_dump_json()}\nPolicy Name:{policy['policy_name']}\nPolicy Description:{policy['description']}\nExample:{example}" - - response = await self.llm.chat_json(generate_messages(system_prompt, user_content)) - fixed_examples.append(response["revised_example"]) - policy[etype + "_examples"] = fixed_examples - - - save_output(self.out_dir, f"{tool_name}_fix_examples.json", tptd) - return tptd - - #todo: change to revew examples, write prompts - async def review_examples(self, tool_name:str,tptd:dict) -> dict: - print("review_examples") - system_prompt = read_prompt_file("examples_reviewer") - for policy in tptd["policies"]: - print(policy['policy_name']) - for etype in ["violating","compliance"]: - print(etype) - passed_examples = [] - for example in policy[etype + "_examples"]: - print(example) - reviews = [] - for iteration in range(5): - #user_content = f"Policy Document:{state['policy_text']}\nTools Descriptions:{json.dumps(state['tools'])}\nTarget Tool:{json.dumps(state['target_tool_description'])}\nPolicy Name:{policy['policy_name']}\nPolicy Description:{policy['description']}\nExample:{example}" - user_content = f"Tools Descriptions:{json.dumps(self.tools_descriptions)}\nTarget Tool:{self.tools_details[tool_name].model_dump_json()}\nPolicy Name:{policy['policy_name']}\nPolicy Description:{policy['description']}\nExample:{example}" - response = await self.llm.chat_json(generate_messages(system_prompt, user_content)) - reviews.append(response) - keep = self.keep_example(reviews) - if keep: - passed_examples.append(example) - - policy[etype + "_examples"] = passed_examples - - - save_output(self.out_dir, f"{tool_name}_example_rev.json", tptd) - return tptd - - def keep_example(self, reviews) -> bool: - bads = 0 - totals = 0 - for r in reviews: - for vals in r.values(): - totals+=1 - if "value" not in vals: - print(reviews) - elif not vals["value"]: - bads += 1 - if bads/totals > 0.8: - return False - return True - -async def extract_toolguard_specs(policy_text:str, tools:List[ToolInfo], step1_output_dir:str, llm:I_TG_LLM, tools_shortlist: Optional[List[str]]=None, short=False)->List[ToolGuardSpec]: - process_dir = os.path.join(step1_output_dir, "process") - if not os.path.isdir(process_dir): - os.makedirs(process_dir) - output_tool_policies = [] - tpg = ToolGuardSpecGenerator(llm, policy_text, tools, process_dir) - async def do_one_tool(tool_name): - if short: - final_output = await tpg.generate_minimal_policy(tool_name) - else: - final_output = await tpg.generate_policy(tool_name) - - with open(os.path.join(step1_output_dir, tool_name + ".json"), "w") as outfile1: - outfile1.write(json.dumps(final_output, indent=2)) - output_tool_policies.append(load_tool_policy(os.path.join(step1_output_dir, tool_name + ".json"), tool_name)) - - await asyncio.gather(*[do_one_tool(tool.name) for tool in tools if ((tools_shortlist is None) or (tool.name in tools_shortlist))]) - print("All tools done") - return output_tool_policies - + def __init__(self, llm:I_TG_LLM, policy_document:str, tools:List[ToolInfo], out_dir:str) -> None: + self.llm = llm + self.policy_document = policy_document + self.tools_descriptions = {tool.name: tool.description for tool in tools} + self.tools_details = {tool.name: tool for tool in tools} + self.out_dir = out_dir + + async def generate_minimal_policy(self, tool_name: str) -> dict: + tptd = await self.create_policy(tool_name) + tptd = await self.example_creator(tool_name, tptd) + return tptd + + async def generate_policy(self, tool_name: str) -> dict: + tptd = await self.create_policy(tool_name) + for i in range(3): + tptd = await self.add_policies(tool_name, tptd, i) + tptd = await self.split(tool_name, tptd) + tptd = await self.merge(tool_name, tptd) + tptd = await self.review_policy(tool_name, tptd) + tptd = await self.add_references(tool_name, tptd) + tptd = await self.reference_correctness(tool_name, tptd) + tptd = await self.example_creator(tool_name, tptd) + for i in range(5): # FIXME + tptd = await self.add_examples(tool_name, tptd, i) + tptd = await self.merge_examples(tool_name, tptd) + # tptd = self.fix_examples(tool_name, tptd) + tptd = await self.review_examples(tool_name, tptd) + return tptd + + async def create_policy(self, tool_name: str) -> dict: + print("policy_creator_node") + system_prompt = read_prompt_file("create_policy") + system_prompt = system_prompt.replace("ToolX", tool_name) + user_content = f"Policy Document:{self.policy_document}\nTools Descriptions:{json.dumps(self.tools_descriptions)}\nTarget Tool:{self.tools_details[tool_name].model_dump_json()}\n" + tptd = await self.llm.chat_json(generate_messages(system_prompt, user_content)) + save_output(self.out_dir, f"{tool_name}.json", tptd) + return tptd + + async def add_policies( + self, tool_name: str, tptd: dict, iteration: int = 0 + ) -> dict: + print("add_policy") + system_prompt = read_prompt_file("add_policies") + user_content = f"Policy Document:{self.policy_document}\nTools Descriptions:{json.dumps(self.tools_descriptions)}\nTarget Tool:{self.tools_details[tool_name].model_dump_json()}\nTPTD: {json.dumps(tptd)}" + response = await self.llm.chat_json( + generate_messages(system_prompt, user_content) + ) + + policies = ( + response["additionalProperties"]["policies"] + if "additionalProperties" in response and "policies" not in response + else response["policies"] + ) + + for policy in policies: + # for policy in response["policies"]: + policy["iteration"] = iteration + tptd["policies"].append(policy) + + save_output(self.out_dir, f"{tool_name}_ADD_{iteration}.json", tptd) + return tptd + + async def split(self, tool_name, tptd: dict) -> dict: + # todo: consider addition step to split policy by policy and not overall + print("split") + system_prompt = read_prompt_file("split") + user_content = f"Policy Document:{self.policy_document}\nTools Descriptions:{json.dumps(self.tools_descriptions)}\nTarget Tool:{self.tools_details[tool_name].model_dump_json()}\nTPTD: {json.dumps(tptd)}" + tptd = await self.llm.chat_json(generate_messages(system_prompt, user_content)) + save_output(self.out_dir, f"{tool_name}_split.json", tptd) + return tptd + + async def merge(self, tool_name, tptd: dict) -> dict: + # todo: consider addition step to split policy by policy and not overall + print("merge") + system_prompt = read_prompt_file("merge") + user_content = f"Policy Document:{self.policy_document}\nTools Descriptions:{json.dumps(self.tools_descriptions)}\nTarget Tool:{self.tools_details[tool_name].model_dump_json()}\nTPTD: {json.dumps(tptd)}" + tptd = await self.llm.chat_json(generate_messages(system_prompt, user_content)) + + save_output(self.out_dir, f"{tool_name}_merge.json", tptd) + return tptd + + def move2archive(self, reviews) -> (bool, str): + comments = "" + num = len(reviews) + if num == 0: + return False + counts = { + "is_relevant": 0, + "is_tool_specific": 0, + "can_be_validated": 0, + "is_actionable": 0, + } + + for r in reviews: + print( + f"{r['is_relevant'] if 'is_relevant' in r else ''}\t{r['is_tool_specific'] if 'is_tool_specific' in r else ''}\t{r['can_be_validated'] if 'can_be_validated' in r else ''}\t{r['is_actionable'] if 'is_actionable' in r else ''}\t{r['is_self_contained'] if 'is_self_contained' in r else ''}\t{r['score'] if 'score' in r else ''}\t" + ) + + counts["is_relevant"] += r["is_relevant"] if "is_relevant" in r else 0 + counts["is_tool_specific"] += ( + r["is_tool_specific"] if "is_tool_specific" in r else 0 + ) + counts["can_be_validated"] += ( + r["can_be_validated"] if "can_be_validated" in r else 0 + ) + counts["is_actionable"] += r["is_actionable"] if "is_actionable" in r else 0 + + if not all( + e in r + for e in [ + "is_relevant", + "is_tool_specific", + "can_be_validated", + "is_actionable", + ] + ) or not ( + r["is_relevant"] + and r["is_tool_specific"] + and r["can_be_validated"] + and r["is_actionable"] + ): + comments += r["comments"] + "\n" + + return not (all(float(counts[key]) / num > 0.5 for key in counts)), comments + + async def review_policy(self, tool_name, tptd) -> dict: + print("review_policy") + system_prompt = read_prompt_file("policy_reviewer") + newTPTD = {"policies": []} + + if "policies" not in tptd: + tptd["policies"] = [] + + for policy in tptd["policies"]: + reviews = [] + for _iteration in range(5): + user_content = f"Policy Document:{self.policy_document}\nTools Descriptions:{json.dumps(self.tools_descriptions)}\nTarget Tool:{json.dumps(self.tools_descriptions[tool_name])}\npolicy: {json.dumps(policy)}" + response = await self.llm.chat_json( + generate_messages(system_prompt, user_content) + ) + if "is_self_contained" in response: + is_self_contained = response["is_self_contained"] + if not is_self_contained: + if "alternative_description" in response: + policy["description"] = response["alternative_description"] + else: + print( + "Error: review is_self_contained is false but no alternative_description." + ) + else: + print("Error: review did not provide is_self_contained.") + reviews.append(response) + archive, comments = self.move2archive(reviews) + print(archive) + if archive: + if "archive" not in newTPTD: + newTPTD["archive"] = [] + policy["comments"] = comments + newTPTD["archive"].append(policy) + else: + newTPTD["policies"].append(policy) + save_output(self.out_dir, f"{tool_name}_rev.json", newTPTD) + return newTPTD + + async def add_references(self, tool_name: str, tptd: dict) -> dict: + print("add_ref") + system_prompt = read_prompt_file("add_references") + # remove old refs (used to help avoid duplications) + for policy in tptd["policies"]: + policy["references"] = [] + user_content = f"Policy Document:{self.policy_document}\nTools Descriptions:{json.dumps(self.tools_descriptions)}\nTarget Tool:{self.tools_details[tool_name].model_dump_json()}\npolicy: {json.dumps(policy)}" + response = await self.llm.chat_json( + generate_messages(system_prompt, user_content) + ) + if "references" in response: + policy["references"] = response["references"] + else: + print("Error! no references in response") + print(response) + + save_output(self.out_dir, f"{tool_name}_ref.json", tptd) + return tptd + + async def reference_correctness(self, tool_name: str, tptd: dict) -> dict: + print("reference_correctness") + tptd, unmatched_policies = find_mismatched_references( + self.policy_document, tptd + ) + save_output(self.out_dir, f"{tool_name}_ref_orig_.json", unmatched_policies) + save_output(self.out_dir, f"{tool_name}_ref_correction_.json", tptd) + return tptd + + async def example_creator(self, tool_name: str, tptd: dict) -> dict: + print("example_creator") + system_prompt = read_prompt_file("create_examples") + system_prompt = system_prompt.replace("ToolX", tool_name) + + for policy in tptd["policies"]: + # user_content = f"Policy Document:{state['policy_text']}\nTools Descriptions:{json.dumps(state['tools'])}\nTarget Tool:{json.dumps(state['target_tool_description'])}\nPolicy:{policy}" + user_content = f"Tools Descriptions:{json.dumps(self.tools_descriptions)}\nTarget Tool:{self.tools_details[tool_name].model_dump_json()}\nPolicy:{policy}" + + response = await self.llm.chat_json( + generate_messages(system_prompt, user_content) + ) + if "violating_examples" in response: + policy["violating_examples"] = response["violating_examples"] + + if "compliance_examples" in response: + policy["compliance_examples"] = response["compliance_examples"] + + save_output(self.out_dir, f"{tool_name}_examples.json", tptd) + return tptd + + async def add_examples(self, tool_name: str, tptd: dict, iteration: int) -> dict: + print("add_examples") + system_prompt = read_prompt_file("add_examples") + system_prompt = system_prompt.replace("ToolX", tool_name) + for policy in tptd["policies"]: + # user_content = f"Policy Document:{state['policy_text']}\nTools Descriptions:{json.dumps(state['tools'])}\nTarget Tool:{json.dumps(state['target_tool_description'])}\nPolicy:{policy}" + user_content = f"Tools Descriptions:{json.dumps(self.tools_descriptions)}\nTarget Tool:{self.tools_details[tool_name].model_dump_json()}\nPolicy:{policy}" + response = await self.llm.chat_json( + generate_messages(system_prompt, user_content) + ) + if "violating_examples" in response: + for vexample in response["violating_examples"]: + # vexample["iteration"] = state["iteration"] + if "violating_examples" not in policy: + policy["violating_examples"] = [] + policy["violating_examples"].append(vexample) + if "compliance_examples" in response: + for cexample in response["compliance_examples"]: + if "compliance_examples" not in policy: + policy["compliance_examples"] = [] + # cexample["iteration"] = state["iteration"] + policy["compliance_examples"].append(cexample) + + save_output(self.out_dir, f"{tool_name}_ADD_examples{iteration}.json", tptd) + return tptd + + async def merge_examples(self, tool_name: str, tptd: dict) -> dict: + print("merge_examples") + system_prompt = read_prompt_file("merge_examples") + system_prompt = system_prompt.replace("ToolX", tool_name) + for policy in tptd["policies"]: + # user_content = f"Policy Document:{state['policy_text']}\nTools Descriptions:{json.dumps(state['tools'])}\nTarget Tool:{json.dumps(state['target_tool_description'])}\nPolicy Name:{policy['policy_name']}\nPolicy Description:{policy['description']}" + user_content = f"Tools Descriptions:{json.dumps(self.tools_descriptions)}\nTarget Tool:{self.tools_details[tool_name].model_dump_json()}\nPolicy Name:{policy['policy_name']}\nPolicy Description:{policy['description']}" + user_content += f"\n\nViolating Examples: {policy['violating_examples']}" + user_content += f"\n\nCompliance Examples: {policy['compliance_examples']}" + response = await self.llm.chat_json( + generate_messages(system_prompt, user_content) + ) + policy["violating_examples"] = response["violating_examples"] + policy["compliance_examples"] = response["compliance_examples"] + + save_output(self.out_dir, f"{tool_name}_merge_examples.json", tptd) + return tptd + + async def fix_examples(self, tool_name: str, tptd: dict) -> dict: + print("fix_examples") + orig_prompt = read_prompt_file("fix_example") + for policy in tptd["policies"]: + for etype in ["violating", "compliance"]: + fixed_examples = [] + for example in policy[etype + "_examples"]: + system_prompt = orig_prompt.replace("ToolX", tool_name) + system_prompt = system_prompt.replace("__EXAMPLE_TYPE__", "") + + # user_content = f"Policy Document:{state['policy_text']}\nTools Descriptions:{json.dumps(state['tools'])}\nTarget Tool:{json.dumps(state['target_tool_description'])}\nPolicy Name:{policy['policy_name']}\nPolicy Description:{policy['description']}\nExample:{example}" + user_content = f"Tools Descriptions:{json.dumps(self.tools_descriptions)}\nTarget Tool:{self.tools_details[tool_name].model_dump_json()}\nPolicy Name:{policy['policy_name']}\nPolicy Description:{policy['description']}\nExample:{example}" + + response = await self.llm.chat_json( + generate_messages(system_prompt, user_content) + ) + fixed_examples.append(response["revised_example"]) + policy[etype + "_examples"] = fixed_examples + + save_output(self.out_dir, f"{tool_name}_fix_examples.json", tptd) + return tptd + + # todo: change to revew examples, write prompts + async def review_examples(self, tool_name: str, tptd: dict) -> dict: + print("review_examples") + system_prompt = read_prompt_file("examples_reviewer") + for policy in tptd["policies"]: + print(policy["policy_name"]) + for etype in ["violating", "compliance"]: + print(etype) + passed_examples = [] + for example in policy[etype + "_examples"]: + print(example) + reviews = [] + for _iteration in range(5): + # user_content = f"Policy Document:{state['policy_text']}\nTools Descriptions:{json.dumps(state['tools'])}\nTarget Tool:{json.dumps(state['target_tool_description'])}\nPolicy Name:{policy['policy_name']}\nPolicy Description:{policy['description']}\nExample:{example}" + user_content = f"Tools Descriptions:{json.dumps(self.tools_descriptions)}\nTarget Tool:{self.tools_details[tool_name].model_dump_json()}\nPolicy Name:{policy['policy_name']}\nPolicy Description:{policy['description']}\nExample:{example}" + response = await self.llm.chat_json( + generate_messages(system_prompt, user_content) + ) + reviews.append(response) + keep = self.keep_example(reviews) + if keep: + passed_examples.append(example) + + policy[etype + "_examples"] = passed_examples + + save_output(self.out_dir, f"{tool_name}_example_rev.json", tptd) + return tptd + + def keep_example(self, reviews) -> bool: + bads = 0 + totals = 0 + for r in reviews: + for vals in r.values(): + totals += 1 + if "value" not in vals: + print(reviews) + elif not vals["value"]: + bads += 1 + if bads / totals > 0.8: + return False + return True + + +async def extract_toolguard_specs( + policy_text: str, + tools: List[ToolInfo], + step1_output_dir: str, + llm: I_TG_LLM, + tools_shortlist: Optional[List[str]] = None, + short=False, +) -> List[ToolGuardSpec]: + if not os.path.isdir(step1_output_dir): + os.makedirs(step1_output_dir) + + process_dir = os.path.join(step1_output_dir, "process") + if not os.path.isdir(process_dir): + os.makedirs(process_dir) + output_tool_policies = [] + tpg = ToolGuardSpecGenerator(llm, policy_text, tools, process_dir) + + async def do_one_tool(tool_name): + if short: + final_output = await tpg.generate_minimal_policy(tool_name) + else: + final_output = await tpg.generate_policy(tool_name) + + with open(os.path.join(step1_output_dir, tool_name + ".json"), "w") as outfile1: + outfile1.write(json.dumps(final_output, indent=2)) + output_tool_policies.append( + load_tool_policy( + os.path.join(step1_output_dir, tool_name + ".json"), tool_name + ) + ) + + await asyncio.gather( + *[ + do_one_tool(tool.name) + for tool in tools + if ((tools_shortlist is None) or (tool.name in tools_shortlist)) + ] + ) + print("All tools done") + return output_tool_policies \ No newline at end of file diff --git a/altk/pre_tool/toolguard/toolguard/gen_spec/utils.py b/altk/pre_tool/toolguard/toolguard/gen_spec/utils.py index dfdc9be..c40641e 100644 --- a/altk/pre_tool/toolguard/toolguard/gen_spec/utils.py +++ b/altk/pre_tool/toolguard/toolguard/gen_spec/utils.py @@ -2,84 +2,95 @@ import os import json -from typing import List, Optional +from typing import List def read_prompt_file(filename: str) -> str: - with open(os.path.join(os.path.dirname(__file__), "prompts", filename + ".txt"), "r") as f: - return f.read() + with open( + os.path.join(os.path.dirname(__file__), "prompts", filename + ".txt"), "r" + ) as f: + return f.read() def generate_messages(system_prompt: str, user_content: str) -> List[Dict[str, str]]: - return [ - {"role": "system", "content": system_prompt}, - {"role": "user", "content": user_content} - ] + return [ + {"role": "system", "content": system_prompt}, + {"role": "user", "content": user_content}, + ] + def save_output(outdir: str, filename: str, content: Any): - with open(os.path.join(outdir, filename), "w") as outfile: - json.dump(content, outfile, indent=4) + with open(os.path.join(outdir, filename), "w") as outfile: + json.dump(content, outfile, indent=4) def normalize_text(text): - """Normalize text by removing punctuation, converting to lowercase, and standardizing spaces.""" - #return re.sub(r'\s+', ' ', re.sub(r'[^a-zA-Z0-9\s]', '', text)).strip().lower() - return text.lower() + """Normalize text by removing punctuation, converting to lowercase, and standardizing spaces.""" + # return re.sub(r'\s+', ' ', re.sub(r'[^a-zA-Z0-9\s]', '', text)).strip().lower() + return text.lower() + def split_reference_if_both_parts_exist(reference, policy_text): - words = reference.split() - for split_point in range(1, len(words)): - part1 = ' '.join(words[:split_point]) - part2 = ' '.join(words[split_point:]) + words = reference.split() + for split_point in range(1, len(words)): + part1 = " ".join(words[:split_point]) + part2 = " ".join(words[split_point:]) - normalized_part1 = normalize_text(part1) - normalized_part2 = normalize_text(part2) - normalized_policy_text = normalize_text(policy_text) + normalized_part1 = normalize_text(part1) + normalized_part2 = normalize_text(part2) + normalized_policy_text = normalize_text(policy_text) - if normalized_part1 in normalized_policy_text and normalized_part2 in normalized_policy_text: - start_idx1 = normalized_policy_text.find(normalized_part1) - end_idx1 = start_idx1 + len(part1) - start_idx2 = normalized_policy_text.find(normalized_part2) - end_idx2 = start_idx2 + len(part2) - return [policy_text[start_idx1:end_idx1], policy_text[start_idx2:end_idx2]] - return None + if ( + normalized_part1 in normalized_policy_text + and normalized_part2 in normalized_policy_text + ): + start_idx1 = normalized_policy_text.find(normalized_part1) + end_idx1 = start_idx1 + len(part1) + start_idx2 = normalized_policy_text.find(normalized_part2) + end_idx2 = start_idx2 + len(part2) + return [policy_text[start_idx1:end_idx1], policy_text[start_idx2:end_idx2]] + return None def find_mismatched_references(policy_text, policy_json): - corrections = json.loads(json.dumps(policy_json)) - unmatched_policies = [] - if isinstance(corrections["policies"], str): - return corrections, unmatched_policies - - normalized_policy_text = normalize_text(policy_text) - - for policy in corrections["policies"]: - corrected_references = [] - has_unmatched = False - - for reference in policy["references"]: - normalized_ref = normalize_text(reference) - # if normalized ref in policy doc- just copy the original - if normalized_ref in normalized_policy_text: - start_idx = normalized_policy_text.find(normalized_ref) - end_idx = start_idx + len(reference) - corrected_references.append(policy_text[start_idx:end_idx]) - else: - # close_match = get_close_matches(normalized_ref, [normalized_policy_text], n=1, cutoff=0.9) - # if close_match: - # start_idx = normalized_policy_text.find(close_match[0]) - # end_idx = start_idx + len(close_match[0]) - # corrected_references.append(policy_text[start_idx:end_idx]) - # else: - split_segments = split_reference_if_both_parts_exist(reference, policy_text) - if split_segments: - corrected_references.extend(split_segments) - else: - corrected_references.append(reference) # Keep original if no match found - has_unmatched = True - - policy["references"] = corrected_references - if has_unmatched: - unmatched_policies.append(policy["policy_name"]) - - return corrections, unmatched_policies \ No newline at end of file + corrections = json.loads(json.dumps(policy_json)) + unmatched_policies = [] + if isinstance(corrections["policies"], str): + return corrections, unmatched_policies + + normalized_policy_text = normalize_text(policy_text) + + for policy in corrections["policies"]: + corrected_references = [] + has_unmatched = False + + for reference in policy["references"]: + normalized_ref = normalize_text(reference) + # if normalized ref in policy doc- just copy the original + if normalized_ref in normalized_policy_text: + start_idx = normalized_policy_text.find(normalized_ref) + end_idx = start_idx + len(reference) + corrected_references.append(policy_text[start_idx:end_idx]) + else: + # close_match = get_close_matches(normalized_ref, [normalized_policy_text], n=1, cutoff=0.9) + # if close_match: + # start_idx = normalized_policy_text.find(close_match[0]) + # end_idx = start_idx + len(close_match[0]) + # corrected_references.append(policy_text[start_idx:end_idx]) + # else: + split_segments = split_reference_if_both_parts_exist( + reference, policy_text + ) + if split_segments: + corrected_references.extend(split_segments) + else: + corrected_references.append( + reference + ) # Keep original if no match found + has_unmatched = True + + policy["references"] = corrected_references + if has_unmatched: + unmatched_policies.append(policy["policy_name"]) + + return corrections, unmatched_policies \ No newline at end of file diff --git a/altk/pre_tool/toolguard/toolguard/runtime.py b/altk/pre_tool/toolguard/toolguard/runtime.py index 876067e..d7a88a3 100644 --- a/altk/pre_tool/toolguard/toolguard/runtime.py +++ b/altk/pre_tool/toolguard/toolguard/runtime.py @@ -1,3 +1,4 @@ +import importlib import inspect import json import os diff --git a/altk/pre_tool/toolguard/toolguard_code_component.py b/altk/pre_tool/toolguard/toolguard_code_component.py index c5a819e..1be9d5f 100644 --- a/altk/pre_tool/toolguard/toolguard_code_component.py +++ b/altk/pre_tool/toolguard/toolguard_code_component.py @@ -5,7 +5,7 @@ from typing import Set from .toolguard.data_types import MeleaSessionData -from altk.toolkit_core.core.toolkit import ComponentConfig, ComponentInput, AgentPhase, ComponentBase +from altk.core.toolkit import ComponentConfig, ComponentInput, AgentPhase, ComponentBase from .toolguard import generate_guards_from_specs, ToolGuardSpec, ToolGuardsCodeGenerationResult, load_toolguards from .toolguard.runtime import IToolInvoker, ToolGuardsCodeGenerationResult diff --git a/altk/pre_tool/toolguard/toolguard_spec_component.py b/altk/pre_tool/toolguard/toolguard_spec_component.py index ccae219..d2251c4 100644 --- a/altk/pre_tool/toolguard/toolguard_spec_component.py +++ b/altk/pre_tool/toolguard/toolguard_spec_component.py @@ -4,8 +4,8 @@ from langchain.tools import BaseTool from pydantic import Field -from ..toolkit_core.llm import ValidatingLLMClient -from ..toolkit_core.core.toolkit import AgentPhase, ComponentBase, ComponentConfig, ComponentInput +from ...core.llm import ValidatingLLMClient +from ...core.toolkit import AgentPhase, ComponentBase, ComponentConfig, ComponentInput from .toolguard import ToolGuardSpec, generate_guard_specs from .toolguard.llm.i_tg_llm import I_TG_LLM diff --git a/tests/pre_tool/toolguard/test_toolguard_code.py b/tests/pre_tool/toolguard/test_toolguard_code.py index d46393b..a6c70da 100644 --- a/tests/pre_tool/toolguard/test_toolguard_code.py +++ b/tests/pre_tool/toolguard/test_toolguard_code.py @@ -18,26 +18,26 @@ import dotenv import pytest -from altk.pre_tool_guard_toolkit import ( +from altk.pre_tool.toolguard import ( ToolGuardCodeComponent, ToolGuardCodeBuildInput, ) -from altk.pre_tool_guard_toolkit.toolguard.data_types import ( +from altk.pre_tool.toolguard.toolguard.data_types import ( MeleaSessionData, load_tool_policy, ) -from altk.pre_tool_guard_toolkit.toolguard.runtime import ( +from altk.pre_tool.toolguard.toolguard.runtime import ( ToolFunctionsInvoker, ToolGuardsCodeGenerationResult, load_toolguard_code_result, ) -from altk.pre_tool_guard_toolkit.toolguard_code_component import ( +from altk.pre_tool.toolguard.toolguard_code_component import ( ToolGuardCodeComponentConfig, ToolGuardCodeRunInput, ToolGuardCodeRunOutput, ViolationLevel, ) -from altk.toolkit_core.core.toolkit import AgentPhase +from altk.core.toolkit import AgentPhase # The calculator tools under test from .inputs.tool_functions import ( diff --git a/tests/pre_tool/toolguard/test_toolguard_specs.py b/tests/pre_tool/toolguard/test_toolguard_specs.py index 7157d6f..e9585ba 100644 --- a/tests/pre_tool/toolguard/test_toolguard_specs.py +++ b/tests/pre_tool/toolguard/test_toolguard_specs.py @@ -8,14 +8,14 @@ import dotenv import pytest -from altk.pre_tool_guard_toolkit.toolguard_spec_component import ( +from altk.pre_tool.toolguard.toolguard_spec_component import ( ToolGuardSpecBuildInput, ToolGuardSpecComponent, ToolGuardSpecComponentConfig, ToolGuardSpecs, ) -from altk.toolkit_core.core.toolkit import AgentPhase -from altk.toolkit_core.llm.base import get_llm +from altk.core.toolkit import AgentPhase +from altk.core.llm.base import get_llm from .inputs.tool_functions import ( divide_tool, @@ -66,20 +66,20 @@ async def test_tool_guard_calculator_policy(work_dir: str): """ # Example alternative LLM: - # LLMClient = get_llm("litellm.output_val") - # llm_client = LLMClient( - # model_name="gpt-4o-2024-08-06", - # custom_llm_provider="azure", - # ) - - LLMClient = get_llm("watsonx.output_val") + LLMClient = get_llm("litellm.output_val") llm_client = LLMClient( - model_name="mistralai/mistral-medium-2505", - api_key=os.getenv("WX_API_KEY"), - project_id=os.getenv("WX_PROJECT_ID"), - url=os.getenv("WX_URL", "https://us-south.ml.cloud.ibm.com"), + model_name="gpt-4o-2024-08-06", + custom_llm_provider="azure", ) + # LLMClient = get_llm("watsonx.output_val") + # llm_client = LLMClient( + # model_name="mistralai/mistral-medium-2505", + # api_key=os.getenv("WX_API_KEY"), + # project_id=os.getenv("WX_PROJECT_ID"), + # url=os.getenv("WX_URL", "https://us-south.ml.cloud.ibm.com"), + # ) + toolguard_spec = ToolGuardSpecComponent( ToolGuardSpecComponentConfig(llm_client=llm_client) ) From 7e775784dd5094b6f66c6e8989199d91a0048636 Mon Sep 17 00:00:00 2001 From: DAVID BOAZ Date: Wed, 3 Dec 2025 16:48:33 +0200 Subject: [PATCH 03/42] toolguard readme --- altk/pre_tool/toolguard/README.md | 66 +++++++++++++++---- altk/pre_tool/toolguard/toolguard/runtime.py | 2 +- .../toolguard/toolguard_code_component.py | 9 +-- .../toolguard/toolguard_spec_component.py | 6 +- .../toolguard/test_toolguard_specs.py | 6 +- 5 files changed, 64 insertions(+), 25 deletions(-) diff --git a/altk/pre_tool/toolguard/README.md b/altk/pre_tool/toolguard/README.md index 7a6680c..bde27df 100644 --- a/altk/pre_tool/toolguard/README.md +++ b/altk/pre_tool/toolguard/README.md @@ -3,14 +3,14 @@ An agent lifecycle solution for enforcing business policy adherence in agentic w ## Table of Contents - [Overview](#overview) -- [When to Use This Component](#when-it-is-recommended-to-use-this-component) -- [LLM Configuration Requirements](#llm-configuration-requirements) -- [Quick Start](#quick-start) -- [Parameters](#parameters) - - [Constructor Parameters](#constructor-parameters) - - [Build Phase Input Format](#build-phase-input-format) - - [Run Phase Input Format](#run-phase-input-format) - - [Run Phase Output Format](#run-phase-output-format) +- [ToolGuardSpecComponent](#ToolGuardSpecComponent) + - [Configuarion](#component-configuarion) + - [Inputs and Outputs](#input-and-output) + - [Usage example](#usage-example) +- [ToolGuardCodeComponent](#ToolGuardCodeComponent) + - [Configuarion](#component-configuarion-1) + - [Inputs and Outputs](#input-and-output-1) + - [Usage example](#usage-example-1) @@ -28,9 +28,6 @@ The specifications are aimed to be used as input into our next component - the ` The two components are not concatenated by design. As the geneartion involves a non-deterministic language model, the results need to be reviewed by a human. Hence, the output specification files should be reviewed and optionaly edited. For example, removing a wrong compliance example. -### Usage example -see [simple calculator test](../../tests/pre_tool_guard_toolkit/test_toolguard_specs.py) - ### Component Configuarion This component expects an LLM client configuarion: ```python @@ -61,6 +58,19 @@ llm_client = LLMClient( ) ``` + +### Input and Output +The component build input is a `ToolGuardSpecBuildInput` object containing the following fields: + * `policy_text: str`: Text of the policy document + * `tools: List[Callable] | List[BaseTool] | str`: List of available tools. Either as Python functions, methods, Langgraph Tools, or a path to an Open API specification file. + * `out_dir: str`: A directory in the local file system where the specification objects will be saved. + +The component build output is a list of `ToolGuardSpec`, as described above. + +### Usage example +see [simple calculator test](../../../tests/pre_tool/toolguard/test_toolguard_specs.py) + + ## ToolGuardCodeComponent This components enfoorces policy adherence through a two-phase process: @@ -72,9 +82,6 @@ Similar to ToolGuard Specifications, generated `ToolGuards` are a good start, bu The ToolGuards checks if a planned action complies with the policy. If it violates, the agent is prompted to self-reflect and revise its plan before proceeding. -### Usage example -see [simple calculator test](../../tests/pre_tool_guard_toolkit/test_toolguard_code.py) - ### Component Configuarion Backed by Mellea, which requires parameters aligning to: @@ -99,3 +106,34 @@ export TOOLGUARD_GENPY_BACKEND_NAME="openai" export TOOLGUARD_GENPY_MODEL_ID="GCP/claude-4-sonnet" export TOOLGUARD_GENPY_ARGS='{"base_url":"https://your-litellm-endpoint","api_key":""}' ``` + +### Input and Output +The Component has two phases: +#### Build phase +An agent owner should use this API to generate ToolGuards - Python function that enforce the given business policy. +The input of the build phase is a `ToolGuardCodeBuildInput` object, containing: + * `tools: List[Callable] | List[BaseTool] | str`: List of available tools. Either as Python functions, methods, Langgraph Tools, or a path to an Open API specification file. + * `toolguard_specs: List[ToolGuardSpec]`: List of specifications, optionaly generated by `ToolGuardSpecComponent` component and reviewed. + * `out_dir: str`: A directory in the local file system where the ToolGuard objects will be saved. + +The output of the build phase is a `ToolGuardsCodeGenerationResult` object with: + * `out_dir: str`: Path to the file system where the results were saved. It is the same as the `input.out_dir`. + * `domain: RuntimeDomain`: A complex object descibing the generated APIs. For example, refernces to Python file names and class names. + * `tools: Dict[str, ToolGuardCodeResult]`: A Dictionary of the ToolGuardsResults, by the tool names. + * Each `ToolGuardCodeResult` details the name of guard Python file name and the guard function name. It also reference to the generated unit test files. + +#### Runtime phase +A running agent should use the runtime API to check if a tool call complies with the given policy. +The input of the runtime phase is a `ToolGuardCodeRunInput` object: + * `generated_guard_dir: str`: Path in the local file system where the generated guard Python code (The code that was generated during the build time, described above) is located. + * `tool_name: str`: The name of the tool that the agent is about to call + * `tool_args: Dict[str, Any]`: A dictionary of the toolcall arguments, by the argument name. + * `tool_invoker: IToolInvoker`: A proxy object that enables the guard to call other read-only tools. This is needed when the policy enforcement logic involves getting data from another tool. For example, before booking a flight, you need to check the flight status by calling the "get_flight_status" API. + +The outpput of the runtime phase is a `ToolGuardCodeRunOutput` object with an optional `violation` field. + * `violation: PolicyViolation | None`: Polpulated only if a violation was identified. If the toolcall complies with the policy, the violation is None. + * `violation_level: "info" | "warn" | "error"`: Severity level of a safety violation. + * `user_message: str | None`: A meaningful error message to the user (this message can be also passed to the agent reasoning phase to find an alternative next action). + +### Usage example +see [simple calculator test](../../../tests/pre_tool/toolguard/test_toolguard_code.py) \ No newline at end of file diff --git a/altk/pre_tool/toolguard/toolguard/runtime.py b/altk/pre_tool/toolguard/toolguard/runtime.py index d7a88a3..e27c038 100644 --- a/altk/pre_tool/toolguard/toolguard/runtime.py +++ b/altk/pre_tool/toolguard/toolguard/runtime.py @@ -40,7 +40,7 @@ class ToolGuardCodeResult(BaseModel): class ToolGuardsCodeGenerationResult(BaseModel): - root_dir: str + out_dir: str domain: RuntimeDomain tools: Dict[str, ToolGuardCodeResult] diff --git a/altk/pre_tool/toolguard/toolguard_code_component.py b/altk/pre_tool/toolguard/toolguard_code_component.py index 1be9d5f..c01057d 100644 --- a/altk/pre_tool/toolguard/toolguard_code_component.py +++ b/altk/pre_tool/toolguard/toolguard_code_component.py @@ -1,11 +1,12 @@ import logging -from typing import Callable, List, cast +from typing import Any, Callable, Dict, List, cast from enum import Enum from pydantic import BaseModel, Field from typing import Set +from langchain.tools import BaseTool -from .toolguard.data_types import MeleaSessionData from altk.core.toolkit import ComponentConfig, ComponentInput, AgentPhase, ComponentBase +from .toolguard.data_types import MeleaSessionData from .toolguard import generate_guards_from_specs, ToolGuardSpec, ToolGuardsCodeGenerationResult, load_toolguards from .toolguard.runtime import IToolInvoker, ToolGuardsCodeGenerationResult @@ -15,7 +16,7 @@ class ToolGuardCodeComponentConfig(ComponentConfig): llm_config: MeleaSessionData class ToolGuardCodeBuildInput(ComponentInput): - tools: List[Callable] | str + tools: List[Callable] | List[BaseTool] | str toolguard_specs: List[ToolGuardSpec] out_dir: str @@ -24,7 +25,7 @@ class ToolGuardCodeBuildInput(ComponentInput): class ToolGuardCodeRunInput(ComponentInput): generated_guard_dir: str tool_name: str = Field(description="Tool name") - tool_args: dict = Field(default={}, description="Tool arguments") + tool_args: Dict[str, Any] = Field(default={}, description="Tool arguments") tool_invoker: IToolInvoker model_config = { diff --git a/altk/pre_tool/toolguard/toolguard_spec_component.py b/altk/pre_tool/toolguard/toolguard_spec_component.py index d2251c4..c830055 100644 --- a/altk/pre_tool/toolguard/toolguard_spec_component.py +++ b/altk/pre_tool/toolguard/toolguard_spec_component.py @@ -17,7 +17,7 @@ class ToolGuardSpecComponentConfig(ComponentConfig): class ToolGuardSpecBuildInput(ComponentInput): policy_text: str = Field(description="Text of the policy document file") tools: List[Callable] | List[BaseTool] | str - work_dir: str + out_dir: str ToolGuardSpecs=List[ToolGuardSpec] @@ -34,11 +34,11 @@ def _build(self, data: ToolGuardSpecBuildInput) -> ToolGuardSpecs: raise NotImplementedError("Please use the aprocess() function in an async context") async def _abuild(self, data: ToolGuardSpecBuildInput) -> ToolGuardSpecs: - os.makedirs(data.work_dir, exist_ok=True) + os.makedirs(data.out_dir, exist_ok=True) return await generate_guard_specs( policy_text=data.policy_text, tools=data.tools, - work_dir=data.work_dir, + work_dir=data.out_dir, llm=TG_LLMEval(self.config.llm_client) ) diff --git a/tests/pre_tool/toolguard/test_toolguard_specs.py b/tests/pre_tool/toolguard/test_toolguard_specs.py index e9585ba..a909e69 100644 --- a/tests/pre_tool/toolguard/test_toolguard_specs.py +++ b/tests/pre_tool/toolguard/test_toolguard_specs.py @@ -32,7 +32,7 @@ # Fixtures # --------------------------------------------------------------------------- @pytest.fixture -def work_dir(): +def out_dir(): """ Create a timestamped directory for test output, then delete it after the test. """ @@ -50,7 +50,7 @@ def work_dir(): # Main Test # --------------------------------------------------------------------------- @pytest.mark.asyncio -async def test_tool_guard_calculator_policy(work_dir: str): +async def test_tool_guard_calculator_policy(out_dir: str): funcs = [ divide_tool, add_tool, @@ -87,7 +87,7 @@ async def test_tool_guard_calculator_policy(work_dir: str): input_data = ToolGuardSpecBuildInput( policy_text=policy_text, tools=funcs, - work_dir=work_dir, + out_dir=out_dir, ) specs = cast( From 86d252f0f1be838b2a1bc38c8b4b9cd2c459679b Mon Sep 17 00:00:00 2001 From: DAVID BOAZ Date: Wed, 3 Dec 2025 17:09:02 +0200 Subject: [PATCH 04/42] readme --- altk/pre_tool/toolguard/README.md | 10 ++++++++++ altk/pre_tool/toolguard/toolguard/common/safe_py.py | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/altk/pre_tool/toolguard/README.md b/altk/pre_tool/toolguard/README.md index bde27df..707ccde 100644 --- a/altk/pre_tool/toolguard/README.md +++ b/altk/pre_tool/toolguard/README.md @@ -129,6 +129,16 @@ The input of the runtime phase is a `ToolGuardCodeRunInput` object: * `tool_name: str`: The name of the tool that the agent is about to call * `tool_args: Dict[str, Any]`: A dictionary of the toolcall arguments, by the argument name. * `tool_invoker: IToolInvoker`: A proxy object that enables the guard to call other read-only tools. This is needed when the policy enforcement logic involves getting data from another tool. For example, before booking a flight, you need to check the flight status by calling the "get_flight_status" API. + The `IToolInvoker` interface contains a single method: + ``` + def invoke(self, toolname: str, arguments: Dict[str, Any], return_type: Type[T]) -> T + ``` + + ToolGuard library currently ships with three predefined ToolInvokers: + * `toolguard.runtime.ToolFunctionsInvoker(funcs: List[Callable])` where the tools are defined as plain global Python functions. + * `toolguard.runtime.ToolMethodsInvoker(obj: object)` where the tools are defined as methods in a given Python object. + * `toolguard.runtime.LangchainToolInvoker(tools: List[BaseTool])` where the tools are a list of langchain tools. + The outpput of the runtime phase is a `ToolGuardCodeRunOutput` object with an optional `violation` field. * `violation: PolicyViolation | None`: Polpulated only if a violation was identified. If the toolcall complies with the policy, the violation is None. diff --git a/altk/pre_tool/toolguard/toolguard/common/safe_py.py b/altk/pre_tool/toolguard/toolguard/common/safe_py.py index 90b690c..3e44218 100644 --- a/altk/pre_tool/toolguard/toolguard/common/safe_py.py +++ b/altk/pre_tool/toolguard/toolguard/common/safe_py.py @@ -1,7 +1,7 @@ from typing import List +from smolagents.local_python_executor import LocalPythonExecutor def run_safe_python(code: str, libs: List[str] = []): - from smolagents.local_python_executor import LocalPythonExecutor exec = LocalPythonExecutor( additional_authorized_imports = libs, max_print_outputs_length= None, From 91936c9de375b6525d36fe98562257f280ae7694 Mon Sep 17 00:00:00 2001 From: DAVID BOAZ Date: Wed, 3 Dec 2025 17:22:37 +0200 Subject: [PATCH 05/42] DCO Remediation Commit for DAVID BOAZ I, DAVID BOAZ , hereby add my Signed-off-by to this commit: 3ac6e7afbb92cc3520b0c8f82390e7a659849001 I, DAVID BOAZ , hereby add my Signed-off-by to this commit: ec34c8b6a28f5ae776ac2a78f0dc0c4c5fed5e73 I, DAVID BOAZ , hereby add my Signed-off-by to this commit: 7e775784dd5094b6f66c6e8989199d91a0048636 I, DAVID BOAZ , hereby add my Signed-off-by to this commit: 86d252f0f1be838b2a1bc38c8b4b9cd2c459679b Signed-off-by: DAVID BOAZ --- altk/pre_tool/toolguard/README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/altk/pre_tool/toolguard/README.md b/altk/pre_tool/toolguard/README.md index 707ccde..f09d460 100644 --- a/altk/pre_tool/toolguard/README.md +++ b/altk/pre_tool/toolguard/README.md @@ -1,5 +1,5 @@ # ToolGuards for Enforcing Agentic Policy Adherence -An agent lifecycle solution for enforcing business policy adherence in agentic workflows. Enabling this component has demonstrated up to a **20‑point improvement** in end‑to‑end agent accuracy when invoking tools. +An agent lifecycle solution for enforcing business policy adherence in agentic workflows. Enabling this component has demonstrated up to a **20‑point improvement** in end‑to‑end agent accuracy when invoking tools. This work is described in [Towards Enforcing Company Policy Adherence in Agentic Workflows](https://arxiv.org/pdf/2507.16459). ## Table of Contents - [Overview](#overview) @@ -13,7 +13,6 @@ An agent lifecycle solution for enforcing business policy adherence in agentic w - [Usage example](#usage-example-1) - ## Overview Business policies (or guidelines) are normally detailed in company documents, and have traditionally been hard-coded into automatic assistant platforms. Contemporary agentic approaches take the "best-effort" strategy, where the policies are appended to the agent's system prompt, an inherently non-deterministic approach, that does not scale effectively. Here we propose a deterministic, predictable and interpretable two-phase solution for agentic policy adherence at the tool-level: guards are executed prior to function invocation and raise alerts in case a tool-related policy deem violated. From 640ea37bd02cfaa480778501dcd56c5ec9b0b7b1 Mon Sep 17 00:00:00 2001 From: DAVID BOAZ Date: Wed, 3 Dec 2025 17:27:57 +0200 Subject: [PATCH 06/42] DCO Remediation Commit for DAVID BOAZ I, DAVID BOAZ , hereby add my Signed-off-by to this commit: 3ac6e7afbb92cc3520b0c8f82390e7a659849001 I, DAVID BOAZ , hereby add my Signed-off-by to this commit: ec34c8b6a28f5ae776ac2a78f0dc0c4c5fed5e73 I, DAVID BOAZ , hereby add my Signed-off-by to this commit: 7e775784dd5094b6f66c6e8989199d91a0048636 I, DAVID BOAZ , hereby add my Signed-off-by to this commit: 86d252f0f1be838b2a1bc38c8b4b9cd2c459679b I, DAVID BOAZ , hereby add my Signed-off-by to this commit: 91936c9de375b6525d36fe98562257f280ae7694 Signed-off-by: DAVID BOAZ --- altk/pre_tool/toolguard/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/altk/pre_tool/toolguard/README.md b/altk/pre_tool/toolguard/README.md index f09d460..5de8725 100644 --- a/altk/pre_tool/toolguard/README.md +++ b/altk/pre_tool/toolguard/README.md @@ -1,5 +1,5 @@ # ToolGuards for Enforcing Agentic Policy Adherence -An agent lifecycle solution for enforcing business policy adherence in agentic workflows. Enabling this component has demonstrated up to a **20‑point improvement** in end‑to‑end agent accuracy when invoking tools. This work is described in [Towards Enforcing Company Policy Adherence in Agentic Workflows](https://arxiv.org/pdf/2507.16459). +An agent lifecycle solution for enforcing business policy adherence in agentic workflows. Enabling this component has demonstrated up to a **20‑point improvement** in end‑to‑end agent accuracy when invoking tools. This work is described in [EMNLP 2025 Towards Enforcing Company Policy Adherence in Agentic Workflows](https://arxiv.org/pdf/2507.16459). ## Table of Contents - [Overview](#overview) From 6b5c9dc238c951ec038574cf5ac8b358f4c0f542 Mon Sep 17 00:00:00 2001 From: DAVID BOAZ Date: Thu, 4 Dec 2025 08:12:04 +0200 Subject: [PATCH 07/42] tg config\n Signed-off-by: DAVID BOAZ --- altk/pre_tool/toolguard/README.md | 2 +- altk/pre_tool/toolguard/toolguard_code_component.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/altk/pre_tool/toolguard/README.md b/altk/pre_tool/toolguard/README.md index 5de8725..5c38739 100644 --- a/altk/pre_tool/toolguard/README.md +++ b/altk/pre_tool/toolguard/README.md @@ -83,7 +83,7 @@ The ToolGuards checks if a planned action complies with the policy. If it violat ### Component Configuarion -Backed by Mellea, which requires parameters aligning to: +The build time functionality is backed by [Mellea](https://mellea.ai/), which requires parameters aligning to: ```python mellea.MelleaSession.start_session( backend_name=..., diff --git a/altk/pre_tool/toolguard/toolguard_code_component.py b/altk/pre_tool/toolguard/toolguard_code_component.py index c01057d..b976b9e 100644 --- a/altk/pre_tool/toolguard/toolguard_code_component.py +++ b/altk/pre_tool/toolguard/toolguard_code_component.py @@ -13,7 +13,7 @@ logger = logging.getLogger(__name__) class ToolGuardCodeComponentConfig(ComponentConfig): - llm_config: MeleaSessionData + llm_config: MeleaSessionData | None = None class ToolGuardCodeBuildInput(ComponentInput): tools: List[Callable] | List[BaseTool] | str From f94d0161ec645915e17decb9fe1ac8214cd42b4c Mon Sep 17 00:00:00 2001 From: DAVID BOAZ Date: Thu, 4 Dec 2025 12:23:44 +0200 Subject: [PATCH 08/42] tg fix. Signed-off-by: DAVID BOAZ --- altk/pre_tool/core/types.py | 56 +++++++++---------- .../pre_tool/toolguard/toolguard/buildtime.py | 1 + 2 files changed, 29 insertions(+), 28 deletions(-) diff --git a/altk/pre_tool/core/types.py b/altk/pre_tool/core/types.py index 63a9e85..bfb2870 100644 --- a/altk/pre_tool/core/types.py +++ b/altk/pre_tool/core/types.py @@ -119,41 +119,41 @@ class RefractionRunOutput(PreToolReflectionRunOutput): result: Optional[DebuggingResult] = None -class ToolGuardBuildInputMetaData(BaseModel): - model_config = ConfigDict(arbitrary_types_allowed=True) - policy_text: str = Field(description="Text of the policy document file") - short1: bool = Field(default=True, description="Run build short or long version. ") - validating_llm_client: LLMClient = Field( - description="ValidatingLLMClient for build time" - ) +# class ToolGuardBuildInputMetaData(BaseModel): +# model_config = ConfigDict(arbitrary_types_allowed=True) +# policy_text: str = Field(description="Text of the policy document file") +# short1: bool = Field(default=True, description="Run build short or long version. ") +# validating_llm_client: LLMClient = Field( +# description="ValidatingLLMClient for build time" +# ) -class ToolGuardBuildInput(ComponentInput): - metadata: ToolGuardBuildInputMetaData = Field( - default_factory=lambda: ToolGuardBuildInputMetaData() - ) +# class ToolGuardBuildInput(ComponentInput): +# metadata: ToolGuardBuildInputMetaData = Field( +# default_factory=lambda: ToolGuardBuildInputMetaData() +# ) -class ToolGuardRunInputMetaData(BaseModel): - model_config = ConfigDict(arbitrary_types_allowed=True) - tool_name: str = Field(description="Tool name") - tool_parms: dict = Field(default={}, description="Tool parameters") - llm_client: LLMClient = Field(description="LLMClient for build time") +# class ToolGuardRunInputMetaData(BaseModel): +# model_config = ConfigDict(arbitrary_types_allowed=True) +# tool_name: str = Field(description="Tool name") +# tool_parms: dict = Field(default={}, description="Tool parameters") +# llm_client: LLMClient = Field(description="LLMClient for build time") -class ToolGuardRunInput(ComponentInput): - metadata: ToolGuardRunInputMetaData = Field( - default_factory=lambda: ToolGuardRunInputMetaData() - ) +# class ToolGuardRunInput(ComponentInput): +# metadata: ToolGuardRunInputMetaData = Field( +# default_factory=lambda: ToolGuardRunInputMetaData() +# ) -class ToolGuardRunOutputMetaData(BaseModel): - error_message: Union[str, bool] = Field( - description="Error string or False if no error occurred" - ) +# class ToolGuardRunOutputMetaData(BaseModel): +# error_message: Union[str, bool] = Field( +# description="Error string or False if no error occurred" +# ) -class ToolGuardRunOutput(ComponentOutput): - output: ToolGuardRunOutputMetaData = Field( - default_factory=lambda: ToolGuardRunOutputMetaData() - ) +# class ToolGuardRunOutput(ComponentOutput): +# output: ToolGuardRunOutputMetaData = Field( +# default_factory=lambda: ToolGuardRunOutputMetaData() +# ) diff --git a/altk/pre_tool/toolguard/toolguard/buildtime.py b/altk/pre_tool/toolguard/toolguard/buildtime.py index a46bab9..82dd9cd 100644 --- a/altk/pre_tool/toolguard/toolguard/buildtime.py +++ b/altk/pre_tool/toolguard/toolguard/buildtime.py @@ -108,6 +108,7 @@ async def generate_guards_from_specs( # case2: List of Langchain tools if isinstance(tools, list) and all([isinstance(tool, BaseTool) for tool in tools]): oas = langchain_tools_to_openapi(tools) # type: ignore + os.makedirs(work_dir, exist_ok=True) oas_path = join(work_dir, "oas.json") oas.save(oas_path) From d2cd3899a53e334f138397af77a0013fec4df64a Mon Sep 17 00:00:00 2001 From: DAVID BOAZ Date: Thu, 4 Dec 2025 12:32:14 +0200 Subject: [PATCH 09/42] tg fix Signed-off-by: DAVID BOAZ --- altk/pre_tool/toolguard/toolguard/buildtime.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/altk/pre_tool/toolguard/toolguard/buildtime.py b/altk/pre_tool/toolguard/toolguard/buildtime.py index 82dd9cd..210759f 100644 --- a/altk/pre_tool/toolguard/toolguard/buildtime.py +++ b/altk/pre_tool/toolguard/toolguard/buildtime.py @@ -101,6 +101,7 @@ async def generate_guards_from_specs( tool_names: Optional[List[str]] = None) -> ToolGuardsCodeGenerationResult: tool_specs = [policy for policy in tool_specs if (not tool_names) or (policy.tool_name in tool_names)] + os.makedirs(work_dir, exist_ok=True) # case1: path to OpenAPI spec oas_path = tools if isinstance(tools, str) else None @@ -108,7 +109,6 @@ async def generate_guards_from_specs( # case2: List of Langchain tools if isinstance(tools, list) and all([isinstance(tool, BaseTool) for tool in tools]): oas = langchain_tools_to_openapi(tools) # type: ignore - os.makedirs(work_dir, exist_ok=True) oas_path = join(work_dir, "oas.json") oas.save(oas_path) From f043eb6a67dbce2e1309a020225be2a94a286dd5 Mon Sep 17 00:00:00 2001 From: DAVID BOAZ Date: Thu, 4 Dec 2025 12:49:38 +0200 Subject: [PATCH 10/42] tg fix Signed-off-by: DAVID BOAZ --- altk/pre_tool/toolguard/toolguard/gen_py/gen_toolguards.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/altk/pre_tool/toolguard/toolguard/gen_py/gen_toolguards.py b/altk/pre_tool/toolguard/toolguard/gen_py/gen_toolguards.py index fd2f6cc..8faccf1 100644 --- a/altk/pre_tool/toolguard/toolguard/gen_py/gen_toolguards.py +++ b/altk/pre_tool/toolguard/toolguard/gen_py/gen_toolguards.py @@ -86,5 +86,5 @@ async def generate_toolguards_from_domain( tool.tool_name: res for tool, res in zip(tools_w_poilicies, tool_results) } return ToolGuardsCodeGenerationResult( - root_dir=py_root, domain=domain, tools=tools_result + out_dir=py_root, domain=domain, tools=tools_result ).save(py_root) From a4df6304484c86bdf241f81c30179024758b2090 Mon Sep 17 00:00:00 2001 From: DAVID BOAZ Date: Sun, 7 Dec 2025 09:04:17 +0200 Subject: [PATCH 11/42] DCO Remediation Commit for DAVID BOAZ I, DAVID BOAZ , hereby add my Signed-off-by to this commit: 6b5c9dc238c951ec038574cf5ac8b358f4c0f542 I, DAVID BOAZ , hereby add my Signed-off-by to this commit: f94d0161ec645915e17decb9fe1ac8214cd42b4c Signed-off-by: DAVID BOAZ --- altk/pre_tool/toolguard/toolguard/common/jschema.py | 2 +- altk/pre_tool/toolguard/toolguard/gen_py/gen_toolguards.py | 4 +++- tests/pre_tool/toolguard/test_toolguard_code.py | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/altk/pre_tool/toolguard/toolguard/common/jschema.py b/altk/pre_tool/toolguard/toolguard/common/jschema.py index 867a900..faa183e 100644 --- a/altk/pre_tool/toolguard/toolguard/common/jschema.py +++ b/altk/pre_tool/toolguard/toolguard/common/jschema.py @@ -2,7 +2,7 @@ from typing import Any, Dict, List, Optional, Union -from altk.pre_tool.toolguard.toolguard.common.ref import DocumentWithRef, Reference +from .ref import DocumentWithRef, Reference class StrEnum(str, Enum): diff --git a/altk/pre_tool/toolguard/toolguard/gen_py/gen_toolguards.py b/altk/pre_tool/toolguard/toolguard/gen_py/gen_toolguards.py index 8faccf1..921e5ad 100644 --- a/altk/pre_tool/toolguard/toolguard/gen_py/gen_toolguards.py +++ b/altk/pre_tool/toolguard/toolguard/gen_py/gen_toolguards.py @@ -86,5 +86,7 @@ async def generate_toolguards_from_domain( tool.tool_name: res for tool, res in zip(tools_w_poilicies, tool_results) } return ToolGuardsCodeGenerationResult( - out_dir=py_root, domain=domain, tools=tools_result + out_dir=py_root, + domain=domain, + tools=tools_result ).save(py_root) diff --git a/tests/pre_tool/toolguard/test_toolguard_code.py b/tests/pre_tool/toolguard/test_toolguard_code.py index a6c70da..99dab31 100644 --- a/tests/pre_tool/toolguard/test_toolguard_code.py +++ b/tests/pre_tool/toolguard/test_toolguard_code.py @@ -121,7 +121,7 @@ async def test_tool_guard_calculator_policy(work_dir: str): expected_tools = ["multiply_tool", "divide_tool", "add_tool"] # Basic structure assertions - assert build_output.root_dir + assert build_output.out_dir assert build_output.domain assert len(build_output.tools) == len(expected_tools) From ec005bcb5762a7626fd1d92edf3b09f3307b9195 Mon Sep 17 00:00:00 2001 From: DAVID BOAZ Date: Tue, 9 Dec 2025 12:38:36 +0200 Subject: [PATCH 12/42] toolguard oss --- altk/pre_tool/toolguard/__init__.py | 3 +- altk/pre_tool/toolguard/toolguard/__init__.py | 5 - altk/pre_tool/toolguard/toolguard/__main__.py | 8 - .../pre_tool/toolguard/toolguard/buildtime.py | 133 ---- altk/pre_tool/toolguard/toolguard/cli.py | 64 -- .../toolguard/toolguard/common/__init__.py | 0 .../toolguard/toolguard/common/array.py | 62 -- .../toolguard/toolguard/common/dict.py | 108 --- .../toolguard/toolguard/common/http.py | 55 -- .../toolguard/toolguard/common/jschema.py | 42 - .../toolguard/toolguard/common/langchain.py | 53 -- .../toolguard/toolguard/common/llm_py.py | 12 - .../toolguard/common/multi_process.py | 48 -- .../toolguard/toolguard/common/open_api.py | 208 ----- .../pre_tool/toolguard/toolguard/common/py.py | 97 --- .../toolguard/toolguard/common/py_doc_str.py | 70 -- .../toolguard/toolguard/common/ref.py | 22 - .../toolguard/toolguard/common/safe_py.py | 11 - .../toolguard/toolguard/common/str.py | 38 - .../toolguard/toolguard/data_types.py | 188 ----- .../toolguard/toolguard/gen_py/__init__.py | 0 .../toolguard/gen_py/api_extractor.py | 746 ------------------ .../toolguard/toolguard/gen_py/consts.py | 31 - .../toolguard/gen_py/domain_from_funcs.py | 56 -- .../toolguard/gen_py/domain_from_openapi.py | 244 ------ .../toolguard/gen_py/gen_toolguards.py | 92 --- .../toolguard/gen_py/prompts/__init__.py | 0 .../toolguard/gen_py/prompts/gen_tests.py | 135 ---- .../toolguard/gen_py/prompts/improve_guard.py | 64 -- .../toolguard/gen_py/prompts/pseudo_code.py | 124 --- .../toolguard/gen_py/templates/__init__.py | 18 - .../toolguard/gen_py/templates/api.j2 | 12 - .../toolguard/gen_py/templates/api_impl.j2 | 22 - .../toolguard/gen_py/templates/tool_guard.j2 | 25 - .../gen_py/templates/tool_item_guard.j2 | 17 - .../toolguard/gen_py/tool_dependencies.py | 34 - .../toolguard/gen_py/tool_guard_generator.py | 424 ---------- .../toolguard/gen_py/utils/__init__.py | 0 .../gen_py/utils/datamodel_codegen.py | 34 - .../toolguard/gen_py/utils/pyright.py | 109 --- .../toolguard/gen_py/utils/pytest.py | 170 ---- .../toolguard/toolguard/gen_spec/__init__.py | 0 .../toolguard/gen_spec/oas_summary.py | 252 ------ .../gen_spec/prompts/add_examples.txt | 49 -- .../gen_spec/prompts/add_policies.txt | 58 -- .../gen_spec/prompts/add_references.txt | 38 - .../gen_spec/prompts/create_examples.txt | 64 -- .../gen_spec/prompts/create_policy.txt | 57 -- .../gen_spec/prompts/examples_reviewer.txt | 41 - .../gen_spec/prompts/fix_example.txt | 30 - .../toolguard/gen_spec/prompts/functions.txt | 56 -- .../toolguard/gen_spec/prompts/merge.txt | 52 -- .../gen_spec/prompts/merge_and_split.txt | 53 -- .../gen_spec/prompts/merge_examples.txt | 58 -- .../gen_spec/prompts/policy_reviewer.txt | 39 - .../toolguard/gen_spec/prompts/split.txt | 49 -- .../toolguard/gen_spec/spec_generator.py | 374 --------- .../toolguard/toolguard/gen_spec/utils.py | 96 --- .../toolguard/toolguard/llm/__init__.py | 0 .../toolguard/toolguard/llm/i_tg_llm.py | 12 - .../toolguard/toolguard/llm/tg_litellm.py | 176 ----- .../toolguard/toolguard/logging_utils.py | 34 - altk/pre_tool/toolguard/toolguard/runtime.py | 189 ----- .../toolguard/toolguard_code_component.py | 6 +- .../toolguard/toolguard_spec_component.py | 4 +- pyproject.toml | 1 + .../pre_tool/toolguard/test_toolguard_code.py | 6 +- 67 files changed, 10 insertions(+), 5368 deletions(-) delete mode 100644 altk/pre_tool/toolguard/toolguard/__init__.py delete mode 100644 altk/pre_tool/toolguard/toolguard/__main__.py delete mode 100644 altk/pre_tool/toolguard/toolguard/buildtime.py delete mode 100644 altk/pre_tool/toolguard/toolguard/cli.py delete mode 100644 altk/pre_tool/toolguard/toolguard/common/__init__.py delete mode 100644 altk/pre_tool/toolguard/toolguard/common/array.py delete mode 100644 altk/pre_tool/toolguard/toolguard/common/dict.py delete mode 100644 altk/pre_tool/toolguard/toolguard/common/http.py delete mode 100644 altk/pre_tool/toolguard/toolguard/common/jschema.py delete mode 100644 altk/pre_tool/toolguard/toolguard/common/langchain.py delete mode 100644 altk/pre_tool/toolguard/toolguard/common/llm_py.py delete mode 100644 altk/pre_tool/toolguard/toolguard/common/multi_process.py delete mode 100644 altk/pre_tool/toolguard/toolguard/common/open_api.py delete mode 100644 altk/pre_tool/toolguard/toolguard/common/py.py delete mode 100644 altk/pre_tool/toolguard/toolguard/common/py_doc_str.py delete mode 100644 altk/pre_tool/toolguard/toolguard/common/ref.py delete mode 100644 altk/pre_tool/toolguard/toolguard/common/safe_py.py delete mode 100644 altk/pre_tool/toolguard/toolguard/common/str.py delete mode 100644 altk/pre_tool/toolguard/toolguard/data_types.py delete mode 100644 altk/pre_tool/toolguard/toolguard/gen_py/__init__.py delete mode 100644 altk/pre_tool/toolguard/toolguard/gen_py/api_extractor.py delete mode 100644 altk/pre_tool/toolguard/toolguard/gen_py/consts.py delete mode 100644 altk/pre_tool/toolguard/toolguard/gen_py/domain_from_funcs.py delete mode 100644 altk/pre_tool/toolguard/toolguard/gen_py/domain_from_openapi.py delete mode 100644 altk/pre_tool/toolguard/toolguard/gen_py/gen_toolguards.py delete mode 100644 altk/pre_tool/toolguard/toolguard/gen_py/prompts/__init__.py delete mode 100644 altk/pre_tool/toolguard/toolguard/gen_py/prompts/gen_tests.py delete mode 100644 altk/pre_tool/toolguard/toolguard/gen_py/prompts/improve_guard.py delete mode 100644 altk/pre_tool/toolguard/toolguard/gen_py/prompts/pseudo_code.py delete mode 100644 altk/pre_tool/toolguard/toolguard/gen_py/templates/__init__.py delete mode 100644 altk/pre_tool/toolguard/toolguard/gen_py/templates/api.j2 delete mode 100644 altk/pre_tool/toolguard/toolguard/gen_py/templates/api_impl.j2 delete mode 100644 altk/pre_tool/toolguard/toolguard/gen_py/templates/tool_guard.j2 delete mode 100644 altk/pre_tool/toolguard/toolguard/gen_py/templates/tool_item_guard.j2 delete mode 100644 altk/pre_tool/toolguard/toolguard/gen_py/tool_dependencies.py delete mode 100644 altk/pre_tool/toolguard/toolguard/gen_py/tool_guard_generator.py delete mode 100644 altk/pre_tool/toolguard/toolguard/gen_py/utils/__init__.py delete mode 100644 altk/pre_tool/toolguard/toolguard/gen_py/utils/datamodel_codegen.py delete mode 100644 altk/pre_tool/toolguard/toolguard/gen_py/utils/pyright.py delete mode 100644 altk/pre_tool/toolguard/toolguard/gen_py/utils/pytest.py delete mode 100644 altk/pre_tool/toolguard/toolguard/gen_spec/__init__.py delete mode 100644 altk/pre_tool/toolguard/toolguard/gen_spec/oas_summary.py delete mode 100644 altk/pre_tool/toolguard/toolguard/gen_spec/prompts/add_examples.txt delete mode 100644 altk/pre_tool/toolguard/toolguard/gen_spec/prompts/add_policies.txt delete mode 100644 altk/pre_tool/toolguard/toolguard/gen_spec/prompts/add_references.txt delete mode 100644 altk/pre_tool/toolguard/toolguard/gen_spec/prompts/create_examples.txt delete mode 100644 altk/pre_tool/toolguard/toolguard/gen_spec/prompts/create_policy.txt delete mode 100644 altk/pre_tool/toolguard/toolguard/gen_spec/prompts/examples_reviewer.txt delete mode 100644 altk/pre_tool/toolguard/toolguard/gen_spec/prompts/fix_example.txt delete mode 100644 altk/pre_tool/toolguard/toolguard/gen_spec/prompts/functions.txt delete mode 100644 altk/pre_tool/toolguard/toolguard/gen_spec/prompts/merge.txt delete mode 100644 altk/pre_tool/toolguard/toolguard/gen_spec/prompts/merge_and_split.txt delete mode 100644 altk/pre_tool/toolguard/toolguard/gen_spec/prompts/merge_examples.txt delete mode 100644 altk/pre_tool/toolguard/toolguard/gen_spec/prompts/policy_reviewer.txt delete mode 100644 altk/pre_tool/toolguard/toolguard/gen_spec/prompts/split.txt delete mode 100644 altk/pre_tool/toolguard/toolguard/gen_spec/spec_generator.py delete mode 100644 altk/pre_tool/toolguard/toolguard/gen_spec/utils.py delete mode 100644 altk/pre_tool/toolguard/toolguard/llm/__init__.py delete mode 100644 altk/pre_tool/toolguard/toolguard/llm/i_tg_llm.py delete mode 100644 altk/pre_tool/toolguard/toolguard/llm/tg_litellm.py delete mode 100644 altk/pre_tool/toolguard/toolguard/logging_utils.py delete mode 100644 altk/pre_tool/toolguard/toolguard/runtime.py diff --git a/altk/pre_tool/toolguard/__init__.py b/altk/pre_tool/toolguard/__init__.py index 98eefdd..d29a083 100644 --- a/altk/pre_tool/toolguard/__init__.py +++ b/altk/pre_tool/toolguard/__init__.py @@ -1,3 +1,2 @@ from .toolguard_code_component import * -from .toolguard_spec_component import * -from .toolguard import * \ No newline at end of file +from .toolguard_spec_component import * \ No newline at end of file diff --git a/altk/pre_tool/toolguard/toolguard/__init__.py b/altk/pre_tool/toolguard/toolguard/__init__.py deleted file mode 100644 index 3528d66..0000000 --- a/altk/pre_tool/toolguard/toolguard/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from .buildtime import generate_guard_specs, generate_guards_from_specs, build_toolguards -from .llm.tg_litellm import LitellmModel, I_TG_LLM -from .data_types import * - -from .runtime import IToolInvoker, ToolFunctionsInvoker, ToolGuardsCodeGenerationResult, ToolMethodsInvoker, load_toolguard_code_result, load_toolguards, LangchainToolInvoker \ No newline at end of file diff --git a/altk/pre_tool/toolguard/toolguard/__main__.py b/altk/pre_tool/toolguard/toolguard/__main__.py deleted file mode 100644 index 595126e..0000000 --- a/altk/pre_tool/toolguard/toolguard/__main__.py +++ /dev/null @@ -1,8 +0,0 @@ -#important to load the env variables BEFORE policy_adherence library (so programmatic_ai configuration will take place) -import dotenv -dotenv.load_dotenv() - -from .cli import main - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/altk/pre_tool/toolguard/toolguard/buildtime.py b/altk/pre_tool/toolguard/toolguard/buildtime.py deleted file mode 100644 index 210759f..0000000 --- a/altk/pre_tool/toolguard/toolguard/buildtime.py +++ /dev/null @@ -1,133 +0,0 @@ -import os -from os.path import join -from typing import Callable, List, Optional, cast -import json -import logging - -from langchain.tools import BaseTool - - -from .llm.i_tg_llm import I_TG_LLM -from .runtime import ToolGuardsCodeGenerationResult -from .data_types import MeleaSessionData, ToolGuardSpec, load_tool_policy, ToolInfo -from .gen_py.gen_toolguards import generate_toolguards_from_functions, generate_toolguards_from_openapi -from .gen_spec.oas_summary import OASSummarizer -from .gen_spec.spec_generator import extract_toolguard_specs -from .common.langchain import langchain_tools_to_openapi - -logger = logging.getLogger(__name__) - - -#Step1 and immediatly step2 -async def build_toolguards( - policy_text:str, - tools: List[Callable]|str, - step1_out_dir:str, - step2_out_dir:str, - step1_llm:I_TG_LLM, - step2_llm: MeleaSessionData, - app_name:str= "my_app", - tools2run: List[str] | None=None, - short1=True)->ToolGuardsCodeGenerationResult: - - os.makedirs(step1_out_dir, exist_ok=True) - os.makedirs(step2_out_dir, exist_ok=True) - - # case1: path to OpenAPI spec - oas_path = tools if isinstance(tools, str) else None - - # case2: List of Langchain tools - if isinstance(tools, list) and all([isinstance(tool, BaseTool) for tool in tools]): - oas = langchain_tools_to_openapi(tools) # type: ignore - oas_path = f"{step1_out_dir}/oas.json" - oas.save(oas_path) - - if oas_path: # for cases 1 and 2 - with open(oas_path, 'r', encoding='utf-8') as file: - oas = json.load(file) - summarizer = OASSummarizer(oas) - tools_info = summarizer.summarize() - tool_policies = await extract_toolguard_specs(policy_text, tools_info, step1_out_dir, step1_llm, tools2run, short1) - return await generate_guards_from_specs(oas_path, tool_policies, step2_out_dir, llm_data=step2_llm, app_name=app_name, lib_names=None, tool_names=tools2run) - - # Case 3: List of functions + case 4: List of methods - if isinstance(tools, list) and not oas_path: - tools_info = [ToolInfo.from_function(tool) for tool in tools] - tool_policies = await extract_toolguard_specs(policy_text, tools_info, step1_out_dir, step1_llm, tools2run, short1) - return await generate_guards_from_specs(tools, tool_policies, step2_out_dir, llm_data=step2_llm, app_name=app_name, lib_names=None, tool_names=tools2run) - - raise Exception("Unknown tools") - - -#Step1 only -async def generate_guard_specs( - policy_text: str, - tools: List[Callable] | List[BaseTool] | str, - llm: I_TG_LLM, - work_dir: str, -)->List[ToolGuardSpec]: - # case1: path to OpenAPI spec - oas_path = tools if isinstance(tools, str) else None - - # case2: List of Langchain tools - if isinstance(tools, list) and all([isinstance(tool, BaseTool) for tool in tools]): - oas = langchain_tools_to_openapi(tools) # type: ignore - oas_path = join(work_dir, "oas.json") - oas.save(oas_path) - - if oas_path: # for cases 1 and 2 - with open(oas_path, 'r', encoding='utf-8') as file: - oas = json.load(file) - summarizer = OASSummarizer(oas) - tools_info = summarizer.summarize() - return await extract_toolguard_specs(policy_text, tools_info, work_dir, llm, None, True) - - # Case 3: List of functions + case 4: List of methods - if isinstance(tools, list): - tools_info = [ToolInfo.from_function(tool) for tool in tools] - return await extract_toolguard_specs(policy_text, tools_info, work_dir, llm, None, True) - - raise NotImplementedError() - - -#Step2 only -async def generate_guards_from_specs( - tools: List[Callable] | List[BaseTool] | str, - tool_specs: List[ToolGuardSpec], - work_dir: str, - llm_data: MeleaSessionData, - app_name: str = "myapp", - lib_names: Optional[List[str]] = None, - tool_names: Optional[List[str]] = None) -> ToolGuardsCodeGenerationResult: - - tool_specs = [policy for policy in tool_specs if (not tool_names) or (policy.tool_name in tool_names)] - os.makedirs(work_dir, exist_ok=True) - - # case1: path to OpenAPI spec - oas_path = tools if isinstance(tools, str) else None - - # case2: List of Langchain tools - if isinstance(tools, list) and all([isinstance(tool, BaseTool) for tool in tools]): - oas = langchain_tools_to_openapi(tools) # type: ignore - oas_path = join(work_dir, "oas.json") - oas.save(oas_path) - - if oas_path: # for cases 1 and 2 - return await generate_toolguards_from_openapi(app_name, tool_specs, work_dir, oas_path, llm_data) - - # Case 3: List of functions + case 4: List of methods - funcs = [cast(Callable, tool) for tool in tools] - return await generate_toolguards_from_functions(app_name, tool_specs, work_dir, funcs=funcs, llm_data=llm_data, module_roots=lib_names) - - -#load step1 results -def load_policies_in_folder(folder:str)->List[ToolGuardSpec]: - files = [f for f in os.listdir(folder) - if os.path.isfile(join(folder, f)) and f.endswith(".json")] - tool_policies = [] - for file in files: - tool_name = file[:-len(".json")] - policy = load_tool_policy(join(folder, file), tool_name) - if policy.policy_items: - tool_policies.append(policy) - return tool_policies diff --git a/altk/pre_tool/toolguard/toolguard/cli.py b/altk/pre_tool/toolguard/toolguard/cli.py deleted file mode 100644 index f327959..0000000 --- a/altk/pre_tool/toolguard/toolguard/cli.py +++ /dev/null @@ -1,64 +0,0 @@ - -import asyncio -import inspect -import os -from os.path import join -from typing import Callable, List - -import markdown -import logging - -from .buildtime import build_toolguards -from .common import py -from .llm.tg_litellm import LitellmModel - -logger = logging.getLogger(__name__) - -import argparse - -def main(): - parser = argparse.ArgumentParser(description='parser') - parser.add_argument('--policy-path', type=str, help='Path to the policy file. Currently, in `markdown` syntax. eg: `/Users/me/airline/wiki.md`') - parser.add_argument('--app-root', type=str, default="../ToolGuardAgent/src/appointment_app") - parser.add_argument('--app-tools-py-file', type=str, default="lg_tools.py") - parser.add_argument('--out-dir', type=str, help='Path to an output folder where the generated artifacts will be written. eg: `/Users/me/airline/outdir2') - parser.add_argument('--step1-dir-name', type=str, default='Step1', help='Step1 folder name under the output folder') - parser.add_argument('--step2-dir-name', type=str, default='Step2', help='Step2 folder name under the output folder') - parser.add_argument('--step1-model-name', type=str, default='gpt-4o-2024-08-06', help='Model to use for generating in step 1') - parser.add_argument('--tools2run', nargs='+', default=None, help='Optional list of tool names. These are a subset of the tools in the openAPI operation ids.') - parser.add_argument('--short-step1', action='store_true', default=False, help='run short version of step 1') - - args = parser.parse_args() - policy_path = args.policy_path - - policy_text = open(policy_path, 'r', encoding='utf-8').read() - policy_text = markdown.markdown(policy_text) - - llm = LitellmModel(args.step1_model_name, "azure") #FIXME from args - tools = load_functions_in_file(args.app_root, args.app_tools_py_file) - os.makedirs(args.out_dir, exist_ok=True) - step1_out_dir = join(args.out_dir, args.step1_dir_name) - step2_out_dir = join(args.out_dir, args.step2_dir_name) - asyncio.run( - build_toolguards( - policy_text = policy_text, - tools = tools, - step1_out_dir = step1_out_dir, - step2_out_dir = step2_out_dir, - step1_llm = llm, - tools2run = args.tools2run, - short1 = args.short_step1 - ) - ) - -from importlib import import_module -def load_functions_in_file(py_root:str, file_path: str) -> List[Callable]: - with py.temp_python_path(py_root): - module = import_module(py.path_to_module(file_path)) - funcs = [] - for name, obj in inspect.getmembers(module): - # if isinstance(obj, BaseTool): - # funcs.append(py.unwrap_fn(obj)) - if callable(obj) and not (name=='tool' and obj.__module__ =='langchain_core.tools.convert'): - funcs.append(obj) - return funcs \ No newline at end of file diff --git a/altk/pre_tool/toolguard/toolguard/common/__init__.py b/altk/pre_tool/toolguard/toolguard/common/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/altk/pre_tool/toolguard/toolguard/common/array.py b/altk/pre_tool/toolguard/toolguard/common/array.py deleted file mode 100644 index 412df9c..0000000 --- a/altk/pre_tool/toolguard/toolguard/common/array.py +++ /dev/null @@ -1,62 +0,0 @@ -import functools -from typing import Callable, List, TypeVar, Any - - -def flatten(arr_arr): - return [b for bs in arr_arr for b in bs] - - -def break_array_into_chunks(arr: List, chunk_size: int) -> List[List[Any]]: - res = [] - for i, v in enumerate(arr): - if i % chunk_size == 0: - cur = [] - res.append(cur) - cur.append(v) - return res - - -def sum(array): - return functools.reduce(lambda a, b: a + b, array) if len(array) > 0 else 0 - - -T = TypeVar("T") - - -def find(array: List[T], pred: Callable[[T], bool]): - for item in array: - if pred(item): - return item - - -# remove duplicates and preserve ordering -T = TypeVar("T") - - -def remove_duplicates(array: List[T]) -> List[T]: - res = [] - visited = set() - for item in array: - if item not in visited: - res.append(item) - visited.add(item) - return res - - -def not_none(array: List[T]) -> List[T]: - return [item for item in array if item is not None] - - -def split_array(arr: List[T], delimiter: T) -> List[List[T]]: - result = [] - temp = [] - for item in arr: - if item == delimiter: - if temp: # Avoid adding empty lists - result.append(temp) - temp = [] - else: - temp.append(item) - if temp: # Add the last group if not empty - result.append(temp) - return result diff --git a/altk/pre_tool/toolguard/toolguard/common/dict.py b/altk/pre_tool/toolguard/toolguard/common/dict.py deleted file mode 100644 index 590e767..0000000 --- a/altk/pre_tool/toolguard/toolguard/common/dict.py +++ /dev/null @@ -1,108 +0,0 @@ -from typing import Dict, Any, Callable -import hashlib -import json -from collections import deque - - -def dict_deep_merge(trg_dct: Dict, merge_dct: Dict): - for k, v in merge_dct.items(): - trg_v = trg_dct.get(k) - if k in trg_dct and isinstance(trg_v, dict) and isinstance(v, dict): - dict_deep_merge(trg_v, v) - elif isinstance(trg_v, list) and isinstance(v, list): - if all([type(item) is dict for item in v]) and all( - [type(item) is dict for item in trg_v] - ): # both are lists of objects - for a, b in zip(trg_v, v): - dict_deep_merge(a, b) - else: - for item in v: - if item not in trg_dct[k]: - trg_dct[k].append(item) - else: - trg_dct[k] = v - - -def get_keys_recursive(d: Dict): - keys = [] - for key, value in d.items(): - keys.append(key) - if isinstance(value, dict): - keys.extend(get_keys_recursive(value)) - elif isinstance(value, list): - for item in value: - if isinstance(item, dict): - keys.extend(get_keys_recursive(item)) - return keys - - -# adapted from https://www.doc.ic.ac.uk/~nuric/posts/coding/how-to-hash-a-dictionary-in-python/ -def dict_hash(dictionary: Dict[str, Any]) -> int: - encoded = json.dumps(dictionary, sort_keys=True).encode() - return int(hashlib.sha256(encoded).hexdigest(), 16) - - -def visit_all(d: Any, cb: Callable[[Dict, Any], bool]): - if type(d) is dict: - for k in list( - d - ): # list() to avoid RuntimeError: dictionary changed size during iteration - changed = cb(d, k) - if not changed: - visit_all(d[k], cb) - if type(d) is list: - for item in d: - visit_all(item, cb) - - -def resolve_ref(ref, root_schema, resolved_refs): - """Resolve a $ref to its actual definition in the schema.""" - if ref in resolved_refs: - return resolved_refs[ref] - - resolved_schema = find_ref(root_schema, ref) - resolved_refs[ref] = resolved_schema - return substitute_refs(resolved_schema, root_schema, resolved_refs) - - -def substitute_refs(schema, root_schema=None, resolved_refs=None): - """Substitute all $refs in the JSON schema with their definitions.""" - if root_schema is None: - root_schema = schema - if resolved_refs is None: - resolved_refs = {} - - if isinstance(schema, dict): - if "$ref" in schema: - ref = schema["$ref"] - if ref in resolved_refs: - return resolved_refs[ref] - resolved = resolve_ref(ref, root_schema, resolved_refs) - resolved_refs[ref] = resolved - return resolved - else: - return { - k: substitute_refs(v, root_schema, resolved_refs) - for k, v in schema.items() - } - elif isinstance(schema, list): - return [substitute_refs(item, root_schema, resolved_refs) for item in schema] - else: - return schema - - -def find_ref(doc: dict, ref: str): - q = deque(ref.split("/")) - cur = doc - while q: - field = q.popleft() - if field == "#": - cur = doc - continue - if field in cur: - cur = cur[field] - else: - return None - if "$ref" in cur: - return find_ref(doc, cur.get("$ref")) # recursive. infinte loops? - return cur diff --git a/altk/pre_tool/toolguard/toolguard/common/http.py b/altk/pre_tool/toolguard/toolguard/common/http.py deleted file mode 100644 index 2cb37be..0000000 --- a/altk/pre_tool/toolguard/toolguard/common/http.py +++ /dev/null @@ -1,55 +0,0 @@ -from enum import Enum -from functools import cache -from typing import List - - -MEDIA_TYPE_APP_JSON = "application/json" -MEDIA_TYPE_MULTIPART_FORM = "multipart/form-data" -MEDIA_TYPE_APP_FORM = "application/x-www-form-urlencoded" - - -class StrEnum(str, Enum): - """An abstract base class for string-based enums.""" - - pass - - -class HttpMethod(StrEnum): - GET = "GET" - POST = "POST" - PUT = "PUT" - DELETE = "DELETE" - PATCH = "PATCH" - - def __eq__(self, value: object) -> bool: - return str(value).upper() == self.value - - def __ne__(self, value: object) -> bool: - return not self.__eq__(value) - - def __hash__(self): - return self.value.__hash__() - - @classmethod - @cache - def list(cls) -> List["HttpMethod"]: - return list(map(lambda c: c.value, cls)) - - -def is_valid_http_method(val: str): - return val and val.upper() in HttpMethod.list() - - -PARAM_API_KEY = "api_key" - -AUTH_HEADER = "Authorization" - -SECURITY_COMPONENT_TYPE_API_KEY = "apiKey" -SECURITY_COMPONENT_SCHEME_BEARER = "bearer" -SECURITY_COMPONENT_SCHEME_BASIC = "basic" - -SECURITY_COMPONENT_BEARER = { - "type": "http", - "scheme": SECURITY_COMPONENT_SCHEME_BEARER, - "bearerFormat": "JWT", -} diff --git a/altk/pre_tool/toolguard/toolguard/common/jschema.py b/altk/pre_tool/toolguard/toolguard/common/jschema.py deleted file mode 100644 index faa183e..0000000 --- a/altk/pre_tool/toolguard/toolguard/common/jschema.py +++ /dev/null @@ -1,42 +0,0 @@ -from enum import Enum -from typing import Any, Dict, List, Optional, Union - - -from .ref import DocumentWithRef, Reference - - -class StrEnum(str, Enum): - """An abstract base class for string-based enums.""" - - pass - - -class JSONSchemaTypes(StrEnum): - string = "string" - number = "number" - integer = "integer" - boolean = "boolean" - array = "array" - object = "object" - null = "null" - - -class JSchema(DocumentWithRef): - type: Optional[JSONSchemaTypes] = None - properties: Optional[Dict[str, Union[Reference, "JSchema"]]] = None - items: Optional[Union[Reference, "JSchema"]] = None - additionalProperties: Optional[Union["JSchema", bool]] = None - format: Optional[str] = None - enum: Optional[list] = None - default: Optional[Any] = None - description: Optional[str] = None - example: Optional[Any] = None - required: Optional[List[str]] = None - allOf: Optional[List[Union[Reference, "JSchema"]]] = None - anyOf: Optional[List[Union[Reference, "JSchema"]]] = None - nullable: Optional[bool] = ( - None # in OPenAPISpec https://swagger.io/docs/specification/v3_0/data-models/data-types/#null - ) - - def __str__(self) -> str: - return self.model_dump_json(exclude_none=True, indent=2) diff --git a/altk/pre_tool/toolguard/toolguard/common/langchain.py b/altk/pre_tool/toolguard/toolguard/common/langchain.py deleted file mode 100644 index 638f399..0000000 --- a/altk/pre_tool/toolguard/toolguard/common/langchain.py +++ /dev/null @@ -1,53 +0,0 @@ - - -from typing import List -from langchain.tools import BaseTool - -from .open_api import OpenAPI - -def langchain_tools_to_openapi( - tools: List[BaseTool], - title: str = "LangChain Tools API", - version: str = "1.0.0", -)->OpenAPI: - paths = {} - components = {"schemas": {}} - - for tool in tools: - # Get JSON schema from the args model - if tool.get_input_schema(): - components["schemas"][tool.name + "Args"] = tool.get_input_schema().model_json_schema() - - request_body = { - "description": tool.description, - "required": True, - "content": { - "application/json": { - "schema": {"$ref": f"#/components/schemas/{tool.name}Args"} - } - }, - } - else: - # Tools without args → empty schema - request_body = None - - paths[f"/tools/{tool.name}"] = { - "post": { - "summary": tool.description, - "operationId": tool.name, - "requestBody": request_body, - "responses": { - "200": { - "description": "Tool result", - "content": {"application/json": tool.get_output_jsonschema()}, - } - }, - } - } - - return OpenAPI.model_validate({ - "openapi": "3.1.0", - "info": {"title": title, "version": version}, - "paths": paths, - "components": components, - }) diff --git a/altk/pre_tool/toolguard/toolguard/common/llm_py.py b/altk/pre_tool/toolguard/toolguard/common/llm_py.py deleted file mode 100644 index b6ab4fa..0000000 --- a/altk/pre_tool/toolguard/toolguard/common/llm_py.py +++ /dev/null @@ -1,12 +0,0 @@ -import re - -PYTHON_PATTERN = r"^```python\s*\n([\s\S]*)\n```" - - -def get_code_content(llm_code) -> str: - code = llm_code.replace("\\n", "\n") - match = re.match(PYTHON_PATTERN, code) - if match: - return match.group(1) - - return code diff --git a/altk/pre_tool/toolguard/toolguard/common/multi_process.py b/altk/pre_tool/toolguard/toolguard/common/multi_process.py deleted file mode 100644 index f31de7a..0000000 --- a/altk/pre_tool/toolguard/toolguard/common/multi_process.py +++ /dev/null @@ -1,48 +0,0 @@ -import sys -import importlib -from pathlib import Path -import multiprocessing - -def process_target(queue, func, args, kwargs, extra_paths): - try: - # Add dynamic python paths - if extra_paths: - for p in extra_paths: - p = str(Path(p).resolve()) - if p not in sys.path: - sys.path.insert(0, p) - - # Force reload of modules from those paths (optional) - if extra_paths: - for modname, module in list(sys.modules.items()): - file = getattr(module, "__file__", None) - if not file: - continue - - for p in extra_paths: - p = Path(p).resolve() - try: - if Path(file).resolve().is_relative_to(p): - importlib.reload(module) - except Exception: - pass - - # Run the function - result = func(*args, **kwargs) - queue.put(("ok", result)) - except Exception as e: - queue.put(("error", e)) - -def run_in_process(func, *args, extra_paths=None, **kwargs): - queue = multiprocessing.Queue() - p = multiprocessing.Process( - target=process_target, - args=(queue, func, args, kwargs, extra_paths) - ) - p.start() - p.join() - - status, value = queue.get() - if status == "error": - raise value - return value diff --git a/altk/pre_tool/toolguard/toolguard/common/open_api.py b/altk/pre_tool/toolguard/toolguard/common/open_api.py deleted file mode 100644 index b72f7be..0000000 --- a/altk/pre_tool/toolguard/toolguard/common/open_api.py +++ /dev/null @@ -1,208 +0,0 @@ -from enum import Enum -from pydantic import BaseModel, Field, HttpUrl -from typing import List, Dict, Optional, Any, TypeVar, Union -import json -import yaml - -from .array import not_none -from .dict import find_ref -from .http import MEDIA_TYPE_APP_JSON -from .jschema import JSchema -from .ref import Reference - - -class Contact(BaseModel): - name: Optional[str] = None - url: Optional[HttpUrl] = None - email: Optional[str] = None - - -class License(BaseModel): - name: str - identifier: Optional[str] = None - url: Optional[HttpUrl] = None - - -class Info(BaseModel): - title: str - summary: Optional[str] = None - description: Optional[str] = None - termsOfService: Optional[HttpUrl] = None - contact: Optional[Contact] = None - license: Optional[License] = None - version: str - - -class ServerVariable(BaseModel): - enum: Optional[List[str]] = None - default: str - description: Optional[str] = None - - -class Server(BaseModel): - url: str - description: Optional[str] = None - variables: Optional[Dict[str, ServerVariable]] = None - - -class ExternalDocumentation(BaseModel): - description: Optional[str] = None - url: HttpUrl - - -class Tag(BaseModel): - name: str - description: Optional[str] = None - externalDocs: Optional[ExternalDocumentation] = None - - -class MediaType(BaseModel): - schema_: Optional[Union[Reference, JSchema]] = Field(None, alias="schema") - example: Optional[Any] = None - examples: Optional[Dict[str, Any]] = None - - -class RequestBody(BaseModel): - description: Optional[str] = None - required: Optional[bool] = None - content: Optional[Dict[str, MediaType]] = None - - @property - def content_json(self): - if self.content: - return self.content.get(MEDIA_TYPE_APP_JSON) - - -class Response(BaseModel): - description: Optional[str] = None - content: Optional[Dict[str, MediaType]] = None - - @property - def content_json(self): - if self.content: - return self.content.get(MEDIA_TYPE_APP_JSON) - - -class StrEnum(str, Enum): - """An abstract base class for string-based enums.""" - - pass - - -class ParameterIn(StrEnum): - query = "query" - header = "header" - cookie = "cookie" - path = "path" - - -class Parameter(BaseModel): - name: str - description: Optional[str] = None - in_: ParameterIn = Field(ParameterIn.query, alias="in") - required: Optional[bool] = None - schema_: Optional[Union[Reference, JSchema]] = Field(None, alias="schema") - - -class Operation(BaseModel): - summary: Optional[str] = None - description: Optional[str] = None - operationId: Optional[str] = None - tags: Optional[List[str]] = None - parameters: Optional[List[Union[Reference, Parameter]]] = None - requestBody: Optional[Union[Reference, RequestBody]] = None - responses: Optional[Dict[str, Union[Reference, Response]]] = None - security: Optional[Dict[str, List[str]]] = None - - -class PathItem(BaseModel): - summary: Optional[str] = None - description: Optional[str] = None - servers: Optional[List[Server]] = None - parameters: Optional[List[Union[Reference, Parameter]]] = None - get: Optional[Operation] = None - put: Optional[Operation] = None - post: Optional[Operation] = None - delete: Optional[Operation] = None - options: Optional[Operation] = None - head: Optional[Operation] = None - patch: Optional[Operation] = None - trace: Optional[Operation] = None - - @property - def operations(self): - d = { - "get": self.get, - "put": self.put, - "post": self.post, - "delete": self.delete, - "options": self.options, - "head": self.head, - "patch": self.patch, - "trace": self.trace, - } - return {k: v for k, v in d.items() if v is not None} - - -class Components(BaseModel): - schemas: Optional[Dict[str, JSchema]] = None - responses: Optional[Dict[str, Response]] = None - parameters: Optional[Dict[str, Parameter]] = None - examples: Optional[Dict[str, Any]] = None - requestBodies: Optional[Dict[str, RequestBody]] = None - headers: Optional[Dict[str, Any]] = None - securitySchemes: Optional[Dict[str, Any]] = None - links: Optional[Dict[str, Any]] = None - callbacks: Optional[Dict[str, Any]] = None - pathItems: Optional[Dict[str, PathItem]] = None - - -BaseModelT = TypeVar("BaseModelT", bound=BaseModel) - - -class OpenAPI(BaseModel): - openapi: str = Field(..., pattern=r"^3\.\d\.\d+(-.+)?$") - info: Info - jsonSchemaDialect: Optional[HttpUrl] = ( - "https://spec.openapis.org/oas/3.1/dialect/WORK-IN-PROGRESS" - ) - servers: Optional[List[Server]] = [Server(url="/")] - paths: Dict[str, Union[Reference, PathItem]] = {} - webhooks: Optional[Dict[str, PathItem]] = None - components: Optional[Components] = None - security: Optional[List[Dict[str, List[str]]]] = None # Refined to List of Dicts - tags: Optional[List[Tag]] = None - externalDocs: Optional[ExternalDocumentation] = None - - def get_operation_by_operationId(self, operationId: str) -> Operation | None: - for path_item in self.paths.values(): - for op in path_item.operations.values(): - if op.operationId == operationId: - return op - - def resolve_ref( - self, obj: Reference | BaseModelT | None, object_type: type[BaseModelT] - ) -> BaseModelT | None: - if isinstance(obj, Reference): - tmp = find_ref(self.model_dump(), obj.ref) - return object_type.model_validate(tmp) - return obj - - def save(self, file_name: str): - if file_name.endswith(".json"): - with open(file_name, "w", encoding="utf-8") as f: - f.write( - self.model_dump_json(indent=2, by_alias=True, exclude_none=True) - ) - return - # TODO yaml - raise NotImplementedError() - - -def read_openapi(file_path: str) -> OpenAPI: - with open(file_path, "r") as file: - if file_path.endswith("json"): - d = json.load(file) - else: - d = yaml.safe_load(file) - return OpenAPI.model_validate(d, strict=False) diff --git a/altk/pre_tool/toolguard/toolguard/common/py.py b/altk/pre_tool/toolguard/toolguard/common/py.py deleted file mode 100644 index a9278b3..0000000 --- a/altk/pre_tool/toolguard/toolguard/common/py.py +++ /dev/null @@ -1,97 +0,0 @@ - -import os -import inspect -from typing import Callable -import sys -from pathlib import Path -from contextlib import contextmanager - -from .str import to_snake_case - -def py_extension(filename: str) -> str: - return filename if filename.endswith(".py") else filename + ".py" - - -def un_py_extension(filename: str) -> str: - return filename.removesuffix(".py") if filename.endswith(".py") else filename - - -def path_to_module(file_path: str) -> str: - assert file_path - parts = file_path.split("/") - if parts[-1].endswith(".py"): - parts[-1] = un_py_extension(parts[-1]) - return ".".join([to_snake_case(part) for part in parts]) - - -def module_to_path(module: str) -> str: - parts = module.split(".") - return os.path.join(*parts[:-1], py_extension(parts[-1])) - -@contextmanager -def temp_python_path(path: str): - path = str(Path(path).resolve()) - if path not in sys.path: - sys.path.insert(0, path) - try: - yield - finally: - sys.path.remove(path) - else: - # Already in sys.path, no need to remove - yield - -def extract_docstr_args(func:Callable) -> str: - doc = inspect.getdoc(func) - if not doc: - return "" - - lines = doc.splitlines() - args_start = None - for i, line in enumerate(lines): - if line.strip().lower() == "args:": - args_start = i - break - - if args_start is None: - return "" - - # List of known docstring section headers - next_sections = { - "returns:", - "raises:", - "examples:", - "notes:", - "attributes:", - "yields:", - } - - # Capture lines after "Args:" that are indented - args_lines = [] - for line in lines[args_start + 1 :]: - # Stop if we hit a new section (like "Returns:", "Raises:", etc.) - stripped = line.strip().lower() - if stripped in next_sections: - break - args_lines.append(" " * 8 + line.strip()) - - # Join all lines into a single string - if not args_lines: - return "" - - return "\n".join(args_lines) - -def get_func_signature(obj)->str: - if inspect.isfunction(obj): - return inspect.signature(obj) - if hasattr(obj, "func") and inspect.isfunction(obj.func): # a @tool - return inspect.signature(obj.func) - if hasattr(obj, "args_schema"): - schema = obj.args_schema - fields = schema.model_fields - params = ", ".join( - f"{name}: {field.annotation.__name__ if hasattr(field.annotation, '__name__') else field.annotation}" - for name, field in fields.items() - ) - return f"({params})" - return None \ No newline at end of file diff --git a/altk/pre_tool/toolguard/toolguard/common/py_doc_str.py b/altk/pre_tool/toolguard/toolguard/common/py_doc_str.py deleted file mode 100644 index 26c6188..0000000 --- a/altk/pre_tool/toolguard/toolguard/common/py_doc_str.py +++ /dev/null @@ -1,70 +0,0 @@ -import inspect -import re -from typing import Callable - - -def extract_docstr_args(func: Callable) -> str: - doc = inspect.getdoc(func) - if not doc: - return "" - - lines = doc.splitlines() - - def args_start_line(): - for i, line in enumerate(lines): - if line.strip().lower() == "args:": # Google style docstr - return i + 1 - if ( - line.strip().lower().startswith(":param ") - ): # Sphinx-style docstring. https://sphinx-rtd-tutorial.readthedocs.io/en/latest/docstrings.html - return i - - args_start = args_start_line() - if args_start is None: - return "" - - # List of known docstring section headers - next_sections = { - "returns:", - "raises:", - "examples:", - "notes:", - "attributes:", - "yields:", - } - - # Capture lines after "Args:" that are indented - args_lines = [] - indent = " " * 4 * 2 - for line in lines[args_start:]: - # Stop if we hit a new section (like "Returns:", "Raises:", etc.) - stripped = line.strip().lower() - if stripped in next_sections or stripped.startswith(":return:"): - break - - args_lines.append(indent + sphinx_param_to_google(line.strip())) - - # Join all lines into a single string - if not args_lines: - return "" - - return "\n".join(args_lines) - - -def sphinx_param_to_google(line: str) -> str: - """ - Convert a single Sphinx-style ':param' line to Google style. - - Args: - line: A Sphinx param line, e.g. - ':param user_id: The unique identifier of the user.' - - Returns: - str: Google style equivalent, e.g. - 'user_id: The unique identifier of the user.' - """ - m = re.match(r"\s*:param\s+(\w+)\s*:\s*(.*)", line) - if not m: - return line - name, desc = m.groups() - return f"{name}: {desc}" diff --git a/altk/pre_tool/toolguard/toolguard/common/ref.py b/altk/pre_tool/toolguard/toolguard/common/ref.py deleted file mode 100644 index ad25a80..0000000 --- a/altk/pre_tool/toolguard/toolguard/common/ref.py +++ /dev/null @@ -1,22 +0,0 @@ - -from typing import TypeVar -from pydantic import BaseModel, Field - -from .dict import find_ref - - -class Reference(BaseModel): - ref: str = Field(..., alias="$ref") - - -BaseModelT = TypeVar("BaseModelT", bound=BaseModel) - - -class DocumentWithRef(BaseModel): - def resolve_ref( - self, obj: Reference | BaseModelT | None, object_type: type[BaseModelT] - ) -> BaseModelT | None: - if isinstance(obj, Reference): - tmp = find_ref(self.model_dump(), obj.ref) - return object_type.model_validate(tmp) - return obj diff --git a/altk/pre_tool/toolguard/toolguard/common/safe_py.py b/altk/pre_tool/toolguard/toolguard/common/safe_py.py deleted file mode 100644 index 3e44218..0000000 --- a/altk/pre_tool/toolguard/toolguard/common/safe_py.py +++ /dev/null @@ -1,11 +0,0 @@ -from typing import List -from smolagents.local_python_executor import LocalPythonExecutor - -def run_safe_python(code: str, libs: List[str] = []): - exec = LocalPythonExecutor( - additional_authorized_imports = libs, - max_print_outputs_length= None, - additional_functions = None, - ) - out = exec(code) - return out \ No newline at end of file diff --git a/altk/pre_tool/toolguard/toolguard/common/str.py b/altk/pre_tool/toolguard/toolguard/common/str.py deleted file mode 100644 index 665efbb..0000000 --- a/altk/pre_tool/toolguard/toolguard/common/str.py +++ /dev/null @@ -1,38 +0,0 @@ -import re - - -def to_camel_case(snake_str: str) -> str: - return ( - snake_str.replace("_", " ") - .title() - .replace(" ", "") - .replace("-", "_") - .replace("'", "_") - .replace(",", "_") - .replace("’","_") - ) - - -def to_snake_case(human_name: str) -> str: - return ( - human_name.lower() - .replace(" ", "_") - .replace("-", "_") - .replace("'", "_") - .replace(",", "_") - .replace("’","_") - ) - -def to_pascal_case(name: str) -> str: - # Split by underscores first - parts = name.split('_') - - result_parts = [] - for part in parts: - # Split camelCase or mixedCase segments into words - subparts = re.findall(r'[A-Z]?[a-z]+|[A-Z]+(?![a-z])|\d+', part) - # Capitalize each segment - cap = ''.join(s[0].upper() + s[1:] if s else '' for s in subparts) - result_parts.append(cap) - - return ''.join(result_parts) \ No newline at end of file diff --git a/altk/pre_tool/toolguard/toolguard/data_types.py b/altk/pre_tool/toolguard/toolguard/data_types.py deleted file mode 100644 index e5b30f5..0000000 --- a/altk/pre_tool/toolguard/toolguard/data_types.py +++ /dev/null @@ -1,188 +0,0 @@ -import inspect -import json -import logging -import os -from pathlib import Path -from pydantic import BaseModel, Field -from typing import Any, Callable, Dict, List, Literal, Optional, cast - -logger = logging.getLogger(__name__) - -DEBUG_DIR = "debug" -TESTS_DIR = "tests" -RESULTS_FILENAME = "result.json" -API_PARAM = "api" - -ENV_GENPY_BACKEND_NAME = "TOOLGUARD_GENPY_BACKEND_NAME" -ENV_GENPY_MODEL_ID = "TOOLGUARD_GENPY_MODEL_ID" -ENV_GENPY_ARGS = "TOOLGUARD_GENPY_ARGS" -MeleaBackendName = Literal["ollama", "hf", "openai", "watsonx", "litellm"] - -class MeleaSessionData(BaseModel): - backend_name: MeleaBackendName | None = None - model_id: str| None = None - kw_args: Dict[str, Any]| None = None - - def model_post_init(self, __context): - if not self.backend_name: - self.backend_name = cast(MeleaBackendName, - os.getenv(ENV_GENPY_BACKEND_NAME, "openai"), - ) - - if not self.model_id: - self.model_id = os.getenv(ENV_GENPY_MODEL_ID) - assert self.model_id, f"'{ENV_GENPY_MODEL_ID}' environment variable not set" - - if not self.kw_args: - kw_args_val = os.getenv(ENV_GENPY_ARGS) - self.kw_args = {} - if kw_args_val: - try: - self.kw_args = json.loads(kw_args_val) - except Exception as e: - logger.warning( - f"Failed to parse {ENV_GENPY_ARGS}: {e}. Using empty dict instead." - ) - -class ToolInfo(BaseModel): - name: str - description: str - parameters: Any - signature: str - full_description:str - - @classmethod - def from_function(cls, fn: Callable) -> "ToolInfo": - # Assumes @tool decorator from langchain https://python.langchain.com/docs/how_to/custom_tools/ - # or a plain function with doc string - def doc_summary(doc:str): - paragraphs = [p.strip() for p in doc.split("\n\n") if p.strip()] - return paragraphs[0] if paragraphs else "" - - fn_name = fn.name if hasattr(fn, 'name') else fn.__name__ - sig =fn_name + str(inspect.signature(fn)) - full_desc = fn.description if hasattr(fn,'description') else fn.__doc__.strip() if fn.__doc__ else (inspect.getdoc(fn) or "") - return cls( - name=fn_name, - description=doc_summary(full_desc), - full_description = full_desc, - parameters=fn.args_schema.model_json_schema() if hasattr(fn, 'args_schema') else inspect.getdoc(fn), - signature=sig, - ) - - -class FileTwin(BaseModel): - file_name: str - content: str - - def save(self, folder: str) -> "FileTwin": - full_path = os.path.join(folder, self.file_name) - parent = Path(full_path).parent - os.makedirs(parent, exist_ok=True) - with open(full_path, "w") as file: - file.write(self.content) - return self - - def save_as(self, folder: str, file_name: str) -> "FileTwin": - file_path = os.path.join(folder, file_name) - with open(file_path, "w") as file: - file.write(self.content) - return FileTwin(file_name=file_name, content=self.content) - - @staticmethod - def load_from(folder: str, file_path: str) -> "FileTwin": - with open(os.path.join(folder, file_path), "r") as file: - data = file.read() - return FileTwin(file_name=file_path, content=data) - - -class ToolGuardSpecItem(BaseModel): - name: str = Field(..., description="Policy item name") - description: str = Field(..., description="Policy item description") - references: List[str] = Field(..., description="original texts") - compliance_examples: Optional[List[str]] = Field( - ..., description="Example of cases that comply with the policy" - ) - violation_examples: Optional[List[str]] = Field( - ..., description="Example of cases that violate the policy" - ) - - def to_md_bulltets(self, items: List[str]) -> str: - s = "" - for item in items: - s += f"* {item}\n" - return s - - def __str__(self) -> str: - s = "#### Policy item " + self.name + "\n" - s += f"{self.description}\n" - if self.compliance_examples: - s += f"##### Positive examples\n{self.to_md_bulltets(self.compliance_examples)}" - if self.violation_examples: - s += f"##### Negative examples\n{self.to_md_bulltets(self.violation_examples)}" - return s - - -class ToolGuardSpec(BaseModel): - tool_name: str = Field(..., description="Name of the tool") - policy_items: List[ToolGuardSpecItem] = Field( - ..., - description="Policy items. All (And logic) policy items must hold whehn invoking the tool.", - ) - - -def load_tool_policy(file_path: str, tool_name: str) -> ToolGuardSpec: - with open(file_path, "r") as file: - d = json.load(file) - - items = [ - ToolGuardSpecItem( - name=item.get("policy_name"), - description=item.get("description"), - references=item.get("references"), - compliance_examples=item.get("compliance_examples"), - violation_examples=item.get("violating_examples"), - ) - for item in d.get("policies", []) - if not item.get("skip") - ] - return ToolGuardSpec(tool_name=tool_name, policy_items=items) - - -class Domain(BaseModel): - app_name: str = Field(..., description="Application name") - toolguard_common: FileTwin = Field( - ..., description="Pydantic data types used by toolguard framework." - ) - app_types: FileTwin = Field( - ..., description="Data types defined used in the application API as payloads." - ) - app_api_class_name: str = Field(..., description="Name of the API class name.") - app_api: FileTwin = Field( - ..., description="Python class (abstract) containing all the API signatures." - ) - app_api_size: int = Field(..., description="Number of functions in the API") - - -class RuntimeDomain(Domain): - app_api_impl_class_name: str = Field( - ..., description="Python class (implementaton) class name." - ) - app_api_impl: FileTwin = Field( - ..., description="Python class containing all the API method implementations." - ) - - def get_definitions_only(self): - return Domain.model_validate(self.model_dump()) - - -class PolicyViolationException(Exception): - _msg: str - - def __init__(self, message: str): - super().__init__(message) - self._msg = message - - @property - def message(self): - return self._msg diff --git a/altk/pre_tool/toolguard/toolguard/gen_py/__init__.py b/altk/pre_tool/toolguard/toolguard/gen_py/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/altk/pre_tool/toolguard/toolguard/gen_py/api_extractor.py b/altk/pre_tool/toolguard/toolguard/gen_py/api_extractor.py deleted file mode 100644 index ecda453..0000000 --- a/altk/pre_tool/toolguard/toolguard/gen_py/api_extractor.py +++ /dev/null @@ -1,746 +0,0 @@ -from dataclasses import is_dataclass -from enum import Enum -import inspect -import os -import textwrap -from types import FunctionType, UnionType -import types -from typing import ( - Callable, - DefaultDict, - Dict, - List, - Literal, - Optional, - Set, - Tuple, - get_type_hints, - get_origin, - get_args, -) -from typing import Annotated, Union -from collections import defaultdict, deque -import typing -from ..common.py import module_to_path -from ..data_types import FileTwin - -Dependencies = DefaultDict[type, Set[type]] - -class APIExtractor: - def __init__(self, py_path:str, include_module_roots:List[str] = []): - self.py_path = py_path - self.include_module_roots = include_module_roots - - def extract_from_functions( - self, - funcs: List[Callable], - interface_name: str, - interface_module_name: str, - types_module_name: str, - impl_module_name: str, - impl_class_name: str, - ) -> Tuple[FileTwin, FileTwin, FileTwin]: - assert all([_is_global_or_class_function(func) for func in funcs]) - - os.makedirs(self.py_path, exist_ok=True) - - # used types - types = FileTwin( - file_name=module_to_path(types_module_name), - content=self._generate_types_file( - *self._collect_all_types_from_functions(funcs) - ), - ).save(self.py_path) - - # API interface - interface = FileTwin( - file_name=module_to_path(interface_module_name), - content=self._generate_interface_from_functions( - funcs, interface_name, types_module_name - ), - ).save(self.py_path) - - # API impl interface - impl = FileTwin( - file_name=module_to_path(impl_module_name), - content=self._generate_impl_from_functions( - funcs, - impl_class_name, - interface_module_name, - interface_name, - types_module_name, - ), - ).save(self.py_path) - - return interface, types, impl - - def extract_from_class( - self, - typ: type, - *, - interface_name: Optional[str] = None, - interface_module_name: Optional[str] = None, - types_module_name: Optional[str] = None, - ) -> Tuple[FileTwin, FileTwin]: - """Extract interface and types from a class and save to files.""" - class_name = _get_type_name(typ) - interface_name = interface_name or "I_" + class_name - interface_module_name = interface_module_name or f"I_{class_name}".lower() - types_module_name = types_module_name or f"{class_name}_types".lower() - - os.makedirs(self.py_path, exist_ok=True) - - # Types - collected, dependencies = self._collect_all_types_from_class(typ) - types_content = self._generate_types_file(collected, dependencies) - types_file = module_to_path(types_module_name) - types = FileTwin(file_name=types_file, content=types_content).save(self.py_path) - - # API interface - if_content = self._generate_interface_from_class( - typ, interface_name, types_module_name - ) - if_file = module_to_path(interface_module_name) - interface = FileTwin(file_name=if_file, content=if_content).save(self.py_path) - - return interface, types - - def _generate_interface_from_class( - self, typ: type, interface_name: str, types_module: str - ) -> str: - # Start building the interface - lines = [ - "# Auto-generated class interface", - "from typing import * # type: ignore", - "from abc import ABC, abstractmethod", - f"from {types_module} import *", - "", - ] - - lines.append(f"class {interface_name}(ABC):") # Abstract class - - # Add class docstring if available - if typ.__doc__: - docstring = typ.__doc__.strip() - if docstring: - lines.append(' """') - # Handle multi-line docstrings - for line in docstring.split("\n"): - lines.append(f" {line.strip()}") - lines.append(' """') - - # Get all methods - methods = [] - for name, method in inspect.getmembers(typ, predicate=inspect.isfunction): - if not name.startswith("_"): - methods.append((name, method)) - - if not methods: - lines.append(" pass") - else: - for method_name, method in methods: - # Add method docstring and signature - lines.append(" @abstractmethod") - method_lines = self._get_function_with_docstring(method, method_name) - lines.extend([line if line else "" for line in method_lines]) - lines.append(" ...") - lines.append("") - - return textwrap.dedent("\n".join(lines)) - - def _generate_interface_from_functions( - self, funcs: List[Callable], interface_name: str, types_module: str - ) -> str: - lines = [ - "# Auto-generated class interface", - "from typing import * # type: ignore", - "from abc import ABC, abstractmethod", - f"from {types_module} import *", - "", - ] - - lines.append(f"class {interface_name}(ABC):") # Abstract class - lines.append("") - - indent = " " * 4 - if not funcs: - lines.append(f"{indent}pass") - else: - for func in funcs: - # Add method docstring and signature - lines.append(f"{indent}@abstractmethod") - method_lines = self._get_function_with_docstring( - func, _get_type_name(func) - ) - lines.extend([line if line else "" for line in method_lines]) - lines.append(f"{indent}{indent}...") - lines.append("") - - if any(["Decimal" in line for line in lines]): - lines.insert(2, "from decimal import Decimal") - - return "\n".join(lines) - - def _generate_impl_from_functions( - self, - funcs: List[Callable], - class_name: str, - interface_module_name: str, - interface_name: str, - types_module: str, - ) -> str: - lines = [ - "# Auto-generated class", - "from typing import *", - "from abc import ABC, abstractmethod", - f"from {interface_module_name} import {interface_name}", - f"from {types_module} import *", - "", - """class IToolInvoker(ABC): - T = TypeVar("T") - @abstractmethod - def invoke(self, toolname: str, arguments: Dict[str, Any], model: Type[T])->T: - ...""", - "", - ] - - lines.append(f"class {class_name}({interface_name}):") # class - lines.append("") - lines.append(""" def __init__(self, delegate: IToolInvoker): - self._delegate = delegate - """) - - if not funcs: - lines.append(" pass") - else: - for func in funcs: - # Add method docstring and signature - method_lines = self._get_function_with_docstring( - func, _get_type_name(func) - ) - lines.extend([line if line else "" for line in method_lines]) - lines.extend(self._generate_delegate_code(func)) - lines.append("") - - if any(["Decimal" in line for line in lines]): - lines.insert(2, "from decimal import Decimal") - - return "\n".join(lines) - - def _generate_delegate_code(self, func: Callable) -> List[str]: - func_name = _get_type_name(func) - indent = " " * 4 * 2 - sig = inspect.signature(func) - ret = sig.return_annotation - if ret is inspect._empty: - ret_name = "None" - elif hasattr(ret, "__name__"): - ret_name = ret.__name__ - else: - ret_name = str(ret) - return [ - indent + "args = {k: v for k, v in locals().items() if k != 'self'}", - indent + f"return self._delegate.invoke('{func_name}', args, {ret_name})", - ] - - def _get_function_with_docstring( - self, func: FunctionType, func_name: str - ) -> List[str]: - """Extract method signature with type hints and docstring.""" - lines = [] - - # Get method signature - method_signature = self._get_method_signature(func, func_name) - lines.append(f" {method_signature}:") - - # Add method docstring if available - if func.__doc__: - docstring = func.__doc__ - indent = " " * 8 - if docstring: - lines.append(indent + '"""') - lines.extend(docstring.strip("\n").split("\n")) - lines.append(indent + '"""') - - return lines - - def should_include_type(self, typ: type) -> bool: - if hasattr(typ, "__module__"): - module_root = typ.__module__.split(".")[0] - if module_root in self.include_module_roots: - return True - return any([self.should_include_type(arg) for arg in get_args(typ)]) - - def _generate_class_definition(self, typ: type) -> List[str]: - """Generate a class definition with its fields.""" - lines = [] - class_name = _get_type_name(typ) - - if is_dataclass(typ): - lines.append("@dataclass") - - # Determine base classes - bases = [_get_type_name(b) for b in _get_type_bases(typ)] - inheritance = f"({', '.join(bases)})" if bases else "" - lines.append(f"class {class_name}{inheritance}:") - - # #is Pydantic? - # is_pydantic = False - # for base in cls.__bases__: - # if hasattr(base, '__module__') and 'pydantic' in str(base.__module__): - # is_pydantic = True - - indent = " " * 4 - # Add class docstring if available - if typ.__doc__: - docstring = typ.__doc__ - if docstring: - lines.append(f'{indent}"""') - lines.extend( - [f"{indent}{line}" for line in docstring.strip("\n").split("\n")] - ) - lines.append(f'{indent}"""') - - # Fields - annotations = getattr(typ, "__annotations__", {}) - if annotations: - field_descriptions = self._extract_field_descriptions(typ) - for field_name, field_type in annotations.items(): - if field_name.startswith("_"): - continue - - # Handle optional field detection by default=None - is_optional = False - default_val = getattr(typ, field_name, ...) - if default_val is None: - is_optional = True - elif hasattr(typ, "__fields__"): - # Pydantic field with default=None - field_info = typ.__fields__.get(field_name) - if field_info and field_info.is_required() is False: - is_optional = True - - type_str = self._format_type(field_type) - - # Avoid wrapping Optional twice - if is_optional: - origin = get_origin(field_type) - args = get_args(field_type) - already_optional = ( - origin is typing.Union - and type(None) in args - or type_str.startswith("Optional[") - ) - if not already_optional: - type_str = f"Optional[{type_str}]" - - # Check if we have a description for this field - description = field_descriptions.get(field_name) - - # if description and is_pydantic: - # # Use Pydantic Field with description - # lines.append(f' {field_name}: {type_str} = Field(description="{description}")') - if description: - # Add description as comment for non-Pydantic classes - lines.append(f"{indent}{field_name}: {type_str} # {description}") - else: - # No description available - lines.append(f"{indent}{field_name}: {type_str}") - - # Enum - elif issubclass(typ, Enum): - if issubclass(typ, str): - lines.extend( - [f'{indent}{entry.name} = "{entry.value}"' for entry in typ] - ) - else: - lines.extend([f"{indent}{entry.name} = {entry.value}" for entry in typ]) - - else: - lines.append(f"{indent}pass") - - return lines - - def _extract_field_descriptions(self, typ: type) -> Dict[str, str]: - """Extract field descriptions from various sources.""" - descriptions = {} - - # Method 1: Check for Pydantic Field definitions - if hasattr(typ, "__fields__"): # Pydantic v1 - for field_name, field_info in typ.__fields__.items(): - if hasattr(field_info, "field_info") and hasattr( - field_info.field_info, "description" - ): - descriptions[field_name] = field_info.field_info.description - elif hasattr(field_info, "description") and field_info.description: - descriptions[field_name] = field_info.description - - # Method 2: Check for Pydantic v2 model fields - if hasattr(typ, "model_fields"): # Pydantic v2 - for field_name, field_info in typ.model_fields.items(): - if hasattr(field_info, "description") and field_info.description: - descriptions[field_name] = field_info.description - - # Method 3: Check class attributes for Field() definitions - for attr_name in dir(typ): - if not attr_name.startswith("_"): - try: - attr_value = getattr(typ, attr_name) - # Check if it's a Pydantic Field - if hasattr(attr_value, "description") and attr_value.description: - descriptions[attr_name] = attr_value.description - elif hasattr(attr_value, "field_info") and hasattr( - attr_value.field_info, "description" - ): - descriptions[attr_name] = attr_value.field_info.description - except Exception: - pass - - # Method 4: Parse class source for inline comments or docstrings - try: - source_lines = inspect.getsourcelines(typ)[0] - current_field = None - - for line in source_lines: - line = line.strip() - - # Look for field definitions with type hints - if ( - ":" in line - and not line.startswith("def ") - and not line.startswith("class ") - ): - # Extract field name - field_part = line.split(":")[0].strip() - if " " not in field_part and field_part.isidentifier(): - current_field = field_part - - # Look for comments on the same line or next line - if "#" in line and current_field: - comment = line.split("#", 1)[1].strip() - if comment and current_field not in descriptions: - descriptions[current_field] = comment - current_field = None - - except Exception: - pass - - # Method 5: Check for dataclass field descriptions - if hasattr(typ, "__dataclass_fields__"): - for field_name, field in typ.__dataclass_fields__.items(): - if hasattr(field, "metadata") and "description" in field.metadata: - descriptions[field_name] = field.metadata["description"] - - return descriptions - - def _get_method_signature(self, method: FunctionType, method_name: str): - """Extract method signature with type hints.""" - try: - sig = inspect.signature(method) - # Get param hints - try: - param_hints = get_type_hints(method) - except Exception: - param_hints = {} - - params = [] - if not sig.parameters.get("self"): - params.append("self") - - for param_name, param in sig.parameters.items(): - param_str = param_name - - # Add type annotation if available - if param_name in param_hints: - type_str = self._format_type(param_hints[param_name]) - param_str += f": {type_str}" - elif param.annotation != param.empty: - param_str += f": {param.annotation}" - - # Add default value if present - if param.default != param.empty: - if isinstance(param.default, str): - param_str += f' = "{param.default}"' - else: - param_str += f" = {repr(param.default)}" - - params.append(param_str) - - # Handle return type - return_annotation = "" - if "return" in param_hints: - if param_hints["return"] is not type(None): - return_type = self._format_type(param_hints["return"]) - return_annotation = f" -> {return_type}" - elif sig.return_annotation != sig.empty: - return_annotation = f" -> {sig.return_annotation}" - - params_str = ", ".join(params) - return f"def {method_name}({params_str}){return_annotation}" - - except Exception: - # Fallback for problematic signatures - return f"def {method_name}(self, *args, **kwargs)" - - def _collect_all_types_from_functions( - self, funcs: List[Callable] - ) -> Tuple[Set[type], Dependencies]: - processed_types = set() - collected = set() - dependencies = defaultdict(set) - - for func in funcs: - for param, hint in get_type_hints(func).items(): # noqa: B007 - self._collect_types_recursive( - hint, processed_types, collected, dependencies - ) - - return collected, dependencies - - def _collect_all_types_from_class( - self, typ: type - ) -> Tuple[Set[type], Dependencies]: - """Collect all types used in the class recursively.""" - visited = set() - collected = set() - dependencies = defaultdict(set) - - # Field types - try: - class_hints = get_type_hints(typ) - for field, hint in class_hints.items(): # noqa: B007 - self._collect_types_recursive(hint, visited, collected, dependencies) - except Exception: - pass - - # Methods and param types - for name, method in inspect.getmembers(typ, predicate=inspect.isfunction): # noqa: B007 - try: - method_hints = get_type_hints(method) - for hint in method_hints.values(): - self._collect_types_recursive( - hint, visited, collected, dependencies - ) - except Exception: - pass - - # Also collect base class types - for base in _get_type_bases(typ): - self._collect_types_recursive(base, visited, collected, dependencies) - - return collected, dependencies - - def _collect_types_recursive( - self, typ: type, visited: Set[type], acc: Set[type], dependencies: Dependencies - ): - """Recursively collect all types from a type hint.""" - visited.add(typ) - - if not self.should_include_type(typ): - return - - acc.add(typ) - origin = get_origin(typ) - args = get_args(typ) - - # Type with generic arguments. eg: List[Person] - if origin and args: - for f_arg in args: - self._collect_types_recursive(f_arg, visited, acc, dependencies) - self._add_dependency(typ, f_arg, dependencies) - return - - # If it's a custom class, try to get its type hints - try: - field_hints = typ.__annotations__ # direct fields - for field_name, field_hint in field_hints.items(): # noqa: B007 - f_origin = get_origin(field_hint) - if f_origin: - for f_arg in get_args(field_hint): - self._collect_types_recursive(f_arg, visited, acc, dependencies) - self._add_dependency(typ, f_arg, dependencies) - else: - self._collect_types_recursive( - field_hint, visited, acc, dependencies - ) - self._add_dependency(typ, field_hint, dependencies) - - for base in _get_type_bases(typ): # Base classes - self._collect_types_recursive(base, visited, acc, dependencies) - self._add_dependency(typ, base, dependencies) - except Exception: - pass - - def _add_dependency( - self, dependent_type: type, dependency_type: type, dependencies: Dependencies - ): - """Add a dependency relationship between types.""" - dep_name = _get_type_name(dependent_type) - dep_on_name = _get_type_name(dependency_type) - if dep_name != dep_on_name: - dependencies[dependent_type].add(dependency_type) - - for arg in get_args(dependency_type): - dependencies[dependent_type].add(arg) - - def _topological_sort_types(self, types: List[type], dependencies: Dependencies): - """Sort types by their dependencies using topological sort.""" - # Create a mapping of type names to types for easier lookup - type_map = {_get_type_name(t): t for t in types} - - # Build adjacency list and in-degree count - adj_list = defaultdict(list) - in_degree = defaultdict(int) - - # Initialize in-degree for all types - for t in types: - type_name = _get_type_name(t) - if type_name not in in_degree: - in_degree[type_name] = 0 - - # Build the dependency graph - for dependent_type in types: - dependent_name = _get_type_name(dependent_type) - for dependency_type in dependencies.get(dependent_type, set()): - dependency_name = _get_type_name(dependency_type) - if ( - dependency_name in type_map - ): # Only consider types we're actually processing - adj_list[dependency_name].append(dependent_name) - in_degree[dependent_name] += 1 - - # Kahn's algorithm for topological sorting - queue = deque([name for name in in_degree if in_degree[name] == 0]) - result = [] - - while queue: - current = queue.popleft() - result.append(type_map[current]) - - for neighbor in adj_list[current]: - in_degree[neighbor] -= 1 - if in_degree[neighbor] == 0: - queue.append(neighbor) - - # If we couldn't sort all types, there might be circular dependencies - # Add remaining types at the end - sorted_names = {_get_type_name(t) for t in result} - remaining = [t for t in types if _get_type_name(t) not in sorted_names] - result.extend(remaining) - - return result - - def _generate_types_file( - self, collected_types: Set[type], dependencies: Dependencies - ) -> str: - """Generate the types file content.""" - lines = [] - lines.append("# Auto-generated type definitions") - lines.append("from datetime import date, datetime") - lines.append("from enum import Enum") - lines.append("from typing import *") - lines.append("from pydantic import BaseModel, Field") - lines.append("from dataclasses import dataclass") - lines.append("") - - custom_classes = [] - for typ in collected_types: - # Check if it's a class with attributes - if hasattr(typ, "__annotations__") or ( - hasattr(typ, "__dict__") - and any( - not callable(getattr(typ, attr, None)) - for attr in dir(typ) - if not attr.startswith("_") - ) - ): - custom_classes.append(typ) - custom_classes = self._topological_sort_types(custom_classes, dependencies) - - # Generate custom classes (sorted by dependency) - for cls in custom_classes: - class_def = self._generate_class_definition(cls) - if class_def: # Only add non-empty class definitions - lines.extend(class_def) - lines.append("") - - if any(["Decimal" in line for line in lines]): - lines.insert(2, "from decimal import Decimal") - - return "\n".join(lines) - - def _format_type(self, typ: type) -> str: - if typ is None: - return "Any" - - # Unwrap Annotated[T, ...] - origin = get_origin(typ) - if origin is Annotated: - typ = get_args(typ)[0] - origin = get_origin(typ) - - # Literal - if origin is Literal: - args = get_args(typ) - literals = ", ".join(repr(a) for a in args) - return f"Literal[{literals}]" - - # Union (Optional or other) - if origin is Union: - args = get_args(typ) - non_none = [a for a in args if a is not type(None)] - if len(non_none) == 1: - return f"Optional[{self._format_type(non_none[0])}]" - else: - inner = ", ".join(self._format_type(a) for a in args) - return f"Union[{inner}]" - - if origin is UnionType: - args = get_args(typ) - return "| ".join(self._format_type(a) for a in args) - - # Generic containers - if origin: - args = get_args(typ) - inner = ", ".join(self._format_type(a) for a in args) - if inner: - return f"{_get_type_name(origin)}[{inner}]" - return _get_type_name(origin) - - # Simple type - return _get_type_name(origin or typ) - - -def _get_type_name(typ) -> str: - """Get a consistent name for a type object.""" - if hasattr(typ, "__name__"): - return typ.__name__ - return str(typ) - - -def _get_type_bases(typ: type) -> List[type]: - if hasattr(typ, "__bases__"): - return typ.__bases__ # type: ignore - return [] - - -def _is_global_or_class_function(func): - if not callable(func): - return False - - # Reject lambdas - if _get_type_name(func) == "": - return False - - # Static methods and global functions are of type FunctionType - if isinstance(func, types.FunctionType): - return True - - # Class methods are MethodType but have __self__ as the class, not instance - if isinstance(func, types.MethodType): - if inspect.isclass(func.__self__): - return True # classmethod - else: - return False # instance method - - return False diff --git a/altk/pre_tool/toolguard/toolguard/gen_py/consts.py b/altk/pre_tool/toolguard/toolguard/gen_py/consts.py deleted file mode 100644 index 1ab6a75..0000000 --- a/altk/pre_tool/toolguard/toolguard/gen_py/consts.py +++ /dev/null @@ -1,31 +0,0 @@ -from ..common.str import to_snake_case -from ..data_types import ToolGuardSpec, ToolGuardSpecItem - - -RUNTIME_PACKAGE_NAME = "rt_toolguard" -RUNTIME_INIT_PY = "__init__.py" -RUNTIME_TYPES_PY = "data_types.py" -RUNTIME_APP_TYPES_PY = "domain_types.py" - -def guard_fn_name(tool_policy: ToolGuardSpec) -> str: - return to_snake_case(f"guard_{tool_policy.tool_name}") - - -def guard_fn_module_name(tool_policy: ToolGuardSpec) -> str: - return to_snake_case(f"guard_{tool_policy.tool_name}") - - -def guard_item_fn_name(tool_item: ToolGuardSpecItem) -> str: - return to_snake_case(f"guard_{tool_item.name}") - - -def guard_item_fn_module_name(tool_item: ToolGuardSpecItem) -> str: - return to_snake_case(f"guard_{tool_item.name}") - - -def test_fn_name(tool_item: ToolGuardSpecItem) -> str: - return to_snake_case(f"test_guard_{tool_item.name}") - - -def test_fn_module_name(tool_item: ToolGuardSpecItem) -> str: - return to_snake_case(f"test_guard_{tool_item.name}") diff --git a/altk/pre_tool/toolguard/toolguard/gen_py/domain_from_funcs.py b/altk/pre_tool/toolguard/toolguard/gen_py/domain_from_funcs.py deleted file mode 100644 index 0a170e2..0000000 --- a/altk/pre_tool/toolguard/toolguard/gen_py/domain_from_funcs.py +++ /dev/null @@ -1,56 +0,0 @@ -import os -from pathlib import Path -from typing import Callable, List -from os.path import join - -from .api_extractor import APIExtractor -from ..common.str import to_camel_case, to_snake_case -from ..data_types import FileTwin, RuntimeDomain -from . import consts - - -def generate_domain_from_functions( - py_path: str, app_name: str, funcs: List[Callable], include_module_roots: List[str] -) -> RuntimeDomain: - # ToolGuard Runtime - os.makedirs(join(py_path, consts.RUNTIME_PACKAGE_NAME), exist_ok=True) - - root = str(Path(__file__).parent.parent) - common = FileTwin.load_from(root, "data_types.py").save_as( - py_path, join(consts.RUNTIME_PACKAGE_NAME, consts.RUNTIME_TYPES_PY) - ) - runtime = FileTwin.load_from(root, "runtime.py") - # runtime.content = runtime.content.replace( - # "toolguard.", f"{consts.RUNTIME_PACKAGE_NAME}." - # ) - runtime.save_as(py_path, join(consts.RUNTIME_PACKAGE_NAME, consts.RUNTIME_INIT_PY)) - - # APP init and Types - os.makedirs(join(py_path, to_snake_case(app_name)), exist_ok=True) - FileTwin(file_name=join(to_snake_case(app_name), "__init__.py"), content="").save( - py_path - ) - - extractor = APIExtractor(py_path=py_path, include_module_roots=include_module_roots) - api_cls_name = f"I_{to_camel_case(app_name)}" - impl_module_name = to_snake_case(f"{app_name}.{app_name}_impl") - impl_class_name = to_camel_case(f"{app_name}_Impl") - api, types, impl = extractor.extract_from_functions( - funcs, - interface_name=api_cls_name, - interface_module_name=to_snake_case(f"{app_name}.i_{app_name}"), - types_module_name=to_snake_case(f"{app_name}.{app_name}_types"), - impl_module_name=impl_module_name, - impl_class_name=impl_class_name, - ) - - return RuntimeDomain( - app_name=app_name, - toolguard_common=common, - app_types=types, - app_api_class_name=api_cls_name, - app_api=api, - app_api_impl_class_name=impl_class_name, - app_api_impl=impl, - app_api_size=len(funcs), - ) diff --git a/altk/pre_tool/toolguard/toolguard/gen_py/domain_from_openapi.py b/altk/pre_tool/toolguard/toolguard/gen_py/domain_from_openapi.py deleted file mode 100644 index 223385b..0000000 --- a/altk/pre_tool/toolguard/toolguard/gen_py/domain_from_openapi.py +++ /dev/null @@ -1,244 +0,0 @@ -import os -from pathlib import Path -from typing import List, Optional, Tuple, Union -from os.path import join - -from ..common.array import find -from ..common.py import module_to_path -from ..common.str import to_camel_case, to_pascal_case, to_snake_case -from . import consts -from .templates import load_template -from .utils.datamodel_codegen import ( - run as dm_codegen, -) -from ..common.open_api import ( - OpenAPI, - Operation, - Parameter, - ParameterIn, - PathItem, - Reference, - RequestBody, - Response, - JSchema, - read_openapi, -) -from ..data_types import FileTwin, RuntimeDomain - -ARGS = "args" - -def generate_domain_from_openapi( - py_path: str, app_name: str, openapi_file: str -) -> RuntimeDomain: - # ToolGuard Runtime - os.makedirs(join(py_path, consts.RUNTIME_PACKAGE_NAME), exist_ok=True) - - root = str(Path(__file__).parent.parent) - common = FileTwin.load_from(root, "data_types.py").save_as( - py_path, join(consts.RUNTIME_PACKAGE_NAME, consts.RUNTIME_TYPES_PY) - ) - runtime = FileTwin.load_from(root, "runtime.py") - # runtime.content = runtime.content.replace( - # "toolguard.", f"{consts.RUNTIME_PACKAGE_NAME}." - # ) - runtime.save_as(py_path, join(consts.RUNTIME_PACKAGE_NAME, consts.RUNTIME_INIT_PY)) - - # APP Types - oas = read_openapi(openapi_file) - os.makedirs(join(py_path, to_snake_case(app_name)), exist_ok=True) - - types_name = f"{app_name}_types" - types_module_name = f"{app_name}.{types_name}" - types = FileTwin( - file_name=module_to_path(types_module_name), content=dm_codegen(openapi_file) - ).save(py_path) - - # APP Init - FileTwin( - file_name=join(to_snake_case(app_name), "__init__.py"), - content=f"from . import {types_name}", - ).save(py_path) - - # APP API - api_cls_name = to_camel_case("I " + app_name) - methods = _get_oas_methods(oas) - api_module_name = to_snake_case(f"{app_name}.i_{app_name}") - api = FileTwin( - file_name=module_to_path(api_module_name), - content=_generate_api(methods, api_cls_name, types_module_name), - ).save(py_path) - - # APP API Impl - impl_cls_name = to_camel_case(app_name + " impl") - impl_module_name = to_snake_case(f"{app_name}.{app_name}_impl") - cls_str = _generate_api_impl( - methods, api_module_name, types_module_name, api_cls_name, impl_cls_name - ) - api_impl = FileTwin( - file_name=module_to_path(impl_module_name), content=cls_str - ).save(py_path) - - return RuntimeDomain( - app_name=app_name, - toolguard_common=common, - app_types=types, - app_api_class_name=api_cls_name, - app_api=api, - app_api_impl_class_name=impl_cls_name, - app_api_impl=api_impl, - app_api_size=len(methods), - ) - - -def _get_oas_methods(oas: OpenAPI): - methods = [] - for path, path_item in oas.paths.items(): # noqa: B007 - path_item = oas.resolve_ref(path_item, PathItem) - assert path_item - for mtd, op in path_item.operations.items(): # noqa: B007 - op = oas.resolve_ref(op, Operation) - if not op: - continue - params = (path_item.parameters or []) + (op.parameters or []) - params = [oas.resolve_ref(p, Parameter) for p in params] - args, ret = _make_signature(op, params, oas) # type: ignore - args_str = ", ".join(["self"] + [f"{arg}:{type}" for arg, type in args]) - sig = f"({args_str})->{ret}" - - body = f"return self._delegate.invoke('{to_snake_case(op.operationId)}', {ARGS}.model_dump(), {ret})" - # if orign_funcs: - # func = find(orign_funcs or [], lambda fn: fn.__name__ == op.operationId) # type: ignore - # if func: - # body = _call_fn_body(func) - methods.append( - { - "name": to_snake_case(op.operationId), # type: ignore - "signature": sig, - "doc": op.description, - "body": body, - } - ) - return methods - - -# def _call_fn_body(func:Callable): -# module = inspect.getmodule(func) -# if module is None or not hasattr(module, '__file__'): -# raise ValueError("Function must be from an importable module") - -# module_name = module.__name__ -# qualname = func.__qualname__ -# func_name = func.__name__ -# parts = qualname.split('.') - -# if len(parts) == 1: # Regular function -# return f""" -# mod = importlib.import_module("{module_name}") -# func = getattr(mod, "{func_name}") -# return func(locals())""" - -# if len(parts) == 2: # Classmethod or staticmethod -# class_name = parts[0] -# return f""" -# mod = importlib.import_module("{module_name}") -# cls = getattr(mod, "{class_name}") -# func = getattr(cls, "{func_name}") -# return func(locals())""" - -# if len(parts) > 2: # Instance method -# class_name = parts[-2] -# return f""" -# mod = importlib.import_module("{module_name}") -# cls = getattr(mod, "{class_name}") -# instance = cls() -# func = getattr(instance, "{func_name}") -# return func(locals())""" -# raise NotImplementedError("Unsupported function type or nested depth") - - -def _generate_api(methods: List, cls_name: str, types_module: str) -> str: - return load_template("api.j2").render( - types_module=types_module, class_name=cls_name, methods=methods - ) - - -def _generate_api_impl( - methods: List, api_module: str, types_module: str, api_cls_name: str, cls_name: str -) -> str: - return load_template("api_impl.j2").render( - api_cls_name=api_cls_name, - types_module=types_module, - api_module=api_module, - class_name=cls_name, - methods=methods, - ) - - -def _make_signature( - op: Operation, params: List[Parameter], oas: OpenAPI -) -> Tuple[Tuple[str, str], str]: - fn_name = to_camel_case(op.operationId) - args = [] - - for param in params: - if param.in_ == ParameterIn.path: - args.append((param.name, _oas_to_py_type(param.schema_, oas) or "Any")) - - if find(params, lambda p: p.in_ == ParameterIn.query): - query_type = f"{fn_name}ParametersQuery" - args.append((ARGS, query_type)) - - req_body = oas.resolve_ref(op.requestBody, RequestBody) - if req_body: - scm_or_ref = req_body.content_json.schema_ - body_type = _oas_to_py_type(scm_or_ref, oas) - if body_type is None: - body_type = f"{fn_name}Request" - args.append((ARGS, body_type)) - - rsp_or_ref = op.responses.get("200") - rsp = oas.resolve_ref(rsp_or_ref, Response) - if rsp: - scm_or_ref = rsp.content_json.schema_ - if scm_or_ref: - rsp_type = _oas_to_py_type(scm_or_ref, oas) - if rsp_type is None: - rsp_type = f"{fn_name}Response" - else: - rsp_type = "Any" - return args, rsp_type - - -def _oas_to_py_type(scm_or_ref: Union[Reference, JSchema], oas: OpenAPI) -> str | None: - if isinstance(scm_or_ref, Reference): - typ = scm_or_ref.ref.split("/")[-1] - return to_pascal_case(typ) - - scm = oas.resolve_ref(scm_or_ref, JSchema) - if scm: - py_type = _primitive_jschema_types_to_py(scm.type, scm.format) - if py_type: - return py_type - # if scm.type == JSONSchemaTypes.array and scm.items: - # return f"List[{oas_to_py_type(scm.items, oas) or 'Any'}]" - - -def _primitive_jschema_types_to_py( - type: Optional[str], format: Optional[str] -) -> Optional[str]: - # https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.2.md#data-types - if type == "string": - if format == "date": - return "datetime.date" - if format == "date-time": - return "datetime.datetime" - if format in ["byte", "binary"]: - return "bytes" - return "str" - if type == "integer": - return "int" - if type == "number": - return "float" - if type == "boolean": - return "bool" - return None diff --git a/altk/pre_tool/toolguard/toolguard/gen_py/gen_toolguards.py b/altk/pre_tool/toolguard/toolguard/gen_py/gen_toolguards.py deleted file mode 100644 index 921e5ad..0000000 --- a/altk/pre_tool/toolguard/toolguard/gen_py/gen_toolguards.py +++ /dev/null @@ -1,92 +0,0 @@ -import asyncio -import logging -from typing import Callable, List, Optional - -import mellea - -from .domain_from_funcs import generate_domain_from_functions -from ..data_types import RuntimeDomain, ToolGuardSpec, MeleaSessionData -from .domain_from_openapi import generate_domain_from_openapi -from ..runtime import ToolGuardsCodeGenerationResult -from .tool_guard_generator import ToolGuardGenerator -from .utils import pytest, pyright - -logger = logging.getLogger(__name__) - -async def generate_toolguards_from_functions( - app_name: str, - tool_policies: List[ToolGuardSpec], - py_root: str, - funcs: List[Callable], - llm_data: MeleaSessionData, - module_roots: Optional[List[str]] = None, -) -> ToolGuardsCodeGenerationResult: - assert funcs, "Funcs cannot be empty" - logger.debug(f"Starting... will save into {py_root}") - - if not module_roots: - if len(funcs) > 0: - module_roots = list( - {func.__module__.split(".")[0] for func in funcs} - ) - assert module_roots - - # Domain from functions - domain = generate_domain_from_functions(py_root, app_name, funcs, module_roots) - return await generate_toolguards_from_domain( - app_name, tool_policies, py_root, domain, llm_data - ) - - -async def generate_toolguards_from_openapi( - app_name: str, tool_policies: List[ToolGuardSpec], py_root: str, openapi_file: str, llm_data: MeleaSessionData -) -> ToolGuardsCodeGenerationResult: - logger.debug(f"Starting... will save into {py_root}") - - # Domain from OpenAPI - domain = generate_domain_from_openapi(py_root, app_name, openapi_file) - return await generate_toolguards_from_domain( - app_name, tool_policies, py_root, domain, llm_data - ) - -async def generate_toolguards_from_domain( - app_name: str, tool_policies: List[ToolGuardSpec], py_root: str, domain: RuntimeDomain, - llm_data: MeleaSessionData -) -> ToolGuardsCodeGenerationResult: - # Setup env - # venv.run(join(py_root, consts.PY_ENV), consts.PY_PACKAGES) - pyright.config(py_root) - pytest.configure(py_root) - - for tool_policy in tool_policies: - for policy in tool_policy.policy_items: - policy.name = policy.name.replace(".","_") - - with mellea.start_session( - backend_name = llm_data.backend_name, - model_id=llm_data.model_id, - **llm_data.kw_args - ): - # tools - tools_w_poilicies = [ - tool_policy - for tool_policy in tool_policies - if len(tool_policy.policy_items) > 0 - ] - tool_results = await asyncio.gather( - *[ - ToolGuardGenerator( - app_name, tool_policy, py_root, domain - ).generate() - for tool_policy in tools_w_poilicies - ] - ) - - tools_result = { - tool.tool_name: res for tool, res in zip(tools_w_poilicies, tool_results) - } - return ToolGuardsCodeGenerationResult( - out_dir=py_root, - domain=domain, - tools=tools_result - ).save(py_root) diff --git a/altk/pre_tool/toolguard/toolguard/gen_py/prompts/__init__.py b/altk/pre_tool/toolguard/toolguard/gen_py/prompts/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/altk/pre_tool/toolguard/toolguard/gen_py/prompts/gen_tests.py b/altk/pre_tool/toolguard/toolguard/gen_py/prompts/gen_tests.py deleted file mode 100644 index 20b79be..0000000 --- a/altk/pre_tool/toolguard/toolguard/gen_py/prompts/gen_tests.py +++ /dev/null @@ -1,135 +0,0 @@ -from typing import List -from ...data_types import Domain, FileTwin, ToolGuardSpecItem -from mellea import generative - -# from toolguard.gen_py.prompts.python_code import PythonCodeModel - - -@generative -def generate_init_tests( - fn_src: FileTwin, - policy_item: ToolGuardSpecItem, - domain: Domain, - dependent_tool_names: List[str], -) -> str: - """ - Generate Python unit tests for a function to verify tool-call compliance with policy constraints. - - Args: - fn_src (FileTwin): Source code containing the function-under-test signature. - policy_item (ToolGuardSpecItem): Specification of the function-under-test, including positive and negative examples. - domain (Domain): available data types and interfaces the test can use. - dependent_tool_names (List[str]): other tool names that this tool depends on. - - Returns: - str: Generated Python unit test code. - - This function creates unit tests to validate the behavior of a given function-under-test. - The function-under-test checks the argument data, and raise an exception if they violated the requirements in the policy item. - - Test Generation Rules: - - Make sure to Python import all items in fn_src, common and domain modules. - - A `policy_item` has multiple `compliance_examples` and `violation_examples` examples. - - For each `compliance_examples`, ONE test method is generated. - - For each `violation_examples`, ONE test method is generated. - - The function-under-test is EXPECTED to raise a `PolicyViolationException`. - - use `with pytest.raises(PolicyViolationException): function_under_test()` to expect for exceptions. - - - Test class and method names should be meaningful and use up to **six words in snake_case**. - - For each test, add a comment quoting the policy item case that this function is testing - - Failure message should describe the test scenario that failed, the expected and the actual outcomes. - - Data population and references: - - For compliance examples, populate all fields. - - For collections (arrays, dict and sets) populate at least one item. - - You should mock the return_value from ALL tools listed in `dependent_tool_names`. - - Use `side_effect` to return the expected value only when the expected parameters are passed. - - For time dependent attributes, compute the timestamp dynamically (avoid hardcoded times). - - for example, to set a timestamp occurred 24 hours ago, use something like: `created_at = (datetime.now() - timedelta(hours=24)).strftime("%Y-%m-%dT%H:%M:%S")`. - - import the required date and time libraries. for example: `from datetime import datetime, timedelta` - - If you have a choice passing a plain a Pydantic model or a `Dictionary`, prefer Pydantic. - - Example: - * fn_src: - ```python - # file: my_app/guard_create_reservation.py - def guard_create_reservation(api: SomeAPI, user_id: str, hotel_id: str, reservation_date: str, persons: int): - ... - ``` - * policy_item.description = "cannot book a room for a date in the past" - * policy_item.violation_examples = ["book a room for a hotel, one week ago"] - * Dependent_tool_names: `["get_user", "get_hotel"]` - * Domain: - ```python - # file: my_app/api.py - class SomeAPI(ABC): - def get_user(self, user_id): - ... - def get_hotel(self, hotel_id): - ... - def create_reservation(self, user_id: str, hotel_id: str, reservation_date: str, persons: int): - \"\"\" - Args: - ... - reservation_date: check in date, in `YYYY-MM-DDTHH:MM:SS` format - \"\"\" - ... - ``` - - Should return this snippet: - ```python - from unittest.mock import MagicMock - import pytest - from toolguard.data_types import PolicyViolationException - from my_app.guard_create_reservation import guard_create_reservation - from my_app.api import * - - def test_violation_book_room_in_the_past(): - \"\"\" - Policy: "cannot book room for a date in the past" - Example: "book a room for a hotel, one week ago" - \"\"\" - - # mock other tools function return values - user = User(user_id="123", ...) - hotel = Hotel(hotel_id="789", ...) - - api = MagicMock(spec=SomeAPI) - api.get_user.side_effect = lambda user_id: user if user_id == "123" else None - api.get_hotel.side_effect = lambda hotel_id: hotel if hotel_id == "789" else None - - #invoke function under test. - with pytest.raises(PolicyViolationException): - next_week = (datetime.now() + timedelta(days=7)).strftime("%Y-%m-%dT%H:%M:%S") - guard_create_reservation(api, user_id="123", hotel_id="789", reservation_date=next_week, persons=3) - ``` - """ - ... - - -@generative -def improve_tests( - prev_impl: str, - domain: Domain, - policy_item: ToolGuardSpecItem, - review_comments: List[str], - dependent_tool_names: List[str], -) -> str: - """ - Improve the previous test functions (in Python) to check the given tool policy-items according to the review-comments. - - Args: - prev_impl (str): previous implementation of a Python function. - domain (Domain): Python source code defining available data types and APIs that the test can use. - tool (ToolGuardSpecItem): Requirements for this tool. - review_comments (List[str]): Review comments on the current implementation. For example, pylint errors or Failed unit-tests. - dependent_tool_names (List[str]): other tool names that this tool depends on. - - Returns: - str: Improved implementation pytest test functions. - - Implementation Rules: - - Do not change the function signatures. - - You can add import statements, but dont remove them. - """ - ... diff --git a/altk/pre_tool/toolguard/toolguard/gen_py/prompts/improve_guard.py b/altk/pre_tool/toolguard/toolguard/gen_py/prompts/improve_guard.py deleted file mode 100644 index 5070fd3..0000000 --- a/altk/pre_tool/toolguard/toolguard/gen_py/prompts/improve_guard.py +++ /dev/null @@ -1,64 +0,0 @@ -from typing import List -from ...data_types import Domain, ToolGuardSpecItem -from mellea import generative - -# from toolguard.gen_py.prompts.python_code import PythonCodeModel - - -@generative -def improve_tool_guard( - prev_impl: str, - domain: Domain, - policy_item: ToolGuardSpecItem, - dependent_tool_names: List[str], - review_comments: List[str], -) -> str: - """ - Improve the previous tool-call guard implementation (in Python) so that it fully adheres to the given policy and addresses all review comments. - - Args: - prev_impl (str): The previous implementation of the tool-call check. - domain (Domain): Python code defining available data types and other tool interfaces. - policy_item (ToolGuardSpecItem): Requirements for this tool. - dependent_tool_names (List[str]): Names of other tools that this tool may call to obtain required information. - review_comments (List[str]): Review feedback on the current implementation (e.g., pylint errors, failed unit tests). - - Returns: - str: The improved implementation of the tool-call check. - - Implementation Rules: - - Do not modify the function signature, parameter names, or type annotations. - - All policy requirements must be validated. - - Keep the implementation simple and well-documented. - - Only validate the tool-call arguments; never call the tool itself. - - If additional information is needed beyond the function arguments, use only the APIs of tools listed in `dependent_tool_names`. - - Generate code that enforces the given policy only, do not generate any additional logic that is not explicitly mentioned in the policy. - - **Example: ** - prev_impl = ```python - from typing import * - from airline.airline_types import * - from airline.i_airline import I_Airline - - def guard_Checked_Bag_Allowance_by_Membership_Tier(api: I_Airline, user_id: str, passengers: list[Passenger]): - \"\"\" - Limit to five passengers per reservation. - \"\"\" - pass #FIXME - ``` - - should return something like: - ```python - from typing import * - from airline.airline_types import * - from airline.i_airline import I_Airline - - def guard_Checked_Bag_Allowance_by_Membership_Tier(api: I_Airline, user_id: str, passengers: list[Passenger]): - \"\"\" - Limit to five passengers per reservation. - \"\"\" - if len(passengers) > 5: - raise PolicyViolationException("More than five passengers are not allowed.") - ``` - """ - ... diff --git a/altk/pre_tool/toolguard/toolguard/gen_py/prompts/pseudo_code.py b/altk/pre_tool/toolguard/toolguard/gen_py/prompts/pseudo_code.py deleted file mode 100644 index 5f9e324..0000000 --- a/altk/pre_tool/toolguard/toolguard/gen_py/prompts/pseudo_code.py +++ /dev/null @@ -1,124 +0,0 @@ -from ...data_types import Domain, ToolGuardSpecItem -from mellea import generative - - -@generative -def tool_policy_pseudo_code( - policy_item: ToolGuardSpecItem, fn_to_analyze: str, domain: Domain -) -> str: - """ - Returns a pseudo code to check business constraints on a tool cool using an API - - Args: - policy_item (ToolGuardSpecItem): Business policy, in natural language, specifying a constraint on a process involving the tool under analysis. - fn_to_analyze (str): The function signature of the tool under analysis. - domain (Domain): Python code defining available data types and APIs for invoking other tools. - - Returns: - str: A pseudo code descibing how to use the API to check the tool call - - * The available API functions are listed in the `domain.app_api.content`. - * Analyze the API functions' signatures (input and output parameter types). - * You cannot assume other API functions. - * For data objects (dataclasses or Pydantic models), only reference the explicitly declared fields. - * Do not assume the presence of any additional fields. - * Do not assume any implicit logic or relationships between field values (e.g., naming conventions). - * List all the required API calls to check the business constraints. - * If some information is missing, you should call another api function declared in the domain API. - - Examples: - ```python - domain = { - "app_types": { - "file_name": "car_types.py", - "content": ''' - class CarType(Enum): - SEDAN = "sedan" - SUV = "suv" - VAN = "van" - class Car: - plate_num: str - car_type: CarType - class Person: - id: str - driving_licence: str - class Insurance: - doc_id: str - class CarOwnership: - owenr_id: str - start_date: str - end_date: str - ''' - }, - "app_api": { - "file_name": "cars_api.py", - "content": ''' - class CarAPI(ABC): - def buy_car(self, plate_num: str, owner_id: str, insurance_id: str): pass - def get_person(self, id: str) -> Person: pass - def get_insurance(self, id: str) -> Insurance: pass - def get_car(self, plate_num: str) -> Car: pass - def car_ownership_history(self, plate_num: str) -> List[CarOwnership]: pass - def delete_car(self, plate_num: str): pass - def list_all_cars_owned_by(self, id: str) -> List[Car]: pass - def are_relatives(self, person1_id: str, person2_id: str) -> bool: pass - ''' - } - } - ``` - * Example 1: - ``` - tool_policy_pseudo_code( - {"name": "documents exists", "description": "when buying a car, check that the car owner has a driving licence and that the insurance is valid."}, - "buy_car(plate_num: str, owner_id: str, insurance_id: str)", - domain - ) - ``` - may return: - ``` - assert api.get_person(owner_id).driving_licence - assert api.get_insurance(insurance_id) - ``` - - * Example 2: - ``` - tool_policy_pseudo_code( - {"name": "has driving licence", "description": "when buying a car, check that the car owner has a driving licence"}, - "buy_car(plate_num: str, owner_id: str, insurance_id: str)", - domain - ) - ``` - may return: - ``` - assert api.get_insurance(insurance_id) - ``` - - * Example 3: - ``` - tool_policy_pseudo_code( - {"name": "no transfers on holidays", "description": "when buying a car, check that it is not a holiday today"}, - "buy_car(plate_num: str, owner_id: str, insurance_id: str)", - domain - ) - ``` - should return an empty string. - - * Example 4: - ``` - tool_policy_pseudo_code( - {"name": "Not in the same family", - "description": "when buying a van, check that the van was never owned by someone from the buyer's family."}, - "buy_car(plate_num: str, owner_id: str, insurance_id: str)", - domain - ) - ``` - should return: - ``` - car = api.get_car(plate_num) - if car.car_type == CarType.VAN: - history = api.car_ownership_history(plate_num) - for each ownership in history: - assert(not api.are_relatives(ownership.owenr_id, owner_id)) - ``` - """ - ... diff --git a/altk/pre_tool/toolguard/toolguard/gen_py/templates/__init__.py b/altk/pre_tool/toolguard/toolguard/gen_py/templates/__init__.py deleted file mode 100644 index 049d3ae..0000000 --- a/altk/pre_tool/toolguard/toolguard/gen_py/templates/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -from pathlib import Path -from jinja2 import Environment, FileSystemLoader, select_autoescape - -from ...common.py import path_to_module -from ...common.str import to_snake_case - -TEMPLATES_DIR = Path(__file__).parent - -env = Environment( - loader=FileSystemLoader(str(TEMPLATES_DIR)), - autoescape=select_autoescape(), -) -env.globals["path_to_module"] = path_to_module -env.globals["to_snake_case"] = to_snake_case - - -def load_template(template_name: str): - return env.get_template(template_name) \ No newline at end of file diff --git a/altk/pre_tool/toolguard/toolguard/gen_py/templates/api.j2 b/altk/pre_tool/toolguard/toolguard/gen_py/templates/api.j2 deleted file mode 100644 index ce22327..0000000 --- a/altk/pre_tool/toolguard/toolguard/gen_py/templates/api.j2 +++ /dev/null @@ -1,12 +0,0 @@ -from typing import Any, Dict, List -from abc import ABC, abstractmethod -from datetime import date, datetime -from {{ types_module }} import * - -class {{ class_name }}(ABC): -{% for method in methods %} - @abstractmethod - def {{ method.name }}{{ method.signature }}: - {% if method.doc %}"""{{ method.doc }}"""{% endif %} - pass -{% endfor %} diff --git a/altk/pre_tool/toolguard/toolguard/gen_py/templates/api_impl.j2 b/altk/pre_tool/toolguard/toolguard/gen_py/templates/api_impl.j2 deleted file mode 100644 index d8d8414..0000000 --- a/altk/pre_tool/toolguard/toolguard/gen_py/templates/api_impl.j2 +++ /dev/null @@ -1,22 +0,0 @@ -from abc import ABC, abstractmethod -from datetime import date, datetime -from {{ types_module }} import * -from {{ api_module }} import * - -class IToolInvoker(ABC): - T = TypeVar("T") - @abstractmethod - def invoke(self, toolname: str, arguments: Dict[str, Any], return_type: Type[T])->T: - ... - - -class {{ class_name }}({{ api_cls_name }}): - - def __init__(self, delegate: IToolInvoker): - self._delegate = delegate - -{% for method in methods %} - def {{ method.name }}{{ method.signature }}: - {% if method.doc %}"""{{ method.doc }}"""{% endif %} - {{ method.body }} -{% endfor %} \ No newline at end of file diff --git a/altk/pre_tool/toolguard/toolguard/gen_py/templates/tool_guard.j2 b/altk/pre_tool/toolguard/toolguard/gen_py/templates/tool_guard.j2 deleted file mode 100644 index c432a2a..0000000 --- a/altk/pre_tool/toolguard/toolguard/gen_py/templates/tool_guard.j2 +++ /dev/null @@ -1,25 +0,0 @@ -from typing import * -{% for imp in extra_imports %}{{ imp }} -{% endfor %} -import {{to_snake_case(domain.app_name)}} -from {{path_to_module(domain.toolguard_common.file_name)}} import PolicyViolationException -from {{ path_to_module(domain.app_types.file_name) }} import * -from {{ path_to_module(domain.app_api.file_name) }} import {{ domain.app_api_class_name }} - -{% for item in items %}from {{ path_to_module(item.file_name) }} import {{ item.guard_fn }} -{% endfor %} - -def {{ method.name }}(api: {{ domain.app_api_class_name }}, {{ method.signature}}): - """ - Checks that a tool call complies to the policies. - - Args: - api ({{ domain.app_api_class_name }}): api to access other tools. -{{method.args_doc_str}} - - Raises: - PolicyViolationException: If the tool call does not comply to the policy. - """ - -{% for item in items %} {{ item.guard_fn }}(api, {{method.args_call}}) -{% endfor %} diff --git a/altk/pre_tool/toolguard/toolguard/gen_py/templates/tool_item_guard.j2 b/altk/pre_tool/toolguard/toolguard/gen_py/templates/tool_item_guard.j2 deleted file mode 100644 index f8deed1..0000000 --- a/altk/pre_tool/toolguard/toolguard/gen_py/templates/tool_item_guard.j2 +++ /dev/null @@ -1,17 +0,0 @@ -from typing import * -{% for imp in extra_imports %}{{ imp }} -{% endfor %} -import {{to_snake_case(domain.app_name)}} -from {{path_to_module(domain.toolguard_common.file_name)}} import PolicyViolationException -from {{ path_to_module(domain.app_types.file_name) }} import * -from {{ path_to_module(domain.app_api.file_name) }} import {{ domain.app_api_class_name }} - -def {{ method.name }}(api: {{ domain.app_api_class_name }}, {{ method.signature }}): - """ - Policy to check: {{ policy }} - - Args: - api ({{ domain.app_api_class_name }}): api to access other tools. -{{method.args_doc_str}} - """ - pass #FIXME diff --git a/altk/pre_tool/toolguard/toolguard/gen_py/tool_dependencies.py b/altk/pre_tool/toolguard/toolguard/gen_py/tool_dependencies.py deleted file mode 100644 index b97d941..0000000 --- a/altk/pre_tool/toolguard/toolguard/gen_py/tool_dependencies.py +++ /dev/null @@ -1,34 +0,0 @@ -import asyncio -import re -from typing import Set -from mellea.backends.types import ModelOption -from ..data_types import Domain, ToolGuardSpecItem -from .prompts.pseudo_code import tool_policy_pseudo_code - -MAX_TRIALS = 3 - - -async def tool_dependencies( - policy_item: ToolGuardSpecItem, tool_signature: str, domain: Domain, trial=0 -) -> Set[str]: - model_options = {ModelOption.TEMPERATURE: 0.8} - pseudo_code = await asyncio.to_thread( # FIXME when melea will support aysnc - lambda: tool_policy_pseudo_code( - policy_item=policy_item, - fn_to_analyze=tool_signature, - domain=domain, - model_options=model_options, - ) # type: ignore - ) - fn_names = _extract_api_calls(pseudo_code) - if all([f"{fn_name}(" in domain.app_api.content for fn_name in fn_names]): - return fn_names - if trial <= MAX_TRIALS: - # as tool_policy_pseudo_code has some temerature, we retry hoping next time the pseudo code will be correct - return await tool_dependencies(policy_item, tool_signature, domain, trial + 1) - raise Exception("Failed to analyze api dependencies") - - -def _extract_api_calls(code: str) -> Set[str]: - pattern = re.compile(r"\bapi\.(\w+)\s*\(") - return set(pattern.findall(code)) \ No newline at end of file diff --git a/altk/pre_tool/toolguard/toolguard/gen_py/tool_guard_generator.py b/altk/pre_tool/toolguard/toolguard/gen_py/tool_guard_generator.py deleted file mode 100644 index 80bc1d6..0000000 --- a/altk/pre_tool/toolguard/toolguard/gen_py/tool_guard_generator.py +++ /dev/null @@ -1,424 +0,0 @@ -from importlib import import_module -import inspect -import os -import asyncio -import logging -from os.path import join -import re -from typing import Callable, List, Tuple - -from ..common import py -from ..common.llm_py import get_code_content -from ..common.py_doc_str import extract_docstr_args -from ..common.str import to_snake_case -from ..data_types import ( - DEBUG_DIR, - TESTS_DIR, - FileTwin, - RuntimeDomain, - ToolGuardSpec, - ToolGuardSpecItem, -) -from ..gen_py.consts import ( - guard_fn_module_name, - guard_fn_name, - guard_item_fn_module_name, - guard_item_fn_name, - test_fn_module_name, -) -from ..runtime import ToolGuardCodeResult, find_class_in_module -from ..gen_py.tool_dependencies import tool_dependencies -from ..gen_py.prompts.gen_tests import generate_init_tests, improve_tests -from ..gen_py.prompts.improve_guard import improve_tool_guard -from ..gen_py.templates import load_template -from .utils import pytest -from .utils import pyright - -logger = logging.getLogger(__name__) - -MAX_TOOL_IMPROVEMENTS = 5 -MAX_TEST_GEN_TRIALS = 3 - -class ToolGuardGenerator: - app_name: str - py_path: str - tool_policy: ToolGuardSpec - domain: RuntimeDomain - common: FileTwin - - def __init__( - self, - app_name: str, - tool_policy: ToolGuardSpec, - py_path: str, - domain: RuntimeDomain - ) -> None: - self.py_path = py_path - self.app_name = app_name - self.tool_policy = tool_policy - self.domain = domain - - def start(self): - app_path = join(self.py_path, to_snake_case(self.app_name)) - os.makedirs(app_path, exist_ok=True) - os.makedirs( - join(app_path, to_snake_case(self.tool_policy.tool_name)), exist_ok=True - ) - os.makedirs(join(self.py_path, to_snake_case(DEBUG_DIR)), exist_ok=True) - os.makedirs( - join( - self.py_path, - to_snake_case(DEBUG_DIR), - to_snake_case(self.tool_policy.tool_name), - ), - exist_ok=True, - ) - for item in self.tool_policy.policy_items: - os.makedirs( - join( - self.py_path, - to_snake_case(DEBUG_DIR), - to_snake_case(self.tool_policy.tool_name), - to_snake_case(item.name), - ), - exist_ok=True, - ) - os.makedirs(join(self.py_path, to_snake_case(TESTS_DIR)), exist_ok=True) - - async def generate(self) -> ToolGuardCodeResult: - self.start() - - with py.temp_python_path(self.py_path): - tool_guard, init_item_guards = self._create_initial_tool_guards() - - # Generate guards for all tool items - tests_and_guards = await asyncio.gather(* [ - self._generate_item_tests_and_guard(item, item_guard) - for item, item_guard in zip(self.tool_policy.policy_items, init_item_guards) - ]) - - item_tests, item_guards = zip(*tests_and_guards) - return ToolGuardCodeResult( - tool=self.tool_policy, - guard_fn_name=guard_fn_name(self.tool_policy), - guard_file=tool_guard, - item_guard_files=item_guards, - test_files=item_tests, - ) - - async def _generate_item_tests_and_guard( - self, item: ToolGuardSpecItem, init_guard: FileTwin - ) -> Tuple[FileTwin | None, FileTwin]: - # Dependencies of this tool - tool_fn_name = to_snake_case(self.tool_policy.tool_name) - tool_fn = self._find_api_function(tool_fn_name) - sig_str = f"{tool_fn_name}{str(inspect.signature(tool_fn))}" - dep_tools = [] - if self.domain.app_api_size > 1: - domain = self.domain.get_definitions_only() # remove runtime fields - dep_tools = list(await tool_dependencies(item, sig_str, domain)) - logger.debug(f"Dependencies of '{item.name}': {dep_tools}") - - # Generate tests - try: - guard_tests = await self._generate_tests(item, init_guard, dep_tools) - except Exception as ex: - logger.warning(f"Tests generation failed for item {item.name} %s", str(ex)) - try: - logger.warning("try to generate the code without tests... %s", str(ex)) - guard = await self._improve_guard(item, init_guard, [], dep_tools) - return None, guard - except Exception as ex: - logger.exception(ex) - logger.warning( - "guard generation failed. returning initial guard: %s", str(ex) - ) - return None, init_guard - - # Tests generated, now generate guards - try: - guard = await self._improve_guard_green_loop( - item, init_guard, guard_tests, dep_tools - ) - logger.debug( - f"tool item generated successfully '{item.name}'" - ) # 😄🎉 Happy path - return guard_tests, guard - except Exception as ex: - logger.exception(ex) - logger.warning( - "guard generation failed. returning initial guard: %s", str(ex) - ) - return None, init_guard - - # async def tool_dependencies(self, policy_item: ToolGuardSpecItem, tool_signature: str) -> Set[str]: - # domain = self.domain.get_definitions_only() #remove runtime fields - # pseudo_code = await tool_policy_pseudo_code(policy_item, tool_signature, domain) - # dep_tools = await extract_api_dependencies_from_pseudo_code(pseudo_code, domain) - # return set(dep_tools) - - async def _generate_tests( - self, item: ToolGuardSpecItem, guard: FileTwin, dep_tools: List[str] - ) -> FileTwin: - test_file_name = join( - TESTS_DIR, self.tool_policy.tool_name, f"{test_fn_module_name(item)}.py" - ) - errors = [] - test_file = None - trials = "a b c".split() - for trial_no in trials: - logger.debug( - f"Generating tests iteration '{trial_no}' for tool {self.tool_policy.tool_name} '{item.name}'." - ) - domain = self.domain.get_definitions_only() # remove runtime fields - first_time = trial_no == "a" - if first_time: - res = generate_init_tests( - fn_src=guard, - policy_item=item, - domain=domain, # noqa: B023 - dependent_tool_names=dep_tools, - ) - else: - assert test_file - res = improve_tests( - prev_impl=test_file.content, # noqa: B023 - domain=domain, # noqa: B023 - policy_item=item, - review_comments=errors, # noqa: B023 - dependent_tool_names=dep_tools, - ) - - test_file = FileTwin( - file_name=test_file_name, content=get_code_content(res) - ).save(self.py_path) - test_file.save_as(self.py_path, self.debug_dir(item, f"test_{trial_no}.py")) - - syntax_report = pyright.run(self.py_path, test_file.file_name) - FileTwin( - file_name= self.debug_dir(item, f"test_{trial_no}_pyright.json"), - content=syntax_report.model_dump_json(indent=2) - ).save(self.py_path) - - if syntax_report.summary.errorCount>0: - logger.warning(f"{syntax_report.summary.errorCount} syntax errors in tests iteration '{trial_no}' in item '{item.name}'.") - errors = syntax_report.list_error_messages(test_file.content) - continue - - # syntax ok, try to run it... - logger.debug( - f"Generated Tests for tool '{self.tool_policy.tool_name}' '{item.name}'(trial='{trial_no}')" - ) - return test_file - # report_file_name = self.debug_dir(item, f"test_{trial_no}_pytest.json") - # pytest_report = pytest.run( - # self.py_path, test_file.file_name, report_file_name - # ) - # if ( - # pytest_report.all_tests_collected_successfully() - # and pytest_report.non_empty_tests() - # ): - # return test_file - # if not pytest_report.non_empty_tests(): # empty test set - # errors = ["empty set of generated unit tests is not allowed"] - # else: - # errors = pytest_report.list_errors() - - raise Exception("Generated tests contain syntax errors") - - async def _improve_guard_green_loop( - self, - item: ToolGuardSpecItem, - guard: FileTwin, - tests: FileTwin, - dep_tools: List[str], - ) -> FileTwin: - trial_no = 0 - while trial_no < MAX_TOOL_IMPROVEMENTS: - pytest_report_file = self.debug_dir(item, f"guard_{trial_no}_pytest.json") - errors = pytest.run( - self.py_path, tests.file_name, pytest_report_file - ).list_errors() - if errors: - logger.debug(f"'{item.name}' guard function tests failed. Retrying...") - - trial_no += 1 - try: - guard = await self._improve_guard( - item, guard, errors, dep_tools, trial_no - ) - except Exception: - continue # probably a syntax error in the generated code. lets retry... - else: - logger.debug( - f"'{item.name}' guard function generated succefully and is Green 😄🎉. " - ) - return guard # Green - - raise Exception( - f"Failed {MAX_TOOL_IMPROVEMENTS} times to generate guard function for tool {to_snake_case(self.tool_policy.tool_name)} policy: {item.name}" - ) - - async def _improve_guard( - self, - item: ToolGuardSpecItem, - prev_guard: FileTwin, - review_comments: List[str], - dep_tools: List[str], - round: int = 0, - ) -> FileTwin: - module_name = guard_item_fn_module_name(item) - errors = [] - trials = "a b c".split() - for trial in trials: - logger.debug( - f"Improving guard function '{module_name}'... (trial = {round}.{trial})" - ) - domain = self.domain.get_definitions_only() # omit runtime fields - prev_python = get_code_content(prev_guard.content) - res = improve_tool_guard( - prev_impl=prev_python, # noqa: B023 - domain=domain, # noqa: B023 - policy_item=item, - dependent_tool_names=dep_tools, - review_comments=review_comments + errors, # noqa: B023 - ) - - guard = FileTwin( - file_name=prev_guard.file_name, content=get_code_content(res) - ).save(self.py_path) - guard.save_as( - self.py_path, self.debug_dir(item, f"guard_{round}_{trial}.py") - ) - - syntax_report = pyright.run(self.py_path, guard.file_name) - FileTwin( - file_name=self.debug_dir(item, f"guard_{round}_{trial}.pyright.json"), - content=syntax_report.model_dump_json(indent=2), - ).save(self.py_path) - logger.info( - f"Generated function {module_name} with {syntax_report.summary.errorCount} errors." - ) - - if syntax_report.summary.errorCount > 0: - # Syntax errors. retry... - errors = syntax_report.list_error_messages(guard.content) - continue - - guard.save_as(self.py_path, self.debug_dir(item, f"guard_{round}_final.py")) - return ( - guard # Happy path. improved vesion of the guard with no syntax errors - ) - - # Failed to generate valid python after iterations - raise Exception(f"Syntax error generating for tool '{item.name}'.") - - def _find_api_function(self, tool_fn_name: str): - module = import_module(py.path_to_module(self.domain.app_api.file_name)) - assert module, f"File not found {self.domain.app_api.file_name}" - cls = find_class_in_module(module, self.domain.app_api_class_name) - return getattr(cls, tool_fn_name) - - def _create_initial_tool_guards(self) -> Tuple[FileTwin, List[FileTwin]]: - tool_fn_name = to_snake_case(self.tool_policy.tool_name) - tool_fn = self._find_api_function(tool_fn_name) - assert tool_fn, f"Function not found, {tool_fn_name}" - - # __init__.py - path = join(to_snake_case(self.app_name), tool_fn_name, "__init__.py") - FileTwin(file_name=path, content="").save(self.py_path) - - # item guards files - item_files = [ - self._create_item_module(item, tool_fn) - for item in self.tool_policy.policy_items - ] - # tool guard file - tool_file = self._create_tool_module(tool_fn, item_files) - - # Save to debug folder - for item_guard_fn, policy_item in zip( - item_files, self.tool_policy.policy_items - ): - item_guard_fn.save_as(self.py_path, self.debug_dir(policy_item, "g0.py")) - - return (tool_file, item_files) - - def _create_tool_module( - self, tool_fn: Callable, item_files: List[FileTwin] - ) -> FileTwin: - file_name = join( - to_snake_case(self.app_name), - to_snake_case(self.tool_policy.tool_name), - py.py_extension(guard_fn_module_name(self.tool_policy)), - ) - items = [ - {"guard_fn": guard_item_fn_name(item), "file_name": file.file_name} - for (item, file) in zip(self.tool_policy.policy_items, item_files) - ] - sig = inspect.signature(tool_fn) - sig_str = self._signature_str(sig) - args_call = ", ".join([p for p in sig.parameters if p != "self"]) - args_doc_str = extract_docstr_args(tool_fn) - extra_imports = [] - if "Decimal" in sig_str: - extra_imports.append("from decimal import Decimal") - - return FileTwin( - file_name=file_name, - content=load_template("tool_guard.j2").render( - domain=self.domain, - method={ - "name": guard_fn_name(self.tool_policy), - "signature": sig_str, - "args_call": args_call, - "args_doc_str": args_doc_str, - }, - items=items, - extra_imports=extra_imports, - ), - ).save(self.py_path) - - def _signature_str(self, sig: inspect.Signature): - sig_str = str(sig) - sig_str = sig_str[ - sig_str.find("self,") + len("self,") : sig_str.rfind(")") - ].strip() - # Strip module prefixes like airline.airline_types.XXX → XXX - clean_sig_str = re.sub(r"\b(?:\w+\.)+(\w+)", r"\1", sig_str) - return clean_sig_str - - def _create_item_module( - self, tool_item: ToolGuardSpecItem, tool_fn: Callable - ) -> FileTwin: - file_name = join( - to_snake_case(self.app_name), - to_snake_case(self.tool_policy.tool_name), - py.py_extension(guard_item_fn_module_name(tool_item)), - ) - sig_str = self._signature_str(inspect.signature(tool_fn)) - args_doc_str = extract_docstr_args(tool_fn) - extra_imports = [] - if "Decimal" in sig_str: - extra_imports.append("from decimal import Decimal") - return FileTwin( - file_name=file_name, - content=load_template("tool_item_guard.j2").render( - domain=self.domain, - method={ - "name": guard_item_fn_name(tool_item), - "signature": sig_str, - "args_doc_str": args_doc_str, - }, - policy=tool_item.description, - extra_imports=extra_imports, - ), - ).save(self.py_path) - - def debug_dir(self, policy_item: ToolGuardSpecItem, dir: str): - return join( - DEBUG_DIR, - to_snake_case(self.tool_policy.tool_name), - to_snake_case(policy_item.name), - dir, - ) diff --git a/altk/pre_tool/toolguard/toolguard/gen_py/utils/__init__.py b/altk/pre_tool/toolguard/toolguard/gen_py/utils/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/altk/pre_tool/toolguard/toolguard/gen_py/utils/datamodel_codegen.py b/altk/pre_tool/toolguard/toolguard/gen_py/utils/datamodel_codegen.py deleted file mode 100644 index 7d3d33e..0000000 --- a/altk/pre_tool/toolguard/toolguard/gen_py/utils/datamodel_codegen.py +++ /dev/null @@ -1,34 +0,0 @@ -import subprocess - - -def run(oas_file: str): - # see https://github.com/koxudaxi/datamodel-code-generator - res = subprocess.run( - [ - "datamodel-codegen", - "--use-field-description", - "--use-schema-description", - "--output-model-type", - "pydantic_v2.BaseModel", # "typing.TypedDict", - "--collapse-root-models", - # "--force-optional", - "--reuse-model", - "--enum-field-as-literal", - "all", - "--input-file-type", - "openapi", - "--use-operation-id-as-name", - "--openapi-scopes", - "paths", - "parameters", - "schemas", - "--input", - oas_file, - # "--output", domain_file - ], - capture_output=True, - text=True, - ) - if res.returncode != 0: - raise Exception(res.stderr) - return res.stdout diff --git a/altk/pre_tool/toolguard/toolguard/gen_py/utils/pyright.py b/altk/pre_tool/toolguard/toolguard/gen_py/utils/pyright.py deleted file mode 100644 index 71a2917..0000000 --- a/altk/pre_tool/toolguard/toolguard/gen_py/utils/pyright.py +++ /dev/null @@ -1,109 +0,0 @@ -import json -import sys -import subprocess -from pydantic import BaseModel -from typing import List, Optional - -from ...data_types import FileTwin - -ERROR = "error" -WARNING = "warning" - - -class Position(BaseModel): - line: int - character: int - - -class Range(BaseModel): - start: Position - end: Position - - -class GeneralDiagnostic(BaseModel): - file: str - severity: str - message: str - range: Range - rule: Optional[str] = None - - -class Summary(BaseModel): - filesAnalyzed: int - errorCount: int - warningCount: int - informationCount: int - timeInSec: float - - -class DiagnosticsReport(BaseModel): - version: str - time: str - generalDiagnostics: List[GeneralDiagnostic] - summary: Summary - - def list_error_messages(self, file_content: str) -> List[str]: - msgs = set() - for d in self.generalDiagnostics: - if d.severity == ERROR: - msgs.add( - f"Syntax error: {d.message}. code block: '{get_text_by_range(file_content, d.range)}, '" - ) - return list(msgs) - - -def get_text_by_range(file_content: str, rng: Range) -> str: - lines = file_content.splitlines() - - if rng.start.line == rng.end.line: - # Single-line span - return lines[rng.start.line][rng.start.character : rng.end.character] - - # Multi-line span - selected_lines = [] - selected_lines.append( - lines[rng.start.line][rng.start.character :] - ) # First line, from start.character - for line_num in range(rng.start.line + 1, rng.end.line): - selected_lines.append(lines[line_num]) # Full middle lines - selected_lines.append( - lines[rng.end.line][: rng.end.character] - ) # Last line, up to end.character - - return "\n".join(selected_lines) - - -def run(folder: str, py_file: str) -> DiagnosticsReport: - py_path = sys.executable - res = subprocess.run( - [ - "pyright", - # "--venv-path", venv_path, - "--pythonpath", - py_path, - "--outputjson", - py_file, - ], - cwd=folder, - capture_output=True, - text=True, - ) - # if res.returncode !=0: - # raise Exception(res.stderr) - - data = json.loads(res.stdout) - return DiagnosticsReport.model_validate(data) - - -def config(folder: str): - cfg = { - "typeCheckingMode": "basic", - "reportOptionalIterable": WARNING, - "reportArgumentType": WARNING, # "Object of type \"None\" cannot be used as iterable value", - "reportOptionalMemberAccess": WARNING, - "reportOptionalSubscript": WARNING, - "reportAttributeAccessIssue": ERROR, - } - FileTwin(file_name="pyrightconfig.json", content=json.dumps(cfg, indent=2)).save( - folder - ) diff --git a/altk/pre_tool/toolguard/toolguard/gen_py/utils/pytest.py b/altk/pre_tool/toolguard/toolguard/gen_py/utils/pytest.py deleted file mode 100644 index a0367ae..0000000 --- a/altk/pre_tool/toolguard/toolguard/gen_py/utils/pytest.py +++ /dev/null @@ -1,170 +0,0 @@ - -from enum import StrEnum -import json -import os -from os.path import join -from typing import Any, List, Dict, Optional -from pydantic import BaseModel, Field - -from ...common.multi_process import run_in_process -from ...common.safe_py import run_safe_python -from ...data_types import FileTwin - - - - -class TestOutcome(StrEnum): - passed = "passed" - failed = "failed" - - -class TracebackEntry(BaseModel): - path: str - lineno: int - message: str - - -class CrashInfo(BaseModel): - path: str - lineno: int - message: str - - -class CallInfo(BaseModel): - duration: float - outcome: TestOutcome - crash: Optional[CrashInfo] = None - traceback: Optional[List[TracebackEntry]] = None - longrepr: Optional[str] = None - - -class TestPhase(BaseModel): - duration: float - outcome: TestOutcome - - -class TestResult(BaseModel): - nodeid: str - lineno: int - outcome: TestOutcome - keywords: List[str] - setup: TestPhase - call: CallInfo - user_properties: Optional[List[Any]] = None - teardown: TestPhase - - -class ResultEntry(BaseModel): - nodeid: str - type: str - lineno: Optional[int] = None - - -class Collector(BaseModel): - nodeid: str - outcome: TestOutcome - result: List[ResultEntry] - longrepr: Optional[str] = None - - -class Summary(BaseModel): - failed: Optional[int] = 0 - total: int - collected: int - - -class TestReport(BaseModel): - created: float - duration: float - exitcode: int - root: str - environment: Dict[str, str] - summary: Summary - collectors: List[Collector] = Field(default=[]) - tests: List[TestResult] - - def all_tests_passed(self) -> bool: - return all([test.outcome == TestOutcome.passed for test in self.tests]) - - def all_tests_collected_successfully(self) -> bool: - return all([col.outcome == TestOutcome.passed for col in self.collectors]) - - def non_empty_tests(self) -> bool: - return self.summary.total > 0 - - def list_errors(self) -> List[str]: - errors = set() - - # Python errors in the function under test - for col in self.collectors: - if col.outcome == TestOutcome.failed and col.longrepr: - errors.add(col.longrepr) - - # applicative test failure - for test in self.tests: - if test.outcome == TestOutcome.failed: - error = test.call.longrepr - if test.call.crash: - error = test.call.crash.message - if test.user_properties: - case_desc = test.user_properties[0].get("docstring") - if case_desc: - error = f"""Test case {case_desc} failed with the following message:\n {test.call.crash.message}""" - errors.add(error) - return list(errors) - -def run(folder:str, test_file:str, report_file:str)->TestReport: - run_tests_in_safe_python_separate_process(folder, test_file, report_file) - - report = read_test_report(os.path.join(folder, report_file)) - - #overwrite it with indented version - with open(os.path.join(folder, report_file), "w", encoding="utf-8") as f: - json.dump(report.model_dump(), f, indent=2) - - return report - -# Run the tests in this environment. -# run the tests in safe mode, so network and os operations are blocked. only specified libraries can be used. -# run the tests in a separate process. so python modules are isolated. as the code is evolving in the filesystem, we need a way to avoid python module caching. otherwise, it will not see that the code in the file has changed. -def run_tests_in_safe_python_separate_process(folder:str, test_file:str, report_file:str): - code = f""" -import pytest -pytest.main(["{join(folder, test_file)}", "--quiet", "--json-report", "--json-report-file={join(folder, report_file)}"]) -""" - return run_in_process(run_safe_python, code, ["pytest"]) - -# def _run_in_subprocess(folder:str, test_file:str, report_file:str): -# subprocess.run([ -# "pytest", -# test_file, -# # "--verbose", -# "--quiet", -# "--json-report", -# f"--json-report-file={report_file}" -# ], -# env={ -# **os.environ, -# "PYTHONPATH": "." -# }, -# cwd=folder) - - -def configure(folder: str): - """adds the test function docstring to the output report""" - - hook = """ -import pytest - -def pytest_runtest_protocol(item, nextitem): - docstring = item.function.__doc__ - if docstring: - item.user_properties.append(("docstring", docstring)) -""" - FileTwin(file_name="conftest.py", content=hook).save(folder) - - -def read_test_report(file_path: str) -> TestReport: - with open(file_path, "r") as file: - data = json.load(file) - return TestReport.model_validate(data, strict=False) diff --git a/altk/pre_tool/toolguard/toolguard/gen_spec/__init__.py b/altk/pre_tool/toolguard/toolguard/gen_spec/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/altk/pre_tool/toolguard/toolguard/gen_spec/oas_summary.py b/altk/pre_tool/toolguard/toolguard/gen_spec/oas_summary.py deleted file mode 100644 index 40bd7c9..0000000 --- a/altk/pre_tool/toolguard/toolguard/gen_spec/oas_summary.py +++ /dev/null @@ -1,252 +0,0 @@ -import json -from typing import Any, Dict, List - -from ..data_types import ToolInfo - - -class OASSummarizer: - def __init__(self, oas: Dict[str, Any]): - self.oas = oas - self.components = oas.get("components", {}).get("schemas", {}) - - def summarize(self) -> List[ToolInfo]: - operations = [] - for path, methods in self.oas.get("paths", {}).items(): - for method, operation in methods.items(): - summary = self._format_operation(path, method, operation) - tool_info = ToolInfo( - name=summary.get("name"), - description=summary.get("description"), - signature=summary.get("signature"), - parameters=summary.get("params"), - full_description=json.dumps(summary, indent=4), - ) - operations.append(tool_info) - operations.append(tool_info) - return operations - - def _format_operation( - self, path: str, method: str, operation: Dict[str, Any] - ) -> Dict[str, Any]: - operation_id = operation.get( - "operationId", f"{method}_{path.strip('/').replace('/', '_')}" - ) - class_name = operation_id - description = operation.get("description", "") - request_body = operation.get("requestBody", {}) - params = self._parse_request_body(request_body) if request_body else {} - signature = self._generate_signature( - class_name, params, operation.get("responses", {}) - ) - example = operation.get( - "x-input-examples", [] - ) # self._generate_example(class_name, params) - output_examples = operation.get( - "x-output-examples", [] - ) # self._parse_response_examples(operation.get("responses", {})) - - return { - "name": class_name, - "signature": signature, - "description": description, - "params": params, - "examples": [example], - "output_examples": output_examples, - } - - def _parse_request_body(self, request_body: Dict[str, Any]) -> Dict[str, Any]: - content = request_body.get("content", {}).get("application/json", {}) - schema = self._resolve_ref(content.get("schema", {})) - props = schema.get("properties", {}) - required = schema.get("required", []) - params = {} - for param_name, param_schema in props.items(): - resolved_schema = self._resolve_ref(param_schema) - param_type = self._resolve_schema_type(resolved_schema) - param_desc = resolved_schema.get("description", "") - params[param_name] = { - "type": param_type, - "description": param_desc, - "required": param_name in required, - } - return params - - # def _resolve_schema_type(self, schema: Dict[str, Any]) -> str: - # if "anyOf" in schema: - # return "Union[" + ", ".join(self._resolve_schema_type(s) for s in schema["anyOf"]) + "]" - # if "oneOf" in schema: - # return "Union[" + ", ".join(self._resolve_schema_type(s) for s in schema["oneOf"]) + "]" - # if "$ref" in schema: - # return self._resolve_schema_type(self._resolve_ref(schema)) - # if schema.get("type") == "array": - # item_type = self._resolve_schema_type(schema.get("items", {})) - # return f"List[{item_type}]" - # if schema.get("type") == "object": - # return "Dict[str, Any]" - # - # return { - # "string": "str", - # "integer": "int", - # "number": "float", - # "boolean": "bool", - # "object": "Dict[str, Any]", - # }.get(schema.get("type", "Any"), "Any") - - def _resolve_schema_type(self, schema: Dict[str, Any]) -> str: - if "anyOf" in schema: - return ( - "Union[" - + ", ".join(self._resolve_schema_type(s) for s in schema["anyOf"]) - + "]" - ) - if "oneOf" in schema: - return ( - "Union[" - + ", ".join(self._resolve_schema_type(s) for s in schema["oneOf"]) - + "]" - ) - if "$ref" in schema: - return self._resolve_schema_type(self._resolve_ref(schema)) - if schema.get("type") == "array": - item_type = self._resolve_schema_type(schema.get("items", {})) - return f"List[{item_type}]" - if schema.get("type") == "object": - return "Dict[str, Any]" - - type_value = schema.get("type", "Any") - if isinstance(type_value, list): - # Filter out "null" and resolve remaining types - non_null_types = [t for t in type_value if t != "null"] - if not non_null_types: - return "Optional[Any]" - if len(non_null_types) == 1: - base_type = self._resolve_schema_type( - {**schema, "type": non_null_types[0]} - ) - else: - base_type = ( - "Union[" - + ", ".join( - self._resolve_schema_type({"type": t}) for t in non_null_types - ) - + "]" - ) - return f"Optional[{base_type}]" if "null" in type_value else base_type - - return { - "string": "str", - "integer": "int", - "number": "float", - "boolean": "bool", - "object": "Dict[str, Any]", - }.get(type_value, "Any") - - def _resolve_ref(self, schema: Dict[str, Any]) -> Dict[str, Any]: - if isinstance(schema, Dict): - if "$ref" in schema: - ref_path = schema["$ref"] - if ref_path.startswith("#/components/schemas/"): - key = ref_path.split("/")[-1] - return self.components.get(key, {}) - return schema - return schema - - def _generate_signature( - self, class_name: str, params: Dict[str, Any], responses: Dict[str, Any] - ) -> str: - args = ", ".join(f"{name}: {meta['type']}" for name, meta in params.items()) - out = "str" - if responses and "200" in responses: - content = responses["200"]["content"] - app_json = content.get("application/json", {}) - schema = self._resolve_ref(app_json.get("schema", {})) - out = self._resolve_schema_type(schema) - return f"{class_name}({args}) -> {out}" - - def _generate_example(self, class_name: str, params: Dict[str, Any]) -> str: - args = ", ".join( - '"example_string"' if meta["type"].startswith("str") else "0" - for _, meta in params.items() - ) - return f"{class_name}({args})" - - def _parse_response_examples(self, responses: Dict[str, Any]) -> List[str]: - examples = [] - for response in responses.values(): - content = response.get("content", {}) - app_json = content.get("application/json", {}) - schema = self._resolve_ref(app_json.get("schema", {})) - - if "example" in app_json: - example_data = app_json["example"] - elif "examples" in app_json: - example_data = next(iter(app_json["examples"].values()), {}).get( - "value" - ) - else: - example_data = self._construct_example_from_schema(schema) - - if example_data is not None: - try: - examples.append(json.dumps(example_data)) - except Exception: - examples.append(str(example_data)) - return examples - - def _construct_example_from_schema(self, schema: Dict[str, Any]) -> Any: - schema = self._resolve_ref(schema) - if not isinstance(schema, Dict): - return schema - schema_type = schema.get("type") - - if schema_type == "object": - if "additionalProperties" in schema: - value_schema = self._resolve_ref(schema["additionalProperties"]) - return { - "example_key": self._construct_example_from_schema(value_schema) - } - props = schema.get("properties", {}) - return { - key: self._construct_example_from_schema(self._resolve_ref(subschema)) - for key, subschema in props.items() - } - - if schema_type == "array": - item_schema = self._resolve_ref(schema.get("items", {})) - return [self._construct_example_from_schema(item_schema)] - - if schema_type == "string": - return schema.get("example", "example_string") - if schema_type == "integer": - return schema.get("example", 42) - if schema_type == "number": - return schema.get("example", 3.14) - if schema_type == "boolean": - return schema.get("example", True) - - if "anyOf" in schema: - return self._construct_example_from_schema(schema["anyOf"][0]) - if "oneOf" in schema: - return self._construct_example_from_schema(schema["oneOf"][0]) - if "allOf" in schema: - return self._construct_example_from_schema(schema["allOf"][0]) - - return "example_value" - - -# if __name__ == "__main__": -# oas_file = ( -# "/Users/naamazwerdling/Documents/OASB/policy_validation/airline/oas2.json" -# ) -# # oas_file = "/Users/naamazwerdling/Documents/OASB/policy_validation/orca/bank/oas.json" -# shortfile = "/Users/naamazwerdling/Documents/OASB/policy_validation/airline/s1.json" -# # shortfile = "/Users/naamazwerdling/Documents/OASB/policy_validation/orca/bank/short.json" -# with open(oas_file) as f: -# oas_data = json.load(f) - -# summarizer = OASSummarizer(oas_data) -# summary = summarizer.summarize() -# with open(shortfile, "w") as outfile: -# json.dump(summary, outfile, indent=4) - -# print(json.dumps(summary, indent=2)) \ No newline at end of file diff --git a/altk/pre_tool/toolguard/toolguard/gen_spec/prompts/add_examples.txt b/altk/pre_tool/toolguard/toolguard/gen_spec/prompts/add_examples.txt deleted file mode 100644 index dd7a068..0000000 --- a/altk/pre_tool/toolguard/toolguard/gen_spec/prompts/add_examples.txt +++ /dev/null @@ -1,49 +0,0 @@ -Task: -For the given policy, check if any examples (violating or compliance) are missing. If so, generate additional examples and return only the newly created examples, without modifying or repeating the existing ones. - -Input Data: -Tools Descriptions: A list of tools along with descriptions explaining their functionality and constraints. -Target Tool (ToolX): The specific tool for which relevant policies need to be identified. -Policy: The policy to write additional the examples for with extracted examples from previous stages - -Steps: -1.Identify Missing Examples: -Locate the examples under "examples". -If the "examples" array is missing or empty, generate new examples in natural text, both violating and compliance examples. -If only one type of example is missing (violating or compliance), generate only the missing type. -Do not repeat or modify existing examples—only add new ones. - -2. Generate Violating Examples (if missing): -Provide new violating examples that highlight different ways the policy could be breached. -Cover common mistakes, edge cases, and boundary conditions. -Clearly state why each example violates the policy. - -3. Generate Compliance Examples (if missing): -Provide new compliance examples demonstrating correct adherence to the policy. -Ensure compliance cases clearly illustrate why they meet the policy’s requirements. - -4. Ensure Full Coverage: -If the policy has multiple conditions, generate examples covering each condition separately. -Consider numeric limits, timing constraints, and optional parameters where relevant. - -5. Format the Output in JSON: -Only include newly generated examples. Make sure examples are written in natural text. -Maintain the correct structure: - -Output Format (JSON): -{ - "violating_examples": [ - "Violating example 1", - "Violating example 2, - "Violating example 3" - ], - "compliance_examples": [ - "Compliance example 1", - "Compliance example 2", - "Compliance example 3" - ] -} - -If no new examples are needed, return an empty object: - -{} \ No newline at end of file diff --git a/altk/pre_tool/toolguard/toolguard/gen_spec/prompts/add_policies.txt b/altk/pre_tool/toolguard/toolguard/gen_spec/prompts/add_policies.txt deleted file mode 100644 index dc9686e..0000000 --- a/altk/pre_tool/toolguard/toolguard/gen_spec/prompts/add_policies.txt +++ /dev/null @@ -1,58 +0,0 @@ -Task: -Given the extracted TPTD (Tool Policy Text Description) for the given target tool (ToolX) , verify completeness and identify any additional policies that were missed. Only return newly identified policies. - -Instructions: - -1. Identify Missing Policies: - * Reanalyze the Policy Document to check if any relevant policies for ToolX were overlooked in previous stage. - -2. Extract and format any missing policies following these criteria: - * Must be specific to ToolX. - * Must be actionable and enforceable based on ToolX’s parameters, chat history, and data access. - * Must be self-contained with all necessary details. - * Must have exact verbatim references from the Policy Document (not inferred from tool descriptions). - -3. Validate Policy References: - * Verify that each reference is a verbatim excerpt from the Policy Document and not inferred from tool descriptions. - * If a policy is supported by multiple passages, list them separately in the "references" array. - -4. Output Only New Policies: - * If a policy was already extracted in Stage 1, do not include it in the output. - * If no additional policies are found, return an empty "policies" array. - -Input Format: -Policy Document – A text containing policies, rules, or constraints governing tool usage. -Tools Descriptions – A list of tools with descriptions explaining their functionality and constraints. -Target Tool (ToolX) – The specific tool for which relevant policies need to be identified. -TPTD (Tool Policy Text Description) – A JSON object containing extracted policies from previous stages. - -Output Format (JSON): -{ - "policies": [ - { - "policy_name": "", - "description": "", - "references": [ - "", - "", - ... - ] - }, - ... - { - "policy_name": "", - "description": "", - "references": [ - "" - ... - ] - } - ] -} - - -If no additional relevant policies exist, return: -{ - "policies": [] -} - diff --git a/altk/pre_tool/toolguard/toolguard/gen_spec/prompts/add_references.txt b/altk/pre_tool/toolguard/toolguard/gen_spec/prompts/add_references.txt deleted file mode 100644 index 14f6031..0000000 --- a/altk/pre_tool/toolguard/toolguard/gen_spec/prompts/add_references.txt +++ /dev/null @@ -1,38 +0,0 @@ -Task: -Given a Policy Document, a Target Tool (ToolX), and a specific policy, extract all verbatim supporting references from the Policy Document that directly support the given policy. - -Instructions: -1. Identify Supporting References: - * Search the Policy Document for text that explicitly or implicitly supports the given policy description. - * Extract only the relevant passages that provide direct support for enforcing this policy. - - -2. Ensure Contiguous Verbatim References: - * Each reference must be an exact excerpt from the Policy Document, without alterations. - * If the supportive text appears in multiple non-contiguous segments, extract them separately as distinct items in the list. - * If the relevant text is interrupted by unrelated content, split it into multiple separate references. - * Do not infer meaning beyond the verbatim text. - -3. Output Format: - * If multiple non-contiguous passages support the policy, return them as separate strings in a list. - * If no supporting references are found, return an empty list. - -Input Format: -Policy Document: A text containing policies, rules, or constraints governing tool usage. -Tools Descriptions: A list of tools with descriptions explaining their functionality and constraints. -Target Tool (ToolX): The specific tool for which relevant policies need to be identified. -Policy: The policy for which supporting references need to be extracted. - -Output Format (JSON): -{ - "references": [ - "", - "", - "" - ], -} - -If no relevant references are found, return: -{ - "references": [] -} \ No newline at end of file diff --git a/altk/pre_tool/toolguard/toolguard/gen_spec/prompts/create_examples.txt b/altk/pre_tool/toolguard/toolguard/gen_spec/prompts/create_examples.txt deleted file mode 100644 index 4f13f90..0000000 --- a/altk/pre_tool/toolguard/toolguard/gen_spec/prompts/create_examples.txt +++ /dev/null @@ -1,64 +0,0 @@ -Task Overview: -Your task is to generate in natural text, examples for the given policy. This policy define constraints and rules that must be validated before calling a specific tool, referred to as "ToolX." - -Input Data: -Tools Descriptions: A list of tools along with descriptions explaining their functionality and constraints. -Target Tool (ToolX): The specific tool for which relevant policies need to be identified. -Policy: The policy to write the examples for - -Objective: -For the given policy, generate examples that illustrate both violations (where the policy is not followed) and compliance (where the policy is correctly followed). The goal is to create specific and actionable examples that would be able to be validated. -Ensure you provide a detailed textual description outlining the use case and the conditions that would either violate or comply with the policy in the given example. - -Guidelines for Creating Examples: - -1. Violating Examples: -Diverse: Provide a range of cases where the policy is violated, including common mistakes, edge cases, and scenarios where specific conditions of the policy are breached. -Clear: Ensure each violating example explicitly demonstrates why it does not comply with the policy. Highlight the incorrect aspects. -Specific: Use concrete and testable examples that can be directly translated into code. - -2. Compliance Examples: -Diverse: Include a variety of correct cases, covering different valid ways of adhering to the policy. -Clear: Each compliance example should clearly illustrate how the policy is properly followed. -Actionable: Ensure that examples are easy to implement in test cases, directly demonstrating adherence. - -3. Edge Cases: -Include edge cases for both compliance and violation. Pay special attention to boundary conditions such as numeric limits, optional parameters, or timing constraints. - -4. Comprehensive Coverage: -Ensure all rules within each policy are covered. If a policy contains multiple conditions, provide examples for each possible combination of compliance and violation. - -Output Format (JSON): -{ - "violating_examples": [ - "Violating example 1", - "Violating example 2, - "Violating example 3" - ], - "compliance_examples": [ - "Compliance example 1", - "Compliance example 2", - "Compliance example 3" - ] -} - -Example: - -Target Tool: rent_car -Policy: The user can rent only medium cars in red or small cars in blue or any yellow car - -Output: -{ - "violating_examples": [ - "A user asks to rent a green car", - "The user requests to rent medium blue car", - "Renting a small red car", - "Renting a big red car" - ], - "compliance_examples": [ - "A user asks to rent a red medium car", - "The user requests to rent small blue car", - "Renting a big yellow car", - "Renting a small yellow car" - ] -} diff --git a/altk/pre_tool/toolguard/toolguard/gen_spec/prompts/create_policy.txt b/altk/pre_tool/toolguard/toolguard/gen_spec/prompts/create_policy.txt deleted file mode 100644 index 5a0b061..0000000 --- a/altk/pre_tool/toolguard/toolguard/gen_spec/prompts/create_policy.txt +++ /dev/null @@ -1,57 +0,0 @@ -Task: -Given a Policy Document, Tools Descriptions, and a Target Tool (ToolX), extract and format all relevant policies that should be verified before invoking ToolX. - -Instructions: -1. Identify policies from the Policy Document that are relevant to ToolX. -2. Ensure that extracted policies: - * Are specifically applicable to ToolX (not general for all tools). - * Can be validated before calling ToolX. - * Are actionable, meaning they can be enforced based on ToolX’s parameters, chat history, and data access. - * Are self-contained, meaning the description should contain all necessary details without requiring access to external references. -3. Split policies into the smallest reasonable parts so that: - * Each extracted policy contains only one condition. - * If a policy includes multiple conditions, split it into separate policies whenever possible. -4. Provide exact references to the Policy Document for each extracted policy. - * Locate the exact passage(s) in the Policy Document that fully support the policy statement. - * The corrected reference must be a contiguous, verbatim excerpt from the Policy Document, ensuring it can be precisely highlighted. - * If no single passage fully supports the policy, replace the incorrect reference with multiple distinct references. - * Each reference must be a verbatim excerpt that appears exactly as written in the Policy Document. - * Each supporting passage should be listed separately within the "references" array. - * Make sure to extract only from the Policy Document and not from other input information like the tool description -5. If no policies are applicable, return: -"There are no relevant policies to validate before calling ToolX." - -Input Format: -Policy Document – A text containing policies, rules, or constraints governing tool usage. -Tools Descriptions – A list of tools with descriptions explaining their functionality and constraints. -Target Tool (ToolX) – The specific tool for which relevant policies need to be identified. - -Output Format (JSON): -{ - "policies": [ - { - "policy_name": "", - "description": "", - "references": [ - "", - "", - ... - ], - "iteration_added": 0 - }, - ... - { - "policy_name": "", - "description": "", - "references": [ - "" - ... - ], - "iteration_added": 0 - } - ] -} -If no relevant policies exist, return: -{ - "policies": [] -} diff --git a/altk/pre_tool/toolguard/toolguard/gen_spec/prompts/examples_reviewer.txt b/altk/pre_tool/toolguard/toolguard/gen_spec/prompts/examples_reviewer.txt deleted file mode 100644 index 1fa0d59..0000000 --- a/altk/pre_tool/toolguard/toolguard/gen_spec/prompts/examples_reviewer.txt +++ /dev/null @@ -1,41 +0,0 @@ -Objective: -You are a reviewer. Your job is to check if a given example correctly shows a good __EXAMPLE_TYPE__ example of a specific rule (policy) for using a tool called ToolX. - -Input: -Tools Descriptions: A list of tools and their functions and constraints. -The Target Tool (ToolX): The tool for which the policy applies. -The Policy Name and Description: Specifies the specific policy under evaluation. -A __EXAMPLE_TYPE__ Example: A natural language use-case for __EXAMPLE_TYPE__ the policy. - -What You Need to Do -For the __EXAMPLE_TYPE__ example, answer these four questions: - * Does this example is indeed a valid __EXAMPLE_TYPE__ example of the policy? or the opposite? (complience or violating example) - * Is the example clear and detailed enough to understand? - * Can someone write a test case based on this example? - * Can a validation function catch this problem before ToolX runs? - -For each question: - * First, explain why you think the example passes or fails. - * Then return a true or false value. - -Format the Output in JSON: -``` -{ - "is_valid": { - "justification": "Explain whether this example actually is a __EXAMPLE_TYPE__ example , and why.", - "value": true or false - }, - "clarity_and_detail": { - "justification": "Explain if the example is clear, specific, and detailed enough to understand the problem.", - "value": true or false - }, - "actionability": { - "justification": "Explain if someone could write a test case based on this example.", - "value": true or false - }, - "verifiability": { - "justification": "Explain if a validation function could detect this problem before ToolX is used.", - "value": true or false - } -} -``` \ No newline at end of file diff --git a/altk/pre_tool/toolguard/toolguard/gen_spec/prompts/fix_example.txt b/altk/pre_tool/toolguard/toolguard/gen_spec/prompts/fix_example.txt deleted file mode 100644 index ea910e1..0000000 --- a/altk/pre_tool/toolguard/toolguard/gen_spec/prompts/fix_example.txt +++ /dev/null @@ -1,30 +0,0 @@ -Objective: -Your task is to revise and improve a single __EXAMPLE_TYPE__ example for a given policy. The goal is to ensure that the example is clear, precise, actionable, and suitable for automated validation or unit testing. - -Task: Fix and Improve an Example -Given: -* Policy Document: A text outlining rules, constraints, or usage policies for various tools. -* Tools Descriptions: A list of tools along with explanations of their capabilities and constraints. -* Target Tool (ToolX): The specific tool for which the policy is being analyzed. -* Policy Name: The name of the policy -* Policy Description: The description of the policy -* Example: The __EXAMPLE_TYPE__ example to fix - - -Update the example as follows: - -1. Clarity and Specificity - * Clearly show how and why the example violates or complies with the policy. - * Replace vague or ambiguous descriptions with precise actions, conditions, and parameters. - * Ensure the language is unambiguous and easy to understand. - -2. Actionability for Testing - * The revised example must be concrete and testable. - * Include enough detail so that a developer can implement a unit test or validation rule from it without referring back to the full policy document. - * Examples should include relevant inputs, conditions, and outcomes when applicable. - - -Output Format (JSON): -{ - "revised_example": "The updated example text, or the original text if no changes were necessary." -} diff --git a/altk/pre_tool/toolguard/toolguard/gen_spec/prompts/functions.txt b/altk/pre_tool/toolguard/toolguard/gen_spec/prompts/functions.txt deleted file mode 100644 index 3b65b39..0000000 --- a/altk/pre_tool/toolguard/toolguard/gen_spec/prompts/functions.txt +++ /dev/null @@ -1,56 +0,0 @@ -Given an OpenAPI Specification (OAS) file and a selected operation (such as GET /items/{item_id}), follow these steps to create a Python function and sample input/output examples. - -Step 1: Identify Operation Components -From the operation, extract: -* Operation ID (or generate a function name using HTTP method + path) -* HTTP method and path -* Parameters: - * Path, query, header, and cookie parameters -* Request body (if defined) -* Response schema (focus on HTTP 200 or default response) - -Step 2: Write the Python Function Signature -Create a Python function named after the operation. Follow these rules: -* Use snake_case for function names. -* Each parameter should become a function argument: - * Required parameters have no default. - * Optional parameters have default values (e.g., None, False, ""). -* Use basic type hints: str, int, float, bool, dict, List[str], etc. - - -Step 3: Generate 3 Diverse Parameter Examples -Write three different function calls that showcase a variety of: -* Simple vs. complex values -* Optional vs. required parameters -* Empty, large, or edge-case values -* Realistic business logic (e.g., filters, limits) - -Example variations: -* Empty query strings -* Lists or dicts as input -* Special characters -* Boundary numbers - -Step 4: Generate 3 Diverse Return Value Examples -Create three realistic return values in Python dict format, based on the operation's response schema. The examples should vary in: -* Field presence (e.g., missing optional fields) -* Field types (e.g., one returns a list, one returns a dict) -* Structure (nested vs. flat) -* Value range (small, large, edge cases) - -Step 5: Return in JSON Format -Return the following in a single JSON object: - -{ - "function_signature": "Python function definition as a string", - "input_examples": [ - "Function call as string #1", - "Function call as string #2", - "Function call as string #3" - ], - "output_examples": [ - { "example_response_1": "..." }, - { "example_response_2": "..." }, - { "example_response_3": "..." } - ] -} diff --git a/altk/pre_tool/toolguard/toolguard/gen_spec/prompts/merge.txt b/altk/pre_tool/toolguard/toolguard/gen_spec/prompts/merge.txt deleted file mode 100644 index e41b8ab..0000000 --- a/altk/pre_tool/toolguard/toolguard/gen_spec/prompts/merge.txt +++ /dev/null @@ -1,52 +0,0 @@ -Task: -Given a Policy Document, Tools Descriptions, a Target Tool (ToolX), and a TPTD JSON object containing extracted policies, refine the policies by: - -1. Merging Identical Policies: -If two or more policies have identical descriptions and references, consolidate them into a single policy. -Preserve all references from the merged policies. - -2. Merging Policies with Logical OR Relationships: -If multiple policies describe conditions where at least one must be satisfied (i.e., connected by an OR relationship), merge them into a single policy. -Ensure the combined policy reflects the OR logic and retains all original references. - -3. Ensuring Clarity and Enforceability: -Each extracted policy should contain only a single actionable condition. -Maintain clear references for each policy to ensure traceability. -Retain the iteration_added field and increment it by 1 for any newly created policies. - -Input Format: -Policy Document – A text containing policies, rules, or constraints governing tool usage. -Tools Descriptions – A list of tools with descriptions explaining their functionality and constraints. -Target Tool (ToolX) – The specific tool for which relevant policies need to be identified. -TPTD (Tool Policy Text Description) – A JSON object containing extracted policies from previous stages. - -Output Format (JSON): -{ - "policies": [ - { - "policy_name": "", - "description": "", - "references": [ - "", - "", - ... - ] - }, - ... - { - "policy_name": "", - "description": "", - "references": [ - "" - ... - ] - } - ] -} - - -If no additional relevant policies exist, return: -{ - "policies": [] -} - diff --git a/altk/pre_tool/toolguard/toolguard/gen_spec/prompts/merge_and_split.txt b/altk/pre_tool/toolguard/toolguard/gen_spec/prompts/merge_and_split.txt deleted file mode 100644 index 3728338..0000000 --- a/altk/pre_tool/toolguard/toolguard/gen_spec/prompts/merge_and_split.txt +++ /dev/null @@ -1,53 +0,0 @@ -Task: -Given a Policy Document, Tools Descriptions, a Target Tool (ToolX), and a TPTD JSON object containing extracted policies, refine the policies by: - -1. Merging Identical Policies: -If two or more policies have identical descriptions and references, consolidate them into a single policy. -Preserve all references from the merged policies. - -2. Breaking Down Multi-Condition Policies: -If a policy contains multiple conditions, separate them into distinct policies whenever feasible. -If a policy requires both Condition A and Condition B to be met, create two separate policies. -If a policy allows either Condition A or Condition B to apply, it should remain as a single policy. - -3. Ensuring Clarity and Enforceability: -Each extracted policy should contain only a single actionable condition. -Maintain clear references for each policy to ensure traceability. -Retain the iteration_added field and increment it by 1 for any newly created policies. - -Input Format: -Policy Document – A text containing policies, rules, or constraints governing tool usage. -Tools Descriptions – A list of tools with descriptions explaining their functionality and constraints. -Target Tool (ToolX) – The specific tool for which relevant policies need to be identified. -TPTD (Tool Policy Text Description) – A JSON object containing extracted policies from previous stages. - -Output Format (JSON): -{ - "policies": [ - { - "policy_name": "", - "description": "", - "references": [ - "", - "", - ... - ] - }, - ... - { - "policy_name": "", - "description": "", - "references": [ - "" - ... - ] - } - ] -} - - -If no additional relevant policies exist, return: -{ - "policies": [] -} - diff --git a/altk/pre_tool/toolguard/toolguard/gen_spec/prompts/merge_examples.txt b/altk/pre_tool/toolguard/toolguard/gen_spec/prompts/merge_examples.txt deleted file mode 100644 index 9f7dc10..0000000 --- a/altk/pre_tool/toolguard/toolguard/gen_spec/prompts/merge_examples.txt +++ /dev/null @@ -1,58 +0,0 @@ -Task: Deduplicate Examples in Policy Evaluation Context - -You are given the following inputs: -* Policy Document: A text outlining rules, constraints, or usage policies for various tools. -* Tools Descriptions: A list of tools along with explanations of their capabilities and constraints. -* Target Tool (ToolX): The specific tool for which the policy is being analyzed. -* Policy Name: The name of the policy -* Policy Description: The description of the policy -* Violating Examples: A list of example scenarios that violate the policy. -* Compliance Examples: A list of example scenarios that are compliant with the policy. - -Your task is to refine both sets of examples (violating and compliance) by merging identical or semantically duplicate examples: -Instructions: -1. Review the examples in each set independently (i.e., review violating examples separately from compliance examples). -2. Identify duplicates: - * If two or more examples describe the same scenario (identical or paraphrased), merge them into one clear and representative example. - * Examples are considered duplicates if they involve the same conditions and policy points being violated or followed. -3. Do not merge examples that: - * Refer to different scenarios, distinct edge cases, or variations of policy application. - * Provide unique value in understanding how the policy is applied or violated. - -Output JSON Format: -{ - "violating_examples": [ ... ], - "compliance_examples": [ ... ] -} - -Example: -Input (Before Merging): -{ - "violating_examples": [ - "ToolX is used without an authentication token.", - "A request is sent to ToolX without the required authentication token.", - "Input exceeds the character limit for ToolX.", - "ToolX request has input of more than 500 characters.", - "A restricted keyword is used in ToolX query." - ], - "compliance_examples": [ - "A valid authentication token is used when calling ToolX.", - "The input for ToolX is within the allowed character limit.", - "No restricted keywords are present in the ToolX request.", - "ToolX is accessed with proper credentials and policy-compliant query." - ] -} - -Output (After Merging): -{ - "violating_examples": [ - "A request is sent to ToolX without the required authentication token, violating the policy that mandates authentication.", - "The input parameters for ToolX exceed the allowed 500-character limit, which violates input size constraints.", - "A restricted keyword is used in the ToolX request, which the policy explicitly disallows." - ], - "compliance_examples": [ - "A user provides a valid authentication token when calling ToolX, following the authentication requirement.", - "The input parameters for ToolX are within the allowed character limit, meeting the policy criteria.", - "ToolX is accessed with valid credentials and a policy-compliant query containing no restricted keywords." - ] -} diff --git a/altk/pre_tool/toolguard/toolguard/gen_spec/prompts/policy_reviewer.txt b/altk/pre_tool/toolguard/toolguard/gen_spec/prompts/policy_reviewer.txt deleted file mode 100644 index 2f2daa4..0000000 --- a/altk/pre_tool/toolguard/toolguard/gen_spec/prompts/policy_reviewer.txt +++ /dev/null @@ -1,39 +0,0 @@ -Task: -Evaluate and refine the given policy to ensure it meets standards for quality, relevance, and applicability. Your objectives are: -Determine if the policy is relevant and actionable for ToolX. -Verify whether the policy can be validated before invoking ToolX whenever possible. -Identify gaps in the policy and suggest improvements to make it self-contained if necessary. - -Input: -Policy Document: A text outlining policies, rules, or constraints related to tool usage. -Tool Descriptions: A list detailing tools, their functionalities, and constraints. -Target Tool (ToolX): The specific tool for which relevant policies must be identified. -Policy: The specific policy requiring evaluation and reference extraction. - -Evaluation Criteria: -is_relevant: Does the policy specifically apply to ToolX? -is_tool_specific: Is the policy tailored to ToolX, or is it too broad (e.g., applicable to all tools)? -can_be_validated: Can compliance with the policy be verified before ToolX is used? -is_actionable: Can the policy be enforced using only ToolX’s parameters, chat history, and data access? -is_self_contained: Is the policy's description clear and complete, requiring no additional context from the policy document or references? If not, suggest an improved version. - -Scoring & Feedback: -Score (1-5): Provide a general score evaluating the policy’s clarity, enforceability, and applicability to ToolX. -Comments: Justify the assigned score and explain any deficiencies. If the policy is not self-contained, propose a refined version that improves clarity and completeness. - -Output JSON Format: -{ - "policy_name": "", - "description": "", - "references": [ - "" - ], - "is_relevant": , - "is_tool_specific": , - "can_be_validated": , - "is_actionable": , - "is_self_contained": , - "alternative_description": , - "comments": "", - "score": -} diff --git a/altk/pre_tool/toolguard/toolguard/gen_spec/prompts/split.txt b/altk/pre_tool/toolguard/toolguard/gen_spec/prompts/split.txt deleted file mode 100644 index 5bcf82d..0000000 --- a/altk/pre_tool/toolguard/toolguard/gen_spec/prompts/split.txt +++ /dev/null @@ -1,49 +0,0 @@ -Task: -Given a Policy Document, Tools Descriptions, a Target Tool (ToolX), and a TPTD JSON object containing extracted policies, refine the policies by: - -1. Breaking Down Multi-Condition Policies: -If a policy contains multiple conditions, separate them into distinct policies whenever feasible. -If a policy requires both Condition A and Condition B to be met, create two separate policies. -If a policy allows either Condition A or Condition B to apply, it should remain as a single policy. - -2. Ensuring Clarity and Enforceability: -Each extracted policy should contain only a single actionable condition. -Maintain clear references for each policy to ensure traceability. -Retain the iteration_added field and increment it by 1 for any newly created policies. - -Input Format: -Policy Document – A text containing policies, rules, or constraints governing tool usage. -Tools Descriptions – A list of tools with descriptions explaining their functionality and constraints. -Target Tool (ToolX) – The specific tool for which relevant policies need to be identified. -TPTD (Tool Policy Text Description) – A JSON object containing extracted policies from previous stages. - -Output Format (JSON): -{ - "policies": [ - { - "policy_name": "", - "description": "", - "references": [ - "", - "", - ... - ] - }, - ... - { - "policy_name": "", - "description": "", - "references": [ - "" - ... - ] - } - ] -} - - -If no additional relevant policies exist, return: -{ - "policies": [] -} - diff --git a/altk/pre_tool/toolguard/toolguard/gen_spec/spec_generator.py b/altk/pre_tool/toolguard/toolguard/gen_spec/spec_generator.py deleted file mode 100644 index 41a1af6..0000000 --- a/altk/pre_tool/toolguard/toolguard/gen_spec/spec_generator.py +++ /dev/null @@ -1,374 +0,0 @@ - -import asyncio -import json -import os -from typing import List, Optional - -from ..data_types import ToolInfo, ToolGuardSpec, load_tool_policy -from ..llm.i_tg_llm import I_TG_LLM -from .utils import read_prompt_file, generate_messages, save_output, find_mismatched_references - -class ToolGuardSpecGenerator: - def __init__(self, llm:I_TG_LLM, policy_document:str, tools:List[ToolInfo], out_dir:str) -> None: - self.llm = llm - self.policy_document = policy_document - self.tools_descriptions = {tool.name: tool.description for tool in tools} - self.tools_details = {tool.name: tool for tool in tools} - self.out_dir = out_dir - - - async def generate_minimal_policy(self, tool_name: str) -> dict: - tptd = await self.create_policy(tool_name) - tptd = await self.example_creator(tool_name, tptd) - return tptd - - async def generate_policy(self, tool_name: str) -> dict: - tptd = await self.create_policy(tool_name) - for i in range(3): - tptd = await self.add_policies(tool_name, tptd, i) - tptd = await self.split(tool_name, tptd) - tptd = await self.merge(tool_name, tptd) - tptd = await self.review_policy(tool_name, tptd) - tptd = await self.add_references(tool_name, tptd) - tptd = await self.reference_correctness(tool_name, tptd) - tptd = await self.example_creator(tool_name, tptd) - for i in range(5): # FIXME - tptd = await self.add_examples(tool_name, tptd, i) - tptd = await self.merge_examples(tool_name, tptd) - # tptd = self.fix_examples(tool_name, tptd) - tptd = await self.review_examples(tool_name, tptd) - return tptd - - async def create_policy(self, tool_name: str) -> dict: - print("policy_creator_node") - system_prompt = read_prompt_file("create_policy") - system_prompt = system_prompt.replace("ToolX", tool_name) - user_content = f"Policy Document:{self.policy_document}\nTools Descriptions:{json.dumps(self.tools_descriptions)}\nTarget Tool:{self.tools_details[tool_name].model_dump_json()}\n" - tptd = await self.llm.chat_json(generate_messages(system_prompt, user_content)) - save_output(self.out_dir, f"{tool_name}.json", tptd) - return tptd - - async def add_policies( - self, tool_name: str, tptd: dict, iteration: int = 0 - ) -> dict: - print("add_policy") - system_prompt = read_prompt_file("add_policies") - user_content = f"Policy Document:{self.policy_document}\nTools Descriptions:{json.dumps(self.tools_descriptions)}\nTarget Tool:{self.tools_details[tool_name].model_dump_json()}\nTPTD: {json.dumps(tptd)}" - response = await self.llm.chat_json( - generate_messages(system_prompt, user_content) - ) - - policies = ( - response["additionalProperties"]["policies"] - if "additionalProperties" in response and "policies" not in response - else response["policies"] - ) - - for policy in policies: - # for policy in response["policies"]: - policy["iteration"] = iteration - tptd["policies"].append(policy) - - save_output(self.out_dir, f"{tool_name}_ADD_{iteration}.json", tptd) - return tptd - - async def split(self, tool_name, tptd: dict) -> dict: - # todo: consider addition step to split policy by policy and not overall - print("split") - system_prompt = read_prompt_file("split") - user_content = f"Policy Document:{self.policy_document}\nTools Descriptions:{json.dumps(self.tools_descriptions)}\nTarget Tool:{self.tools_details[tool_name].model_dump_json()}\nTPTD: {json.dumps(tptd)}" - tptd = await self.llm.chat_json(generate_messages(system_prompt, user_content)) - save_output(self.out_dir, f"{tool_name}_split.json", tptd) - return tptd - - async def merge(self, tool_name, tptd: dict) -> dict: - # todo: consider addition step to split policy by policy and not overall - print("merge") - system_prompt = read_prompt_file("merge") - user_content = f"Policy Document:{self.policy_document}\nTools Descriptions:{json.dumps(self.tools_descriptions)}\nTarget Tool:{self.tools_details[tool_name].model_dump_json()}\nTPTD: {json.dumps(tptd)}" - tptd = await self.llm.chat_json(generate_messages(system_prompt, user_content)) - - save_output(self.out_dir, f"{tool_name}_merge.json", tptd) - return tptd - - def move2archive(self, reviews) -> (bool, str): - comments = "" - num = len(reviews) - if num == 0: - return False - counts = { - "is_relevant": 0, - "is_tool_specific": 0, - "can_be_validated": 0, - "is_actionable": 0, - } - - for r in reviews: - print( - f"{r['is_relevant'] if 'is_relevant' in r else ''}\t{r['is_tool_specific'] if 'is_tool_specific' in r else ''}\t{r['can_be_validated'] if 'can_be_validated' in r else ''}\t{r['is_actionable'] if 'is_actionable' in r else ''}\t{r['is_self_contained'] if 'is_self_contained' in r else ''}\t{r['score'] if 'score' in r else ''}\t" - ) - - counts["is_relevant"] += r["is_relevant"] if "is_relevant" in r else 0 - counts["is_tool_specific"] += ( - r["is_tool_specific"] if "is_tool_specific" in r else 0 - ) - counts["can_be_validated"] += ( - r["can_be_validated"] if "can_be_validated" in r else 0 - ) - counts["is_actionable"] += r["is_actionable"] if "is_actionable" in r else 0 - - if not all( - e in r - for e in [ - "is_relevant", - "is_tool_specific", - "can_be_validated", - "is_actionable", - ] - ) or not ( - r["is_relevant"] - and r["is_tool_specific"] - and r["can_be_validated"] - and r["is_actionable"] - ): - comments += r["comments"] + "\n" - - return not (all(float(counts[key]) / num > 0.5 for key in counts)), comments - - async def review_policy(self, tool_name, tptd) -> dict: - print("review_policy") - system_prompt = read_prompt_file("policy_reviewer") - newTPTD = {"policies": []} - - if "policies" not in tptd: - tptd["policies"] = [] - - for policy in tptd["policies"]: - reviews = [] - for _iteration in range(5): - user_content = f"Policy Document:{self.policy_document}\nTools Descriptions:{json.dumps(self.tools_descriptions)}\nTarget Tool:{json.dumps(self.tools_descriptions[tool_name])}\npolicy: {json.dumps(policy)}" - response = await self.llm.chat_json( - generate_messages(system_prompt, user_content) - ) - if "is_self_contained" in response: - is_self_contained = response["is_self_contained"] - if not is_self_contained: - if "alternative_description" in response: - policy["description"] = response["alternative_description"] - else: - print( - "Error: review is_self_contained is false but no alternative_description." - ) - else: - print("Error: review did not provide is_self_contained.") - reviews.append(response) - archive, comments = self.move2archive(reviews) - print(archive) - if archive: - if "archive" not in newTPTD: - newTPTD["archive"] = [] - policy["comments"] = comments - newTPTD["archive"].append(policy) - else: - newTPTD["policies"].append(policy) - save_output(self.out_dir, f"{tool_name}_rev.json", newTPTD) - return newTPTD - - async def add_references(self, tool_name: str, tptd: dict) -> dict: - print("add_ref") - system_prompt = read_prompt_file("add_references") - # remove old refs (used to help avoid duplications) - for policy in tptd["policies"]: - policy["references"] = [] - user_content = f"Policy Document:{self.policy_document}\nTools Descriptions:{json.dumps(self.tools_descriptions)}\nTarget Tool:{self.tools_details[tool_name].model_dump_json()}\npolicy: {json.dumps(policy)}" - response = await self.llm.chat_json( - generate_messages(system_prompt, user_content) - ) - if "references" in response: - policy["references"] = response["references"] - else: - print("Error! no references in response") - print(response) - - save_output(self.out_dir, f"{tool_name}_ref.json", tptd) - return tptd - - async def reference_correctness(self, tool_name: str, tptd: dict) -> dict: - print("reference_correctness") - tptd, unmatched_policies = find_mismatched_references( - self.policy_document, tptd - ) - save_output(self.out_dir, f"{tool_name}_ref_orig_.json", unmatched_policies) - save_output(self.out_dir, f"{tool_name}_ref_correction_.json", tptd) - return tptd - - async def example_creator(self, tool_name: str, tptd: dict) -> dict: - print("example_creator") - system_prompt = read_prompt_file("create_examples") - system_prompt = system_prompt.replace("ToolX", tool_name) - - for policy in tptd["policies"]: - # user_content = f"Policy Document:{state['policy_text']}\nTools Descriptions:{json.dumps(state['tools'])}\nTarget Tool:{json.dumps(state['target_tool_description'])}\nPolicy:{policy}" - user_content = f"Tools Descriptions:{json.dumps(self.tools_descriptions)}\nTarget Tool:{self.tools_details[tool_name].model_dump_json()}\nPolicy:{policy}" - - response = await self.llm.chat_json( - generate_messages(system_prompt, user_content) - ) - if "violating_examples" in response: - policy["violating_examples"] = response["violating_examples"] - - if "compliance_examples" in response: - policy["compliance_examples"] = response["compliance_examples"] - - save_output(self.out_dir, f"{tool_name}_examples.json", tptd) - return tptd - - async def add_examples(self, tool_name: str, tptd: dict, iteration: int) -> dict: - print("add_examples") - system_prompt = read_prompt_file("add_examples") - system_prompt = system_prompt.replace("ToolX", tool_name) - for policy in tptd["policies"]: - # user_content = f"Policy Document:{state['policy_text']}\nTools Descriptions:{json.dumps(state['tools'])}\nTarget Tool:{json.dumps(state['target_tool_description'])}\nPolicy:{policy}" - user_content = f"Tools Descriptions:{json.dumps(self.tools_descriptions)}\nTarget Tool:{self.tools_details[tool_name].model_dump_json()}\nPolicy:{policy}" - response = await self.llm.chat_json( - generate_messages(system_prompt, user_content) - ) - if "violating_examples" in response: - for vexample in response["violating_examples"]: - # vexample["iteration"] = state["iteration"] - if "violating_examples" not in policy: - policy["violating_examples"] = [] - policy["violating_examples"].append(vexample) - if "compliance_examples" in response: - for cexample in response["compliance_examples"]: - if "compliance_examples" not in policy: - policy["compliance_examples"] = [] - # cexample["iteration"] = state["iteration"] - policy["compliance_examples"].append(cexample) - - save_output(self.out_dir, f"{tool_name}_ADD_examples{iteration}.json", tptd) - return tptd - - async def merge_examples(self, tool_name: str, tptd: dict) -> dict: - print("merge_examples") - system_prompt = read_prompt_file("merge_examples") - system_prompt = system_prompt.replace("ToolX", tool_name) - for policy in tptd["policies"]: - # user_content = f"Policy Document:{state['policy_text']}\nTools Descriptions:{json.dumps(state['tools'])}\nTarget Tool:{json.dumps(state['target_tool_description'])}\nPolicy Name:{policy['policy_name']}\nPolicy Description:{policy['description']}" - user_content = f"Tools Descriptions:{json.dumps(self.tools_descriptions)}\nTarget Tool:{self.tools_details[tool_name].model_dump_json()}\nPolicy Name:{policy['policy_name']}\nPolicy Description:{policy['description']}" - user_content += f"\n\nViolating Examples: {policy['violating_examples']}" - user_content += f"\n\nCompliance Examples: {policy['compliance_examples']}" - response = await self.llm.chat_json( - generate_messages(system_prompt, user_content) - ) - policy["violating_examples"] = response["violating_examples"] - policy["compliance_examples"] = response["compliance_examples"] - - save_output(self.out_dir, f"{tool_name}_merge_examples.json", tptd) - return tptd - - async def fix_examples(self, tool_name: str, tptd: dict) -> dict: - print("fix_examples") - orig_prompt = read_prompt_file("fix_example") - for policy in tptd["policies"]: - for etype in ["violating", "compliance"]: - fixed_examples = [] - for example in policy[etype + "_examples"]: - system_prompt = orig_prompt.replace("ToolX", tool_name) - system_prompt = system_prompt.replace("__EXAMPLE_TYPE__", "") - - # user_content = f"Policy Document:{state['policy_text']}\nTools Descriptions:{json.dumps(state['tools'])}\nTarget Tool:{json.dumps(state['target_tool_description'])}\nPolicy Name:{policy['policy_name']}\nPolicy Description:{policy['description']}\nExample:{example}" - user_content = f"Tools Descriptions:{json.dumps(self.tools_descriptions)}\nTarget Tool:{self.tools_details[tool_name].model_dump_json()}\nPolicy Name:{policy['policy_name']}\nPolicy Description:{policy['description']}\nExample:{example}" - - response = await self.llm.chat_json( - generate_messages(system_prompt, user_content) - ) - fixed_examples.append(response["revised_example"]) - policy[etype + "_examples"] = fixed_examples - - save_output(self.out_dir, f"{tool_name}_fix_examples.json", tptd) - return tptd - - # todo: change to revew examples, write prompts - async def review_examples(self, tool_name: str, tptd: dict) -> dict: - print("review_examples") - system_prompt = read_prompt_file("examples_reviewer") - for policy in tptd["policies"]: - print(policy["policy_name"]) - for etype in ["violating", "compliance"]: - print(etype) - passed_examples = [] - for example in policy[etype + "_examples"]: - print(example) - reviews = [] - for _iteration in range(5): - # user_content = f"Policy Document:{state['policy_text']}\nTools Descriptions:{json.dumps(state['tools'])}\nTarget Tool:{json.dumps(state['target_tool_description'])}\nPolicy Name:{policy['policy_name']}\nPolicy Description:{policy['description']}\nExample:{example}" - user_content = f"Tools Descriptions:{json.dumps(self.tools_descriptions)}\nTarget Tool:{self.tools_details[tool_name].model_dump_json()}\nPolicy Name:{policy['policy_name']}\nPolicy Description:{policy['description']}\nExample:{example}" - response = await self.llm.chat_json( - generate_messages(system_prompt, user_content) - ) - reviews.append(response) - keep = self.keep_example(reviews) - if keep: - passed_examples.append(example) - - policy[etype + "_examples"] = passed_examples - - save_output(self.out_dir, f"{tool_name}_example_rev.json", tptd) - return tptd - - def keep_example(self, reviews) -> bool: - bads = 0 - totals = 0 - for r in reviews: - for vals in r.values(): - totals += 1 - if "value" not in vals: - print(reviews) - elif not vals["value"]: - bads += 1 - if bads / totals > 0.8: - return False - return True - - -async def extract_toolguard_specs( - policy_text: str, - tools: List[ToolInfo], - step1_output_dir: str, - llm: I_TG_LLM, - tools_shortlist: Optional[List[str]] = None, - short=False, -) -> List[ToolGuardSpec]: - if not os.path.isdir(step1_output_dir): - os.makedirs(step1_output_dir) - - process_dir = os.path.join(step1_output_dir, "process") - if not os.path.isdir(process_dir): - os.makedirs(process_dir) - output_tool_policies = [] - tpg = ToolGuardSpecGenerator(llm, policy_text, tools, process_dir) - - async def do_one_tool(tool_name): - if short: - final_output = await tpg.generate_minimal_policy(tool_name) - else: - final_output = await tpg.generate_policy(tool_name) - - with open(os.path.join(step1_output_dir, tool_name + ".json"), "w") as outfile1: - outfile1.write(json.dumps(final_output, indent=2)) - output_tool_policies.append( - load_tool_policy( - os.path.join(step1_output_dir, tool_name + ".json"), tool_name - ) - ) - - await asyncio.gather( - *[ - do_one_tool(tool.name) - for tool in tools - if ((tools_shortlist is None) or (tool.name in tools_shortlist)) - ] - ) - print("All tools done") - return output_tool_policies \ No newline at end of file diff --git a/altk/pre_tool/toolguard/toolguard/gen_spec/utils.py b/altk/pre_tool/toolguard/toolguard/gen_spec/utils.py deleted file mode 100644 index c40641e..0000000 --- a/altk/pre_tool/toolguard/toolguard/gen_spec/utils.py +++ /dev/null @@ -1,96 +0,0 @@ -from typing import Dict, Any -import os - -import json -from typing import List - - -def read_prompt_file(filename: str) -> str: - with open( - os.path.join(os.path.dirname(__file__), "prompts", filename + ".txt"), "r" - ) as f: - return f.read() - - -def generate_messages(system_prompt: str, user_content: str) -> List[Dict[str, str]]: - return [ - {"role": "system", "content": system_prompt}, - {"role": "user", "content": user_content}, - ] - - -def save_output(outdir: str, filename: str, content: Any): - with open(os.path.join(outdir, filename), "w") as outfile: - json.dump(content, outfile, indent=4) - - -def normalize_text(text): - """Normalize text by removing punctuation, converting to lowercase, and standardizing spaces.""" - # return re.sub(r'\s+', ' ', re.sub(r'[^a-zA-Z0-9\s]', '', text)).strip().lower() - return text.lower() - - -def split_reference_if_both_parts_exist(reference, policy_text): - words = reference.split() - for split_point in range(1, len(words)): - part1 = " ".join(words[:split_point]) - part2 = " ".join(words[split_point:]) - - normalized_part1 = normalize_text(part1) - normalized_part2 = normalize_text(part2) - normalized_policy_text = normalize_text(policy_text) - - if ( - normalized_part1 in normalized_policy_text - and normalized_part2 in normalized_policy_text - ): - start_idx1 = normalized_policy_text.find(normalized_part1) - end_idx1 = start_idx1 + len(part1) - start_idx2 = normalized_policy_text.find(normalized_part2) - end_idx2 = start_idx2 + len(part2) - return [policy_text[start_idx1:end_idx1], policy_text[start_idx2:end_idx2]] - return None - - -def find_mismatched_references(policy_text, policy_json): - corrections = json.loads(json.dumps(policy_json)) - unmatched_policies = [] - if isinstance(corrections["policies"], str): - return corrections, unmatched_policies - - normalized_policy_text = normalize_text(policy_text) - - for policy in corrections["policies"]: - corrected_references = [] - has_unmatched = False - - for reference in policy["references"]: - normalized_ref = normalize_text(reference) - # if normalized ref in policy doc- just copy the original - if normalized_ref in normalized_policy_text: - start_idx = normalized_policy_text.find(normalized_ref) - end_idx = start_idx + len(reference) - corrected_references.append(policy_text[start_idx:end_idx]) - else: - # close_match = get_close_matches(normalized_ref, [normalized_policy_text], n=1, cutoff=0.9) - # if close_match: - # start_idx = normalized_policy_text.find(close_match[0]) - # end_idx = start_idx + len(close_match[0]) - # corrected_references.append(policy_text[start_idx:end_idx]) - # else: - split_segments = split_reference_if_both_parts_exist( - reference, policy_text - ) - if split_segments: - corrected_references.extend(split_segments) - else: - corrected_references.append( - reference - ) # Keep original if no match found - has_unmatched = True - - policy["references"] = corrected_references - if has_unmatched: - unmatched_policies.append(policy["policy_name"]) - - return corrections, unmatched_policies \ No newline at end of file diff --git a/altk/pre_tool/toolguard/toolguard/llm/__init__.py b/altk/pre_tool/toolguard/toolguard/llm/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/altk/pre_tool/toolguard/toolguard/llm/i_tg_llm.py b/altk/pre_tool/toolguard/toolguard/llm/i_tg_llm.py deleted file mode 100644 index df658e4..0000000 --- a/altk/pre_tool/toolguard/toolguard/llm/i_tg_llm.py +++ /dev/null @@ -1,12 +0,0 @@ -from abc import ABC, abstractmethod -from typing import List, Dict - - -class I_TG_LLM(ABC): - @abstractmethod - async def chat_json(self, messages: List[Dict]) -> Dict: - pass - - @abstractmethod - async def generate(self, messages: List[Dict]) -> str: - pass diff --git a/altk/pre_tool/toolguard/toolguard/llm/tg_litellm.py b/altk/pre_tool/toolguard/toolguard/llm/tg_litellm.py deleted file mode 100644 index 625a928..0000000 --- a/altk/pre_tool/toolguard/toolguard/llm/tg_litellm.py +++ /dev/null @@ -1,176 +0,0 @@ -import asyncio -import json -import os -import re -from typing import List, Dict -import time -from litellm import acompletion -from litellm.types.utils import ModelResponse -from litellm.exceptions import RateLimitError -import json -from jsonschema import validate, ValidationError - -import dotenv - -from ..llm.i_tg_llm import I_TG_LLM - -rits_model_name_to_endpoint_list=[ -# {"endpoint":"https://ete-litellm.bx.cloud9.ibm.com", "model_name":"claude-3-7-sonnet"}, - {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/avengers-jamba-9b","model_name":"ibm-fms/avengers-jamba-9b"}, - {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/codellama-34b-instruct-hf","model_name":"codellama/CodeLlama-34b-Instruct-hf"}, - {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/deepseek-coder-33b-instruct","model_name":"deepseek-ai/DeepSeek-V2.5"}, - {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/deepseek-v2-5/v1","model_name":"deepseek-ai/DeepSeek-V2.5"}, - {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/deepseek-v3/v1","model_name":"deepseek-ai/DeepSeek-V3"}, - {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/gemma-2-9b-it","model_name":"google/gemma-2-9b-it"}, - {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/granite-13b-chat-v2","model_name":"ibm-granite/granite-13b-chat-v2"}, - {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/granite-13b-instruct-v2","model_name":"ibm-granite/granite-13b-instruct-v2"}, - {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/granite-20b-cbcl","model_name":"ibm-granite/granite-20b-code-base-content-linking"}, - {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/granite-20b-cbsl","model_name":"ibm/granite-20b-schema-sqlinstruct-granite-fine-vs"}, - {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/granite-20b-cbsql","model_name":"ibm-granite/granite-20b-code-base-sql-gen"}, - {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/granite-20b-ci-r1-1","model_name":"ibm-granite/granite-20b-code-instruct-r1.1"}, - {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/granite-20b-code-8k-ansible","model_name":"ibm/granite-20b-code-8k-ansible"}, - {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/granite-20b-code-instruct","model_name":"ibm/granite-20b-code-instruct"}, - {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/granite-20b-code-instruct-8k","model_name":"ibm-granite/granite-20b-code-instruct-8k"}, - {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/granite-20b-code-instruct-uapi","model_name":"ibm-granite/granite-20b-code-instruct-unified-api"}, - {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/granite-20b-functioncalling","model_name":"ibm-granite/granite-20b-functioncalling"}, - {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/granite-3-0-8b-instruct","model_name":"ibm-granite/granite-3.0-8b-instruct"}, - {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/granite-3-1-8b-content-linking","model_name":"ibm/granite-3-1-8b-content-linking"}, - {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/granite-3-1-8b-instruct","model_name":"ibm-granite/granite-3.1-8b-instruct"}, - {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/granite-3-1-8b-question-gen","model_name":"ibm/granite-3-1-8b-question-gen"}, - {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/granite-3-1-8b-schema-linking","model_name":"ibm/granite-3-1-8b-schema-linking"}, - {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/granite-3-1-8b-sql-gen","model_name":"ibm/granite-3-1-8b-sql-gen"}, - {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/granite-3-2-8b-instruct","model_name":"ibm-granite/granite-3.2-8b-instruct"}, - {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/granite-3-3-8b-instruct/v1/","model_name":"ibm-granite/granite-3.3-8b-instruct"}, - {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/granite-34b-code-instruct-8k","model_name":"ibm-granite/granite-34b-code-instruct-8k"}, - {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/granite-34b-content-linking","model_name":"ibm/granite-34b-content-linking"}, - {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/granite-34b-question-gen","model_name":"ibm/granite-34b-question-gen"}, - {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/granite-34b-schema-linking","model_name":"ibm/granite-34b-schema-linking"}, - {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/granite-34b-sql-gen","model_name":"ibm/granite-34b-sql-gen"}, - {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/granite-7b-lab","model_name":"ibm/granite-7b-lab"}, - {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/granite-8b-code-instruct-128k","model_name":"ibm-granite/granite-8b-code-instruct-128k"}, - {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/granite-8b-code-instruct-4k","model_name":"ibm-granite/granite-8b-code-instruct-4k"}, - {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/granite-8b-instruct-preview-4k","model_name":"ibm-granite/granite-8b-instruct-preview-4k"}, - {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/granite-8b-japanese-instruct","model_name":"ibm-granite/granite-8b-japanese-instruct"}, - {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/granite-vision-3-2-2b","model_name":"ibm-granite/granite-vision-3.2-2b"}, - {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/internvl2-llama3-76b","model_name":"OpenGVLab/InternVL2-Llama3-76B"}, - {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/llama-3-1-405b-instruct-fp8","model_name":"meta-llama/llama-3-1-405b-instruct-fp8"}, - {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/llama-3-1-70b-instruct","model_name":"meta-llama/llama-3-1-70b-instruct"}, - {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/llama-3-1-8b-instruct","model_name":"meta-llama/Llama-3.1-8B-Instruct"}, - {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/llama-3-1-test","model_name":"meta-llama/llama-3-1-405b-instruct-fp8"}, - {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/llama-3-2-11b-instruct","model_name":"meta-llama/Llama-3.2-11B-Vision-Instruct"}, - {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/llama-3-2-90b-instruct","model_name":"meta-llama/Llama-3.2-90B-Vision-Instruct"}, - {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/llama-3-3-70b-instruct/v1","model_name":"meta-llama/llama-3-3-70b-instruct"}, - {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/microsoft-phi-4/v1","model_name":"microsoft/phi-4"}, - {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/mistralai-pixtral-12b-2409","model_name":"mistralai/pixtral-12b-2409"}, - {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/mixtral-8x22b-instruct-v01/v1","model_name":"mistralai/mixtral-8x22B-instruct-v0.1"}, - {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/mixtral-8x7b-instruct-v01/v1","model_name":"mistralai/mixtral-8x7B-instruct-v0.1"}, - {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/mixtral-8x7b-instruct-v01-test","model_name":"mistralai/mixtral-8x7B-instruct-v0.1-test"}, - {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/pixtral-large-instruct-2411","model_name":"mistralai/Pixtral-Large-Instruct-2411"}, - {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/qwen2-5-72b-instruct/v1","model_name":"Qwen/Qwen2.5-72B-Instruct"}, - {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/qwen2-vl-72b-instruct","model_name":"Qwen/Qwen2-VL-72B-Instruct"}, - {"endpoint":"https://inference-3scale-apicast-production.apps.rits.fmaas.res.ibm.com/slate-125m-english-rtrvr-v2","model_name":"ibm/slate-125m-english-rtrvr-v2"}] - -rits_model_to_endpoint = { - entry["model_name"]: entry["endpoint"] - for entry in rits_model_name_to_endpoint_list -} - -anthropic_models = ['claude-3-5-sonnet-latest', 'claude-3-5-haiku'] -RITS = "RITS" - -class LitellmModel(I_TG_LLM): - def __init__(self, model_name:str, provider: str): - self.model_name = model_name - self.provider = provider - - async def generate(self, messages: List[Dict])->str: - if self.provider and self.provider.upper() == RITS: - response = await acompletion( - messages=messages, - model=self.model_name, - custom_llm_provider="openai", - base_url=rits_model_to_endpoint[self.model_name], - extra_headers={ - 'RITS_API_KEY': os.getenv("RITS_API_KEY"), - 'Content-Type': 'application/json' - }, - ) - return response.choices[0].message.content - - response = await acompletion( - messages=messages, - model=self.model_name, - custom_llm_provider=self.provider, - extra_headers={ - 'Content-Type': 'application/json' - }, - ) - return response["choices"][0]["message"]["content"] - - - async def chat_json(self, messages: List[Dict], max_retries: int = 5, backoff_factor: float = 1.5) -> Dict: - retries = 0 - while retries < max_retries: - try: - response = await self.generate(messages) - res = self.extract_json_from_string(response) - if res is None: - wait_time = backoff_factor ** retries - print(f"Error: not json format. Retrying in {wait_time:.1f} seconds... (attempt {retries + 1}/{max_retries})") - time.sleep(wait_time) - retries += 1 - else: - return res - except RateLimitError as e: - wait_time = backoff_factor ** retries - print(f"Rate limit hit. Retrying in {wait_time:.1f} seconds... (attempt {retries + 1}/{max_retries})") - time.sleep(wait_time) - retries += 1 - except Exception as e: - raise RuntimeError(f"Unexpected error during chat completion: {e}") - raise RuntimeError("Exceeded maximum retries due to rate limits.") - - def extract_json_from_string(self,s): - # Use regex to extract the JSON part from the string - match = re.search(r'```json\s*(\{.*?\})\s*```', s, re.DOTALL) - if match: - json_str = match.group(1) - try: - return json.loads(json_str) - except json.JSONDecodeError as e: - print(f"Failed to decode JSON: {e}") - return None - else: - ## for rits? - match = re.search(r'(\{[\s\S]*\})', s) - if match: - json_str = match.group(1) - try: - return json.loads(json_str) - except json.JSONDecodeError as e: - print("response:") - print(s) - print("match:") - print(match.group(1)) - print("Failed to parse JSON:", e) - return None - - print("No JSON found in the string.") - print(s) - return None - - -if __name__ == '__main__': - dotenv.load_dotenv() - model = "gpt-4o-2024-08-06" - #model = "claude-3-5-sonnet-20240620" - #model = "meta-llama/llama-3-3-70b-instruct" - aw = LitellmModel(model, "azure") - - async def my_test(): - resp = await aw.generate([{"role": "user", "content": "what is the weather?"}]) - print(resp) - resp = await aw.chat_json([{"role": "user", "content": "what is the weather? please answer in json format"}]) - print(resp) - asyncio.run(my_test()) - diff --git a/altk/pre_tool/toolguard/toolguard/logging_utils.py b/altk/pre_tool/toolguard/toolguard/logging_utils.py deleted file mode 100644 index 250f847..0000000 --- a/altk/pre_tool/toolguard/toolguard/logging_utils.py +++ /dev/null @@ -1,34 +0,0 @@ - -import logging - - -def add_log_file_handler(log_file:str): - formatter = logging.Formatter( - fmt="%(asctime)s | %(levelname)s | %(name)s | %(message)s", - datefmt="%Y-%m-%d %H:%M:%S" - ) - file_handler = logging.FileHandler(log_file, encoding="utf-8") - file_handler.setFormatter(formatter) - logging.getLogger().addHandler(file_handler) - -def init_logging(): - logging.getLogger().setLevel(logging.INFO) #Default for other libraries - logging.getLogger("toolguard").setLevel(logging.DEBUG) # debug for our library - logging.getLogger("mellea").setLevel(logging.DEBUG) - init_log_console_handler() - -def init_log_console_handler(): - formatter = logging.Formatter( - fmt="%(asctime)s | %(levelname)s | %(name)s | %(message)s", - datefmt="%Y-%m-%d %H:%M:%S" - ) - - # Set up console handler - console_handler = logging.StreamHandler() - console_handler.setFormatter(formatter) - - # Set up the root logger - logging.basicConfig( - level=logging.INFO, - handlers=[console_handler] - ) diff --git a/altk/pre_tool/toolguard/toolguard/runtime.py b/altk/pre_tool/toolguard/toolguard/runtime.py deleted file mode 100644 index e27c038..0000000 --- a/altk/pre_tool/toolguard/toolguard/runtime.py +++ /dev/null @@ -1,189 +0,0 @@ -import importlib -import inspect -import json -import os -import sys -from types import ModuleType -from typing import Any, Dict, List, Optional, Type, Callable, TypeVar -from pydantic import BaseModel -from langchain_core.tools import BaseTool - -import functools -from .data_types import API_PARAM, RESULTS_FILENAME, FileTwin, RuntimeDomain, ToolGuardSpec - -from abc import ABC, abstractmethod - - -class IToolInvoker(ABC): - T = TypeVar("T") - - @abstractmethod - def invoke( - self, toolname: str, arguments: Dict[str, Any], return_type: Type[T] - ) -> T: ... - -def load_toolguard_code_result(directory: str, filename: str = RESULTS_FILENAME): - full_path = os.path.join(directory, filename) - with open(full_path, "r", encoding="utf-8") as f: - data = json.load(f) - return ToolGuardsCodeGenerationResult(**data) - -def load_toolguards(directory: str, filename: str = RESULTS_FILENAME) -> "ToolguardRuntime": - return ToolguardRuntime(load_toolguard_code_result(directory, filename), directory) - -class ToolGuardCodeResult(BaseModel): - tool: ToolGuardSpec - guard_fn_name: str - guard_file: FileTwin - item_guard_files: List[FileTwin | None] - test_files: List[FileTwin | None] - - -class ToolGuardsCodeGenerationResult(BaseModel): - out_dir: str - domain: RuntimeDomain - tools: Dict[str, ToolGuardCodeResult] - - def save( - self, directory: str, filename: str = RESULTS_FILENAME - ) -> "ToolGuardsCodeGenerationResult": - full_path = os.path.join(directory, filename) - with open(full_path, "w", encoding="utf-8") as f: - json.dump(self.model_dump(), f, indent=2) - return self - - -class ToolguardRuntime: - _original_pypath:list[str] = [] - - def __init__(self, result: ToolGuardsCodeGenerationResult, ctx_dir: str) -> None: - self._ctx_dir = ctx_dir - self._result = result - - def __enter__(self): - #add folder to python path - self._original_pypath = list(sys.path) #remember old path - sys.path.insert(0, os.path.abspath(self._ctx_dir)) - - #cache the tool guards - self._guards = {} - for tool_name, tool_result in self._result.tools.items(): - mod_name = file_to_module_name(tool_result.guard_file.file_name) - module = importlib.import_module(mod_name) - guard_fn = find_function_in_module(module, tool_result.guard_fn_name) - assert guard_fn, "Guard not found" - self._guards[tool_name] = guard_fn - - return self - - def __exit__(self, exc_type, exc, tb): - del self._guards - #back to original python path - sys.path[:] = self._original_pypath - return False - - def _make_args(self, guard_fn:Callable, args: dict, delegate: IToolInvoker)->Dict[str, Any]: - sig = inspect.signature(guard_fn) - guard_args = {} - for p_name, param in sig.parameters.items(): - if p_name == API_PARAM: - mod_name = file_to_module_name(self._result.domain.app_api_impl.file_name) - module = importlib.import_module(mod_name) - clazz = find_class_in_module(module, self._result.domain.app_api_impl_class_name) - assert clazz, f"class {self._result.domain.app_api_impl_class_name} not found in {self._result.domain.app_api_impl.file_name}" - guard_args[p_name] = clazz(delegate) - else: - arg_val = args.get(p_name) - if inspect.isclass(param.annotation) and issubclass(param.annotation, BaseModel): - guard_args[p_name] = param.annotation.model_construct(**arg_val) - else: - guard_args[p_name] = arg_val - return guard_args - - def check_toolcall(self, tool_name: str, args: dict, delegate: IToolInvoker): - guard_fn = self._guards.get(tool_name) - if guard_fn is None: # No guard assigned to this tool - return - guard_fn(**self._make_args(guard_fn, args, delegate)) - -def file_to_module_name(file_path:str): - return file_path.removesuffix('.py').replace('/', '.') - -def find_function_in_module(module: ModuleType, function_name: str): - func = getattr(module, function_name, None) - if func is None or not inspect.isfunction(func): - raise AttributeError( - f"Function '{function_name}' not found in module '{module.__name__}'" - ) - return func - - -def find_class_in_module(module: ModuleType, class_name: str) -> Optional[Type]: - cls = getattr(module, class_name, None) - if isinstance(cls, type): - return cls - return None - - -T = TypeVar("T") - - -def guard_methods(obj: T, guards_folder: str) -> T: - """Wraps all public bound methods of the given instance using the given wrapper.""" - for attr_name in dir(obj): - if attr_name.startswith("_"): - continue - attr = getattr(obj, attr_name) - if callable(attr): - wrapped = guard_before_call(guards_folder)(attr) - setattr(obj, attr_name, wrapped) - return obj - - -class ToolMethodsInvoker(IToolInvoker): - def __init__(self, object: object) -> None: - self._obj = object - - def invoke(self, toolname: str, arguments: Dict[str, Any], return_type: Type[T]) -> T: - mtd = getattr(self._obj, toolname) - assert callable(mtd), f"Tool {toolname} was not found" - return mtd(**arguments) - - -class ToolFunctionsInvoker(IToolInvoker): - def __init__(self, funcs: List[Callable]) -> None: - self._funcs_by_name = {func.__name__: func for func in funcs} - - def invoke(self, toolname: str, arguments: Dict[str, Any], return_type: Type[T]) -> T: - func = self._funcs_by_name.get(toolname) - assert callable(func), f"Tool {toolname} was not found" - return func(**arguments) - -class LangchainToolInvoker(IToolInvoker): - T = TypeVar("T") - _tools: Dict[str, BaseTool] - - def __init__(self, tools: List[BaseTool]) -> None: - self._tools = {tool.name: tool for tool in tools} - - def invoke(self, toolname: str, arguments: Dict[str, Any], return_type: Type[T])->T: - tool = self._tools.get(toolname) - if tool: - return tool.invoke(arguments) - raise ValueError(f"unknown tool {toolname}") - -def guard_before_call(guards_folder: str) -> Callable[[Callable], Callable]: - """Decorator factory that logs function calls to the given logfile.""" - toolguards = load_toolguards(guards_folder) - - def decorator(func: Callable) -> Callable: - @functools.wraps(func) - def wrapper(*args: Any, **kwargs: Any) -> Any: - toolguards.check_toolcall( - func.__name__, kwargs, ToolMethodsInvoker(func.__self__) - ) - return func(*args, **kwargs) - - return wrapper # type: ignore - - return decorator diff --git a/altk/pre_tool/toolguard/toolguard_code_component.py b/altk/pre_tool/toolguard/toolguard_code_component.py index b976b9e..7cbbb94 100644 --- a/altk/pre_tool/toolguard/toolguard_code_component.py +++ b/altk/pre_tool/toolguard/toolguard_code_component.py @@ -6,9 +6,9 @@ from langchain.tools import BaseTool from altk.core.toolkit import ComponentConfig, ComponentInput, AgentPhase, ComponentBase -from .toolguard.data_types import MeleaSessionData -from .toolguard import generate_guards_from_specs, ToolGuardSpec, ToolGuardsCodeGenerationResult, load_toolguards -from .toolguard.runtime import IToolInvoker, ToolGuardsCodeGenerationResult +from toolguard.data_types import MeleaSessionData +from toolguard import generate_guards_from_specs, ToolGuardSpec, ToolGuardsCodeGenerationResult, load_toolguards +from toolguard.runtime import IToolInvoker, ToolGuardsCodeGenerationResult logger = logging.getLogger(__name__) diff --git a/altk/pre_tool/toolguard/toolguard_spec_component.py b/altk/pre_tool/toolguard/toolguard_spec_component.py index c830055..409963e 100644 --- a/altk/pre_tool/toolguard/toolguard_spec_component.py +++ b/altk/pre_tool/toolguard/toolguard_spec_component.py @@ -6,8 +6,8 @@ from ...core.llm import ValidatingLLMClient from ...core.toolkit import AgentPhase, ComponentBase, ComponentConfig, ComponentInput -from .toolguard import ToolGuardSpec, generate_guard_specs -from .toolguard.llm.i_tg_llm import I_TG_LLM +from toolguard import ToolGuardSpec, generate_guard_specs +from toolguard.llm.i_tg_llm import I_TG_LLM logger = logging.getLogger(__name__) diff --git a/pyproject.toml b/pyproject.toml index 4852900..4f581bb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,6 +20,7 @@ dependencies = [ "pydantic>=2.0.0", "smolagents>=1.21.3", "typing-extensions>=4.0.0", + "toolguard>=0.1.3" ] description = "The Agent Lifecycle Toolkit (ALTK) is a library of components to help agent builders improve their agent with minimal integration effort and setup." diff --git a/tests/pre_tool/toolguard/test_toolguard_code.py b/tests/pre_tool/toolguard/test_toolguard_code.py index 99dab31..b9acce5 100644 --- a/tests/pre_tool/toolguard/test_toolguard_code.py +++ b/tests/pre_tool/toolguard/test_toolguard_code.py @@ -22,11 +22,11 @@ ToolGuardCodeComponent, ToolGuardCodeBuildInput, ) -from altk.pre_tool.toolguard.toolguard.data_types import ( +from toolguard.data_types import ( MeleaSessionData, load_tool_policy, ) -from altk.pre_tool.toolguard.toolguard.runtime import ( +from toolguard.runtime import ( ToolFunctionsInvoker, ToolGuardsCodeGenerationResult, load_toolguard_code_result, @@ -149,7 +149,7 @@ def call(tool_name: str, args: Dict) -> ToolGuardCodeRunOutput: ToolGuardCodeRunOutput, toolguard_code.process( ToolGuardCodeRunInput( - generated_guard_dir=build_output.root_dir, + generated_guard_dir=build_output.out_dir, tool_name=tool_name, tool_args=args, tool_invoker=tool_invoker, From ce0bd8ae23e0e9ea70b74dfac0348ac16ec6f5ef Mon Sep 17 00:00:00 2001 From: DAVID BOAZ Date: Tue, 9 Dec 2025 12:42:30 +0200 Subject: [PATCH 13/42] DCO Remediation Commit for DAVID BOAZ I, DAVID BOAZ , hereby add my Signed-off-by to this commit: ec005bcb5762a7626fd1d92edf3b09f3307b9195 Signed-off-by: DAVID BOAZ --- altk/pre_tool/toolguard/README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/altk/pre_tool/toolguard/README.md b/altk/pre_tool/toolguard/README.md index 8a75d64..dca16d3 100644 --- a/altk/pre_tool/toolguard/README.md +++ b/altk/pre_tool/toolguard/README.md @@ -16,7 +16,6 @@ An agent lifecycle solution for enforcing business policy adherence in agentic w ## Overview Business policies (or guidelines) are normally detailed in company documents, and have traditionally been hard-coded into automatic assistant platforms. Contemporary agentic approaches take the "best-effort" strategy, where the policies are appended to the agent's system prompt, an inherently non-deterministic approach, that does not scale effectively. Here we propose a deterministic, predictable and interpretable two-phase solution for agentic policy adherence at the tool-level: guards are executed prior to function invocation and raise alerts in case a tool-related policy deem violated. - This component enforces **pre‑tool activation policy constraints**, ensuring that agent decisions comply with business rules **before** modifying system state. This prevents policy violations such as unauthorized tool calls or unsafe parameter values. ## ToolGuardSpecComponent From 62ec8025694bbd90b3a8a35b931194b8851444f7 Mon Sep 17 00:00:00 2001 From: DAVID BOAZ Date: Sun, 14 Dec 2025 12:28:22 +0200 Subject: [PATCH 14/42] fix PR comments Signed-off-by: DAVID BOAZ --- altk/pre_tool/core/types.py | 45 +------------------ altk/pre_tool/toolguard/README.md | 4 +- .../toolguard/toolguard_code_component.py | 4 +- pyproject.toml | 2 +- .../pre_tool/toolguard/test_toolguard_code.py | 8 ++-- .../toolguard/test_toolguard_specs.py | 22 ++++----- 6 files changed, 23 insertions(+), 62 deletions(-) diff --git a/altk/pre_tool/core/types.py b/altk/pre_tool/core/types.py index bfb2870..57a024b 100644 --- a/altk/pre_tool/core/types.py +++ b/altk/pre_tool/core/types.py @@ -1,8 +1,7 @@ -from typing import List, Optional, Dict, Any, Union -from pydantic import BaseModel, Field, ConfigDict +from typing import List, Optional, Dict, Any +from pydantic import BaseModel, Field from enum import Enum -from altk.core.llm import LLMClient from altk.core.toolkit import ComponentInput, ComponentOutput from altk.pre_tool.refraction.src.schemas.results import ( DebuggingResult, @@ -117,43 +116,3 @@ class RefractionBuildInput(PreToolReflectionBuildInput): class RefractionRunOutput(PreToolReflectionRunOutput): result: Optional[DebuggingResult] = None - - -# class ToolGuardBuildInputMetaData(BaseModel): -# model_config = ConfigDict(arbitrary_types_allowed=True) -# policy_text: str = Field(description="Text of the policy document file") -# short1: bool = Field(default=True, description="Run build short or long version. ") -# validating_llm_client: LLMClient = Field( -# description="ValidatingLLMClient for build time" -# ) - - -# class ToolGuardBuildInput(ComponentInput): -# metadata: ToolGuardBuildInputMetaData = Field( -# default_factory=lambda: ToolGuardBuildInputMetaData() -# ) - - -# class ToolGuardRunInputMetaData(BaseModel): -# model_config = ConfigDict(arbitrary_types_allowed=True) -# tool_name: str = Field(description="Tool name") -# tool_parms: dict = Field(default={}, description="Tool parameters") -# llm_client: LLMClient = Field(description="LLMClient for build time") - - -# class ToolGuardRunInput(ComponentInput): -# metadata: ToolGuardRunInputMetaData = Field( -# default_factory=lambda: ToolGuardRunInputMetaData() -# ) - - -# class ToolGuardRunOutputMetaData(BaseModel): -# error_message: Union[str, bool] = Field( -# description="Error string or False if no error occurred" -# ) - - -# class ToolGuardRunOutput(ComponentOutput): -# output: ToolGuardRunOutputMetaData = Field( -# default_factory=lambda: ToolGuardRunOutputMetaData() -# ) diff --git a/altk/pre_tool/toolguard/README.md b/altk/pre_tool/toolguard/README.md index dca16d3..ec83d50 100644 --- a/altk/pre_tool/toolguard/README.md +++ b/altk/pre_tool/toolguard/README.md @@ -1,5 +1,5 @@ # ToolGuards for Enforcing Agentic Policy Adherence -An agent lifecycle solution for enforcing business policy adherence in agentic workflows. Enabling this component has demonstrated up to a **20‑point improvement** in end‑to‑end agent accuracy when invoking tools. This work is described in [EMNLP 2025 Towards Enforcing Company Policy Adherence in Agentic Workflows](https://arxiv.org/pdf/2507.16459). +An agent lifecycle solution for enforcing business policy adherence in agentic workflows. Enabling this component has demonstrated up to a **20‑point improvement** in end‑to‑end agent accuracy when invoking tools. This work is described in [EMNLP 2025 Towards Enforcing Company Policy Adherence in Agentic Workflows](https://arxiv.org/pdf/2507.16459), and is publiched in [this GitHub library](https://github.com/IBM/toolguard). ## Table of Contents - [Overview](#overview) @@ -90,7 +90,7 @@ mellea.MelleaSession.start_session( backend_kwargs=... # any additional arguments ) ``` -The `melea` session parameters can be provided explicitely, or loaded from environment variables: +The `mellea` session parameters can be provided explicitely, or loaded from environment variables: | Environment Variable | Mellea Parameter | Description | | ------------------------------ | ---------------- | ------------------------------------------------------------------ | diff --git a/altk/pre_tool/toolguard/toolguard_code_component.py b/altk/pre_tool/toolguard/toolguard_code_component.py index 7cbbb94..e470bfb 100644 --- a/altk/pre_tool/toolguard/toolguard_code_component.py +++ b/altk/pre_tool/toolguard/toolguard_code_component.py @@ -6,14 +6,14 @@ from langchain.tools import BaseTool from altk.core.toolkit import ComponentConfig, ComponentInput, AgentPhase, ComponentBase -from toolguard.data_types import MeleaSessionData +from toolguard.data_types import MelleaSessionData from toolguard import generate_guards_from_specs, ToolGuardSpec, ToolGuardsCodeGenerationResult, load_toolguards from toolguard.runtime import IToolInvoker, ToolGuardsCodeGenerationResult logger = logging.getLogger(__name__) class ToolGuardCodeComponentConfig(ComponentConfig): - llm_config: MeleaSessionData | None = None + llm_config: MelleaSessionData | None = None class ToolGuardCodeBuildInput(ComponentInput): tools: List[Callable] | List[BaseTool] | str diff --git a/pyproject.toml b/pyproject.toml index 4f581bb..d1a4be7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,7 +20,7 @@ dependencies = [ "pydantic>=2.0.0", "smolagents>=1.21.3", "typing-extensions>=4.0.0", - "toolguard>=0.1.3" + "toolguard>=0.1.6" ] description = "The Agent Lifecycle Toolkit (ALTK) is a library of components to help agent builders improve their agent with minimal integration effort and setup." diff --git a/tests/pre_tool/toolguard/test_toolguard_code.py b/tests/pre_tool/toolguard/test_toolguard_code.py index b9acce5..42fa9bd 100644 --- a/tests/pre_tool/toolguard/test_toolguard_code.py +++ b/tests/pre_tool/toolguard/test_toolguard_code.py @@ -23,7 +23,7 @@ ToolGuardCodeBuildInput, ) from toolguard.data_types import ( - MeleaSessionData, + MelleaSessionData, load_tool_policy, ) from toolguard.runtime import ( @@ -51,6 +51,7 @@ # Load environment variables dotenv.load_dotenv() +WATSONX_CREDS_AVAILABLE = all([os.getenv("WX_API_KEY"), os.getenv("WX_PROJECT_ID")]) # --------------------------------------------------------------------------- # Fixtures @@ -74,11 +75,12 @@ def work_dir(): # --------------------------------------------------------------------------- @pytest.mark.asyncio +@pytest.mark.skipif(not WATSONX_CREDS_AVAILABLE, reason="WatsonX credentials not set") async def test_tool_guard_calculator_policy(work_dir: str): # Tools to be guarded funcs = [divide_tool, add_tool, multiply_tool, subtract_tool, map_kdi_number] - # Configure Melea session used in ToolGuard LLM + # Configure Mellea session used in ToolGuard LLM # see https://docs.mellea.ai/api-reference/core-library/stdlib/mellea-stdlib-session#start-session backend_name = "openai" #"ollama", "hf", "openai", "watsonx", "litellm" model_id = "GCP/claude-4-sonnet" @@ -90,7 +92,7 @@ async def test_tool_guard_calculator_policy(work_dir: str): # Build ToolGuard component toolguard_code = ToolGuardCodeComponent( ToolGuardCodeComponentConfig( - llm_config=MeleaSessionData( + llm_config=MelleaSessionData( backend_name=backend_name, model_id=model_id, kw_args=kw_args, diff --git a/tests/pre_tool/toolguard/test_toolguard_specs.py b/tests/pre_tool/toolguard/test_toolguard_specs.py index a909e69..e4f1b59 100644 --- a/tests/pre_tool/toolguard/test_toolguard_specs.py +++ b/tests/pre_tool/toolguard/test_toolguard_specs.py @@ -66,20 +66,20 @@ async def test_tool_guard_calculator_policy(out_dir: str): """ # Example alternative LLM: - LLMClient = get_llm("litellm.output_val") - llm_client = LLMClient( - model_name="gpt-4o-2024-08-06", - custom_llm_provider="azure", - ) - - # LLMClient = get_llm("watsonx.output_val") + # LLMClient = get_llm("litellm.output_val") # llm_client = LLMClient( - # model_name="mistralai/mistral-medium-2505", - # api_key=os.getenv("WX_API_KEY"), - # project_id=os.getenv("WX_PROJECT_ID"), - # url=os.getenv("WX_URL", "https://us-south.ml.cloud.ibm.com"), + # model_name="gpt-4o-2024-08-06", + # custom_llm_provider="azure", # ) + LLMClient = get_llm("watsonx.output_val") + llm_client = LLMClient( + model_name="mistralai/mistral-medium-2505", + api_key=os.getenv("WX_API_KEY"), + project_id=os.getenv("WX_PROJECT_ID"), + url=os.getenv("WX_URL", "https://us-south.ml.cloud.ibm.com"), + ) + toolguard_spec = ToolGuardSpecComponent( ToolGuardSpecComponentConfig(llm_client=llm_client) ) From 10b325f24cbffaeadc8beef5808f8713739a12f6 Mon Sep 17 00:00:00 2001 From: DAVID BOAZ Date: Sun, 21 Dec 2025 08:46:23 +0200 Subject: [PATCH 15/42] fix merge --- altk/pre_llm/core/types.py | 185 +++++++++++++++++++++--------------- altk/pre_tool/core/types.py | 24 +---- 2 files changed, 114 insertions(+), 95 deletions(-) diff --git a/altk/pre_llm/core/types.py b/altk/pre_llm/core/types.py index 7abcbe6..f06c9f5 100644 --- a/altk/pre_llm/core/types.py +++ b/altk/pre_llm/core/types.py @@ -1,93 +1,128 @@ -from typing import List, Optional, Dict, Any, Union -from pydantic import BaseModel, Field, ConfigDict -from enum import Enum +from typing import ( + Any, + Dict, + Generic, + Iterable, + Iterator, + List, + Literal, + Optional, + Protocol, + TypeVar, + runtime_checkable, +) + +from pydantic import BaseModel, ConfigDict, Field -from altk.core.llm import LLMClient from altk.core.toolkit import ComponentInput, ComponentOutput +T = TypeVar("T") -class SPARCReflectionDecision(str, Enum): - """Decision made by the reflection pipeline.""" - APPROVE = "approve" - REJECT = "reject" - ERROR = "error" - - -class SPARCReflectionIssueType(str, Enum): - """Types of issues that can be identified by reflection.""" - - STATIC = "static" - SEMANTIC_GENERAL = "semantic_general" - SEMANTIC_FUNCTION = "semantic_function" - SEMANTIC_PARAMETER = "semantic_parameter" - TRANSFORM = "transform" - ERROR = "error" - - -class SPARCReflectionIssue(BaseModel): - """Represents an issue identified during reflection.""" - - issue_type: SPARCReflectionIssueType - metric_name: str - explanation: str - correction: Optional[Dict[str, Any]] = None - - -class SPARCReflectionResult(BaseModel): - """Result of reflecting on a single tool call.""" - - decision: SPARCReflectionDecision - issues: List[SPARCReflectionIssue] = Field(default_factory=list) - - @property - def has_issues(self) -> bool: - """Check if any issues were found.""" - return len(self.issues) > 0 - - -class PreToolReflectionRunInput(ComponentInput): - tool_specs: list[dict[str, Any]] = Field( - description="List of available tool specifications" +class TopicInfo(BaseModel): + topic: str = Field(description="Text that describes the topic") + expertise: Optional[Literal["expert", "knowledge", "mentions"]] = Field( + description="Level of expertise that the subject has on the topic", default=None ) - tool_calls: list[dict[str, Any]] = Field( - description="List of tool calls to reflect upon" + subject: str = Field( + description="Represents the entity that has certain degree of knowledge on the topic. Might be an agent name." + ) + metadata: Dict[str, bool | int | float | str] = Field( + description="Topic fields that can be used to filter topics during topic retrieval.", + default=dict(), ) - -class PreToolReflectionRunOutput(ComponentOutput): - pass - - -class PreToolReflectionBuildInput(ComponentInput): - pass - - -class PreToolReflectionBuildOutput(ComponentOutput): - pass + def __lt__(self, other: "TopicInfo"): + # expertise level precedence + # expert > knowledge > mentions + if not isinstance(other, __class__): + return NotImplemented + if ( + self.topic == other.topic + and self.subject == other.subject + and ( + ( + self.expertise == "mentions" + and other.expertise in ["knowledge", "expert"] + ) + or (self.expertise == "knowledge" and other.expertise == "expert") + ) + ): + return True + return False + + +class EmbeddedTopic(BaseModel): + topic: TopicInfo + embeddings: List[Any] | None = None + + +class RetrievedTopic(BaseModel): + topic: TopicInfo + distance: float + + +class TopicExtractionBuildOutput(ComponentOutput, Generic[T]): + model_config = ConfigDict(arbitrary_types_allowed=True) + error: Optional[Exception] = None + topics: List[TopicInfo] = Field(default_factory=list) + topic_extractor_output: Optional[T] = None + + +class TopicExtractionInput(ComponentInput): + documents: Iterable[str] + + +class TopicLoadingInput(ComponentInput): + model_config = ConfigDict(arbitrary_types_allowed=True) + topics: List[EmbeddedTopic | TopicInfo] + # embedding_function type: chromadb.api.types.EmbeddingFunction + embedding_function: Optional[Any] = Field( + deprecated="This field is deprecated, use a `TopicsSink` with the desired embedding function instead", + default=None, + ) -class SPARCReflectionRunInput(PreToolReflectionRunInput): - """Input for running SPARC reflection.""" +@runtime_checkable # see https://github.com/pydantic/pydantic/discussions/5767#discussioncomment-5919490 +class TopicsSink(Protocol): + def add_topics(self, topics: List[TopicInfo]): ... + def add_embedded_topics( + self, + topics: List[EmbeddedTopic], + # emb_fn type: chromadb.api.types.EmbeddingFunction + emb_fn: Any | None = None, + ): ... - pass +@runtime_checkable # see https://github.com/pydantic/pydantic/discussions/5767#discussioncomment-5919490 +class ContentProvider(Protocol): + def get_content(self) -> Iterator[str]: ... -class SPARCReflectionRunOutputSchema(BaseModel): - """Output from SPARC reflection.""" - reflection_result: SPARCReflectionResult - execution_time_ms: float - raw_pipeline_result: Optional[Dict[str, Any]] = None +class TopicRetrievalRunInput(ComponentInput): + n_results: int = Field( + description="Number of results to return from the topic retriever", default=10 + ) + query_kwargs: Dict[str, Any] = Field( + description="Keyworded args passed directly to the underlying query function. If using ChromaDB as a TopicsSink the kwargs will be passed in the collection.query function.", + default=dict(), + ) + distance_threshold: Optional[float] = Field( + description="Include only topics that are below a given distance threshold in the proximity search query result", + default=None, + ) - def should_proceed_with_tool_call(self) -> bool: - """Determine if the tool call should proceed based on reflection.""" - return self.reflection_result.decision == SPARCReflectionDecision.APPROVE +class TopicRetrievalRunOutput(ComponentOutput): + topics: Optional[List[RetrievedTopic]] = None -class SPARCReflectionRunOutput(PreToolReflectionRunOutput): - """Output for running SPARC reflection.""" - output: SPARCReflectionRunOutputSchema = Field( - default_factory=lambda: SPARCReflectionRunOutputSchema() - ) \ No newline at end of file +@runtime_checkable # see https://github.com/pydantic/pydantic/discussions/5767#discussioncomment-5919490 +class TopicRetriever(Protocol): + def get_topics( + self, + query: str, + n_results: int = 10, + query_kwargs: Dict[str, Any] | None = None, + distance_threshold: float | None = None, + ) -> List[RetrievedTopic]: ... \ No newline at end of file diff --git a/altk/pre_tool/core/types.py b/altk/pre_tool/core/types.py index 7a07de2..7abcbe6 100644 --- a/altk/pre_tool/core/types.py +++ b/altk/pre_tool/core/types.py @@ -1,7 +1,8 @@ -from typing import List, Optional, Dict, Any -from pydantic import BaseModel, Field +from typing import List, Optional, Dict, Any, Union +from pydantic import BaseModel, Field, ConfigDict from enum import Enum +from altk.core.llm import LLMClient from altk.core.toolkit import ComponentInput, ComponentOutput @@ -89,21 +90,4 @@ class SPARCReflectionRunOutput(PreToolReflectionRunOutput): output: SPARCReflectionRunOutputSchema = Field( default_factory=lambda: SPARCReflectionRunOutputSchema() - ) - - -class RefractionRunInput(PreToolReflectionRunInput): - mappings: Optional[list[Mapping]] = None - memory_objects: Optional[dict[str, Any]] = None - use_given_operators_only: bool = False - - -class RefractionBuildInput(PreToolReflectionBuildInput): - tool_specs: list[dict[str, Any]] | Catalog - top_k: int = 5 - threshold: float = 0.8 - compute_maps: bool = True - - -class RefractionRunOutput(PreToolReflectionRunOutput): - result: Optional[DebuggingResult] = None + ) \ No newline at end of file From 1a6d9bccd9e339a84026c288876999dc1b6956fe Mon Sep 17 00:00:00 2001 From: DAVID BOAZ Date: Sun, 21 Dec 2025 08:47:31 +0200 Subject: [PATCH 16/42] fix merge --- altk/pre_llm/core/types.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/altk/pre_llm/core/types.py b/altk/pre_llm/core/types.py index f06c9f5..bb7971c 100644 --- a/altk/pre_llm/core/types.py +++ b/altk/pre_llm/core/types.py @@ -125,4 +125,5 @@ def get_topics( n_results: int = 10, query_kwargs: Dict[str, Any] | None = None, distance_threshold: float | None = None, - ) -> List[RetrievedTopic]: ... \ No newline at end of file + ) -> List[RetrievedTopic]: ... + \ No newline at end of file From b9c65717606e808925c5eb4db68e8877c50a59fe Mon Sep 17 00:00:00 2001 From: DAVID BOAZ Date: Sun, 21 Dec 2025 08:53:29 +0200 Subject: [PATCH 17/42] DCO Remediation Commit for DAVID BOAZ I, DAVID BOAZ , hereby add my Signed-off-by to this commit: 10b325f24cbffaeadc8beef5808f8713739a12f6 I, DAVID BOAZ , hereby add my Signed-off-by to this commit: 1a6d9bccd9e339a84026c288876999dc1b6956fe Signed-off-by: DAVID BOAZ --- altk/pre_llm/core/types.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/altk/pre_llm/core/types.py b/altk/pre_llm/core/types.py index bb7971c..f06c9f5 100644 --- a/altk/pre_llm/core/types.py +++ b/altk/pre_llm/core/types.py @@ -125,5 +125,4 @@ def get_topics( n_results: int = 10, query_kwargs: Dict[str, Any] | None = None, distance_threshold: float | None = None, - ) -> List[RetrievedTopic]: ... - \ No newline at end of file + ) -> List[RetrievedTopic]: ... \ No newline at end of file From f4ef5c02d7328ec5ef0a5c456040c751cd541da8 Mon Sep 17 00:00:00 2001 From: DAVID BOAZ Date: Thu, 25 Dec 2025 16:13:38 +0200 Subject: [PATCH 18/42] fix test Signed-off-by: DAVID BOAZ --- tests/pre_tool/toolguard/test_toolguard_code.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/tests/pre_tool/toolguard/test_toolguard_code.py b/tests/pre_tool/toolguard/test_toolguard_code.py index 42fa9bd..16bd087 100644 --- a/tests/pre_tool/toolguard/test_toolguard_code.py +++ b/tests/pre_tool/toolguard/test_toolguard_code.py @@ -75,19 +75,20 @@ def work_dir(): # --------------------------------------------------------------------------- @pytest.mark.asyncio -@pytest.mark.skipif(not WATSONX_CREDS_AVAILABLE, reason="WatsonX credentials not set") +# @pytest.mark.skip(reason="Closed model required") async def test_tool_guard_calculator_policy(work_dir: str): # Tools to be guarded funcs = [divide_tool, add_tool, multiply_tool, subtract_tool, map_kdi_number] # Configure Mellea session used in ToolGuard LLM # see https://docs.mellea.ai/api-reference/core-library/stdlib/mellea-stdlib-session#start-session - backend_name = "openai" #"ollama", "hf", "openai", "watsonx", "litellm" - model_id = "GCP/claude-4-sonnet" - kw_args = { - "api_key": os.getenv("TOOLGUARD_GENPY_APIKEY"), - "base_url": "https://ete-litellm.bx.cloud9.ibm.com", - } + backend_name = os.getenv("TOOLGUARD_GENPY_BACKEND_NAME") #"openai" "ollama", "hf", "openai", "watsonx", "litellm" + model_id = os.getenv("TOOLGUARD_GENPY_MODEL_ID") + kw_args = os.getenv("TOOLGUARD_GENPY_ARGS") + + assert backend_name + assert model_id + assert kw_args # Build ToolGuard component toolguard_code = ToolGuardCodeComponent( From 6738fcba90d809e13aca0ee7b4af9f36bd9e0b8f Mon Sep 17 00:00:00 2001 From: DAVID BOAZ Date: Thu, 25 Dec 2025 16:14:03 +0200 Subject: [PATCH 19/42] toolguard extra Signed-off-by: DAVID BOAZ --- pyproject.toml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index ee3181d..367e49f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,7 +20,7 @@ dependencies = [ "pydantic>=2.0.0", "smolagents>=1.21.3", "typing-extensions>=4.0.0", - "toolguard>=0.1.6", + "toolguard>=0.1.11", "tomli==2.2.1", "aiofiles==24.1.0", "black>=25.1.0", @@ -34,11 +34,6 @@ dependencies = [ "langchain-text-splitters>=1.0.0", "nltk>=3.9.1", "scipy>=1.15.3", - "pytest>=8.2.1", - "pytest-json-report>=1.5.0", - "pyright>=1.1.406", - "datamodel-code-generator>=0.34.0", - "mellea~=0.0.6", ] description = "The Agent Lifecycle Toolkit (ALTK) is a library of components to help agent builders improve their agent with minimal integration effort and setup." @@ -135,6 +130,10 @@ spotlight = [ "transformers>=4.53.3", ] +toolguard = [ + "toolguard>=0.1.11" +] + refraction = [ "nl2flow>=0.1.2; sys_platform != 'win32'", "sentence-transformers>=5.0.0", From c54078da8602b066cc30ec5d32bb04a668657a47 Mon Sep 17 00:00:00 2001 From: DAVID BOAZ Date: Thu, 25 Dec 2025 16:39:33 +0200 Subject: [PATCH 20/42] closed models only Signed-off-by: DAVID BOAZ --- altk/pre_tool/toolguard/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/altk/pre_tool/toolguard/README.md b/altk/pre_tool/toolguard/README.md index ec83d50..974baa6 100644 --- a/altk/pre_tool/toolguard/README.md +++ b/altk/pre_tool/toolguard/README.md @@ -105,6 +105,8 @@ export TOOLGUARD_GENPY_MODEL_ID="GCP/claude-4-sonnet" export TOOLGUARD_GENPY_ARGS='{"base_url":"https://your-litellm-endpoint","api_key":""}' ``` +**Important note:** The Code component works best with *closed models* such as [GPT-4o](https://openai.com/index/hello-gpt-4o/), [Gemini](https://deepmind.google/technologies/gemini/), and [Claude](https://www.anthropic.com/claude). + ### Input and Output The Component has two phases: #### Build phase From c1f6a2ef879074557513291da8535ae7315acad7 Mon Sep 17 00:00:00 2001 From: DAVID BOAZ Date: Mon, 29 Dec 2025 09:49:14 +0200 Subject: [PATCH 21/42] toolguard dev Signed-off-by: DAVID BOAZ --- altk/core/llm/base.py | 6 ++ .../providers/auto_from_env/auto_from_env.py | 4 ++ .../ibm_watsonx_ai/ibm_watsonx_ai.py | 6 ++ altk/core/llm/providers/litellm/litellm.py | 6 ++ altk/core/llm/providers/openai/openai.py | 7 +++ altk/pre_tool/toolguard/llm_client.py | 16 +++++ .../toolguard/toolguard_code_component.py | 10 ++-- .../toolguard/toolguard_spec_component.py | 31 +++------- pyproject.toml | 3 +- .../toolguard/inputs/step1/add_tool.json | 7 ++- .../toolguard/inputs/step1/divide_tool.json | 7 ++- .../inputs/step1/map_kdi_number.json | 3 +- .../toolguard/inputs/step1/multiply_tool.json | 7 ++- .../toolguard/inputs/step1/subtract_tool.json | 3 +- .../pre_tool/toolguard/test_toolguard_code.py | 59 +++++++++++-------- 15 files changed, 110 insertions(+), 65 deletions(-) create mode 100644 altk/pre_tool/toolguard/llm_client.py diff --git a/altk/core/llm/base.py b/altk/core/llm/base.py index 6db35cf..08ff3c3 100644 --- a/altk/core/llm/base.py +++ b/altk/core/llm/base.py @@ -175,6 +175,12 @@ def provider_class(cls) -> Type[Any]: Underlying SDK client class, e.g. openai.OpenAI or litellm.LiteLLM. """ + @abstractmethod + def get_model_id(self) -> str|None: + """ + returns the id of the model. eg: "gpt-5.1", "meta-llama/llama-4-maverick-17b-128e-instruct-fp8", and "claude-4-sonnet" + """ + @abstractmethod def _register_methods(self) -> None: """ diff --git a/altk/core/llm/providers/auto_from_env/auto_from_env.py b/altk/core/llm/providers/auto_from_env/auto_from_env.py index 8d622c5..ddcaa53 100644 --- a/altk/core/llm/providers/auto_from_env/auto_from_env.py +++ b/altk/core/llm/providers/auto_from_env/auto_from_env.py @@ -46,6 +46,10 @@ def __init__(self) -> None: def provider_class(cls) -> Type[Any]: raise NotImplementedError + def get_model_id(self) -> str|None: + if self._chosen_provider: + return self._chosen_provider.get_model_id() + def _register_methods(self) -> None: if self._chosen_provider: self._chosen_provider._register_methods() diff --git a/altk/core/llm/providers/ibm_watsonx_ai/ibm_watsonx_ai.py b/altk/core/llm/providers/ibm_watsonx_ai/ibm_watsonx_ai.py index 6a9bd5a..6237c7a 100644 --- a/altk/core/llm/providers/ibm_watsonx_ai/ibm_watsonx_ai.py +++ b/altk/core/llm/providers/ibm_watsonx_ai/ibm_watsonx_ai.py @@ -127,6 +127,9 @@ def provider_class(cls) -> Type[Any]: """ return ModelInference # type: ignore + def get_model_id(self) -> str: + return self.model_name # type: ignore + def _register_methods(self) -> None: """ Register how to call watsonx methods: @@ -471,6 +474,9 @@ def provider_class(cls) -> Type[Any]: """ return ModelInference # type: ignore + def get_model_id(self) -> str: + return self.model_name # type: ignore + def _register_methods(self) -> None: """ Register how to call watsonx methods for validation: diff --git a/altk/core/llm/providers/litellm/litellm.py b/altk/core/llm/providers/litellm/litellm.py index 7ec917f..5dbeaa8 100644 --- a/altk/core/llm/providers/litellm/litellm.py +++ b/altk/core/llm/providers/litellm/litellm.py @@ -35,6 +35,9 @@ def __init__( @classmethod def provider_class(cls) -> type: return litellm # type: ignore + + def get_model_id(self) -> str: + return self.model_path def _register_methods(self) -> None: """Register LiteLLM methods - only chat and chat_async are supported""" @@ -302,6 +305,9 @@ def provider_class(cls) -> Type[Any]: Must be callable with no arguments (per LLMClient __init__ logic). """ return litellm # type: ignore + + def get_model_id(self) -> str: + return self.model_path def _register_methods(self) -> None: """ diff --git a/altk/core/llm/providers/openai/openai.py b/altk/core/llm/providers/openai/openai.py index 765e5c1..dc86e18 100644 --- a/altk/core/llm/providers/openai/openai.py +++ b/altk/core/llm/providers/openai/openai.py @@ -77,6 +77,9 @@ def transform_min_tokens(value: Any, mode: Any) -> dict[str, Any]: ) self._parameter_mapper.set_custom_transform("min_tokens", transform_min_tokens) + def get_model_id(self) -> str|None: + if self._other_kwargs: + return self._other_kwargs.get("model") class BaseValidatingOpenAIClient(ValidatingLLMClient): """Base class for validating OpenAI and Azure OpenAI clients with shared parameter mapping""" @@ -146,6 +149,10 @@ def transform_min_tokens(value: Any, mode: Any) -> dict[str, Any]: ) self._parameter_mapper.set_custom_transform("min_tokens", transform_min_tokens) + def get_model_id(self) -> str|None: + if self._other_kwargs: + return self._other_kwargs.get("model") + @register_llm("openai.sync") class SyncOpenAIClient(BaseOpenAIClient, BaseLLMClient): diff --git a/altk/pre_tool/toolguard/llm_client.py b/altk/pre_tool/toolguard/llm_client.py new file mode 100644 index 0000000..1c46921 --- /dev/null +++ b/altk/pre_tool/toolguard/llm_client.py @@ -0,0 +1,16 @@ + +from ...core.llm import ValidatingLLMClient +from toolguard.llm.tg_litellm import LanguageModelBase + + +class TG_LLMEval(LanguageModelBase): + def __init__(self, llm_client: ValidatingLLMClient): + super().__init__(llm_client.get_model_id()) # type: ignore + self.llm_client = llm_client + + async def generate(self, messages: list[dict]) -> str: + return await self.llm_client.generate_async( + prompt=messages, + schema=str, + params = {"max_tokens":10000} + ) diff --git a/altk/pre_tool/toolguard/toolguard_code_component.py b/altk/pre_tool/toolguard/toolguard_code_component.py index e470bfb..6795079 100644 --- a/altk/pre_tool/toolguard/toolguard_code_component.py +++ b/altk/pre_tool/toolguard/toolguard_code_component.py @@ -3,17 +3,18 @@ from enum import Enum from pydantic import BaseModel, Field from typing import Set -from langchain.tools import BaseTool +from langchain_core.tools import BaseTool from altk.core.toolkit import ComponentConfig, ComponentInput, AgentPhase, ComponentBase -from toolguard.data_types import MelleaSessionData from toolguard import generate_guards_from_specs, ToolGuardSpec, ToolGuardsCodeGenerationResult, load_toolguards from toolguard.runtime import IToolInvoker, ToolGuardsCodeGenerationResult +from altk.pre_tool.toolguard.llm_client import TG_LLMEval + logger = logging.getLogger(__name__) class ToolGuardCodeComponentConfig(ComponentConfig): - llm_config: MelleaSessionData | None = None + pass class ToolGuardCodeBuildInput(ComponentInput): tools: List[Callable] | List[BaseTool] | str @@ -75,11 +76,12 @@ def _build(self, data: ToolGuardCodeBuildInput) -> ToolGuardsCodeGenerationResul async def _abuild(self, data: ToolGuardCodeBuildInput) -> ToolGuardsCodeGenerationResult: config = cast(ToolGuardCodeComponentConfig, self.config) + llm = TG_LLMEval(config.llm_client) return await generate_guards_from_specs( tools=data.tools, tool_specs=data.toolguard_specs, work_dir=data.out_dir, - llm_data=config.llm_config + llm=llm ) def _run(self, data: ToolGuardCodeRunInput) -> ToolGuardCodeRunOutput: diff --git a/altk/pre_tool/toolguard/toolguard_spec_component.py b/altk/pre_tool/toolguard/toolguard_spec_component.py index 409963e..c141758 100644 --- a/altk/pre_tool/toolguard/toolguard_spec_component.py +++ b/altk/pre_tool/toolguard/toolguard_spec_component.py @@ -1,13 +1,13 @@ import logging import os -from typing import Callable, List, Set -from langchain.tools import BaseTool +from typing import Callable, List, Set, cast +from langchain_core.tools import BaseTool from pydantic import Field -from ...core.llm import ValidatingLLMClient +from altk.pre_tool.toolguard.llm_client import TG_LLMEval + from ...core.toolkit import AgentPhase, ComponentBase, ComponentConfig, ComponentInput from toolguard import ToolGuardSpec, generate_guard_specs -from toolguard.llm.i_tg_llm import I_TG_LLM logger = logging.getLogger(__name__) @@ -35,27 +35,12 @@ def _build(self, data: ToolGuardSpecBuildInput) -> ToolGuardSpecs: async def _abuild(self, data: ToolGuardSpecBuildInput) -> ToolGuardSpecs: os.makedirs(data.out_dir, exist_ok=True) + config = cast(ToolGuardSpecComponentConfig, self.config) + llm = TG_LLMEval(config.llm_client) return await generate_guard_specs( policy_text=data.policy_text, tools=data.tools, work_dir=data.out_dir, - llm=TG_LLMEval(self.config.llm_client) - ) - - -class TG_LLMEval(I_TG_LLM): - def __init__(self, llm_client: ValidatingLLMClient): - if not isinstance(llm_client, ValidatingLLMClient): - print("llm_client is a ValidatingLLMClient") - exit(1) - self.llm_client = llm_client - - async def chat_json(self, messages: list[dict], schema=dict) -> dict: - return self.llm_client.generate( - prompt=messages, schema=schema, retries=5, schema_field=None - ) - - async def generate(self, messages: list[dict]) -> str: - return self.llm_client.generate( - prompt=messages, schema=str, retries=5, schema_field=None + llm=llm ) + \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 174d9cf..aeafc95 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,7 +20,6 @@ dependencies = [ "pydantic>=2.0.0", "smolagents>=1.21.3", "typing-extensions>=4.0.0", - "toolguard>=0.1.11", "tomli==2.2.1", "aiofiles==24.1.0", "black>=25.1.0", @@ -131,7 +130,7 @@ spotlight = [ ] toolguard = [ - "toolguard>=0.1.11" + "toolguard>=0.1.13" ] refraction = [ diff --git a/tests/pre_tool/toolguard/inputs/step1/add_tool.json b/tests/pre_tool/toolguard/inputs/step1/add_tool.json index 81fd8b9..21f5a53 100644 --- a/tests/pre_tool/toolguard/inputs/step1/add_tool.json +++ b/tests/pre_tool/toolguard/inputs/step1/add_tool.json @@ -1,13 +1,14 @@ { - "policies": [ + "tool_name": "add_tool", + "policy_items": [ { - "policy_name": "Prohibition of Adding Numbers Whose Product Equals 365", + "name": "Prohibition of Adding Numbers Whose Product Equals 365", "description": "The add_tool must not allow addition of two or more numbers if their multiplication result equals 365. For example, adding 5 + 73 should be disallowed, because 5 * 73 = 365. In such cases, the operation must be rejected with an error like: 'Error: Addition of numbers whose product equals 365 is not allowed.'", "references": [ "The calculator must not allow addition of two or more numbers if their multiplication result equals 365. For example, adding 5 + 73 should be disallowed, because 5 * 73 = 365. In such cases, the operation must be rejected with an error like: 'Error: Addition of numbers whose product equals 365 is not allowed.'" ], "iteration_added": 0, - "violating_examples": [ + "violation_examples": [ "A user tries to add 5 and 73. Since 5 * 73 equals 365, this operation should be rejected with an error message: 'Error: Addition of numbers whose product equals 365 is not allowed.'", "The user attempts to add two numbers, 1 and 365, using the add_tool. This is a violation because 1 * 365 is also 365 and should be met with an error message.", "Adding together the numbers 10 and 36.5 should be disallowed because their product is 365. An error message should be shown to prevent this operation.", diff --git a/tests/pre_tool/toolguard/inputs/step1/divide_tool.json b/tests/pre_tool/toolguard/inputs/step1/divide_tool.json index ae64b76..0744ff4 100644 --- a/tests/pre_tool/toolguard/inputs/step1/divide_tool.json +++ b/tests/pre_tool/toolguard/inputs/step1/divide_tool.json @@ -1,13 +1,14 @@ { - "policies": [ + "tool_name": "divide_tool", + "policy_items": [ { - "policy_name": "Division by Zero is Not Allowed", + "name": "Division by Zero is Not Allowed", "description": "The calculator must not allow division by zero. If a user attempts to divide by zero, the operation must be rejected and an appropriate error message should be shown (e.g., 'Error: Division by zero is not allowed.')", "references": [ "The calculator must not allow division by zero. If a user attempts to divide by zero, the operation must be rejected and an appropriate error message should be shown (e.g., 'Error: Division by zero is not allowed.')" ], "iteration_added": 0, - "violating_examples": [ + "violation_examples": [ "A user attempts to divide 15 by 0, but the program proceeds with the operation, resulting in either an undefined behavior or a system error instead of an appropriate error message being shown.", "The system calculates the result of 42 divided by 0 without any checks, ignoring the divisor being zero and directly providing an undefined or erroneous result.", "A user input of 80 divided by 0 is processed and the system returns a zero or a nonsensical number instead of stopping the operation and displaying an error message about division by zero.", diff --git a/tests/pre_tool/toolguard/inputs/step1/map_kdi_number.json b/tests/pre_tool/toolguard/inputs/step1/map_kdi_number.json index 7189256..2aae27e 100644 --- a/tests/pre_tool/toolguard/inputs/step1/map_kdi_number.json +++ b/tests/pre_tool/toolguard/inputs/step1/map_kdi_number.json @@ -1,3 +1,4 @@ { - "policies": [] + "tool_name": "map_kdi_number", + "policy_items": [] } \ No newline at end of file diff --git a/tests/pre_tool/toolguard/inputs/step1/multiply_tool.json b/tests/pre_tool/toolguard/inputs/step1/multiply_tool.json index b9ef8bf..612e7c3 100644 --- a/tests/pre_tool/toolguard/inputs/step1/multiply_tool.json +++ b/tests/pre_tool/toolguard/inputs/step1/multiply_tool.json @@ -1,13 +1,14 @@ { - "policies": [ + "tool_name": "multiply_tool", + "policy_items": [ { - "policy_name": "Multiplication KDI Constraint", + "name": "Multiplication KDI Constraint", "description": "Multiplying numbers when any operand\u2019s KDI value equals 6.28 is not allowed. The calculator must not allow multiplication if any of the operands correspond to a number whose KDI value equals 6.28. For example, if a number x has KDI(x) = 6.28, then an operation such as x * 2 must be rejected.", "references": [ "Multiplying Numbers When Any Operand\u2019s KDI Value Equals 6.28 is Not Allowed\nThe calculator must not allow multiplication if any of the operands correspond to a number whose KDI value equals 6.28. For example, if a number x has KDI(x) = 6.28, then an operation such as x * 2 must be rejected. In such cases, the calculator should display an error message such as: \"Error: Multiplication involving a number with KDI value 6.28 is not allowed.\"" ], "iteration_added": 0, - "violating_examples": [ + "violation_examples": [ "Attempt to multiply 5 by 3 where KDI(5) = 6.28. This violates the policy since using any operand with KDI value 6.28 in multiplication is not permitted.", "Operation tries to calculate the product of 2 and 10, but KDI(10) yields 6.28. This is a breach of policy because multiplying a number mapped to KDI value 6.28 is disallowed.", "A multiplication command of 8 * 4 is executed, and upon retrieval, KDI(4) = 6.28. Using 4 in multiplication should be rejected per the policy." diff --git a/tests/pre_tool/toolguard/inputs/step1/subtract_tool.json b/tests/pre_tool/toolguard/inputs/step1/subtract_tool.json index 7189256..5b96883 100644 --- a/tests/pre_tool/toolguard/inputs/step1/subtract_tool.json +++ b/tests/pre_tool/toolguard/inputs/step1/subtract_tool.json @@ -1,3 +1,4 @@ { - "policies": [] + "tool_name": "subtract_tool", + "policy_items": [] } \ No newline at end of file diff --git a/tests/pre_tool/toolguard/test_toolguard_code.py b/tests/pre_tool/toolguard/test_toolguard_code.py index 16bd087..3126883 100644 --- a/tests/pre_tool/toolguard/test_toolguard_code.py +++ b/tests/pre_tool/toolguard/test_toolguard_code.py @@ -12,24 +12,22 @@ from datetime import datetime import os from pathlib import Path -import shutil from typing import Dict, cast import dotenv import pytest +from altk.core.llm.base import BaseLLMClient from altk.pre_tool.toolguard import ( ToolGuardCodeComponent, ToolGuardCodeBuildInput, ) from toolguard.data_types import ( - MelleaSessionData, load_tool_policy, ) from toolguard.runtime import ( ToolFunctionsInvoker, ToolGuardsCodeGenerationResult, - load_toolguard_code_result, ) from altk.pre_tool.toolguard.toolguard_code_component import ( ToolGuardCodeComponentConfig, @@ -51,8 +49,6 @@ # Load environment variables dotenv.load_dotenv() -WATSONX_CREDS_AVAILABLE = all([os.getenv("WX_API_KEY"), os.getenv("WX_PROJECT_ID")]) - # --------------------------------------------------------------------------- # Fixtures # --------------------------------------------------------------------------- @@ -66,39 +62,52 @@ def work_dir(): yield dir_path - shutil.rmtree(dir_path) - print("Temporary work dir removed:", dir_path) + # shutil.rmtree(dir_path) + # print("Temporary work dir removed:", dir_path) + +def get_llm()->BaseLLMClient: + # from altk.core.llm.providers.ibm_watsonx_ai.ibm_watsonx_ai import WatsonxLLMClientOutputVal + # return WatsonxLLMClientOutputVal( + # model_name="meta-llama/llama-4-maverick-17b-128e-instruct-fp8", + # api_key=os.getenv("WATSONX_API_KEY"), + # project_id = os.getenv("WATSONX_PROJECT_ID"), + # url=os.getenv("WATSONX_URL"), + # ) + + from altk.core.llm.providers.openai.openai import AsyncAzureOpenAIClientOutputVal + return AsyncAzureOpenAIClientOutputVal( + model="gpt-4o", + api_key=os.getenv("AZURE_OPENAI_API_KEY"), + base_url=os.getenv("AZURE_API_BASE"), + api_version="2024-08-01-preview" + ) + + # from altk.core.llm.providers.litellm.litellm import LiteLLMClientOutputVal + # return LiteLLMClientOutputVal( + # model_name=os.getenv("TOOLGUARD_GENPY_MODEL_ID"), + # api_key=os.getenv("TOOLGUARD_GENPY_MODEL_API_KEY"), + # base_url=os.getenv("TOOLGUARD_GENPY_MODEL_BASE_URL"), + # ) + # from altk.core.llm.providers.openai.openai import AsyncOpenAIClient + # return AsyncOpenAIClient( + # model=os.getenv("TOOLGUARD_GENPY_MODEL_ID"), + # api_key=os.getenv("TOOLGUARD_GENPY_MODEL_API_KEY"), + # url=os.getenv("TOOLGUARD_GENPY_MODEL_BASE_URL"), + # ) # --------------------------------------------------------------------------- # Test: ToolGuard verification for the calculator tool set # --------------------------------------------------------------------------- @pytest.mark.asyncio -# @pytest.mark.skip(reason="Closed model required") async def test_tool_guard_calculator_policy(work_dir: str): # Tools to be guarded funcs = [divide_tool, add_tool, multiply_tool, subtract_tool, map_kdi_number] - # Configure Mellea session used in ToolGuard LLM - # see https://docs.mellea.ai/api-reference/core-library/stdlib/mellea-stdlib-session#start-session - backend_name = os.getenv("TOOLGUARD_GENPY_BACKEND_NAME") #"openai" "ollama", "hf", "openai", "watsonx", "litellm" - model_id = os.getenv("TOOLGUARD_GENPY_MODEL_ID") - kw_args = os.getenv("TOOLGUARD_GENPY_ARGS") - - assert backend_name - assert model_id - assert kw_args - # Build ToolGuard component toolguard_code = ToolGuardCodeComponent( - ToolGuardCodeComponentConfig( - llm_config=MelleaSessionData( - backend_name=backend_name, - model_id=model_id, - kw_args=kw_args, - ) - ) + ToolGuardCodeComponentConfig(llm_client=get_llm()) ) # Load policy JSON files from /step1 From ef00372ee91cd1f608c7c468285c03f2bf869d5d Mon Sep 17 00:00:00 2001 From: DAVID BOAZ Date: Tue, 30 Dec 2025 16:11:21 +0200 Subject: [PATCH 22/42] readme --- altk/pre_tool/toolguard/README.md | 34 +++++++++++-------------------- pyproject.toml | 2 +- 2 files changed, 13 insertions(+), 23 deletions(-) diff --git a/altk/pre_tool/toolguard/README.md b/altk/pre_tool/toolguard/README.md index 974baa6..9749f84 100644 --- a/altk/pre_tool/toolguard/README.md +++ b/altk/pre_tool/toolguard/README.md @@ -54,7 +54,6 @@ llm_client = LLMClient( model_name="gpt-4o-2024-08-06", custom_llm_provider="azure", ) - ``` ### Input and Output @@ -82,29 +81,20 @@ The ToolGuards checks if a planned action complies with the policy. If it violat ### Component Configuarion -The build time functionality is backed by [Mellea](https://mellea.ai/), which requires parameters aligning to: -```python -mellea.MelleaSession.start_session( - backend_name=..., - model_id=..., - backend_kwargs=... # any additional arguments -) +This component expects an LLM client configuarion: ``` -The `mellea` session parameters can be provided explicitely, or loaded from environment variables: - -| Environment Variable | Mellea Parameter | Description | -| ------------------------------ | ---------------- | ------------------------------------------------------------------ | -| `TOOLGUARD_GENPY_BACKEND_NAME` | `backend_name` | Which backend to use (e.g., `openai`, `anthropic`, `vertex`, etc.) | -| `TOOLGUARD_GENPY_MODEL_ID` | `model_id` | Model name / deployment id | -| `TOOLGUARD_GENPY_ARGS` | `backend_kwargs` | JSON dict of any additional connection/LLM parameters | - -Example (Claude-4 Sonnet through OpenAI-compatible endpoint): -```bash -export TOOLGUARD_GENPY_BACKEND_NAME="openai" -export TOOLGUARD_GENPY_MODEL_ID="GCP/claude-4-sonnet" -export TOOLGUARD_GENPY_ARGS='{"base_url":"https://your-litellm-endpoint","api_key":""}' + from altk.core.llm.providers.ibm_watsonx_ai.ibm_watsonx_ai import WatsonxLLMClientOutputVal + llm = WatsonxLLMClientOutputVal( + model_name="meta-llama/llama-4-maverick-17b-128e-instruct-fp8", + api_key=os.getenv("WATSONX_API_KEY"), + project_id = os.getenv("WATSONX_PROJECT_ID"), + url=os.getenv("WATSONX_URL"), + ) + + toolguard_code = ToolGuardCodeComponent( + ToolGuardCodeComponentConfig(llm_client=llm) + ) ``` - **Important note:** The Code component works best with *closed models* such as [GPT-4o](https://openai.com/index/hello-gpt-4o/), [Gemini](https://deepmind.google/technologies/gemini/), and [Claude](https://www.anthropic.com/claude). ### Input and Output diff --git a/pyproject.toml b/pyproject.toml index aeafc95..9f58282 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -130,7 +130,7 @@ spotlight = [ ] toolguard = [ - "toolguard>=0.1.13" + "toolguard>=0.1.14" ] refraction = [ From 3f8f9fbf105e1caf3692c1e9e45b38653d78d58e Mon Sep 17 00:00:00 2001 From: DAVID BOAZ Date: Tue, 30 Dec 2025 16:19:11 +0200 Subject: [PATCH 23/42] toolguard --- altk/pre_tool/examples/__init__.py | 0 .../examples/calculator_example/__init__.py | 0 .../calculator_example/example_tools.py | 81 ------------------- .../calculator_example/policy_document.md | 30 ------- .../calculator_example/run_example.py | 64 --------------- altk/pre_tool/examples/tool_guard_example.py | 51 ------------ altk/pre_tool/toolguard/README.md | 25 +++--- .../pre_tool/toolguard/test_toolguard_code.py | 14 ++-- 8 files changed, 19 insertions(+), 246 deletions(-) delete mode 100644 altk/pre_tool/examples/__init__.py delete mode 100644 altk/pre_tool/examples/calculator_example/__init__.py delete mode 100644 altk/pre_tool/examples/calculator_example/example_tools.py delete mode 100644 altk/pre_tool/examples/calculator_example/policy_document.md delete mode 100644 altk/pre_tool/examples/calculator_example/run_example.py delete mode 100644 altk/pre_tool/examples/tool_guard_example.py diff --git a/altk/pre_tool/examples/__init__.py b/altk/pre_tool/examples/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/altk/pre_tool/examples/calculator_example/__init__.py b/altk/pre_tool/examples/calculator_example/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/altk/pre_tool/examples/calculator_example/example_tools.py b/altk/pre_tool/examples/calculator_example/example_tools.py deleted file mode 100644 index a028d50..0000000 --- a/altk/pre_tool/examples/calculator_example/example_tools.py +++ /dev/null @@ -1,81 +0,0 @@ -from langchain_core.tools import tool - - -@tool -def add_tool(a: float, b: float) -> float: - """ - Add two numbers. - - Parameters - ---------- - a : float - The first number to add. - b : float - The second number to add. - - Returns - ------- - float - The sum of a and b. - """ - return a + b - - -@tool -def subtract_tool(c: float, d: float) -> float: - """ - Subtract one number from another. - - Parameters - ---------- - c : float - The number to subtract from. - d : float - The number to subtract. - - Returns - ------- - float - The result of a minus b. - """ - return c - d - - -@tool -def multiply_tool(e: float, f: float) -> float: - """ - Multiply two numbers. - - Parameters - ---------- - e : float - The first number. - f : float - The second number. - - Returns - ------- - float - The product of a and b. - """ - return e * f - - -@tool -def divide_tool(g: float, h: float) -> float: - """ - Divide one number by another. - - Parameters - ---------- - g : float - The dividend. - h : float - The divisor (must not be zero). - - Returns - ------- - float - The result of a divided by b. - """ - return g / h diff --git a/altk/pre_tool/examples/calculator_example/policy_document.md b/altk/pre_tool/examples/calculator_example/policy_document.md deleted file mode 100644 index 556136b..0000000 --- a/altk/pre_tool/examples/calculator_example/policy_document.md +++ /dev/null @@ -1,30 +0,0 @@ -# Calculator Usage Policy - -This document outlines the rules and constraints that govern the behavior and usage of the calculator application. - -## General Principles - -- The calculator should perform accurate and reliable computations. -- All operations must conform to mathematical standards and avoid undefined behavior. - -## Supported Operations - -The calculator supports the following operations: - -- Addition (`+`) -- Subtraction (`-`) -- Multiplication (`*`) -- Division (`/`) - -## Operation Constraints - -- **Division by Zero is Not Allowed** - The calculator **must not** allow division by zero. - If a user attempts to divide by zero, the operation must be rejected and an appropriate error message should be shown - (e.g., `"Error: Division by zero is not allowed."`). - -- **Summing Numbers Whose Product is 365 is Not Allowed** - The calculator **must not** allow addition of two or more numbers if their multiplication result equals `365`. - For example, adding `5 + 73` should be disallowed, because `5 * 73 = 365` . - In such cases, the operation must be rejected with an error like: - `"Error: Addition of numbers whose product equals 365 is not allowed."` diff --git a/altk/pre_tool/examples/calculator_example/run_example.py b/altk/pre_tool/examples/calculator_example/run_example.py deleted file mode 100644 index 70291a1..0000000 --- a/altk/pre_tool/examples/calculator_example/run_example.py +++ /dev/null @@ -1,64 +0,0 @@ -import os -from pathlib import Path - -import markdown -from altk.core.llm import get_llm - -from examples.calculator_example.example_tools import ( - add_tool, - subtract_tool, - multiply_tool, - divide_tool, -) -from examples.tool_guard_example import ToolGuardExample - -subdir_name = "work_dir_wx" -work_dir = Path.cwd() / subdir_name -policy_doc_path = os.path.join(str(Path.cwd()), "policy_document.md") -work_dir.mkdir(exist_ok=True) - -OPENAILiteLLMClientOutputVal = get_llm("litellm.output_val") -validating_llm_client = OPENAILiteLLMClientOutputVal( - model_path="gpt-4o-2024-08-06", - custom_llm_provider="azure", -) - -OPENAILiteLLMClient = get_llm("litellm") -llm_client = OPENAILiteLLMClient( - model_path="gpt-4o-2024-08-06", - custom_llm_provider="azure", -) -tool_funcs = [add_tool, subtract_tool, multiply_tool, divide_tool] -policy_text = open(policy_doc_path, "r", encoding="utf-8").read() -policy_text = markdown.markdown(policy_text) - -tool_guard_example = ToolGuardExample( - tools=tool_funcs, - workdir=work_dir, - policy_text=policy_text, - validating_llm_client=validating_llm_client, -) -run_output = tool_guard_example.run_example( - "Can you please calculate how much is 3/4?", - "divide_tool", - {"g": 3, "h": 4}, - llm_client, -) -print(run_output) -passed = not run_output.output.error_message -if passed: - print("success!") -else: - print("failure!") -run_output = tool_guard_example.run_example( - "Can you please calculate how much is 5/0?", - "divide_tool", - {"g": 5, "h": 0}, - llm_client, -) -print(run_output) -passed = not run_output.output.error_message -if not passed: - print("success!") -else: - print("failure!") diff --git a/altk/pre_tool/examples/tool_guard_example.py b/altk/pre_tool/examples/tool_guard_example.py deleted file mode 100644 index f0d0d15..0000000 --- a/altk/pre_tool/examples/tool_guard_example.py +++ /dev/null @@ -1,51 +0,0 @@ -import dotenv - -from langchain_core.messages import HumanMessage - -from altk.pre_tool.core.types import ( - ToolGuardBuildInputMetaData, - ToolGuardBuildInput, - ToolGuardRunInputMetaData, - ToolGuardRunInput, -) -from altk.pre_tool.pre_tool_guard import PreToolGuardComponent - -# Load environment variables -dotenv.load_dotenv() - - -class ToolGuardExample: - """ - Runs examples with a ToolGuard component and validates tool invocation against policy. - """ - - def __init__(self, tools, workdir, policy_text, validating_llm_client, short=True): - self.tools = tools - self.middleware = PreToolGuardComponent(tools=tools, workdir=workdir) - - build_input = ToolGuardBuildInput( - metadata=ToolGuardBuildInputMetaData( - policy_text=policy_text, - short1=short, - validating_llm_client=validating_llm_client, - ) - ) - self.middleware._build(build_input) - - def run_example( - self, user_message: str, tool_name: str, tool_params: dict, llm_client - ): - """ - Runs a single example through ToolGuard and checks if the result matches the expectation. - """ - conversation_context = [HumanMessage(content=user_message)] - - run_input = ToolGuardRunInput( - messages=conversation_context, - metadata=ToolGuardRunInputMetaData( - tool_name=tool_name, tool_parms=tool_params, llm_client=llm_client - ), - ) - - run_output = self.middleware._run(run_input) - return run_output diff --git a/altk/pre_tool/toolguard/README.md b/altk/pre_tool/toolguard/README.md index 9749f84..3b1f46b 100644 --- a/altk/pre_tool/toolguard/README.md +++ b/altk/pre_tool/toolguard/README.md @@ -1,5 +1,5 @@ # ToolGuards for Enforcing Agentic Policy Adherence -An agent lifecycle solution for enforcing business policy adherence in agentic workflows. Enabling this component has demonstrated up to a **20‑point improvement** in end‑to‑end agent accuracy when invoking tools. This work is described in [EMNLP 2025 Towards Enforcing Company Policy Adherence in Agentic Workflows](https://arxiv.org/pdf/2507.16459), and is publiched in [this GitHub library](https://github.com/IBM/toolguard). +An agent lifecycle solution for enforcing business policy adherence in agentic workflows. Enabling this component has demonstrated up to a **20‑point improvement** in end‑to‑end agent accuracy when invoking tools. This work is described in [EMNLP 2025 Towards Enforcing Company Policy Adherence in Agentic Workflows](https://arxiv.org/pdf/2507.16459), and is publiched in [this GitHub library](https://github.com/AgentToolkit/toolguard). ## Table of Contents - [Overview](#overview) @@ -81,19 +81,18 @@ The ToolGuards checks if a planned action complies with the policy. If it violat ### Component Configuarion -This component expects an LLM client configuarion: +This component expects an LLM client configuarion. +Here is an example using a Watsonx LLM client: ``` - from altk.core.llm.providers.ibm_watsonx_ai.ibm_watsonx_ai import WatsonxLLMClientOutputVal - llm = WatsonxLLMClientOutputVal( - model_name="meta-llama/llama-4-maverick-17b-128e-instruct-fp8", - api_key=os.getenv("WATSONX_API_KEY"), - project_id = os.getenv("WATSONX_PROJECT_ID"), - url=os.getenv("WATSONX_URL"), - ) - - toolguard_code = ToolGuardCodeComponent( - ToolGuardCodeComponentConfig(llm_client=llm) - ) +from altk.core.llm.providers.ibm_watsonx_ai.ibm_watsonx_ai import WatsonxLLMClientOutputVal +llm = WatsonxLLMClientOutputVal( + model_name="meta-llama/llama-4-maverick-17b-128e-instruct-fp8", + api_key=os.getenv("WATSONX_API_KEY"), + project_id = os.getenv("WATSONX_PROJECT_ID"), + url=os.getenv("WATSONX_URL"), +) +config = ToolGuardCodeComponentConfig(llm_client=llm) +toolguard_code_component = ToolGuardCodeComponent(config) ``` **Important note:** The Code component works best with *closed models* such as [GPT-4o](https://openai.com/index/hello-gpt-4o/), [Gemini](https://deepmind.google/technologies/gemini/), and [Claude](https://www.anthropic.com/claude). diff --git a/tests/pre_tool/toolguard/test_toolguard_code.py b/tests/pre_tool/toolguard/test_toolguard_code.py index 3126883..25a523c 100644 --- a/tests/pre_tool/toolguard/test_toolguard_code.py +++ b/tests/pre_tool/toolguard/test_toolguard_code.py @@ -66,13 +66,13 @@ def work_dir(): # print("Temporary work dir removed:", dir_path) def get_llm()->BaseLLMClient: - # from altk.core.llm.providers.ibm_watsonx_ai.ibm_watsonx_ai import WatsonxLLMClientOutputVal - # return WatsonxLLMClientOutputVal( - # model_name="meta-llama/llama-4-maverick-17b-128e-instruct-fp8", - # api_key=os.getenv("WATSONX_API_KEY"), - # project_id = os.getenv("WATSONX_PROJECT_ID"), - # url=os.getenv("WATSONX_URL"), - # ) + from altk.core.llm.providers.ibm_watsonx_ai.ibm_watsonx_ai import WatsonxLLMClientOutputVal + return WatsonxLLMClientOutputVal( + model_name="meta-llama/llama-4-maverick-17b-128e-instruct-fp8", + api_key=os.getenv("WATSONX_API_KEY"), + project_id = os.getenv("WATSONX_PROJECT_ID"), + url=os.getenv("WATSONX_URL"), + ) from altk.core.llm.providers.openai.openai import AsyncAzureOpenAIClientOutputVal return AsyncAzureOpenAIClientOutputVal( From 4ea9d5f54fcb9a3f008533501a66a4818f542fd3 Mon Sep 17 00:00:00 2001 From: DAVID BOAZ Date: Thu, 1 Jan 2026 10:54:50 +0200 Subject: [PATCH 24/42] llm clients --- altk/pre_tool/toolguard/llm_client.py | 21 ++++++++++---- .../pre_tool/toolguard/test_toolguard_code.py | 29 ++++++++++++------- 2 files changed, 33 insertions(+), 17 deletions(-) diff --git a/altk/pre_tool/toolguard/llm_client.py b/altk/pre_tool/toolguard/llm_client.py index 1c46921..5a54b69 100644 --- a/altk/pre_tool/toolguard/llm_client.py +++ b/altk/pre_tool/toolguard/llm_client.py @@ -1,16 +1,25 @@ -from ...core.llm import ValidatingLLMClient +from typing import Union, cast +from altk.core.llm.types import GenerationArgs +from ...core.llm import ValidatingLLMClient, LLMClient from toolguard.llm.tg_litellm import LanguageModelBase class TG_LLMEval(LanguageModelBase): - def __init__(self, llm_client: ValidatingLLMClient): + def __init__(self, llm_client: Union[LLMClient, ValidatingLLMClient]): super().__init__(llm_client.get_model_id()) # type: ignore self.llm_client = llm_client async def generate(self, messages: list[dict]) -> str: + if isinstance(self.llm_client, ValidatingLLMClient): + llm_client = cast(ValidatingLLMClient, self.llm_client) + return await llm_client.generate_async( + prompt=messages, + schema=str, + generation_args = GenerationArgs(max_tokens=10000) + ) + return await self.llm_client.generate_async( - prompt=messages, - schema=str, - params = {"max_tokens":10000} - ) + prompt=messages, + generation_args = GenerationArgs(max_tokens=10000) + ) # type: ignore diff --git a/tests/pre_tool/toolguard/test_toolguard_code.py b/tests/pre_tool/toolguard/test_toolguard_code.py index 25a523c..40e08f7 100644 --- a/tests/pre_tool/toolguard/test_toolguard_code.py +++ b/tests/pre_tool/toolguard/test_toolguard_code.py @@ -66,24 +66,24 @@ def work_dir(): # print("Temporary work dir removed:", dir_path) def get_llm()->BaseLLMClient: - from altk.core.llm.providers.ibm_watsonx_ai.ibm_watsonx_ai import WatsonxLLMClientOutputVal - return WatsonxLLMClientOutputVal( + from altk.core.llm.providers.ibm_watsonx_ai.ibm_watsonx_ai import WatsonxLLMClient + return WatsonxLLMClient( model_name="meta-llama/llama-4-maverick-17b-128e-instruct-fp8", api_key=os.getenv("WATSONX_API_KEY"), project_id = os.getenv("WATSONX_PROJECT_ID"), url=os.getenv("WATSONX_URL"), ) - from altk.core.llm.providers.openai.openai import AsyncAzureOpenAIClientOutputVal - return AsyncAzureOpenAIClientOutputVal( - model="gpt-4o", - api_key=os.getenv("AZURE_OPENAI_API_KEY"), - base_url=os.getenv("AZURE_API_BASE"), - api_version="2024-08-01-preview" - ) + # from altk.core.llm.providers.openai.openai import AsyncAzureOpenAIClient + # return AsyncAzureOpenAIClient( + # model="gpt-4o-2024-08-06", + # api_key=os.getenv("AZURE_OPENAI_API_KEY"), + # azure_endpoint=os.getenv("AZURE_API_BASE"), + # api_version="2024-08-01-preview" + # ) - # from altk.core.llm.providers.litellm.litellm import LiteLLMClientOutputVal - # return LiteLLMClientOutputVal( + # from altk.core.llm.providers.litellm.litellm import LiteLLMClient + # return LiteLLMClient( # model_name=os.getenv("TOOLGUARD_GENPY_MODEL_ID"), # api_key=os.getenv("TOOLGUARD_GENPY_MODEL_API_KEY"), # base_url=os.getenv("TOOLGUARD_GENPY_MODEL_BASE_URL"), @@ -96,6 +96,13 @@ def get_llm()->BaseLLMClient: # url=os.getenv("TOOLGUARD_GENPY_MODEL_BASE_URL"), # ) + # from altk.core.llm.providers.openai.openai import AsyncOpenAIClientOutputVal + # return AsyncOpenAIClientOutputVal( + # model=os.getenv("TOOLGUARD_GENPY_MODEL_ID"), + # api_key=os.getenv("TOOLGUARD_GENPY_MODEL_API_KEY"), + # url=os.getenv("TOOLGUARD_GENPY_MODEL_BASE_URL"), + # ) + # --------------------------------------------------------------------------- # Test: ToolGuard verification for the calculator tool set # --------------------------------------------------------------------------- From 2368f47b554a64fda1faba41f5fea66f0c71ee9b Mon Sep 17 00:00:00 2001 From: DAVID BOAZ Date: Thu, 8 Jan 2026 12:44:57 +0200 Subject: [PATCH 25/42] revert get_model_id() Signed-off-by: DAVID BOAZ --- altk/core/llm/base.py | 6 ----- .../providers/auto_from_env/auto_from_env.py | 4 --- .../ibm_watsonx_ai/ibm_watsonx_ai.py | 6 ----- altk/core/llm/providers/litellm/litellm.py | 6 ----- altk/core/llm/providers/openai/openai.py | 8 ------ altk/pre_tool/toolguard/llm_client.py | 7 +++--- .../toolguard/toolguard_code_component.py | 9 ++++--- .../toolguard/toolguard_spec_component.py | 10 +++++--- pyproject.toml | 2 +- .../pre_tool/toolguard/test_toolguard_code.py | 13 +++++----- .../toolguard/test_toolguard_specs.py | 25 ++++++++++--------- 11 files changed, 35 insertions(+), 61 deletions(-) diff --git a/altk/core/llm/base.py b/altk/core/llm/base.py index 08ff3c3..6db35cf 100644 --- a/altk/core/llm/base.py +++ b/altk/core/llm/base.py @@ -175,12 +175,6 @@ def provider_class(cls) -> Type[Any]: Underlying SDK client class, e.g. openai.OpenAI or litellm.LiteLLM. """ - @abstractmethod - def get_model_id(self) -> str|None: - """ - returns the id of the model. eg: "gpt-5.1", "meta-llama/llama-4-maverick-17b-128e-instruct-fp8", and "claude-4-sonnet" - """ - @abstractmethod def _register_methods(self) -> None: """ diff --git a/altk/core/llm/providers/auto_from_env/auto_from_env.py b/altk/core/llm/providers/auto_from_env/auto_from_env.py index ddcaa53..e60d62b 100644 --- a/altk/core/llm/providers/auto_from_env/auto_from_env.py +++ b/altk/core/llm/providers/auto_from_env/auto_from_env.py @@ -45,10 +45,6 @@ def __init__(self) -> None: @classmethod def provider_class(cls) -> Type[Any]: raise NotImplementedError - - def get_model_id(self) -> str|None: - if self._chosen_provider: - return self._chosen_provider.get_model_id() def _register_methods(self) -> None: if self._chosen_provider: diff --git a/altk/core/llm/providers/ibm_watsonx_ai/ibm_watsonx_ai.py b/altk/core/llm/providers/ibm_watsonx_ai/ibm_watsonx_ai.py index 6237c7a..f5d89fe 100644 --- a/altk/core/llm/providers/ibm_watsonx_ai/ibm_watsonx_ai.py +++ b/altk/core/llm/providers/ibm_watsonx_ai/ibm_watsonx_ai.py @@ -126,9 +126,6 @@ def provider_class(cls) -> Type[Any]: Underlying SDK client class for watsonx.ai: ModelInference. """ return ModelInference # type: ignore - - def get_model_id(self) -> str: - return self.model_name # type: ignore def _register_methods(self) -> None: """ @@ -474,9 +471,6 @@ def provider_class(cls) -> Type[Any]: """ return ModelInference # type: ignore - def get_model_id(self) -> str: - return self.model_name # type: ignore - def _register_methods(self) -> None: """ Register how to call watsonx methods for validation: diff --git a/altk/core/llm/providers/litellm/litellm.py b/altk/core/llm/providers/litellm/litellm.py index 5dbeaa8..12bc956 100644 --- a/altk/core/llm/providers/litellm/litellm.py +++ b/altk/core/llm/providers/litellm/litellm.py @@ -36,9 +36,6 @@ def __init__( def provider_class(cls) -> type: return litellm # type: ignore - def get_model_id(self) -> str: - return self.model_path - def _register_methods(self) -> None: """Register LiteLLM methods - only chat and chat_async are supported""" self.set_method_config(GenerationMode.CHAT.value, "completion", "messages") @@ -306,9 +303,6 @@ def provider_class(cls) -> Type[Any]: """ return litellm # type: ignore - def get_model_id(self) -> str: - return self.model_path - def _register_methods(self) -> None: """ Register how to call litellm methods - only chat modes are supported: diff --git a/altk/core/llm/providers/openai/openai.py b/altk/core/llm/providers/openai/openai.py index dc86e18..3358e3c 100644 --- a/altk/core/llm/providers/openai/openai.py +++ b/altk/core/llm/providers/openai/openai.py @@ -77,10 +77,6 @@ def transform_min_tokens(value: Any, mode: Any) -> dict[str, Any]: ) self._parameter_mapper.set_custom_transform("min_tokens", transform_min_tokens) - def get_model_id(self) -> str|None: - if self._other_kwargs: - return self._other_kwargs.get("model") - class BaseValidatingOpenAIClient(ValidatingLLMClient): """Base class for validating OpenAI and Azure OpenAI clients with shared parameter mapping""" @@ -149,10 +145,6 @@ def transform_min_tokens(value: Any, mode: Any) -> dict[str, Any]: ) self._parameter_mapper.set_custom_transform("min_tokens", transform_min_tokens) - def get_model_id(self) -> str|None: - if self._other_kwargs: - return self._other_kwargs.get("model") - @register_llm("openai.sync") class SyncOpenAIClient(BaseOpenAIClient, BaseLLMClient): diff --git a/altk/pre_tool/toolguard/llm_client.py b/altk/pre_tool/toolguard/llm_client.py index 5a54b69..da8615d 100644 --- a/altk/pre_tool/toolguard/llm_client.py +++ b/altk/pre_tool/toolguard/llm_client.py @@ -1,13 +1,12 @@ from typing import Union, cast -from altk.core.llm.types import GenerationArgs -from ...core.llm import ValidatingLLMClient, LLMClient from toolguard.llm.tg_litellm import LanguageModelBase +from altk.core.llm.types import GenerationArgs +from altk.core.llm import ValidatingLLMClient, LLMClient -class TG_LLMEval(LanguageModelBase): +class TG_LLMClient(LanguageModelBase): def __init__(self, llm_client: Union[LLMClient, ValidatingLLMClient]): - super().__init__(llm_client.get_model_id()) # type: ignore self.llm_client = llm_client async def generate(self, messages: list[dict]) -> str: diff --git a/altk/pre_tool/toolguard/toolguard_code_component.py b/altk/pre_tool/toolguard/toolguard_code_component.py index 6795079..292fc80 100644 --- a/altk/pre_tool/toolguard/toolguard_code_component.py +++ b/altk/pre_tool/toolguard/toolguard_code_component.py @@ -1,4 +1,5 @@ import logging +from pathlib import Path from typing import Any, Callable, Dict, List, cast from enum import Enum from pydantic import BaseModel, Field @@ -9,7 +10,7 @@ from toolguard import generate_guards_from_specs, ToolGuardSpec, ToolGuardsCodeGenerationResult, load_toolguards from toolguard.runtime import IToolInvoker, ToolGuardsCodeGenerationResult -from altk.pre_tool.toolguard.llm_client import TG_LLMEval +from altk.pre_tool.toolguard.llm_client import TG_LLMClient logger = logging.getLogger(__name__) @@ -19,12 +20,12 @@ class ToolGuardCodeComponentConfig(ComponentConfig): class ToolGuardCodeBuildInput(ComponentInput): tools: List[Callable] | List[BaseTool] | str toolguard_specs: List[ToolGuardSpec] - out_dir: str + out_dir: str| Path ToolGuardBuildOutput = ToolGuardsCodeGenerationResult class ToolGuardCodeRunInput(ComponentInput): - generated_guard_dir: str + generated_guard_dir: str | Path tool_name: str = Field(description="Tool name") tool_args: Dict[str, Any] = Field(default={}, description="Tool arguments") tool_invoker: IToolInvoker @@ -76,7 +77,7 @@ def _build(self, data: ToolGuardCodeBuildInput) -> ToolGuardsCodeGenerationResul async def _abuild(self, data: ToolGuardCodeBuildInput) -> ToolGuardsCodeGenerationResult: config = cast(ToolGuardCodeComponentConfig, self.config) - llm = TG_LLMEval(config.llm_client) + llm = TG_LLMClient(config.llm_client) return await generate_guards_from_specs( tools=data.tools, tool_specs=data.toolguard_specs, diff --git a/altk/pre_tool/toolguard/toolguard_spec_component.py b/altk/pre_tool/toolguard/toolguard_spec_component.py index c141758..34e052d 100644 --- a/altk/pre_tool/toolguard/toolguard_spec_component.py +++ b/altk/pre_tool/toolguard/toolguard_spec_component.py @@ -1,10 +1,11 @@ import logging import os +from pathlib import Path from typing import Callable, List, Set, cast from langchain_core.tools import BaseTool from pydantic import Field -from altk.pre_tool.toolguard.llm_client import TG_LLMEval +from altk.pre_tool.toolguard.llm_client import TG_LLMClient from ...core.toolkit import AgentPhase, ComponentBase, ComponentConfig, ComponentInput from toolguard import ToolGuardSpec, generate_guard_specs @@ -17,7 +18,7 @@ class ToolGuardSpecComponentConfig(ComponentConfig): class ToolGuardSpecBuildInput(ComponentInput): policy_text: str = Field(description="Text of the policy document file") tools: List[Callable] | List[BaseTool] | str - out_dir: str + out_dir: str| Path ToolGuardSpecs=List[ToolGuardSpec] @@ -36,11 +37,12 @@ def _build(self, data: ToolGuardSpecBuildInput) -> ToolGuardSpecs: async def _abuild(self, data: ToolGuardSpecBuildInput) -> ToolGuardSpecs: os.makedirs(data.out_dir, exist_ok=True) config = cast(ToolGuardSpecComponentConfig, self.config) - llm = TG_LLMEval(config.llm_client) + llm = TG_LLMClient(config.llm_client) return await generate_guard_specs( policy_text=data.policy_text, tools=data.tools, work_dir=data.out_dir, - llm=llm + llm=llm, + short=True ) \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 9f58282..a4f5100 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -130,7 +130,7 @@ spotlight = [ ] toolguard = [ - "toolguard>=0.1.14" + "toolguard>=0.1.18" ] refraction = [ diff --git a/tests/pre_tool/toolguard/test_toolguard_code.py b/tests/pre_tool/toolguard/test_toolguard_code.py index 40e08f7..fbc487d 100644 --- a/tests/pre_tool/toolguard/test_toolguard_code.py +++ b/tests/pre_tool/toolguard/test_toolguard_code.py @@ -23,11 +23,12 @@ ToolGuardCodeBuildInput, ) from toolguard.data_types import ( - load_tool_policy, + load_tool_spec, ) from toolguard.runtime import ( ToolFunctionsInvoker, ToolGuardsCodeGenerationResult, + load_toolguard_code_result ) from altk.pre_tool.toolguard.toolguard_code_component import ( ToolGuardCodeComponentConfig, @@ -69,9 +70,9 @@ def get_llm()->BaseLLMClient: from altk.core.llm.providers.ibm_watsonx_ai.ibm_watsonx_ai import WatsonxLLMClient return WatsonxLLMClient( model_name="meta-llama/llama-4-maverick-17b-128e-instruct-fp8", - api_key=os.getenv("WATSONX_API_KEY"), - project_id = os.getenv("WATSONX_PROJECT_ID"), - url=os.getenv("WATSONX_URL"), + api_key=os.getenv("WX_API_KEY"), + project_id = os.getenv("WX_PROJECT_ID"), + url=os.getenv("WX_URL"), ) # from altk.core.llm.providers.openai.openai import AsyncAzureOpenAIClient @@ -120,7 +121,7 @@ async def test_tool_guard_calculator_policy(work_dir: str): # Load policy JSON files from /step1 policy_dir = Path(__file__).parent / "inputs" / "step1" specs = [ - load_tool_policy(str(policy_dir / f"{tool.__name__}.json"), tool.__name__) + load_tool_spec(str(policy_dir / f"{tool.__name__}.json")) for tool in funcs ] @@ -134,7 +135,7 @@ async def test_tool_guard_calculator_policy(work_dir: str): #Toolguarg code generation build_output = cast(ToolGuardsCodeGenerationResult, await toolguard_code.aprocess(input, AgentPhase.BUILDTIME)) - # output = load_toolguard_code_result(work_dir) + # build_output = load_toolguard_code_result("tests/pre_tool/toolguard/outputs/work_XXX") # Expected guarded tools expected_tools = ["multiply_tool", "divide_tool", "add_tool"] diff --git a/tests/pre_tool/toolguard/test_toolguard_specs.py b/tests/pre_tool/toolguard/test_toolguard_specs.py index e4f1b59..344ca5a 100644 --- a/tests/pre_tool/toolguard/test_toolguard_specs.py +++ b/tests/pre_tool/toolguard/test_toolguard_specs.py @@ -65,21 +65,22 @@ async def test_tool_guard_calculator_policy(out_dir: str): correspond to a number whose KDI value equals 6.28. """ - # Example alternative LLM: - # LLMClient = get_llm("litellm.output_val") - # llm_client = LLMClient( - # model_name="gpt-4o-2024-08-06", - # custom_llm_provider="azure", - # ) - - LLMClient = get_llm("watsonx.output_val") - llm_client = LLMClient( - model_name="mistralai/mistral-medium-2505", + from altk.core.llm.providers.ibm_watsonx_ai.ibm_watsonx_ai import WatsonxLLMClient + llm_client = WatsonxLLMClient( + model_name="meta-llama/llama-4-maverick-17b-128e-instruct-fp8", api_key=os.getenv("WX_API_KEY"), - project_id=os.getenv("WX_PROJECT_ID"), - url=os.getenv("WX_URL", "https://us-south.ml.cloud.ibm.com"), + project_id = os.getenv("WX_PROJECT_ID"), + url=os.getenv("WX_URL"), ) + # from altk.core.llm.providers.openai.openai import AsyncAzureOpenAIClient + # llm_client = AsyncAzureOpenAIClient( + # model="gpt-4o-2024-08-06", + # api_key=os.getenv("AZURE_OPENAI_API_KEY"), + # azure_endpoint=os.getenv("AZURE_API_BASE"), + # api_version="2024-08-01-preview" + # ) + toolguard_spec = ToolGuardSpecComponent( ToolGuardSpecComponentConfig(llm_client=llm_client) ) From 6ac6f3285cd30a6ffd29fb1f045662c341e4c82f Mon Sep 17 00:00:00 2001 From: DAVID BOAZ Date: Thu, 8 Jan 2026 12:57:24 +0200 Subject: [PATCH 26/42] DCO Remediation Commit for DAVID BOAZ I, DAVID BOAZ , hereby add my Signed-off-by to this commit: ef00372ee91cd1f608c7c468285c03f2bf869d5d I, DAVID BOAZ , hereby add my Signed-off-by to this commit: 3f8f9fbf105e1caf3692c1e9e45b38653d78d58e I, DAVID BOAZ , hereby add my Signed-off-by to this commit: 4ea9d5f54fcb9a3f008533501a66a4818f542fd3 Signed-off-by: DAVID BOAZ --- altk/pre_tool/toolguard/README.md | 49 ++++++++++--------------------- 1 file changed, 16 insertions(+), 33 deletions(-) diff --git a/altk/pre_tool/toolguard/README.md b/altk/pre_tool/toolguard/README.md index 3b1f46b..307aff2 100644 --- a/altk/pre_tool/toolguard/README.md +++ b/altk/pre_tool/toolguard/README.md @@ -27,34 +27,17 @@ The specifications are aimed to be used as input into our next component - the ` The two components are not concatenated by design. As the geneartion involves a non-deterministic language model, the results need to be reviewed by a human. Hence, the output specification files should be reviewed and optionaly edited. For example, removing a wrong compliance example. ### Component Configuarion -This component expects an LLM client configuarion: +This component expects an LLM client configuarion. Here is a concerete example using WatsonX SDK: ```python -from altk.core.llm import get_llm - -LLMClient = get_llm("litellm.output_val") -llm_client = LLMClient(...) -toolguard_component = ToolGuardSpecComponent( - ToolGuardSpecComponentConfig(llm_client=llm_client) +from altk.core.llm.providers.ibm_watsonx_ai.ibm_watsonx_ai import WatsonxLLMClient +llm_client = WatsonxLLMClient( + model_name="meta-llama/llama-4-maverick-17b-128e-instruct-fp8", + api_key=os.getenv("WX_API_KEY"), + project_id = os.getenv("WX_PROJECT_ID"), + url=os.getenv("WX_URL"), ) ``` -Here is a concerete example with `litellm` and `azure`: -Environment variables: -```bash -export AZURE_OPENAI_API_KEY="" -export AZURE_API_BASE="https://your.azure.endpoint" -export AZURE_API_VERSION="2024-08-01-preview" -``` -code: -```python -from altk.core.llm import get_llm - -LLMClient = get_llm("litellm.output_val") -llm_client = LLMClient( - model_name="gpt-4o-2024-08-06", - custom_llm_provider="azure", -) -``` ### Input and Output The component build input is a `ToolGuardSpecBuildInput` object containing the following fields: @@ -84,17 +67,17 @@ The ToolGuards checks if a planned action complies with the policy. If it violat This component expects an LLM client configuarion. Here is an example using a Watsonx LLM client: ``` -from altk.core.llm.providers.ibm_watsonx_ai.ibm_watsonx_ai import WatsonxLLMClientOutputVal -llm = WatsonxLLMClientOutputVal( +from altk.core.llm.providers.ibm_watsonx_ai.ibm_watsonx_ai import WatsonxLLMClient +llm = WatsonxLLMClient( model_name="meta-llama/llama-4-maverick-17b-128e-instruct-fp8", - api_key=os.getenv("WATSONX_API_KEY"), - project_id = os.getenv("WATSONX_PROJECT_ID"), - url=os.getenv("WATSONX_URL"), + api_key=os.getenv("WX_API_KEY"), + project_id = os.getenv("WX_PROJECT_ID"), + url=os.getenv("WX_URL"), ) config = ToolGuardCodeComponentConfig(llm_client=llm) toolguard_code_component = ToolGuardCodeComponent(config) ``` -**Important note:** The Code component works best with *closed models* such as [GPT-4o](https://openai.com/index/hello-gpt-4o/), [Gemini](https://deepmind.google/technologies/gemini/), and [Claude](https://www.anthropic.com/claude). + ### Input and Output The Component has two phases: @@ -103,10 +86,10 @@ An agent owner should use this API to generate ToolGuards - Python function that The input of the build phase is a `ToolGuardCodeBuildInput` object, containing: * `tools: List[Callable] | List[BaseTool] | str`: List of available tools. Either as Python functions, methods, Langgraph Tools, or a path to an Open API specification file. * `toolguard_specs: List[ToolGuardSpec]`: List of specifications, optionaly generated by `ToolGuardSpecComponent` component and reviewed. - * `out_dir: str`: A directory in the local file system where the ToolGuard objects will be saved. + * `out_dir: str | Path`: A directory in the local file system where the ToolGuard objects will be saved. The output of the build phase is a `ToolGuardsCodeGenerationResult` object with: - * `out_dir: str`: Path to the file system where the results were saved. It is the same as the `input.out_dir`. + * `out_dir: Path`: Path to the file system where the results were saved. It is the same as the `input.out_dir`. * `domain: RuntimeDomain`: A complex object descibing the generated APIs. For example, refernces to Python file names and class names. * `tools: Dict[str, ToolGuardCodeResult]`: A Dictionary of the ToolGuardsResults, by the tool names. * Each `ToolGuardCodeResult` details the name of guard Python file name and the guard function name. It also reference to the generated unit test files. @@ -114,7 +97,7 @@ The output of the build phase is a `ToolGuardsCodeGenerationResult` object with: #### Runtime phase A running agent should use the runtime API to check if a tool call complies with the given policy. The input of the runtime phase is a `ToolGuardCodeRunInput` object: - * `generated_guard_dir: str`: Path in the local file system where the generated guard Python code (The code that was generated during the build time, described above) is located. + * `generated_guard_dir: str | Path`: Path in the local file system where the generated guard Python code (The code that was generated during the build time, described above) is located. * `tool_name: str`: The name of the tool that the agent is about to call * `tool_args: Dict[str, Any]`: A dictionary of the toolcall arguments, by the argument name. * `tool_invoker: IToolInvoker`: A proxy object that enables the guard to call other read-only tools. This is needed when the policy enforcement logic involves getting data from another tool. For example, before booking a flight, you need to check the flight status by calling the "get_flight_status" API. From 5f7e2f9ce77e9425185bc2413df11a4b1df551df Mon Sep 17 00:00:00 2001 From: DAVID BOAZ Date: Sun, 11 Jan 2026 11:59:36 +0200 Subject: [PATCH 27/42] toolguard runtime Signed-off-by: DAVID BOAZ --- altk/pre_tool/toolguard/llm_client.py | 2 +- altk/pre_tool/toolguard/toolguard_code_component.py | 5 ++--- altk/pre_tool/toolguard/toolguard_spec_component.py | 2 +- pyproject.toml | 2 +- tests/pre_tool/toolguard/test_toolguard_code.py | 9 +++------ tests/pre_tool/toolguard/test_toolguard_specs.py | 1 - 6 files changed, 8 insertions(+), 13 deletions(-) diff --git a/altk/pre_tool/toolguard/llm_client.py b/altk/pre_tool/toolguard/llm_client.py index da8615d..f8bdde7 100644 --- a/altk/pre_tool/toolguard/llm_client.py +++ b/altk/pre_tool/toolguard/llm_client.py @@ -1,6 +1,6 @@ from typing import Union, cast -from toolguard.llm.tg_litellm import LanguageModelBase +from toolguard.buildtime.llm.tg_litellm import LanguageModelBase from altk.core.llm.types import GenerationArgs from altk.core.llm import ValidatingLLMClient, LLMClient diff --git a/altk/pre_tool/toolguard/toolguard_code_component.py b/altk/pre_tool/toolguard/toolguard_code_component.py index 292fc80..ee99cc9 100644 --- a/altk/pre_tool/toolguard/toolguard_code_component.py +++ b/altk/pre_tool/toolguard/toolguard_code_component.py @@ -7,8 +7,8 @@ from langchain_core.tools import BaseTool from altk.core.toolkit import ComponentConfig, ComponentInput, AgentPhase, ComponentBase -from toolguard import generate_guards_from_specs, ToolGuardSpec, ToolGuardsCodeGenerationResult, load_toolguards -from toolguard.runtime import IToolInvoker, ToolGuardsCodeGenerationResult +from toolguard.buildtime import generate_guards_from_specs, ToolGuardSpec, ToolGuardsCodeGenerationResult +from toolguard.runtime import IToolInvoker, load_toolguards, PolicyViolationException from altk.pre_tool.toolguard.llm_client import TG_LLMClient @@ -90,7 +90,6 @@ def _run(self, data: ToolGuardCodeRunInput) -> ToolGuardCodeRunOutput: tool_name = data.tool_name tool_params = data.tool_args with load_toolguards(code_root_dir) as toolguards: - from rt_toolguard.data_types import PolicyViolationException try: toolguards.check_toolcall(tool_name, tool_params, data.tool_invoker) return ToolGuardCodeRunOutput() diff --git a/altk/pre_tool/toolguard/toolguard_spec_component.py b/altk/pre_tool/toolguard/toolguard_spec_component.py index 34e052d..13fd296 100644 --- a/altk/pre_tool/toolguard/toolguard_spec_component.py +++ b/altk/pre_tool/toolguard/toolguard_spec_component.py @@ -8,7 +8,7 @@ from altk.pre_tool.toolguard.llm_client import TG_LLMClient from ...core.toolkit import AgentPhase, ComponentBase, ComponentConfig, ComponentInput -from toolguard import ToolGuardSpec, generate_guard_specs +from toolguard.buildtime import ToolGuardSpec, generate_guard_specs logger = logging.getLogger(__name__) diff --git a/pyproject.toml b/pyproject.toml index a4f5100..482ce38 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -130,7 +130,7 @@ spotlight = [ ] toolguard = [ - "toolguard>=0.1.18" + "toolguard>=0.1.19" ] refraction = [ diff --git a/tests/pre_tool/toolguard/test_toolguard_code.py b/tests/pre_tool/toolguard/test_toolguard_code.py index fbc487d..11af71f 100644 --- a/tests/pre_tool/toolguard/test_toolguard_code.py +++ b/tests/pre_tool/toolguard/test_toolguard_code.py @@ -21,14 +21,11 @@ from altk.pre_tool.toolguard import ( ToolGuardCodeComponent, ToolGuardCodeBuildInput, -) -from toolguard.data_types import ( - load_tool_spec, + ToolGuardSpec ) from toolguard.runtime import ( ToolFunctionsInvoker, - ToolGuardsCodeGenerationResult, - load_toolguard_code_result + ToolGuardsCodeGenerationResult ) from altk.pre_tool.toolguard.toolguard_code_component import ( ToolGuardCodeComponentConfig, @@ -121,7 +118,7 @@ async def test_tool_guard_calculator_policy(work_dir: str): # Load policy JSON files from /step1 policy_dir = Path(__file__).parent / "inputs" / "step1" specs = [ - load_tool_spec(str(policy_dir / f"{tool.__name__}.json")) + ToolGuardSpec.load(policy_dir / f"{tool.__name__}.json") for tool in funcs ] diff --git a/tests/pre_tool/toolguard/test_toolguard_specs.py b/tests/pre_tool/toolguard/test_toolguard_specs.py index 344ca5a..60c1c2f 100644 --- a/tests/pre_tool/toolguard/test_toolguard_specs.py +++ b/tests/pre_tool/toolguard/test_toolguard_specs.py @@ -15,7 +15,6 @@ ToolGuardSpecs, ) from altk.core.toolkit import AgentPhase -from altk.core.llm.base import get_llm from .inputs.tool_functions import ( divide_tool, From 86dbd46637c75340569378460f46e1faf2bda8e8 Mon Sep 17 00:00:00 2001 From: DAVID BOAZ Date: Sun, 11 Jan 2026 14:41:35 +0200 Subject: [PATCH 28/42] remove mellea old Signed-off-by: DAVID BOAZ --- pyproject.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 482ce38..0e50093 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -87,7 +87,6 @@ dev = [ "pytest-cov>=4.1.0", "pytest-mock>=3.14.1", "ruff>=0.13.2", - "mellea~=0.0.6", "mypy>=1.18.2", "langchain_ibm>=0.3.6", "langgraph>=0.2.74", From 67b4f773e60391d4554bd56e3f233bd1373ab2b6 Mon Sep 17 00:00:00 2001 From: DAVID BOAZ Date: Mon, 12 Jan 2026 19:17:39 +0200 Subject: [PATCH 29/42] toolguard formatting Signed-off-by: DAVID BOAZ --- .../providers/auto_from_env/auto_from_env.py | 2 +- .../ibm_watsonx_ai/ibm_watsonx_ai.py | 2 +- altk/core/llm/providers/litellm/litellm.py | 4 +- altk/core/llm/providers/openai/openai.py | 1 + altk/pre_llm/core/types.py | 2 +- altk/pre_tool/core/types.py | 7 ++- altk/pre_tool/toolguard/__init__.py | 17 +++++- altk/pre_tool/toolguard/llm_client.py | 14 +++-- .../toolguard/toolguard_code_component.py | 52 ++++++++++++------- .../toolguard/toolguard_spec_component.py | 20 ++++--- .../toolguard/inputs/tool_functions.py | 5 +- .../toolguard/inputs/tool_langchain.py | 9 +++- .../pre_tool/toolguard/inputs/tool_methods.py | 4 +- .../pre_tool/toolguard/test_toolguard_code.py | 31 +++++------ .../toolguard/test_toolguard_specs.py | 3 +- 15 files changed, 104 insertions(+), 69 deletions(-) diff --git a/altk/core/llm/providers/auto_from_env/auto_from_env.py b/altk/core/llm/providers/auto_from_env/auto_from_env.py index e60d62b..8d622c5 100644 --- a/altk/core/llm/providers/auto_from_env/auto_from_env.py +++ b/altk/core/llm/providers/auto_from_env/auto_from_env.py @@ -45,7 +45,7 @@ def __init__(self) -> None: @classmethod def provider_class(cls) -> Type[Any]: raise NotImplementedError - + def _register_methods(self) -> None: if self._chosen_provider: self._chosen_provider._register_methods() diff --git a/altk/core/llm/providers/ibm_watsonx_ai/ibm_watsonx_ai.py b/altk/core/llm/providers/ibm_watsonx_ai/ibm_watsonx_ai.py index f5d89fe..6a9bd5a 100644 --- a/altk/core/llm/providers/ibm_watsonx_ai/ibm_watsonx_ai.py +++ b/altk/core/llm/providers/ibm_watsonx_ai/ibm_watsonx_ai.py @@ -126,7 +126,7 @@ def provider_class(cls) -> Type[Any]: Underlying SDK client class for watsonx.ai: ModelInference. """ return ModelInference # type: ignore - + def _register_methods(self) -> None: """ Register how to call watsonx methods: diff --git a/altk/core/llm/providers/litellm/litellm.py b/altk/core/llm/providers/litellm/litellm.py index 12bc956..7ec917f 100644 --- a/altk/core/llm/providers/litellm/litellm.py +++ b/altk/core/llm/providers/litellm/litellm.py @@ -35,7 +35,7 @@ def __init__( @classmethod def provider_class(cls) -> type: return litellm # type: ignore - + def _register_methods(self) -> None: """Register LiteLLM methods - only chat and chat_async are supported""" self.set_method_config(GenerationMode.CHAT.value, "completion", "messages") @@ -302,7 +302,7 @@ def provider_class(cls) -> Type[Any]: Must be callable with no arguments (per LLMClient __init__ logic). """ return litellm # type: ignore - + def _register_methods(self) -> None: """ Register how to call litellm methods - only chat modes are supported: diff --git a/altk/core/llm/providers/openai/openai.py b/altk/core/llm/providers/openai/openai.py index 3358e3c..765e5c1 100644 --- a/altk/core/llm/providers/openai/openai.py +++ b/altk/core/llm/providers/openai/openai.py @@ -77,6 +77,7 @@ def transform_min_tokens(value: Any, mode: Any) -> dict[str, Any]: ) self._parameter_mapper.set_custom_transform("min_tokens", transform_min_tokens) + class BaseValidatingOpenAIClient(ValidatingLLMClient): """Base class for validating OpenAI and Azure OpenAI clients with shared parameter mapping""" diff --git a/altk/pre_llm/core/types.py b/altk/pre_llm/core/types.py index f06c9f5..33c280f 100644 --- a/altk/pre_llm/core/types.py +++ b/altk/pre_llm/core/types.py @@ -125,4 +125,4 @@ def get_topics( n_results: int = 10, query_kwargs: Dict[str, Any] | None = None, distance_threshold: float | None = None, - ) -> List[RetrievedTopic]: ... \ No newline at end of file + ) -> List[RetrievedTopic]: ... diff --git a/altk/pre_tool/core/types.py b/altk/pre_tool/core/types.py index 7abcbe6..c41ac0c 100644 --- a/altk/pre_tool/core/types.py +++ b/altk/pre_tool/core/types.py @@ -1,8 +1,7 @@ -from typing import List, Optional, Dict, Any, Union -from pydantic import BaseModel, Field, ConfigDict +from typing import List, Optional, Dict, Any +from pydantic import BaseModel, Field from enum import Enum -from altk.core.llm import LLMClient from altk.core.toolkit import ComponentInput, ComponentOutput @@ -90,4 +89,4 @@ class SPARCReflectionRunOutput(PreToolReflectionRunOutput): output: SPARCReflectionRunOutputSchema = Field( default_factory=lambda: SPARCReflectionRunOutputSchema() - ) \ No newline at end of file + ) diff --git a/altk/pre_tool/toolguard/__init__.py b/altk/pre_tool/toolguard/__init__.py index d29a083..fcf6cb0 100644 --- a/altk/pre_tool/toolguard/__init__.py +++ b/altk/pre_tool/toolguard/__init__.py @@ -1,2 +1,15 @@ -from .toolguard_code_component import * -from .toolguard_spec_component import * \ No newline at end of file +from .toolguard_spec_component import ( + ToolGuardSpecComponentConfig, + ToolGuardSpecBuildInput, + ToolGuardSpec, + ToolGuardSpecs, + ToolGuardSpecComponent, +) +from .toolguard_code_component import ( + ToolGuardCodeComponentConfig, + ToolGuardCodeBuildInput, + ToolGuardBuildOutput, + ToolGuardCodeRunInput, + ToolGuardCodeRunOutput, + ToolGuardCodeComponent, +) diff --git a/altk/pre_tool/toolguard/llm_client.py b/altk/pre_tool/toolguard/llm_client.py index f8bdde7..ba11e8e 100644 --- a/altk/pre_tool/toolguard/llm_client.py +++ b/altk/pre_tool/toolguard/llm_client.py @@ -1,4 +1,3 @@ - from typing import Union, cast from toolguard.buildtime.llm.tg_litellm import LanguageModelBase from altk.core.llm.types import GenerationArgs @@ -13,12 +12,11 @@ async def generate(self, messages: list[dict]) -> str: if isinstance(self.llm_client, ValidatingLLMClient): llm_client = cast(ValidatingLLMClient, self.llm_client) return await llm_client.generate_async( - prompt=messages, - schema=str, - generation_args = GenerationArgs(max_tokens=10000) + prompt=messages, + schema=str, + generation_args=GenerationArgs(max_tokens=10000), ) - + return await self.llm_client.generate_async( - prompt=messages, - generation_args = GenerationArgs(max_tokens=10000) - ) # type: ignore + prompt=messages, generation_args=GenerationArgs(max_tokens=10000) + ) # type: ignore diff --git a/altk/pre_tool/toolguard/toolguard_code_component.py b/altk/pre_tool/toolguard/toolguard_code_component.py index ee99cc9..98fb23e 100644 --- a/altk/pre_tool/toolguard/toolguard_code_component.py +++ b/altk/pre_tool/toolguard/toolguard_code_component.py @@ -7,32 +7,39 @@ from langchain_core.tools import BaseTool from altk.core.toolkit import ComponentConfig, ComponentInput, AgentPhase, ComponentBase -from toolguard.buildtime import generate_guards_from_specs, ToolGuardSpec, ToolGuardsCodeGenerationResult +from toolguard.buildtime import ( + generate_guards_from_specs, + ToolGuardSpec, + ToolGuardsCodeGenerationResult, +) from toolguard.runtime import IToolInvoker, load_toolguards, PolicyViolationException from altk.pre_tool.toolguard.llm_client import TG_LLMClient logger = logging.getLogger(__name__) + class ToolGuardCodeComponentConfig(ComponentConfig): pass + class ToolGuardCodeBuildInput(ComponentInput): tools: List[Callable] | List[BaseTool] | str toolguard_specs: List[ToolGuardSpec] - out_dir: str| Path + out_dir: str | Path + ToolGuardBuildOutput = ToolGuardsCodeGenerationResult + class ToolGuardCodeRunInput(ComponentInput): generated_guard_dir: str | Path tool_name: str = Field(description="Tool name") tool_args: Dict[str, Any] = Field(default={}, description="Tool arguments") tool_invoker: IToolInvoker - model_config = { - "arbitrary_types_allowed": True - } + model_config = {"arbitrary_types_allowed": True} + class ViolationLevel(Enum): """Severity level of a safety violation. @@ -46,6 +53,7 @@ class ViolationLevel(Enum): WARN = "warn" ERROR = "error" + class PolicyViolation(BaseModel): """Details of a safety violation detected by content moderation. @@ -58,33 +66,37 @@ class PolicyViolation(BaseModel): # what message should you convey to the user user_message: str | None = None + class ToolGuardCodeRunOutput(BaseModel): violation: PolicyViolation | None = None class ToolGuardCodeComponent(ComponentBase): - - def __init__(self, config:ToolGuardCodeComponentConfig): + def __init__(self, config: ToolGuardCodeComponentConfig): super().__init__(config=config) - + @classmethod def supported_phases(cls) -> Set[AgentPhase]: """Return the supported agent phases.""" return {AgentPhase.BUILDTIME, AgentPhase.RUNTIME} def _build(self, data: ToolGuardCodeBuildInput) -> ToolGuardsCodeGenerationResult: - raise NotImplementedError("Please use the aprocess() function in an async context") + raise NotImplementedError( + "Please use the aprocess() function in an async context" + ) - async def _abuild(self, data: ToolGuardCodeBuildInput) -> ToolGuardsCodeGenerationResult: + async def _abuild( + self, data: ToolGuardCodeBuildInput + ) -> ToolGuardsCodeGenerationResult: config = cast(ToolGuardCodeComponentConfig, self.config) llm = TG_LLMClient(config.llm_client) return await generate_guards_from_specs( - tools=data.tools, - tool_specs=data.toolguard_specs, + tools=data.tools, + tool_specs=data.toolguard_specs, work_dir=data.out_dir, - llm=llm + llm=llm, ) - + def _run(self, data: ToolGuardCodeRunInput) -> ToolGuardCodeRunOutput: code_root_dir = data.generated_guard_dir tool_name = data.tool_name @@ -94,11 +106,11 @@ def _run(self, data: ToolGuardCodeRunInput) -> ToolGuardCodeRunOutput: toolguards.check_toolcall(tool_name, tool_params, data.tool_invoker) return ToolGuardCodeRunOutput() except PolicyViolationException as e: - return ToolGuardCodeRunOutput(violation=PolicyViolation( - violation_level=ViolationLevel.ERROR, - user_message=str(e) - )) - + return ToolGuardCodeRunOutput( + violation=PolicyViolation( + violation_level=ViolationLevel.ERROR, user_message=str(e) + ) + ) + def _arun(self, data: ToolGuardCodeRunInput) -> ToolGuardCodeRunOutput: return self._run(data) - diff --git a/altk/pre_tool/toolguard/toolguard_spec_component.py b/altk/pre_tool/toolguard/toolguard_spec_component.py index 13fd296..3c0bf6e 100644 --- a/altk/pre_tool/toolguard/toolguard_spec_component.py +++ b/altk/pre_tool/toolguard/toolguard_spec_component.py @@ -12,27 +12,32 @@ logger = logging.getLogger(__name__) + class ToolGuardSpecComponentConfig(ComponentConfig): pass + class ToolGuardSpecBuildInput(ComponentInput): policy_text: str = Field(description="Text of the policy document file") tools: List[Callable] | List[BaseTool] | str - out_dir: str| Path + out_dir: str | Path + + +ToolGuardSpecs = List[ToolGuardSpec] -ToolGuardSpecs=List[ToolGuardSpec] class ToolGuardSpecComponent(ComponentBase): - - def __init__(self, config:ToolGuardSpecComponentConfig): + def __init__(self, config: ToolGuardSpecComponentConfig): super().__init__(config=config) - + @classmethod def supported_phases(cls) -> Set[AgentPhase]: return {AgentPhase.BUILDTIME, AgentPhase.RUNTIME} def _build(self, data: ToolGuardSpecBuildInput) -> ToolGuardSpecs: - raise NotImplementedError("Please use the aprocess() function in an async context") + raise NotImplementedError( + "Please use the aprocess() function in an async context" + ) async def _abuild(self, data: ToolGuardSpecBuildInput) -> ToolGuardSpecs: os.makedirs(data.out_dir, exist_ok=True) @@ -43,6 +48,5 @@ async def _abuild(self, data: ToolGuardSpecBuildInput) -> ToolGuardSpecs: tools=data.tools, work_dir=data.out_dir, llm=llm, - short=True + short=True, ) - \ No newline at end of file diff --git a/tests/pre_tool/toolguard/inputs/tool_functions.py b/tests/pre_tool/toolguard/inputs/tool_functions.py index dfab5e4..eb4714b 100644 --- a/tests/pre_tool/toolguard/inputs/tool_functions.py +++ b/tests/pre_tool/toolguard/inputs/tool_functions.py @@ -52,15 +52,16 @@ def multiply_tool(a: float, b: float) -> float: """ return a * b + def map_kdi_number(i: float) -> float: """ return the mapping of the numer i to it's kdi value Args: i (float): The number to map. - + Returns: float: The value of the dki of the given number. """ - return 3.14 * i \ No newline at end of file + return 3.14 * i diff --git a/tests/pre_tool/toolguard/inputs/tool_langchain.py b/tests/pre_tool/toolguard/inputs/tool_langchain.py index 57b8e2b..3221eea 100644 --- a/tests/pre_tool/toolguard/inputs/tool_langchain.py +++ b/tests/pre_tool/toolguard/inputs/tool_langchain.py @@ -1,5 +1,6 @@ from langchain_core.tools import tool + @tool() def divide_tool(g: float, h: float) -> float: """Divides one number by another. @@ -16,6 +17,7 @@ def divide_tool(g: float, h: float) -> float: """ return g / h + @tool() def add_tool(a: float, b: float) -> float: """Adds two numbers. @@ -29,6 +31,7 @@ def add_tool(a: float, b: float) -> float: """ return a + b + @tool() def subtract_tool(a: float, b: float) -> float: """Subtracts one number from another. @@ -42,6 +45,7 @@ def subtract_tool(a: float, b: float) -> float: """ return a - b + @tool() def multiply_tool(a: float, b: float) -> float: """Multiplies two numbers. @@ -55,6 +59,7 @@ def multiply_tool(a: float, b: float) -> float: """ return a * b + @tool() def map_kdi_number(i: float) -> float: """ @@ -62,9 +67,9 @@ def map_kdi_number(i: float) -> float: Args: i (float): The number to map. - + Returns: float: The value of the dki of the given number. """ - return 3.14 * i \ No newline at end of file + return 3.14 * i diff --git a/tests/pre_tool/toolguard/inputs/tool_methods.py b/tests/pre_tool/toolguard/inputs/tool_methods.py index 7154857..c8f4582 100644 --- a/tests/pre_tool/toolguard/inputs/tool_methods.py +++ b/tests/pre_tool/toolguard/inputs/tool_methods.py @@ -58,9 +58,9 @@ def map_kdi_number(self, i: float) -> float: Args: i (float): The number to map. - + Returns: float: The value of the dki of the given number. """ - return 3.14 * i \ No newline at end of file + return 3.14 * i diff --git a/tests/pre_tool/toolguard/test_toolguard_code.py b/tests/pre_tool/toolguard/test_toolguard_code.py index 11af71f..9d8a133 100644 --- a/tests/pre_tool/toolguard/test_toolguard_code.py +++ b/tests/pre_tool/toolguard/test_toolguard_code.py @@ -21,12 +21,9 @@ from altk.pre_tool.toolguard import ( ToolGuardCodeComponent, ToolGuardCodeBuildInput, - ToolGuardSpec -) -from toolguard.runtime import ( - ToolFunctionsInvoker, - ToolGuardsCodeGenerationResult + ToolGuardSpec, ) +from toolguard.runtime import ToolFunctionsInvoker, ToolGuardsCodeGenerationResult from altk.pre_tool.toolguard.toolguard_code_component import ( ToolGuardCodeComponentConfig, ToolGuardCodeRunInput, @@ -51,6 +48,7 @@ # Fixtures # --------------------------------------------------------------------------- + @pytest.fixture def work_dir(): """Creates a temporary folder for test output and cleans it afterward.""" @@ -63,12 +61,14 @@ def work_dir(): # shutil.rmtree(dir_path) # print("Temporary work dir removed:", dir_path) -def get_llm()->BaseLLMClient: + +def get_llm() -> BaseLLMClient: from altk.core.llm.providers.ibm_watsonx_ai.ibm_watsonx_ai import WatsonxLLMClient + return WatsonxLLMClient( model_name="meta-llama/llama-4-maverick-17b-128e-instruct-fp8", api_key=os.getenv("WX_API_KEY"), - project_id = os.getenv("WX_PROJECT_ID"), + project_id=os.getenv("WX_PROJECT_ID"), url=os.getenv("WX_URL"), ) @@ -101,10 +101,12 @@ def get_llm()->BaseLLMClient: # url=os.getenv("TOOLGUARD_GENPY_MODEL_BASE_URL"), # ) + # --------------------------------------------------------------------------- # Test: ToolGuard verification for the calculator tool set # --------------------------------------------------------------------------- + @pytest.mark.asyncio async def test_tool_guard_calculator_policy(work_dir: str): # Tools to be guarded @@ -117,10 +119,7 @@ async def test_tool_guard_calculator_policy(work_dir: str): # Load policy JSON files from /step1 policy_dir = Path(__file__).parent / "inputs" / "step1" - specs = [ - ToolGuardSpec.load(policy_dir / f"{tool.__name__}.json") - for tool in funcs - ] + specs = [ToolGuardSpec.load(policy_dir / f"{tool.__name__}.json") for tool in funcs] # Prepare build input for guard code generation input = ToolGuardCodeBuildInput( @@ -129,9 +128,11 @@ async def test_tool_guard_calculator_policy(work_dir: str): toolguard_specs=specs, ) - #Toolguarg code generation - build_output = cast(ToolGuardsCodeGenerationResult, - await toolguard_code.aprocess(input, AgentPhase.BUILDTIME)) + # Toolguarg code generation + build_output = cast( + ToolGuardsCodeGenerationResult, + await toolguard_code.aprocess(input, AgentPhase.BUILDTIME), + ) # build_output = load_toolguard_code_result("tests/pre_tool/toolguard/outputs/work_XXX") # Expected guarded tools @@ -219,4 +220,4 @@ async def main(): await test_tool_guard_calculator_policy(work_dir) print("[main] Test completed successfully.") - asyncio.run(main()) \ No newline at end of file + asyncio.run(main()) diff --git a/tests/pre_tool/toolguard/test_toolguard_specs.py b/tests/pre_tool/toolguard/test_toolguard_specs.py index 60c1c2f..ceae389 100644 --- a/tests/pre_tool/toolguard/test_toolguard_specs.py +++ b/tests/pre_tool/toolguard/test_toolguard_specs.py @@ -65,10 +65,11 @@ async def test_tool_guard_calculator_policy(out_dir: str): """ from altk.core.llm.providers.ibm_watsonx_ai.ibm_watsonx_ai import WatsonxLLMClient + llm_client = WatsonxLLMClient( model_name="meta-llama/llama-4-maverick-17b-128e-instruct-fp8", api_key=os.getenv("WX_API_KEY"), - project_id = os.getenv("WX_PROJECT_ID"), + project_id=os.getenv("WX_PROJECT_ID"), url=os.getenv("WX_URL"), ) From 6ecdaedeaf0ba9af7184be5de17b525e9c57cca8 Mon Sep 17 00:00:00 2001 From: DAVID BOAZ Date: Mon, 12 Jan 2026 19:20:17 +0200 Subject: [PATCH 30/42] secrets and uv.lock Signed-off-by: DAVID BOAZ --- .secrets.baseline | 50 +- uv.lock | 3339 ++++++++++++++++++++++++--------------------- 2 files changed, 1835 insertions(+), 1554 deletions(-) diff --git a/.secrets.baseline b/.secrets.baseline index 0bbe51f..1b37e1b 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -3,7 +3,7 @@ "files": "^.secrets.baseline$", "lines": null }, - "generated_at": "2025-12-11T14:35:13Z", + "generated_at": "2026-01-12T17:08:49Z", "plugins_used": [ { "name": "AWSKeyDetector" @@ -209,26 +209,6 @@ "verified_result": null } ], - "altk/pre_tool/toolguard/README.md": [ - { - "hashed_secret": "d9e9019d9eb455a3d72a3bc252c26927bb148a10", - "is_secret": false, - "is_verified": false, - "line_number": 91, - "type": "Secret Keyword", - "verified_result": null - } - ], - "altk/pre_tool/toolguard/toolguard/common/http.py": [ - { - "hashed_secret": "d4c3d66fd0c38547a3c7a4c6bdc29c36911bc030", - "is_secret": false, - "is_verified": false, - "line_number": 47, - "type": "Secret Keyword", - "verified_result": null - } - ], "docs/concepts/components/rag-repair.md": [ { "hashed_secret": "e253731ea5c795b60f81485cb343a8b7f913ff3f", @@ -254,7 +234,7 @@ "hashed_secret": "68e6d3aca4501dea82eb7edd34e4f05fb0e36b4c", "is_secret": false, "is_verified": false, - "line_number": 34, + "line_number": 32, "type": "JSON Web Token", "verified_result": null } @@ -269,6 +249,32 @@ "verified_result": null } ], + "examples/Tool-Validation-Example.ipynb": [ + { + "hashed_secret": "28c0a29e58b8b4b14800dcba1ef3ad14e8aa4cbc", + "is_secret": false, + "is_verified": false, + "line_number": 314, + "type": "Secret Keyword", + "verified_result": null + }, + { + "hashed_secret": "1ca2398bcc5ab302d0728f5e44df88578b0268f6", + "is_secret": false, + "is_verified": false, + "line_number": 319, + "type": "Secret Keyword", + "verified_result": null + }, + { + "hashed_secret": "f41044348ae250592e3a131cd50c28a25abfca4a", + "is_secret": false, + "is_verified": false, + "line_number": 331, + "type": "Secret Keyword", + "verified_result": null + } + ], "examples/refraction_getting_started.ipynb": [ { "hashed_secret": "a3dbdc9594fcfb1f065c11f82f5a23dc8ba63c4a", diff --git a/uv.lock b/uv.lock index b9e4e50..02a4597 100644 --- a/uv.lock +++ b/uv.lock @@ -1,27 +1,36 @@ version = 1 -revision = 3 +revision = 2 requires-python = ">=3.10" resolution-markers = [ - "python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin'", + "python_full_version >= '3.14' and platform_machine == 'x86_64' and sys_platform == 'darwin'", + "python_full_version == '3.13.*' and platform_machine == 'x86_64' and sys_platform == 'darwin'", "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'darwin'", "python_full_version == '3.11.*' and platform_machine == 'x86_64' and sys_platform == 'darwin'", - "python_full_version >= '3.10.2' and python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'darwin'", - "python_full_version < '3.10.2' and platform_machine == 'x86_64' and sys_platform == 'darwin'", - "python_full_version >= '3.13' and platform_machine != 'x86_64' and sys_platform == 'darwin'", - "python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux'", - "(python_full_version >= '3.13' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and sys_platform != 'darwin' and sys_platform != 'linux')", + "python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'darwin'", + "python_full_version >= '3.14' and platform_machine != 'x86_64' and sys_platform == 'darwin'", + "python_full_version >= '3.14' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "(python_full_version >= '3.14' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.14' and sys_platform != 'darwin' and sys_platform != 'linux')", + "python_full_version == '3.13.*' and platform_machine != 'x86_64' and sys_platform == 'darwin'", + "python_full_version == '3.13.*' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "(python_full_version == '3.13.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.13.*' and sys_platform != 'darwin' and sys_platform != 'linux')", "python_full_version == '3.12.*' and platform_machine != 'x86_64' and sys_platform == 'darwin'", "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'", "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')", "python_full_version == '3.11.*' and platform_machine != 'x86_64' and sys_platform == 'darwin'", "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'", "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux')", - "python_full_version >= '3.10.2' and python_full_version < '3.11' and platform_machine != 'x86_64' and sys_platform == 'darwin'", - "python_full_version >= '3.10.2' and python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'", - "(python_full_version >= '3.10.2' and python_full_version < '3.11' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.10.2' and python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')", - "python_full_version < '3.10.2' and platform_machine != 'x86_64' and sys_platform == 'darwin'", - "python_full_version < '3.10.2' and platform_machine == 'aarch64' and sys_platform == 'linux'", - "(python_full_version < '3.10.2' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10.2' and sys_platform != 'darwin' and sys_platform != 'linux')", + "python_full_version < '3.11' and platform_machine != 'x86_64' and sys_platform == 'darwin'", + "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "(python_full_version < '3.11' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')", +] + +[[package]] +name = "absl-py" +version = "2.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/10/2a/c93173ffa1b39c1d0395b7e842bbdc62e556ca9d8d3b5572926f3e4ca752/absl_py-2.3.1.tar.gz", hash = "sha256:a97820526f7fbfd2ec1bce83f3f25e3a14840dac0d8e02a0b71cd75db3f77fc9", size = 116588, upload-time = "2025-07-03T09:31:44.05Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8f/aa/ba0014cc4659328dc818a28827be78e6d97312ab0cb98105a770924dc11e/absl_py-2.3.1-py3-none-any.whl", hash = "sha256:eeecf07f0c2a93ace0772c92e596ace6d3d3996c042b2128459aaae2a76de11d", size = 135811, upload-time = "2025-07-03T09:31:42.253Z" }, ] [[package]] @@ -51,11 +60,10 @@ source = { editable = "." } dependencies = [ { name = "aiofiles" }, { name = "black" }, - { name = "datamodel-code-generator" }, { name = "docstring-parser" }, { name = "genson" }, { name = "ibm-watsonx-ai", version = "1.3.42", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "ibm-watsonx-ai", version = "1.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "ibm-watsonx-ai", version = "1.4.11", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "jinja2" }, { name = "jsonschema" }, { name = "langchain-chroma" }, @@ -65,17 +73,13 @@ dependencies = [ { name = "langchain-text-splitters" }, { name = "litellm" }, { name = "llm-sandbox", extra = ["docker", "podman"] }, - { name = "mellea" }, { name = "nltk" }, { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "openai" }, { name = "pydantic" }, - { name = "pyright" }, - { name = "pytest" }, - { name = "pytest-json-report" }, { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "scipy", version = "1.16.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "scipy", version = "1.17.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "smolagents" }, { name = "tomli" }, { name = "typing-extensions" }, @@ -106,7 +110,8 @@ routing = [ { name = "docling" }, { name = "pydash" }, { name = "pymilvus", extra = ["milvus-lite", "model"] }, - { name = "scikit-learn" }, + { name = "scikit-learn", version = "1.7.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "scikit-learn", version = "1.8.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "sentence-transformers" }, ] spotlight = [ @@ -114,12 +119,14 @@ spotlight = [ { name = "torch", version = "2.9.1", source = { registry = "https://pypi.org/simple" }, marker = "platform_machine != 'x86_64' or sys_platform != 'darwin'" }, { name = "transformers" }, ] +toolguard = [ + { name = "toolguard" }, +] [package.dev-dependencies] dev = [ { name = "langchain-ibm" }, { name = "langgraph" }, - { name = "mellea" }, { name = "mypy" }, { name = "pandas-stubs" }, { name = "pre-commit" }, @@ -149,7 +156,6 @@ requires-dist = [ { name = "aiofiles", specifier = "==24.1.0" }, { name = "bertopic", marker = "extra == 'routing'", specifier = ">=0.17.3" }, { name = "black", specifier = ">=25.1.0" }, - { name = "datamodel-code-generator", specifier = ">=0.34.0" }, { name = "docling", marker = "extra == 'routing'", specifier = ">=2.38.1" }, { name = "docstring-parser", specifier = ">=0.16" }, { name = "genson", specifier = ">=1.3.0" }, @@ -165,7 +171,6 @@ requires-dist = [ { name = "langchain-text-splitters", specifier = ">=1.0.0" }, { name = "litellm", specifier = "<2.0.0" }, { name = "llm-sandbox", extras = ["docker", "podman"], specifier = ">=0.2.5" }, - { name = "mellea", specifier = "~=0.0.6" }, { name = "mkdocs-awesome-nav", marker = "extra == 'docs'" }, { name = "mkdocs-click", marker = "extra == 'docs'" }, { name = "mkdocs-enumerate-headings-plugin", marker = "extra == 'docs'" }, @@ -181,28 +186,25 @@ requires-dist = [ { name = "pydantic", specifier = ">=2.0.0" }, { name = "pydash", marker = "extra == 'routing'", specifier = ">=8.0.5" }, { name = "pymilvus", extras = ["milvus-lite", "model"], marker = "extra == 'routing'", specifier = ">=2.6.2" }, - { name = "pyright", specifier = ">=1.1.406" }, - { name = "pytest", specifier = ">=8.2.1" }, - { name = "pytest-json-report", specifier = ">=1.5.0" }, { name = "scikit-learn", marker = "extra == 'routing'", specifier = ">=1.7.1" }, { name = "scipy", specifier = ">=1.15.3" }, { name = "sentence-transformers", marker = "extra == 'refraction'", specifier = ">=5.0.0" }, { name = "sentence-transformers", marker = "extra == 'routing'", specifier = ">=5.0.0" }, { name = "smolagents", specifier = ">=1.21.3" }, { name = "tomli", specifier = "==2.2.1" }, + { name = "toolguard", marker = "extra == 'toolguard'", specifier = ">=0.1.19" }, { name = "torch", marker = "platform_machine == 'x86_64' and sys_platform == 'darwin' and extra == 'spotlight'", specifier = "==2.2.2" }, { name = "torch", marker = "(platform_machine != 'x86_64' and extra == 'spotlight') or (sys_platform != 'darwin' and extra == 'spotlight')", specifier = ">=2.5.1" }, { name = "transformers", marker = "extra == 'spotlight'", specifier = ">=4.53.3" }, { name = "typing-extensions", specifier = ">=4.0.0" }, { name = "uv", marker = "extra == 'build'", specifier = "~=0.8.22" }, ] -provides-extras = ["docs", "build", "spotlight", "refraction", "routing"] +provides-extras = ["docs", "build", "spotlight", "toolguard", "refraction", "routing"] [package.metadata.requires-dev] dev = [ { name = "langchain-ibm", specifier = ">=0.3.6" }, { name = "langgraph", specifier = ">=0.2.74" }, - { name = "mellea", specifier = "~=0.0.6" }, { name = "mypy", specifier = ">=1.18.2" }, { name = "pandas-stubs", specifier = ">=2.3.2.250926" }, { name = "pre-commit", specifier = ">=4.2.0" }, @@ -247,7 +249,7 @@ wheels = [ [[package]] name = "aiohttp" -version = "3.13.2" +version = "3.13.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohappyeyeballs" }, @@ -259,110 +261,110 @@ dependencies = [ { name = "propcache" }, { name = "yarl" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/1c/ce/3b83ebba6b3207a7135e5fcaba49706f8a4b6008153b4e30540c982fae26/aiohttp-3.13.2.tar.gz", hash = "sha256:40176a52c186aefef6eb3cad2cdd30cd06e3afbe88fe8ab2af9c0b90f228daca", size = 7837994, upload-time = "2025-10-28T20:59:39.937Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6d/34/939730e66b716b76046dedfe0842995842fa906ccc4964bba414ff69e429/aiohttp-3.13.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2372b15a5f62ed37789a6b383ff7344fc5b9f243999b0cd9b629d8bc5f5b4155", size = 736471, upload-time = "2025-10-28T20:55:27.924Z" }, - { url = "https://files.pythonhosted.org/packages/fd/cf/dcbdf2df7f6ca72b0bb4c0b4509701f2d8942cf54e29ca197389c214c07f/aiohttp-3.13.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e7f8659a48995edee7229522984bd1009c1213929c769c2daa80b40fe49a180c", size = 493985, upload-time = "2025-10-28T20:55:29.456Z" }, - { url = "https://files.pythonhosted.org/packages/9d/87/71c8867e0a1d0882dcbc94af767784c3cb381c1c4db0943ab4aae4fed65e/aiohttp-3.13.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:939ced4a7add92296b0ad38892ce62b98c619288a081170695c6babe4f50e636", size = 489274, upload-time = "2025-10-28T20:55:31.134Z" }, - { url = "https://files.pythonhosted.org/packages/38/0f/46c24e8dae237295eaadd113edd56dee96ef6462adf19b88592d44891dc5/aiohttp-3.13.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6315fb6977f1d0dd41a107c527fee2ed5ab0550b7d885bc15fee20ccb17891da", size = 1668171, upload-time = "2025-10-28T20:55:36.065Z" }, - { url = "https://files.pythonhosted.org/packages/eb/c6/4cdfb4440d0e28483681a48f69841fa5e39366347d66ef808cbdadddb20e/aiohttp-3.13.2-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6e7352512f763f760baaed2637055c49134fd1d35b37c2dedfac35bfe5cf8725", size = 1636036, upload-time = "2025-10-28T20:55:37.576Z" }, - { url = "https://files.pythonhosted.org/packages/84/37/8708cf678628216fb678ab327a4e1711c576d6673998f4f43e86e9ae90dd/aiohttp-3.13.2-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e09a0a06348a2dd73e7213353c90d709502d9786219f69b731f6caa0efeb46f5", size = 1727975, upload-time = "2025-10-28T20:55:39.457Z" }, - { url = "https://files.pythonhosted.org/packages/e6/2e/3ebfe12fdcb9b5f66e8a0a42dffcd7636844c8a018f261efb2419f68220b/aiohttp-3.13.2-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a09a6d073fb5789456545bdee2474d14395792faa0527887f2f4ec1a486a59d3", size = 1815823, upload-time = "2025-10-28T20:55:40.958Z" }, - { url = "https://files.pythonhosted.org/packages/a1/4f/ca2ef819488cbb41844c6cf92ca6dd15b9441e6207c58e5ae0e0fc8d70ad/aiohttp-3.13.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b59d13c443f8e049d9e94099c7e412e34610f1f49be0f230ec656a10692a5802", size = 1669374, upload-time = "2025-10-28T20:55:42.745Z" }, - { url = "https://files.pythonhosted.org/packages/f8/fe/1fe2e1179a0d91ce09c99069684aab619bf2ccde9b20bd6ca44f8837203e/aiohttp-3.13.2-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:20db2d67985d71ca033443a1ba2001c4b5693fe09b0e29f6d9358a99d4d62a8a", size = 1555315, upload-time = "2025-10-28T20:55:44.264Z" }, - { url = "https://files.pythonhosted.org/packages/5a/2b/f3781899b81c45d7cbc7140cddb8a3481c195e7cbff8e36374759d2ab5a5/aiohttp-3.13.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:960c2fc686ba27b535f9fd2b52d87ecd7e4fd1cf877f6a5cba8afb5b4a8bd204", size = 1639140, upload-time = "2025-10-28T20:55:46.626Z" }, - { url = "https://files.pythonhosted.org/packages/72/27/c37e85cd3ece6f6c772e549bd5a253d0c122557b25855fb274224811e4f2/aiohttp-3.13.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:6c00dbcf5f0d88796151e264a8eab23de2997c9303dd7c0bf622e23b24d3ce22", size = 1645496, upload-time = "2025-10-28T20:55:48.933Z" }, - { url = "https://files.pythonhosted.org/packages/66/20/3af1ab663151bd3780b123e907761cdb86ec2c4e44b2d9b195ebc91fbe37/aiohttp-3.13.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fed38a5edb7945f4d1bcabe2fcd05db4f6ec7e0e82560088b754f7e08d93772d", size = 1697625, upload-time = "2025-10-28T20:55:50.377Z" }, - { url = "https://files.pythonhosted.org/packages/95/eb/ae5cab15efa365e13d56b31b0d085a62600298bf398a7986f8388f73b598/aiohttp-3.13.2-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:b395bbca716c38bef3c764f187860e88c724b342c26275bc03e906142fc5964f", size = 1542025, upload-time = "2025-10-28T20:55:51.861Z" }, - { url = "https://files.pythonhosted.org/packages/e9/2d/1683e8d67ec72d911397fe4e575688d2a9b8f6a6e03c8fdc9f3fd3d4c03f/aiohttp-3.13.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:204ffff2426c25dfda401ba08da85f9c59525cdc42bda26660463dd1cbcfec6f", size = 1714918, upload-time = "2025-10-28T20:55:53.515Z" }, - { url = "https://files.pythonhosted.org/packages/99/a2/ffe8e0e1c57c5e542d47ffa1fcf95ef2b3ea573bf7c4d2ee877252431efc/aiohttp-3.13.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:05c4dd3c48fb5f15db31f57eb35374cb0c09afdde532e7fb70a75aede0ed30f6", size = 1656113, upload-time = "2025-10-28T20:55:55.438Z" }, - { url = "https://files.pythonhosted.org/packages/0d/42/d511aff5c3a2b06c09d7d214f508a4ad8ac7799817f7c3d23e7336b5e896/aiohttp-3.13.2-cp310-cp310-win32.whl", hash = "sha256:e574a7d61cf10351d734bcddabbe15ede0eaa8a02070d85446875dc11189a251", size = 432290, upload-time = "2025-10-28T20:55:56.96Z" }, - { url = "https://files.pythonhosted.org/packages/8b/ea/1c2eb7098b5bad4532994f2b7a8228d27674035c9b3234fe02c37469ef14/aiohttp-3.13.2-cp310-cp310-win_amd64.whl", hash = "sha256:364f55663085d658b8462a1c3f17b2b84a5c2e1ba858e1b79bff7b2e24ad1514", size = 455075, upload-time = "2025-10-28T20:55:58.373Z" }, - { url = "https://files.pythonhosted.org/packages/35/74/b321e7d7ca762638cdf8cdeceb39755d9c745aff7a64c8789be96ddf6e96/aiohttp-3.13.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4647d02df098f6434bafd7f32ad14942f05a9caa06c7016fdcc816f343997dd0", size = 743409, upload-time = "2025-10-28T20:56:00.354Z" }, - { url = "https://files.pythonhosted.org/packages/99/3d/91524b905ec473beaf35158d17f82ef5a38033e5809fe8742e3657cdbb97/aiohttp-3.13.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e3403f24bcb9c3b29113611c3c16a2a447c3953ecf86b79775e7be06f7ae7ccb", size = 497006, upload-time = "2025-10-28T20:56:01.85Z" }, - { url = "https://files.pythonhosted.org/packages/eb/d3/7f68bc02a67716fe80f063e19adbd80a642e30682ce74071269e17d2dba1/aiohttp-3.13.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:43dff14e35aba17e3d6d5ba628858fb8cb51e30f44724a2d2f0c75be492c55e9", size = 493195, upload-time = "2025-10-28T20:56:03.314Z" }, - { url = "https://files.pythonhosted.org/packages/98/31/913f774a4708775433b7375c4f867d58ba58ead833af96c8af3621a0d243/aiohttp-3.13.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e2a9ea08e8c58bb17655630198833109227dea914cd20be660f52215f6de5613", size = 1747759, upload-time = "2025-10-28T20:56:04.904Z" }, - { url = "https://files.pythonhosted.org/packages/e8/63/04efe156f4326f31c7c4a97144f82132c3bb21859b7bb84748d452ccc17c/aiohttp-3.13.2-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:53b07472f235eb80e826ad038c9d106c2f653584753f3ddab907c83f49eedead", size = 1704456, upload-time = "2025-10-28T20:56:06.986Z" }, - { url = "https://files.pythonhosted.org/packages/8e/02/4e16154d8e0a9cf4ae76f692941fd52543bbb148f02f098ca73cab9b1c1b/aiohttp-3.13.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e736c93e9c274fce6419af4aac199984d866e55f8a4cec9114671d0ea9688780", size = 1807572, upload-time = "2025-10-28T20:56:08.558Z" }, - { url = "https://files.pythonhosted.org/packages/34/58/b0583defb38689e7f06798f0285b1ffb3a6fb371f38363ce5fd772112724/aiohttp-3.13.2-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ff5e771f5dcbc81c64898c597a434f7682f2259e0cd666932a913d53d1341d1a", size = 1895954, upload-time = "2025-10-28T20:56:10.545Z" }, - { url = "https://files.pythonhosted.org/packages/6b/f3/083907ee3437425b4e376aa58b2c915eb1a33703ec0dc30040f7ae3368c6/aiohttp-3.13.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a3b6fb0c207cc661fa0bf8c66d8d9b657331ccc814f4719468af61034b478592", size = 1747092, upload-time = "2025-10-28T20:56:12.118Z" }, - { url = "https://files.pythonhosted.org/packages/ac/61/98a47319b4e425cc134e05e5f3fc512bf9a04bf65aafd9fdcda5d57ec693/aiohttp-3.13.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:97a0895a8e840ab3520e2288db7cace3a1981300d48babeb50e7425609e2e0ab", size = 1606815, upload-time = "2025-10-28T20:56:14.191Z" }, - { url = "https://files.pythonhosted.org/packages/97/4b/e78b854d82f66bb974189135d31fce265dee0f5344f64dd0d345158a5973/aiohttp-3.13.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9e8f8afb552297aca127c90cb840e9a1d4bfd6a10d7d8f2d9176e1acc69bad30", size = 1723789, upload-time = "2025-10-28T20:56:16.101Z" }, - { url = "https://files.pythonhosted.org/packages/ed/fc/9d2ccc794fc9b9acd1379d625c3a8c64a45508b5091c546dea273a41929e/aiohttp-3.13.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:ed2f9c7216e53c3df02264f25d824b079cc5914f9e2deba94155190ef648ee40", size = 1718104, upload-time = "2025-10-28T20:56:17.655Z" }, - { url = "https://files.pythonhosted.org/packages/66/65/34564b8765ea5c7d79d23c9113135d1dd3609173da13084830f1507d56cf/aiohttp-3.13.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:99c5280a329d5fa18ef30fd10c793a190d996567667908bef8a7f81f8202b948", size = 1785584, upload-time = "2025-10-28T20:56:19.238Z" }, - { url = "https://files.pythonhosted.org/packages/30/be/f6a7a426e02fc82781afd62016417b3948e2207426d90a0e478790d1c8a4/aiohttp-3.13.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:2ca6ffef405fc9c09a746cb5d019c1672cd7f402542e379afc66b370833170cf", size = 1595126, upload-time = "2025-10-28T20:56:20.836Z" }, - { url = "https://files.pythonhosted.org/packages/e5/c7/8e22d5d28f94f67d2af496f14a83b3c155d915d1fe53d94b66d425ec5b42/aiohttp-3.13.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:47f438b1a28e926c37632bff3c44df7d27c9b57aaf4e34b1def3c07111fdb782", size = 1800665, upload-time = "2025-10-28T20:56:22.922Z" }, - { url = "https://files.pythonhosted.org/packages/d1/11/91133c8b68b1da9fc16555706aa7276fdf781ae2bb0876c838dd86b8116e/aiohttp-3.13.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9acda8604a57bb60544e4646a4615c1866ee6c04a8edef9b8ee6fd1d8fa2ddc8", size = 1739532, upload-time = "2025-10-28T20:56:25.924Z" }, - { url = "https://files.pythonhosted.org/packages/17/6b/3747644d26a998774b21a616016620293ddefa4d63af6286f389aedac844/aiohttp-3.13.2-cp311-cp311-win32.whl", hash = "sha256:868e195e39b24aaa930b063c08bb0c17924899c16c672a28a65afded9c46c6ec", size = 431876, upload-time = "2025-10-28T20:56:27.524Z" }, - { url = "https://files.pythonhosted.org/packages/c3/63/688462108c1a00eb9f05765331c107f95ae86f6b197b865d29e930b7e462/aiohttp-3.13.2-cp311-cp311-win_amd64.whl", hash = "sha256:7fd19df530c292542636c2a9a85854fab93474396a52f1695e799186bbd7f24c", size = 456205, upload-time = "2025-10-28T20:56:29.062Z" }, - { url = "https://files.pythonhosted.org/packages/29/9b/01f00e9856d0a73260e86dd8ed0c2234a466c5c1712ce1c281548df39777/aiohttp-3.13.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b1e56bab2e12b2b9ed300218c351ee2a3d8c8fdab5b1ec6193e11a817767e47b", size = 737623, upload-time = "2025-10-28T20:56:30.797Z" }, - { url = "https://files.pythonhosted.org/packages/5a/1b/4be39c445e2b2bd0aab4ba736deb649fabf14f6757f405f0c9685019b9e9/aiohttp-3.13.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:364e25edaabd3d37b1db1f0cbcee8c73c9a3727bfa262b83e5e4cf3489a2a9dc", size = 492664, upload-time = "2025-10-28T20:56:32.708Z" }, - { url = "https://files.pythonhosted.org/packages/28/66/d35dcfea8050e131cdd731dff36434390479b4045a8d0b9d7111b0a968f1/aiohttp-3.13.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c5c94825f744694c4b8db20b71dba9a257cd2ba8e010a803042123f3a25d50d7", size = 491808, upload-time = "2025-10-28T20:56:34.57Z" }, - { url = "https://files.pythonhosted.org/packages/00/29/8e4609b93e10a853b65f8291e64985de66d4f5848c5637cddc70e98f01f8/aiohttp-3.13.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba2715d842ffa787be87cbfce150d5e88c87a98e0b62e0f5aa489169a393dbbb", size = 1738863, upload-time = "2025-10-28T20:56:36.377Z" }, - { url = "https://files.pythonhosted.org/packages/9d/fa/4ebdf4adcc0def75ced1a0d2d227577cd7b1b85beb7edad85fcc87693c75/aiohttp-3.13.2-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:585542825c4bc662221fb257889e011a5aa00f1ae4d75d1d246a5225289183e3", size = 1700586, upload-time = "2025-10-28T20:56:38.034Z" }, - { url = "https://files.pythonhosted.org/packages/da/04/73f5f02ff348a3558763ff6abe99c223381b0bace05cd4530a0258e52597/aiohttp-3.13.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:39d02cb6025fe1aabca329c5632f48c9532a3dabccd859e7e2f110668972331f", size = 1768625, upload-time = "2025-10-28T20:56:39.75Z" }, - { url = "https://files.pythonhosted.org/packages/f8/49/a825b79ffec124317265ca7d2344a86bcffeb960743487cb11988ffb3494/aiohttp-3.13.2-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e67446b19e014d37342f7195f592a2a948141d15a312fe0e700c2fd2f03124f6", size = 1867281, upload-time = "2025-10-28T20:56:41.471Z" }, - { url = "https://files.pythonhosted.org/packages/b9/48/adf56e05f81eac31edcfae45c90928f4ad50ef2e3ea72cb8376162a368f8/aiohttp-3.13.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4356474ad6333e41ccefd39eae869ba15a6c5299c9c01dfdcfdd5c107be4363e", size = 1752431, upload-time = "2025-10-28T20:56:43.162Z" }, - { url = "https://files.pythonhosted.org/packages/30/ab/593855356eead019a74e862f21523db09c27f12fd24af72dbc3555b9bfd9/aiohttp-3.13.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:eeacf451c99b4525f700f078becff32c32ec327b10dcf31306a8a52d78166de7", size = 1562846, upload-time = "2025-10-28T20:56:44.85Z" }, - { url = "https://files.pythonhosted.org/packages/39/0f/9f3d32271aa8dc35036e9668e31870a9d3b9542dd6b3e2c8a30931cb27ae/aiohttp-3.13.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d8a9b889aeabd7a4e9af0b7f4ab5ad94d42e7ff679aaec6d0db21e3b639ad58d", size = 1699606, upload-time = "2025-10-28T20:56:46.519Z" }, - { url = "https://files.pythonhosted.org/packages/2c/3c/52d2658c5699b6ef7692a3f7128b2d2d4d9775f2a68093f74bca06cf01e1/aiohttp-3.13.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:fa89cb11bc71a63b69568d5b8a25c3ca25b6d54c15f907ca1c130d72f320b76b", size = 1720663, upload-time = "2025-10-28T20:56:48.528Z" }, - { url = "https://files.pythonhosted.org/packages/9b/d4/8f8f3ff1fb7fb9e3f04fcad4e89d8a1cd8fc7d05de67e3de5b15b33008ff/aiohttp-3.13.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8aa7c807df234f693fed0ecd507192fc97692e61fee5702cdc11155d2e5cadc8", size = 1737939, upload-time = "2025-10-28T20:56:50.77Z" }, - { url = "https://files.pythonhosted.org/packages/03/d3/ddd348f8a27a634daae39a1b8e291ff19c77867af438af844bf8b7e3231b/aiohttp-3.13.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:9eb3e33fdbe43f88c3c75fa608c25e7c47bbd80f48d012763cb67c47f39a7e16", size = 1555132, upload-time = "2025-10-28T20:56:52.568Z" }, - { url = "https://files.pythonhosted.org/packages/39/b8/46790692dc46218406f94374903ba47552f2f9f90dad554eed61bfb7b64c/aiohttp-3.13.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9434bc0d80076138ea986833156c5a48c9c7a8abb0c96039ddbb4afc93184169", size = 1764802, upload-time = "2025-10-28T20:56:54.292Z" }, - { url = "https://files.pythonhosted.org/packages/ba/e4/19ce547b58ab2a385e5f0b8aa3db38674785085abcf79b6e0edd1632b12f/aiohttp-3.13.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ff15c147b2ad66da1f2cbb0622313f2242d8e6e8f9b79b5206c84523a4473248", size = 1719512, upload-time = "2025-10-28T20:56:56.428Z" }, - { url = "https://files.pythonhosted.org/packages/70/30/6355a737fed29dcb6dfdd48682d5790cb5eab050f7b4e01f49b121d3acad/aiohttp-3.13.2-cp312-cp312-win32.whl", hash = "sha256:27e569eb9d9e95dbd55c0fc3ec3a9335defbf1d8bc1d20171a49f3c4c607b93e", size = 426690, upload-time = "2025-10-28T20:56:58.736Z" }, - { url = "https://files.pythonhosted.org/packages/0a/0d/b10ac09069973d112de6ef980c1f6bb31cb7dcd0bc363acbdad58f927873/aiohttp-3.13.2-cp312-cp312-win_amd64.whl", hash = "sha256:8709a0f05d59a71f33fd05c17fc11fcb8c30140506e13c2f5e8ee1b8964e1b45", size = 453465, upload-time = "2025-10-28T20:57:00.795Z" }, - { url = "https://files.pythonhosted.org/packages/bf/78/7e90ca79e5aa39f9694dcfd74f4720782d3c6828113bb1f3197f7e7c4a56/aiohttp-3.13.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7519bdc7dfc1940d201651b52bf5e03f5503bda45ad6eacf64dda98be5b2b6be", size = 732139, upload-time = "2025-10-28T20:57:02.455Z" }, - { url = "https://files.pythonhosted.org/packages/db/ed/1f59215ab6853fbaa5c8495fa6cbc39edfc93553426152b75d82a5f32b76/aiohttp-3.13.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:088912a78b4d4f547a1f19c099d5a506df17eacec3c6f4375e2831ec1d995742", size = 490082, upload-time = "2025-10-28T20:57:04.784Z" }, - { url = "https://files.pythonhosted.org/packages/68/7b/fe0fe0f5e05e13629d893c760465173a15ad0039c0a5b0d0040995c8075e/aiohttp-3.13.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5276807b9de9092af38ed23ce120539ab0ac955547b38563a9ba4f5b07b95293", size = 489035, upload-time = "2025-10-28T20:57:06.894Z" }, - { url = "https://files.pythonhosted.org/packages/d2/04/db5279e38471b7ac801d7d36a57d1230feeee130bbe2a74f72731b23c2b1/aiohttp-3.13.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1237c1375eaef0db4dcd7c2559f42e8af7b87ea7d295b118c60c36a6e61cb811", size = 1720387, upload-time = "2025-10-28T20:57:08.685Z" }, - { url = "https://files.pythonhosted.org/packages/31/07/8ea4326bd7dae2bd59828f69d7fdc6e04523caa55e4a70f4a8725a7e4ed2/aiohttp-3.13.2-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:96581619c57419c3d7d78703d5b78c1e5e5fc0172d60f555bdebaced82ded19a", size = 1688314, upload-time = "2025-10-28T20:57:10.693Z" }, - { url = "https://files.pythonhosted.org/packages/48/ab/3d98007b5b87ffd519d065225438cc3b668b2f245572a8cb53da5dd2b1bc/aiohttp-3.13.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a2713a95b47374169409d18103366de1050fe0ea73db358fc7a7acb2880422d4", size = 1756317, upload-time = "2025-10-28T20:57:12.563Z" }, - { url = "https://files.pythonhosted.org/packages/97/3d/801ca172b3d857fafb7b50c7c03f91b72b867a13abca982ed6b3081774ef/aiohttp-3.13.2-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:228a1cd556b3caca590e9511a89444925da87d35219a49ab5da0c36d2d943a6a", size = 1858539, upload-time = "2025-10-28T20:57:14.623Z" }, - { url = "https://files.pythonhosted.org/packages/f7/0d/4764669bdf47bd472899b3d3db91fffbe925c8e3038ec591a2fd2ad6a14d/aiohttp-3.13.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ac6cde5fba8d7d8c6ac963dbb0256a9854e9fafff52fbcc58fdf819357892c3e", size = 1739597, upload-time = "2025-10-28T20:57:16.399Z" }, - { url = "https://files.pythonhosted.org/packages/c4/52/7bd3c6693da58ba16e657eb904a5b6decfc48ecd06e9ac098591653b1566/aiohttp-3.13.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f2bef8237544f4e42878c61cef4e2839fee6346dc60f5739f876a9c50be7fcdb", size = 1555006, upload-time = "2025-10-28T20:57:18.288Z" }, - { url = "https://files.pythonhosted.org/packages/48/30/9586667acec5993b6f41d2ebcf96e97a1255a85f62f3c653110a5de4d346/aiohttp-3.13.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:16f15a4eac3bc2d76c45f7ebdd48a65d41b242eb6c31c2245463b40b34584ded", size = 1683220, upload-time = "2025-10-28T20:57:20.241Z" }, - { url = "https://files.pythonhosted.org/packages/71/01/3afe4c96854cfd7b30d78333852e8e851dceaec1c40fd00fec90c6402dd2/aiohttp-3.13.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:bb7fb776645af5cc58ab804c58d7eba545a97e047254a52ce89c157b5af6cd0b", size = 1712570, upload-time = "2025-10-28T20:57:22.253Z" }, - { url = "https://files.pythonhosted.org/packages/11/2c/22799d8e720f4697a9e66fd9c02479e40a49de3de2f0bbe7f9f78a987808/aiohttp-3.13.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:e1b4951125ec10c70802f2cb09736c895861cd39fd9dcb35107b4dc8ae6220b8", size = 1733407, upload-time = "2025-10-28T20:57:24.37Z" }, - { url = "https://files.pythonhosted.org/packages/34/cb/90f15dd029f07cebbd91f8238a8b363978b530cd128488085b5703683594/aiohttp-3.13.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:550bf765101ae721ee1d37d8095f47b1f220650f85fe1af37a90ce75bab89d04", size = 1550093, upload-time = "2025-10-28T20:57:26.257Z" }, - { url = "https://files.pythonhosted.org/packages/69/46/12dce9be9d3303ecbf4d30ad45a7683dc63d90733c2d9fe512be6716cd40/aiohttp-3.13.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:fe91b87fc295973096251e2d25a811388e7d8adf3bd2b97ef6ae78bc4ac6c476", size = 1758084, upload-time = "2025-10-28T20:57:28.349Z" }, - { url = "https://files.pythonhosted.org/packages/f9/c8/0932b558da0c302ffd639fc6362a313b98fdf235dc417bc2493da8394df7/aiohttp-3.13.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e0c8e31cfcc4592cb200160344b2fb6ae0f9e4effe06c644b5a125d4ae5ebe23", size = 1716987, upload-time = "2025-10-28T20:57:30.233Z" }, - { url = "https://files.pythonhosted.org/packages/5d/8b/f5bd1a75003daed099baec373aed678f2e9b34f2ad40d85baa1368556396/aiohttp-3.13.2-cp313-cp313-win32.whl", hash = "sha256:0740f31a60848d6edb296a0df827473eede90c689b8f9f2a4cdde74889eb2254", size = 425859, upload-time = "2025-10-28T20:57:32.105Z" }, - { url = "https://files.pythonhosted.org/packages/5d/28/a8a9fc6957b2cee8902414e41816b5ab5536ecf43c3b1843c10e82c559b2/aiohttp-3.13.2-cp313-cp313-win_amd64.whl", hash = "sha256:a88d13e7ca367394908f8a276b89d04a3652044612b9a408a0bb22a5ed976a1a", size = 452192, upload-time = "2025-10-28T20:57:34.166Z" }, - { url = "https://files.pythonhosted.org/packages/9b/36/e2abae1bd815f01c957cbf7be817b3043304e1c87bad526292a0410fdcf9/aiohttp-3.13.2-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:2475391c29230e063ef53a66669b7b691c9bfc3f1426a0f7bcdf1216bdbac38b", size = 735234, upload-time = "2025-10-28T20:57:36.415Z" }, - { url = "https://files.pythonhosted.org/packages/ca/e3/1ee62dde9b335e4ed41db6bba02613295a0d5b41f74a783c142745a12763/aiohttp-3.13.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:f33c8748abef4d8717bb20e8fb1b3e07c6adacb7fd6beaae971a764cf5f30d61", size = 490733, upload-time = "2025-10-28T20:57:38.205Z" }, - { url = "https://files.pythonhosted.org/packages/1a/aa/7a451b1d6a04e8d15a362af3e9b897de71d86feac3babf8894545d08d537/aiohttp-3.13.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ae32f24bbfb7dbb485a24b30b1149e2f200be94777232aeadba3eecece4d0aa4", size = 491303, upload-time = "2025-10-28T20:57:40.122Z" }, - { url = "https://files.pythonhosted.org/packages/57/1e/209958dbb9b01174870f6a7538cd1f3f28274fdbc88a750c238e2c456295/aiohttp-3.13.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d7f02042c1f009ffb70067326ef183a047425bb2ff3bc434ead4dd4a4a66a2b", size = 1717965, upload-time = "2025-10-28T20:57:42.28Z" }, - { url = "https://files.pythonhosted.org/packages/08/aa/6a01848d6432f241416bc4866cae8dc03f05a5a884d2311280f6a09c73d6/aiohttp-3.13.2-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:93655083005d71cd6c072cdab54c886e6570ad2c4592139c3fb967bfc19e4694", size = 1667221, upload-time = "2025-10-28T20:57:44.869Z" }, - { url = "https://files.pythonhosted.org/packages/87/4f/36c1992432d31bbc789fa0b93c768d2e9047ec8c7177e5cd84ea85155f36/aiohttp-3.13.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0db1e24b852f5f664cd728db140cf11ea0e82450471232a394b3d1a540b0f906", size = 1757178, upload-time = "2025-10-28T20:57:47.216Z" }, - { url = "https://files.pythonhosted.org/packages/ac/b4/8e940dfb03b7e0f68a82b88fd182b9be0a65cb3f35612fe38c038c3112cf/aiohttp-3.13.2-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b009194665bcd128e23eaddef362e745601afa4641930848af4c8559e88f18f9", size = 1838001, upload-time = "2025-10-28T20:57:49.337Z" }, - { url = "https://files.pythonhosted.org/packages/d7/ef/39f3448795499c440ab66084a9db7d20ca7662e94305f175a80f5b7e0072/aiohttp-3.13.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c038a8fdc8103cd51dbd986ecdce141473ffd9775a7a8057a6ed9c3653478011", size = 1716325, upload-time = "2025-10-28T20:57:51.327Z" }, - { url = "https://files.pythonhosted.org/packages/d7/51/b311500ffc860b181c05d91c59a1313bdd05c82960fdd4035a15740d431e/aiohttp-3.13.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:66bac29b95a00db411cd758fea0e4b9bdba6d549dfe333f9a945430f5f2cc5a6", size = 1547978, upload-time = "2025-10-28T20:57:53.554Z" }, - { url = "https://files.pythonhosted.org/packages/31/64/b9d733296ef79815226dab8c586ff9e3df41c6aff2e16c06697b2d2e6775/aiohttp-3.13.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4ebf9cfc9ba24a74cf0718f04aac2a3bbe745902cc7c5ebc55c0f3b5777ef213", size = 1682042, upload-time = "2025-10-28T20:57:55.617Z" }, - { url = "https://files.pythonhosted.org/packages/3f/30/43d3e0f9d6473a6db7d472104c4eff4417b1e9df01774cb930338806d36b/aiohttp-3.13.2-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a4b88ebe35ce54205c7074f7302bd08a4cb83256a3e0870c72d6f68a3aaf8e49", size = 1680085, upload-time = "2025-10-28T20:57:57.59Z" }, - { url = "https://files.pythonhosted.org/packages/16/51/c709f352c911b1864cfd1087577760ced64b3e5bee2aa88b8c0c8e2e4972/aiohttp-3.13.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:98c4fb90bb82b70a4ed79ca35f656f4281885be076f3f970ce315402b53099ae", size = 1728238, upload-time = "2025-10-28T20:57:59.525Z" }, - { url = "https://files.pythonhosted.org/packages/19/e2/19bd4c547092b773caeb48ff5ae4b1ae86756a0ee76c16727fcfd281404b/aiohttp-3.13.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:ec7534e63ae0f3759df3a1ed4fa6bc8f75082a924b590619c0dd2f76d7043caa", size = 1544395, upload-time = "2025-10-28T20:58:01.914Z" }, - { url = "https://files.pythonhosted.org/packages/cf/87/860f2803b27dfc5ed7be532832a3498e4919da61299b4a1f8eb89b8ff44d/aiohttp-3.13.2-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5b927cf9b935a13e33644cbed6c8c4b2d0f25b713d838743f8fe7191b33829c4", size = 1742965, upload-time = "2025-10-28T20:58:03.972Z" }, - { url = "https://files.pythonhosted.org/packages/67/7f/db2fc7618925e8c7a601094d5cbe539f732df4fb570740be88ed9e40e99a/aiohttp-3.13.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:88d6c017966a78c5265d996c19cdb79235be5e6412268d7e2ce7dee339471b7a", size = 1697585, upload-time = "2025-10-28T20:58:06.189Z" }, - { url = "https://files.pythonhosted.org/packages/0c/07/9127916cb09bb38284db5036036042b7b2c514c8ebaeee79da550c43a6d6/aiohttp-3.13.2-cp314-cp314-win32.whl", hash = "sha256:f7c183e786e299b5d6c49fb43a769f8eb8e04a2726a2bd5887b98b5cc2d67940", size = 431621, upload-time = "2025-10-28T20:58:08.636Z" }, - { url = "https://files.pythonhosted.org/packages/fb/41/554a8a380df6d3a2bba8a7726429a23f4ac62aaf38de43bb6d6cde7b4d4d/aiohttp-3.13.2-cp314-cp314-win_amd64.whl", hash = "sha256:fe242cd381e0fb65758faf5ad96c2e460df6ee5b2de1072fe97e4127927e00b4", size = 457627, upload-time = "2025-10-28T20:58:11Z" }, - { url = "https://files.pythonhosted.org/packages/c7/8e/3824ef98c039d3951cb65b9205a96dd2b20f22241ee17d89c5701557c826/aiohttp-3.13.2-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:f10d9c0b0188fe85398c61147bbd2a657d616c876863bfeff43376e0e3134673", size = 767360, upload-time = "2025-10-28T20:58:13.358Z" }, - { url = "https://files.pythonhosted.org/packages/a4/0f/6a03e3fc7595421274fa34122c973bde2d89344f8a881b728fa8c774e4f1/aiohttp-3.13.2-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:e7c952aefdf2460f4ae55c5e9c3e80aa72f706a6317e06020f80e96253b1accd", size = 504616, upload-time = "2025-10-28T20:58:15.339Z" }, - { url = "https://files.pythonhosted.org/packages/c6/aa/ed341b670f1bc8a6f2c6a718353d13b9546e2cef3544f573c6a1ff0da711/aiohttp-3.13.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c20423ce14771d98353d2e25e83591fa75dfa90a3c1848f3d7c68243b4fbded3", size = 509131, upload-time = "2025-10-28T20:58:17.693Z" }, - { url = "https://files.pythonhosted.org/packages/7f/f0/c68dac234189dae5c4bbccc0f96ce0cc16b76632cfc3a08fff180045cfa4/aiohttp-3.13.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e96eb1a34396e9430c19d8338d2ec33015e4a87ef2b4449db94c22412e25ccdf", size = 1864168, upload-time = "2025-10-28T20:58:20.113Z" }, - { url = "https://files.pythonhosted.org/packages/8f/65/75a9a76db8364b5d0e52a0c20eabc5d52297385d9af9c35335b924fafdee/aiohttp-3.13.2-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:23fb0783bc1a33640036465019d3bba069942616a6a2353c6907d7fe1ccdaf4e", size = 1719200, upload-time = "2025-10-28T20:58:22.583Z" }, - { url = "https://files.pythonhosted.org/packages/f5/55/8df2ed78d7f41d232f6bd3ff866b6f617026551aa1d07e2f03458f964575/aiohttp-3.13.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2e1a9bea6244a1d05a4e57c295d69e159a5c50d8ef16aa390948ee873478d9a5", size = 1843497, upload-time = "2025-10-28T20:58:24.672Z" }, - { url = "https://files.pythonhosted.org/packages/e9/e0/94d7215e405c5a02ccb6a35c7a3a6cfff242f457a00196496935f700cde5/aiohttp-3.13.2-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0a3d54e822688b56e9f6b5816fb3de3a3a64660efac64e4c2dc435230ad23bad", size = 1935703, upload-time = "2025-10-28T20:58:26.758Z" }, - { url = "https://files.pythonhosted.org/packages/0b/78/1eeb63c3f9b2d1015a4c02788fb543141aad0a03ae3f7a7b669b2483f8d4/aiohttp-3.13.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7a653d872afe9f33497215745da7a943d1dc15b728a9c8da1c3ac423af35178e", size = 1792738, upload-time = "2025-10-28T20:58:29.787Z" }, - { url = "https://files.pythonhosted.org/packages/41/75/aaf1eea4c188e51538c04cc568040e3082db263a57086ea74a7d38c39e42/aiohttp-3.13.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:56d36e80d2003fa3fc0207fac644216d8532e9504a785ef9a8fd013f84a42c61", size = 1624061, upload-time = "2025-10-28T20:58:32.529Z" }, - { url = "https://files.pythonhosted.org/packages/9b/c2/3b6034de81fbcc43de8aeb209073a2286dfb50b86e927b4efd81cf848197/aiohttp-3.13.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:78cd586d8331fb8e241c2dd6b2f4061778cc69e150514b39a9e28dd050475661", size = 1789201, upload-time = "2025-10-28T20:58:34.618Z" }, - { url = "https://files.pythonhosted.org/packages/c9/38/c15dcf6d4d890217dae79d7213988f4e5fe6183d43893a9cf2fe9e84ca8d/aiohttp-3.13.2-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:20b10bbfbff766294fe99987f7bb3b74fdd2f1a2905f2562132641ad434dcf98", size = 1776868, upload-time = "2025-10-28T20:58:38.835Z" }, - { url = "https://files.pythonhosted.org/packages/04/75/f74fd178ac81adf4f283a74847807ade5150e48feda6aef024403716c30c/aiohttp-3.13.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:9ec49dff7e2b3c85cdeaa412e9d438f0ecd71676fde61ec57027dd392f00c693", size = 1790660, upload-time = "2025-10-28T20:58:41.507Z" }, - { url = "https://files.pythonhosted.org/packages/e7/80/7368bd0d06b16b3aba358c16b919e9c46cf11587dc572091031b0e9e3ef0/aiohttp-3.13.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:94f05348c4406450f9d73d38efb41d669ad6cd90c7ee194810d0eefbfa875a7a", size = 1617548, upload-time = "2025-10-28T20:58:43.674Z" }, - { url = "https://files.pythonhosted.org/packages/7d/4b/a6212790c50483cb3212e507378fbe26b5086d73941e1ec4b56a30439688/aiohttp-3.13.2-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:fa4dcb605c6f82a80c7f95713c2b11c3b8e9893b3ebd2bc9bde93165ed6107be", size = 1817240, upload-time = "2025-10-28T20:58:45.787Z" }, - { url = "https://files.pythonhosted.org/packages/ff/f7/ba5f0ba4ea8d8f3c32850912944532b933acbf0f3a75546b89269b9b7dde/aiohttp-3.13.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cf00e5db968c3f67eccd2778574cf64d8b27d95b237770aa32400bd7a1ca4f6c", size = 1762334, upload-time = "2025-10-28T20:58:47.936Z" }, - { url = "https://files.pythonhosted.org/packages/7e/83/1a5a1856574588b1cad63609ea9ad75b32a8353ac995d830bf5da9357364/aiohttp-3.13.2-cp314-cp314t-win32.whl", hash = "sha256:d23b5fe492b0805a50d3371e8a728a9134d8de5447dce4c885f5587294750734", size = 464685, upload-time = "2025-10-28T20:58:50.642Z" }, - { url = "https://files.pythonhosted.org/packages/9f/4d/d22668674122c08f4d56972297c51a624e64b3ed1efaa40187607a7cb66e/aiohttp-3.13.2-cp314-cp314t-win_amd64.whl", hash = "sha256:ff0a7b0a82a7ab905cbda74006318d1b12e37c797eb1b0d4eb3e316cf47f658f", size = 498093, upload-time = "2025-10-28T20:58:52.782Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/50/42/32cf8e7704ceb4481406eb87161349abb46a57fee3f008ba9cb610968646/aiohttp-3.13.3.tar.gz", hash = "sha256:a949eee43d3782f2daae4f4a2819b2cb9b0c5d3b7f7a927067cc84dafdbb9f88", size = 7844556, upload-time = "2026-01-03T17:33:05.204Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/36/d6/5aec9313ee6ea9c7cde8b891b69f4ff4001416867104580670a31daeba5b/aiohttp-3.13.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d5a372fd5afd301b3a89582817fdcdb6c34124787c70dbcc616f259013e7eef7", size = 738950, upload-time = "2026-01-03T17:29:13.002Z" }, + { url = "https://files.pythonhosted.org/packages/68/03/8fa90a7e6d11ff20a18837a8e2b5dd23db01aabc475aa9271c8ad33299f5/aiohttp-3.13.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:147e422fd1223005c22b4fe080f5d93ced44460f5f9c105406b753612b587821", size = 496099, upload-time = "2026-01-03T17:29:15.268Z" }, + { url = "https://files.pythonhosted.org/packages/d2/23/b81f744d402510a8366b74eb420fc0cc1170d0c43daca12d10814df85f10/aiohttp-3.13.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:859bd3f2156e81dd01432f5849fc73e2243d4a487c4fd26609b1299534ee1845", size = 491072, upload-time = "2026-01-03T17:29:16.922Z" }, + { url = "https://files.pythonhosted.org/packages/d5/e1/56d1d1c0dd334cd203dd97706ce004c1aa24b34a813b0b8daf3383039706/aiohttp-3.13.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dca68018bf48c251ba17c72ed479f4dafe9dbd5a73707ad8d28a38d11f3d42af", size = 1671588, upload-time = "2026-01-03T17:29:18.539Z" }, + { url = "https://files.pythonhosted.org/packages/5f/34/8d7f962604f4bc2b4e39eb1220dac7d4e4cba91fb9ba0474b4ecd67db165/aiohttp-3.13.3-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:fee0c6bc7db1de362252affec009707a17478a00ec69f797d23ca256e36d5940", size = 1640334, upload-time = "2026-01-03T17:29:21.028Z" }, + { url = "https://files.pythonhosted.org/packages/94/1d/fcccf2c668d87337ddeef9881537baee13c58d8f01f12ba8a24215f2b804/aiohttp-3.13.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c048058117fd649334d81b4b526e94bde3ccaddb20463a815ced6ecbb7d11160", size = 1722656, upload-time = "2026-01-03T17:29:22.531Z" }, + { url = "https://files.pythonhosted.org/packages/aa/98/c6f3b081c4c606bc1e5f2ec102e87d6411c73a9ef3616fea6f2d5c98c062/aiohttp-3.13.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:215a685b6fbbfcf71dfe96e3eba7a6f58f10da1dfdf4889c7dd856abe430dca7", size = 1817625, upload-time = "2026-01-03T17:29:24.276Z" }, + { url = "https://files.pythonhosted.org/packages/2c/c0/cfcc3d2e11b477f86e1af2863f3858c8850d751ce8dc39c4058a072c9e54/aiohttp-3.13.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de2c184bb1fe2cbd2cefba613e9db29a5ab559323f994b6737e370d3da0ac455", size = 1672604, upload-time = "2026-01-03T17:29:26.099Z" }, + { url = "https://files.pythonhosted.org/packages/1e/77/6b4ffcbcac4c6a5d041343a756f34a6dd26174ae07f977a64fe028dda5b0/aiohttp-3.13.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:75ca857eba4e20ce9f546cd59c7007b33906a4cd48f2ff6ccf1ccfc3b646f279", size = 1554370, upload-time = "2026-01-03T17:29:28.121Z" }, + { url = "https://files.pythonhosted.org/packages/f2/f0/e3ddfa93f17d689dbe014ba048f18e0c9f9b456033b70e94349a2e9048be/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:81e97251d9298386c2b7dbeb490d3d1badbdc69107fb8c9299dd04eb39bddc0e", size = 1642023, upload-time = "2026-01-03T17:29:30.002Z" }, + { url = "https://files.pythonhosted.org/packages/eb/45/c14019c9ec60a8e243d06d601b33dcc4fd92379424bde3021725859d7f99/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:c0e2d366af265797506f0283487223146af57815b388623f0357ef7eac9b209d", size = 1649680, upload-time = "2026-01-03T17:29:31.782Z" }, + { url = "https://files.pythonhosted.org/packages/9c/fd/09c9451dae5aa5c5ed756df95ff9ef549d45d4be663bafd1e4954fd836f0/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4e239d501f73d6db1522599e14b9b321a7e3b1de66ce33d53a765d975e9f4808", size = 1692407, upload-time = "2026-01-03T17:29:33.392Z" }, + { url = "https://files.pythonhosted.org/packages/a6/81/938bc2ec33c10efd6637ccb3d22f9f3160d08e8f3aa2587a2c2d5ab578eb/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:0db318f7a6f065d84cb1e02662c526294450b314a02bd9e2a8e67f0d8564ce40", size = 1543047, upload-time = "2026-01-03T17:29:34.855Z" }, + { url = "https://files.pythonhosted.org/packages/f7/23/80488ee21c8d567c83045e412e1d9b7077d27171591a4eb7822586e8c06a/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:bfc1cc2fe31a6026a8a88e4ecfb98d7f6b1fec150cfd708adbfd1d2f42257c29", size = 1715264, upload-time = "2026-01-03T17:29:36.389Z" }, + { url = "https://files.pythonhosted.org/packages/e2/83/259a8da6683182768200b368120ab3deff5370bed93880fb9a3a86299f34/aiohttp-3.13.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:af71fff7bac6bb7508956696dce8f6eec2bbb045eceb40343944b1ae62b5ef11", size = 1657275, upload-time = "2026-01-03T17:29:38.162Z" }, + { url = "https://files.pythonhosted.org/packages/3f/4f/2c41f800a0b560785c10fb316216ac058c105f9be50bdc6a285de88db625/aiohttp-3.13.3-cp310-cp310-win32.whl", hash = "sha256:37da61e244d1749798c151421602884db5270faf479cf0ef03af0ff68954c9dd", size = 434053, upload-time = "2026-01-03T17:29:40.074Z" }, + { url = "https://files.pythonhosted.org/packages/80/df/29cd63c7ecfdb65ccc12f7d808cac4fa2a19544660c06c61a4a48462de0c/aiohttp-3.13.3-cp310-cp310-win_amd64.whl", hash = "sha256:7e63f210bc1b57ef699035f2b4b6d9ce096b5914414a49b0997c839b2bd2223c", size = 456687, upload-time = "2026-01-03T17:29:41.819Z" }, + { url = "https://files.pythonhosted.org/packages/f1/4c/a164164834f03924d9a29dc3acd9e7ee58f95857e0b467f6d04298594ebb/aiohttp-3.13.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5b6073099fb654e0a068ae678b10feff95c5cae95bbfcbfa7af669d361a8aa6b", size = 746051, upload-time = "2026-01-03T17:29:43.287Z" }, + { url = "https://files.pythonhosted.org/packages/82/71/d5c31390d18d4f58115037c432b7e0348c60f6f53b727cad33172144a112/aiohttp-3.13.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cb93e166e6c28716c8c6aeb5f99dfb6d5ccf482d29fe9bf9a794110e6d0ab64", size = 499234, upload-time = "2026-01-03T17:29:44.822Z" }, + { url = "https://files.pythonhosted.org/packages/0e/c9/741f8ac91e14b1d2e7100690425a5b2b919a87a5075406582991fb7de920/aiohttp-3.13.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:28e027cf2f6b641693a09f631759b4d9ce9165099d2b5d92af9bd4e197690eea", size = 494979, upload-time = "2026-01-03T17:29:46.405Z" }, + { url = "https://files.pythonhosted.org/packages/75/b5/31d4d2e802dfd59f74ed47eba48869c1c21552c586d5e81a9d0d5c2ad640/aiohttp-3.13.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3b61b7169ababd7802f9568ed96142616a9118dd2be0d1866e920e77ec8fa92a", size = 1748297, upload-time = "2026-01-03T17:29:48.083Z" }, + { url = "https://files.pythonhosted.org/packages/1a/3e/eefad0ad42959f226bb79664826883f2687d602a9ae2941a18e0484a74d3/aiohttp-3.13.3-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:80dd4c21b0f6237676449c6baaa1039abae86b91636b6c91a7f8e61c87f89540", size = 1707172, upload-time = "2026-01-03T17:29:49.648Z" }, + { url = "https://files.pythonhosted.org/packages/c5/3a/54a64299fac2891c346cdcf2aa6803f994a2e4beeaf2e5a09dcc54acc842/aiohttp-3.13.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:65d2ccb7eabee90ce0503c17716fc77226be026dcc3e65cce859a30db715025b", size = 1805405, upload-time = "2026-01-03T17:29:51.244Z" }, + { url = "https://files.pythonhosted.org/packages/6c/70/ddc1b7169cf64075e864f64595a14b147a895a868394a48f6a8031979038/aiohttp-3.13.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5b179331a481cb5529fca8b432d8d3c7001cb217513c94cd72d668d1248688a3", size = 1899449, upload-time = "2026-01-03T17:29:53.938Z" }, + { url = "https://files.pythonhosted.org/packages/a1/7e/6815aab7d3a56610891c76ef79095677b8b5be6646aaf00f69b221765021/aiohttp-3.13.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d4c940f02f49483b18b079d1c27ab948721852b281f8b015c058100e9421dd1", size = 1748444, upload-time = "2026-01-03T17:29:55.484Z" }, + { url = "https://files.pythonhosted.org/packages/6b/f2/073b145c4100da5511f457dc0f7558e99b2987cf72600d42b559db856fbc/aiohttp-3.13.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f9444f105664c4ce47a2a7171a2418bce5b7bae45fb610f4e2c36045d85911d3", size = 1606038, upload-time = "2026-01-03T17:29:57.179Z" }, + { url = "https://files.pythonhosted.org/packages/0a/c1/778d011920cae03ae01424ec202c513dc69243cf2db303965615b81deeea/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:694976222c711d1d00ba131904beb60534f93966562f64440d0c9d41b8cdb440", size = 1724156, upload-time = "2026-01-03T17:29:58.914Z" }, + { url = "https://files.pythonhosted.org/packages/0e/cb/3419eabf4ec1e9ec6f242c32b689248365a1cf621891f6f0386632525494/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:f33ed1a2bf1997a36661874b017f5c4b760f41266341af36febaf271d179f6d7", size = 1722340, upload-time = "2026-01-03T17:30:01.962Z" }, + { url = "https://files.pythonhosted.org/packages/7a/e5/76cf77bdbc435bf233c1f114edad39ed4177ccbfab7c329482b179cff4f4/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e636b3c5f61da31a92bf0d91da83e58fdfa96f178ba682f11d24f31944cdd28c", size = 1783041, upload-time = "2026-01-03T17:30:03.609Z" }, + { url = "https://files.pythonhosted.org/packages/9d/d4/dd1ca234c794fd29c057ce8c0566b8ef7fd6a51069de5f06fa84b9a1971c/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:5d2d94f1f5fcbe40838ac51a6ab5704a6f9ea42e72ceda48de5e6b898521da51", size = 1596024, upload-time = "2026-01-03T17:30:05.132Z" }, + { url = "https://files.pythonhosted.org/packages/55/58/4345b5f26661a6180afa686c473620c30a66afdf120ed3dd545bbc809e85/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:2be0e9ccf23e8a94f6f0650ce06042cefc6ac703d0d7ab6c7a917289f2539ad4", size = 1804590, upload-time = "2026-01-03T17:30:07.135Z" }, + { url = "https://files.pythonhosted.org/packages/7b/06/05950619af6c2df7e0a431d889ba2813c9f0129cec76f663e547a5ad56f2/aiohttp-3.13.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9af5e68ee47d6534d36791bbe9b646d2a7c7deb6fc24d7943628edfbb3581f29", size = 1740355, upload-time = "2026-01-03T17:30:09.083Z" }, + { url = "https://files.pythonhosted.org/packages/3e/80/958f16de79ba0422d7c1e284b2abd0c84bc03394fbe631d0a39ffa10e1eb/aiohttp-3.13.3-cp311-cp311-win32.whl", hash = "sha256:a2212ad43c0833a873d0fb3c63fa1bacedd4cf6af2fee62bf4b739ceec3ab239", size = 433701, upload-time = "2026-01-03T17:30:10.869Z" }, + { url = "https://files.pythonhosted.org/packages/dc/f2/27cdf04c9851712d6c1b99df6821a6623c3c9e55956d4b1e318c337b5a48/aiohttp-3.13.3-cp311-cp311-win_amd64.whl", hash = "sha256:642f752c3eb117b105acbd87e2c143de710987e09860d674e068c4c2c441034f", size = 457678, upload-time = "2026-01-03T17:30:12.719Z" }, + { url = "https://files.pythonhosted.org/packages/a0/be/4fc11f202955a69e0db803a12a062b8379c970c7c84f4882b6da17337cc1/aiohttp-3.13.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:b903a4dfee7d347e2d87697d0713be59e0b87925be030c9178c5faa58ea58d5c", size = 739732, upload-time = "2026-01-03T17:30:14.23Z" }, + { url = "https://files.pythonhosted.org/packages/97/2c/621d5b851f94fa0bb7430d6089b3aa970a9d9b75196bc93bb624b0db237a/aiohttp-3.13.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a45530014d7a1e09f4a55f4f43097ba0fd155089372e105e4bff4ca76cb1b168", size = 494293, upload-time = "2026-01-03T17:30:15.96Z" }, + { url = "https://files.pythonhosted.org/packages/5d/43/4be01406b78e1be8320bb8316dc9c42dbab553d281c40364e0f862d5661c/aiohttp-3.13.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:27234ef6d85c914f9efeb77ff616dbf4ad2380be0cda40b4db086ffc7ddd1b7d", size = 493533, upload-time = "2026-01-03T17:30:17.431Z" }, + { url = "https://files.pythonhosted.org/packages/8d/a8/5a35dc56a06a2c90d4742cbf35294396907027f80eea696637945a106f25/aiohttp-3.13.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d32764c6c9aafb7fb55366a224756387cd50bfa720f32b88e0e6fa45b27dcf29", size = 1737839, upload-time = "2026-01-03T17:30:19.422Z" }, + { url = "https://files.pythonhosted.org/packages/bf/62/4b9eeb331da56530bf2e198a297e5303e1c1ebdceeb00fe9b568a65c5a0c/aiohttp-3.13.3-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b1a6102b4d3ebc07dad44fbf07b45bb600300f15b552ddf1851b5390202ea2e3", size = 1703932, upload-time = "2026-01-03T17:30:21.756Z" }, + { url = "https://files.pythonhosted.org/packages/7c/f6/af16887b5d419e6a367095994c0b1332d154f647e7dc2bd50e61876e8e3d/aiohttp-3.13.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c014c7ea7fb775dd015b2d3137378b7be0249a448a1612268b5a90c2d81de04d", size = 1771906, upload-time = "2026-01-03T17:30:23.932Z" }, + { url = "https://files.pythonhosted.org/packages/ce/83/397c634b1bcc24292fa1e0c7822800f9f6569e32934bdeef09dae7992dfb/aiohttp-3.13.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2b8d8ddba8f95ba17582226f80e2de99c7a7948e66490ef8d947e272a93e9463", size = 1871020, upload-time = "2026-01-03T17:30:26Z" }, + { url = "https://files.pythonhosted.org/packages/86/f6/a62cbbf13f0ac80a70f71b1672feba90fdb21fd7abd8dbf25c0105fb6fa3/aiohttp-3.13.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9ae8dd55c8e6c4257eae3a20fd2c8f41edaea5992ed67156642493b8daf3cecc", size = 1755181, upload-time = "2026-01-03T17:30:27.554Z" }, + { url = "https://files.pythonhosted.org/packages/0a/87/20a35ad487efdd3fba93d5843efdfaa62d2f1479eaafa7453398a44faf13/aiohttp-3.13.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:01ad2529d4b5035578f5081606a465f3b814c542882804e2e8cda61adf5c71bf", size = 1561794, upload-time = "2026-01-03T17:30:29.254Z" }, + { url = "https://files.pythonhosted.org/packages/de/95/8fd69a66682012f6716e1bc09ef8a1a2a91922c5725cb904689f112309c4/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bb4f7475e359992b580559e008c598091c45b5088f28614e855e42d39c2f1033", size = 1697900, upload-time = "2026-01-03T17:30:31.033Z" }, + { url = "https://files.pythonhosted.org/packages/e5/66/7b94b3b5ba70e955ff597672dad1691333080e37f50280178967aff68657/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:c19b90316ad3b24c69cd78d5c9b4f3aa4497643685901185b65166293d36a00f", size = 1728239, upload-time = "2026-01-03T17:30:32.703Z" }, + { url = "https://files.pythonhosted.org/packages/47/71/6f72f77f9f7d74719692ab65a2a0252584bf8d5f301e2ecb4c0da734530a/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:96d604498a7c782cb15a51c406acaea70d8c027ee6b90c569baa6e7b93073679", size = 1740527, upload-time = "2026-01-03T17:30:34.695Z" }, + { url = "https://files.pythonhosted.org/packages/fa/b4/75ec16cbbd5c01bdaf4a05b19e103e78d7ce1ef7c80867eb0ace42ff4488/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:084911a532763e9d3dd95adf78a78f4096cd5f58cdc18e6fdbc1b58417a45423", size = 1554489, upload-time = "2026-01-03T17:30:36.864Z" }, + { url = "https://files.pythonhosted.org/packages/52/8f/bc518c0eea29f8406dcf7ed1f96c9b48e3bc3995a96159b3fc11f9e08321/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:7a4a94eb787e606d0a09404b9c38c113d3b099d508021faa615d70a0131907ce", size = 1767852, upload-time = "2026-01-03T17:30:39.433Z" }, + { url = "https://files.pythonhosted.org/packages/9d/f2/a07a75173124f31f11ea6f863dc44e6f09afe2bca45dd4e64979490deab1/aiohttp-3.13.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:87797e645d9d8e222e04160ee32aa06bc5c163e8499f24db719e7852ec23093a", size = 1722379, upload-time = "2026-01-03T17:30:41.081Z" }, + { url = "https://files.pythonhosted.org/packages/3c/4a/1a3fee7c21350cac78e5c5cef711bac1b94feca07399f3d406972e2d8fcd/aiohttp-3.13.3-cp312-cp312-win32.whl", hash = "sha256:b04be762396457bef43f3597c991e192ee7da460a4953d7e647ee4b1c28e7046", size = 428253, upload-time = "2026-01-03T17:30:42.644Z" }, + { url = "https://files.pythonhosted.org/packages/d9/b7/76175c7cb4eb73d91ad63c34e29fc4f77c9386bba4a65b53ba8e05ee3c39/aiohttp-3.13.3-cp312-cp312-win_amd64.whl", hash = "sha256:e3531d63d3bdfa7e3ac5e9b27b2dd7ec9df3206a98e0b3445fa906f233264c57", size = 455407, upload-time = "2026-01-03T17:30:44.195Z" }, + { url = "https://files.pythonhosted.org/packages/97/8a/12ca489246ca1faaf5432844adbfce7ff2cc4997733e0af120869345643a/aiohttp-3.13.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:5dff64413671b0d3e7d5918ea490bdccb97a4ad29b3f311ed423200b2203e01c", size = 734190, upload-time = "2026-01-03T17:30:45.832Z" }, + { url = "https://files.pythonhosted.org/packages/32/08/de43984c74ed1fca5c014808963cc83cb00d7bb06af228f132d33862ca76/aiohttp-3.13.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:87b9aab6d6ed88235aa2970294f496ff1a1f9adcd724d800e9b952395a80ffd9", size = 491783, upload-time = "2026-01-03T17:30:47.466Z" }, + { url = "https://files.pythonhosted.org/packages/17/f8/8dd2cf6112a5a76f81f81a5130c57ca829d101ad583ce57f889179accdda/aiohttp-3.13.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:425c126c0dc43861e22cb1c14ba4c8e45d09516d0a3ae0a3f7494b79f5f233a3", size = 490704, upload-time = "2026-01-03T17:30:49.373Z" }, + { url = "https://files.pythonhosted.org/packages/6d/40/a46b03ca03936f832bc7eaa47cfbb1ad012ba1be4790122ee4f4f8cba074/aiohttp-3.13.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7f9120f7093c2a32d9647abcaf21e6ad275b4fbec5b55969f978b1a97c7c86bf", size = 1720652, upload-time = "2026-01-03T17:30:50.974Z" }, + { url = "https://files.pythonhosted.org/packages/f7/7e/917fe18e3607af92657e4285498f500dca797ff8c918bd7d90b05abf6c2a/aiohttp-3.13.3-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:697753042d57f4bf7122cab985bf15d0cef23c770864580f5af4f52023a56bd6", size = 1692014, upload-time = "2026-01-03T17:30:52.729Z" }, + { url = "https://files.pythonhosted.org/packages/71/b6/cefa4cbc00d315d68973b671cf105b21a609c12b82d52e5d0c9ae61d2a09/aiohttp-3.13.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6de499a1a44e7de70735d0b39f67c8f25eb3d91eb3103be99ca0fa882cdd987d", size = 1759777, upload-time = "2026-01-03T17:30:54.537Z" }, + { url = "https://files.pythonhosted.org/packages/fb/e3/e06ee07b45e59e6d81498b591fc589629be1553abb2a82ce33efe2a7b068/aiohttp-3.13.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:37239e9f9a7ea9ac5bf6b92b0260b01f8a22281996da609206a84df860bc1261", size = 1861276, upload-time = "2026-01-03T17:30:56.512Z" }, + { url = "https://files.pythonhosted.org/packages/7c/24/75d274228acf35ceeb2850b8ce04de9dd7355ff7a0b49d607ee60c29c518/aiohttp-3.13.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f76c1e3fe7d7c8afad7ed193f89a292e1999608170dcc9751a7462a87dfd5bc0", size = 1743131, upload-time = "2026-01-03T17:30:58.256Z" }, + { url = "https://files.pythonhosted.org/packages/04/98/3d21dde21889b17ca2eea54fdcff21b27b93f45b7bb94ca029c31ab59dc3/aiohttp-3.13.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fc290605db2a917f6e81b0e1e0796469871f5af381ce15c604a3c5c7e51cb730", size = 1556863, upload-time = "2026-01-03T17:31:00.445Z" }, + { url = "https://files.pythonhosted.org/packages/9e/84/da0c3ab1192eaf64782b03971ab4055b475d0db07b17eff925e8c93b3aa5/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4021b51936308aeea0367b8f006dc999ca02bc118a0cc78c303f50a2ff6afb91", size = 1682793, upload-time = "2026-01-03T17:31:03.024Z" }, + { url = "https://files.pythonhosted.org/packages/ff/0f/5802ada182f575afa02cbd0ec5180d7e13a402afb7c2c03a9aa5e5d49060/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:49a03727c1bba9a97d3e93c9f93ca03a57300f484b6e935463099841261195d3", size = 1716676, upload-time = "2026-01-03T17:31:04.842Z" }, + { url = "https://files.pythonhosted.org/packages/3f/8c/714d53bd8b5a4560667f7bbbb06b20c2382f9c7847d198370ec6526af39c/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3d9908a48eb7416dc1f4524e69f1d32e5d90e3981e4e37eb0aa1cd18f9cfa2a4", size = 1733217, upload-time = "2026-01-03T17:31:06.868Z" }, + { url = "https://files.pythonhosted.org/packages/7d/79/e2176f46d2e963facea939f5be2d26368ce543622be6f00a12844d3c991f/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:2712039939ec963c237286113c68dbad80a82a4281543f3abf766d9d73228998", size = 1552303, upload-time = "2026-01-03T17:31:08.958Z" }, + { url = "https://files.pythonhosted.org/packages/ab/6a/28ed4dea1759916090587d1fe57087b03e6c784a642b85ef48217b0277ae/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:7bfdc049127717581866fa4708791220970ce291c23e28ccf3922c700740fdc0", size = 1763673, upload-time = "2026-01-03T17:31:10.676Z" }, + { url = "https://files.pythonhosted.org/packages/e8/35/4a3daeb8b9fab49240d21c04d50732313295e4bd813a465d840236dd0ce1/aiohttp-3.13.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8057c98e0c8472d8846b9c79f56766bcc57e3e8ac7bfd510482332366c56c591", size = 1721120, upload-time = "2026-01-03T17:31:12.575Z" }, + { url = "https://files.pythonhosted.org/packages/bc/9f/d643bb3c5fb99547323e635e251c609fbbc660d983144cfebec529e09264/aiohttp-3.13.3-cp313-cp313-win32.whl", hash = "sha256:1449ceddcdbcf2e0446957863af03ebaaa03f94c090f945411b61269e2cb5daf", size = 427383, upload-time = "2026-01-03T17:31:14.382Z" }, + { url = "https://files.pythonhosted.org/packages/4e/f1/ab0395f8a79933577cdd996dd2f9aa6014af9535f65dddcf88204682fe62/aiohttp-3.13.3-cp313-cp313-win_amd64.whl", hash = "sha256:693781c45a4033d31d4187d2436f5ac701e7bbfe5df40d917736108c1cc7436e", size = 453899, upload-time = "2026-01-03T17:31:15.958Z" }, + { url = "https://files.pythonhosted.org/packages/99/36/5b6514a9f5d66f4e2597e40dea2e3db271e023eb7a5d22defe96ba560996/aiohttp-3.13.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:ea37047c6b367fd4bd632bff8077449b8fa034b69e812a18e0132a00fae6e808", size = 737238, upload-time = "2026-01-03T17:31:17.909Z" }, + { url = "https://files.pythonhosted.org/packages/f7/49/459327f0d5bcd8c6c9ca69e60fdeebc3622861e696490d8674a6d0cb90a6/aiohttp-3.13.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:6fc0e2337d1a4c3e6acafda6a78a39d4c14caea625124817420abceed36e2415", size = 492292, upload-time = "2026-01-03T17:31:19.919Z" }, + { url = "https://files.pythonhosted.org/packages/e8/0b/b97660c5fd05d3495b4eb27f2d0ef18dc1dc4eff7511a9bf371397ff0264/aiohttp-3.13.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c685f2d80bb67ca8c3837823ad76196b3694b0159d232206d1e461d3d434666f", size = 493021, upload-time = "2026-01-03T17:31:21.636Z" }, + { url = "https://files.pythonhosted.org/packages/54/d4/438efabdf74e30aeceb890c3290bbaa449780583b1270b00661126b8aae4/aiohttp-3.13.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:48e377758516d262bde50c2584fc6c578af272559c409eecbdd2bae1601184d6", size = 1717263, upload-time = "2026-01-03T17:31:23.296Z" }, + { url = "https://files.pythonhosted.org/packages/71/f2/7bddc7fd612367d1459c5bcf598a9e8f7092d6580d98de0e057eb42697ad/aiohttp-3.13.3-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:34749271508078b261c4abb1767d42b8d0c0cc9449c73a4df494777dc55f0687", size = 1669107, upload-time = "2026-01-03T17:31:25.334Z" }, + { url = "https://files.pythonhosted.org/packages/00/5a/1aeaecca40e22560f97610a329e0e5efef5e0b5afdf9f857f0d93839ab2e/aiohttp-3.13.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:82611aeec80eb144416956ec85b6ca45a64d76429c1ed46ae1b5f86c6e0c9a26", size = 1760196, upload-time = "2026-01-03T17:31:27.394Z" }, + { url = "https://files.pythonhosted.org/packages/f8/f8/0ff6992bea7bd560fc510ea1c815f87eedd745fe035589c71ce05612a19a/aiohttp-3.13.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2fff83cfc93f18f215896e3a190e8e5cb413ce01553901aca925176e7568963a", size = 1843591, upload-time = "2026-01-03T17:31:29.238Z" }, + { url = "https://files.pythonhosted.org/packages/e3/d1/e30e537a15f53485b61f5be525f2157da719819e8377298502aebac45536/aiohttp-3.13.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bbe7d4cecacb439e2e2a8a1a7b935c25b812af7a5fd26503a66dadf428e79ec1", size = 1720277, upload-time = "2026-01-03T17:31:31.053Z" }, + { url = "https://files.pythonhosted.org/packages/84/45/23f4c451d8192f553d38d838831ebbc156907ea6e05557f39563101b7717/aiohttp-3.13.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b928f30fe49574253644b1ca44b1b8adbd903aa0da4b9054a6c20fc7f4092a25", size = 1548575, upload-time = "2026-01-03T17:31:32.87Z" }, + { url = "https://files.pythonhosted.org/packages/6a/ed/0a42b127a43712eda7807e7892c083eadfaf8429ca8fb619662a530a3aab/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7b5e8fe4de30df199155baaf64f2fcd604f4c678ed20910db8e2c66dc4b11603", size = 1679455, upload-time = "2026-01-03T17:31:34.76Z" }, + { url = "https://files.pythonhosted.org/packages/2e/b5/c05f0c2b4b4fe2c9d55e73b6d3ed4fd6c9dc2684b1d81cbdf77e7fad9adb/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:8542f41a62bcc58fc7f11cf7c90e0ec324ce44950003feb70640fc2a9092c32a", size = 1687417, upload-time = "2026-01-03T17:31:36.699Z" }, + { url = "https://files.pythonhosted.org/packages/c9/6b/915bc5dad66aef602b9e459b5a973529304d4e89ca86999d9d75d80cbd0b/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:5e1d8c8b8f1d91cd08d8f4a3c2b067bfca6ec043d3ff36de0f3a715feeedf926", size = 1729968, upload-time = "2026-01-03T17:31:38.622Z" }, + { url = "https://files.pythonhosted.org/packages/11/3b/e84581290a9520024a08640b63d07673057aec5ca548177a82026187ba73/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:90455115e5da1c3c51ab619ac57f877da8fd6d73c05aacd125c5ae9819582aba", size = 1545690, upload-time = "2026-01-03T17:31:40.57Z" }, + { url = "https://files.pythonhosted.org/packages/f5/04/0c3655a566c43fd647c81b895dfe361b9f9ad6d58c19309d45cff52d6c3b/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:042e9e0bcb5fba81886c8b4fbb9a09d6b8a00245fd8d88e4d989c1f96c74164c", size = 1746390, upload-time = "2026-01-03T17:31:42.857Z" }, + { url = "https://files.pythonhosted.org/packages/1f/53/71165b26978f719c3419381514c9690bd5980e764a09440a10bb816ea4ab/aiohttp-3.13.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2eb752b102b12a76ca02dff751a801f028b4ffbbc478840b473597fc91a9ed43", size = 1702188, upload-time = "2026-01-03T17:31:44.984Z" }, + { url = "https://files.pythonhosted.org/packages/29/a7/cbe6c9e8e136314fa1980da388a59d2f35f35395948a08b6747baebb6aa6/aiohttp-3.13.3-cp314-cp314-win32.whl", hash = "sha256:b556c85915d8efaed322bf1bdae9486aa0f3f764195a0fb6ee962e5c71ef5ce1", size = 433126, upload-time = "2026-01-03T17:31:47.463Z" }, + { url = "https://files.pythonhosted.org/packages/de/56/982704adea7d3b16614fc5936014e9af85c0e34b58f9046655817f04306e/aiohttp-3.13.3-cp314-cp314-win_amd64.whl", hash = "sha256:9bf9f7a65e7aa20dd764151fb3d616c81088f91f8df39c3893a536e279b4b984", size = 459128, upload-time = "2026-01-03T17:31:49.2Z" }, + { url = "https://files.pythonhosted.org/packages/6c/2a/3c79b638a9c3d4658d345339d22070241ea341ed4e07b5ac60fb0f418003/aiohttp-3.13.3-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:05861afbbec40650d8a07ea324367cb93e9e8cc7762e04dd4405df99fa65159c", size = 769512, upload-time = "2026-01-03T17:31:51.134Z" }, + { url = "https://files.pythonhosted.org/packages/29/b9/3e5014d46c0ab0db8707e0ac2711ed28c4da0218c358a4e7c17bae0d8722/aiohttp-3.13.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2fc82186fadc4a8316768d61f3722c230e2c1dcab4200d52d2ebdf2482e47592", size = 506444, upload-time = "2026-01-03T17:31:52.85Z" }, + { url = "https://files.pythonhosted.org/packages/90/03/c1d4ef9a054e151cd7839cdc497f2638f00b93cbe8043983986630d7a80c/aiohttp-3.13.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0add0900ff220d1d5c5ebbf99ed88b0c1bbf87aa7e4262300ed1376a6b13414f", size = 510798, upload-time = "2026-01-03T17:31:54.91Z" }, + { url = "https://files.pythonhosted.org/packages/ea/76/8c1e5abbfe8e127c893fe7ead569148a4d5a799f7cf958d8c09f3eedf097/aiohttp-3.13.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:568f416a4072fbfae453dcf9a99194bbb8bdeab718e08ee13dfa2ba0e4bebf29", size = 1868835, upload-time = "2026-01-03T17:31:56.733Z" }, + { url = "https://files.pythonhosted.org/packages/8e/ac/984c5a6f74c363b01ff97adc96a3976d9c98940b8969a1881575b279ac5d/aiohttp-3.13.3-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:add1da70de90a2569c5e15249ff76a631ccacfe198375eead4aadf3b8dc849dc", size = 1720486, upload-time = "2026-01-03T17:31:58.65Z" }, + { url = "https://files.pythonhosted.org/packages/b2/9a/b7039c5f099c4eb632138728828b33428585031a1e658d693d41d07d89d1/aiohttp-3.13.3-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:10b47b7ba335d2e9b1239fa571131a87e2d8ec96b333e68b2a305e7a98b0bae2", size = 1847951, upload-time = "2026-01-03T17:32:00.989Z" }, + { url = "https://files.pythonhosted.org/packages/3c/02/3bec2b9a1ba3c19ff89a43a19324202b8eb187ca1e928d8bdac9bbdddebd/aiohttp-3.13.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3dd4dce1c718e38081c8f35f323209d4c1df7d4db4bab1b5c88a6b4d12b74587", size = 1941001, upload-time = "2026-01-03T17:32:03.122Z" }, + { url = "https://files.pythonhosted.org/packages/37/df/d879401cedeef27ac4717f6426c8c36c3091c6e9f08a9178cc87549c537f/aiohttp-3.13.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34bac00a67a812570d4a460447e1e9e06fae622946955f939051e7cc895cfab8", size = 1797246, upload-time = "2026-01-03T17:32:05.255Z" }, + { url = "https://files.pythonhosted.org/packages/8d/15/be122de1f67e6953add23335c8ece6d314ab67c8bebb3f181063010795a7/aiohttp-3.13.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a19884d2ee70b06d9204b2727a7b9f983d0c684c650254679e716b0b77920632", size = 1627131, upload-time = "2026-01-03T17:32:07.607Z" }, + { url = "https://files.pythonhosted.org/packages/12/12/70eedcac9134cfa3219ab7af31ea56bc877395b1ac30d65b1bc4b27d0438/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5f8ca7f2bb6ba8348a3614c7918cc4bb73268c5ac2a207576b7afea19d3d9f64", size = 1795196, upload-time = "2026-01-03T17:32:09.59Z" }, + { url = "https://files.pythonhosted.org/packages/32/11/b30e1b1cd1f3054af86ebe60df96989c6a414dd87e27ad16950eee420bea/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:b0d95340658b9d2f11d9697f59b3814a9d3bb4b7a7c20b131df4bcef464037c0", size = 1782841, upload-time = "2026-01-03T17:32:11.445Z" }, + { url = "https://files.pythonhosted.org/packages/88/0d/d98a9367b38912384a17e287850f5695c528cff0f14f791ce8ee2e4f7796/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:a1e53262fd202e4b40b70c3aff944a8155059beedc8a89bba9dc1f9ef06a1b56", size = 1795193, upload-time = "2026-01-03T17:32:13.705Z" }, + { url = "https://files.pythonhosted.org/packages/43/a5/a2dfd1f5ff5581632c7f6a30e1744deda03808974f94f6534241ef60c751/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:d60ac9663f44168038586cab2157e122e46bdef09e9368b37f2d82d354c23f72", size = 1621979, upload-time = "2026-01-03T17:32:15.965Z" }, + { url = "https://files.pythonhosted.org/packages/fa/f0/12973c382ae7c1cccbc4417e129c5bf54c374dfb85af70893646e1f0e749/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:90751b8eed69435bac9ff4e3d2f6b3af1f57e37ecb0fbeee59c0174c9e2d41df", size = 1822193, upload-time = "2026-01-03T17:32:18.219Z" }, + { url = "https://files.pythonhosted.org/packages/3c/5f/24155e30ba7f8c96918af1350eb0663e2430aad9e001c0489d89cd708ab1/aiohttp-3.13.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:fc353029f176fd2b3ec6cfc71be166aba1936fe5d73dd1992ce289ca6647a9aa", size = 1769801, upload-time = "2026-01-03T17:32:20.25Z" }, + { url = "https://files.pythonhosted.org/packages/eb/f8/7314031ff5c10e6ece114da79b338ec17eeff3a079e53151f7e9f43c4723/aiohttp-3.13.3-cp314-cp314t-win32.whl", hash = "sha256:2e41b18a58da1e474a057b3d35248d8320029f61d70a37629535b16a0c8f3767", size = 466523, upload-time = "2026-01-03T17:32:22.215Z" }, + { url = "https://files.pythonhosted.org/packages/b4/63/278a98c715ae467624eafe375542d8ba9b4383a016df8fdefe0ae28382a7/aiohttp-3.13.3-cp314-cp314t-win_amd64.whl", hash = "sha256:44531a36aa2264a1860089ffd4dce7baf875ee5a6079d5fb42e261c704ef7344", size = 499694, upload-time = "2026-01-03T17:32:24.546Z" }, ] [[package]] @@ -422,17 +424,16 @@ wheels = [ [[package]] name = "anyio" -version = "4.11.0" +version = "4.12.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, { name = "idna" }, - { name = "sniffio" }, { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c6/78/7d432127c41b50bccba979505f272c16cbcadcc33645d5fa3a738110ae75/anyio-4.11.0.tar.gz", hash = "sha256:82a8d0b81e318cc5ce71a5f1f8b5c4e63619620b63141ef8c995fa0db95a57c4", size = 219094, upload-time = "2025-09-23T09:19:12.58Z" } +sdist = { url = "https://files.pythonhosted.org/packages/96/f0/5eb65b2bb0d09ac6776f2eb54adee6abe8228ea05b20a5ad0e4945de8aac/anyio-4.12.1.tar.gz", hash = "sha256:41cfcc3a4c85d3f05c932da7c26d0201ac36f72abd4435ba90d0464a3ffed703", size = 228685, upload-time = "2026-01-06T11:45:21.246Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/15/b3/9b1a8074496371342ec1e796a96f99c82c945a339cd81a8e73de28b4cf9e/anyio-4.11.0-py3-none-any.whl", hash = "sha256:0287e96f4d26d4149305414d4e3bc32f0dcd0862365a4bddea19d7a1ec38c4fc", size = 109097, upload-time = "2025-09-23T09:19:10.601Z" }, + { url = "https://files.pythonhosted.org/packages/38/0e/27be9fdef66e72d64c0cdc3cc2823101b80585f8119b5c112c2e8f5f7dab/anyio-4.12.1-py3-none-any.whl", hash = "sha256:d405828884fc140aa80a3c667b8beed277f1dfedec42ba031bd6ac3db606ab6c", size = 113592, upload-time = "2026-01-06T11:45:19.497Z" }, ] [[package]] @@ -602,20 +603,20 @@ wheels = [ [[package]] name = "beautifulsoup4" -version = "4.14.2" +version = "4.14.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "soupsieve" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/77/e9/df2358efd7659577435e2177bfa69cba6c33216681af51a707193dec162a/beautifulsoup4-4.14.2.tar.gz", hash = "sha256:2a98ab9f944a11acee9cc848508ec28d9228abfd522ef0fad6a02a72e0ded69e", size = 625822, upload-time = "2025-09-29T10:05:42.613Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/b0/1c6a16426d389813b48d95e26898aff79abbde42ad353958ad95cc8c9b21/beautifulsoup4-4.14.3.tar.gz", hash = "sha256:6292b1c5186d356bba669ef9f7f051757099565ad9ada5dd630bd9de5fa7fb86", size = 627737, upload-time = "2025-11-30T15:08:26.084Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/94/fe/3aed5d0be4d404d12d36ab97e2f1791424d9ca39c2f754a6285d59a3b01d/beautifulsoup4-4.14.2-py3-none-any.whl", hash = "sha256:5ef6fa3a8cbece8488d66985560f97ed091e22bbc4e9c2338508a9d5de6d4515", size = 106392, upload-time = "2025-09-29T10:05:43.771Z" }, + { url = "https://files.pythonhosted.org/packages/1a/39/47f9197bdd44df24d67ac8893641e16f386c984a0619ef2ee4c51fbbc019/beautifulsoup4-4.14.3-py3-none-any.whl", hash = "sha256:0918bfe44902e6ad8d57732ba310582e98da931428d231a5ecb9e7c703a735bb", size = 107721, upload-time = "2025-11-30T15:08:24.087Z" }, ] [[package]] name = "bertopic" -version = "0.17.3" +version = "0.17.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "hdbscan" }, @@ -624,19 +625,20 @@ dependencies = [ { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "pandas" }, { name = "plotly" }, - { name = "scikit-learn" }, + { name = "scikit-learn", version = "1.7.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "scikit-learn", version = "1.8.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "sentence-transformers" }, { name = "tqdm" }, { name = "umap-learn" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2c/f6/731a912f10a4c74b442a1e62dea8a492455419c7f8088e8854768b3ca59c/bertopic-0.17.3.tar.gz", hash = "sha256:3f876d2eadf2037df79f059b734935d7ed2eb6f530bcc6c7a19e1a8f5d2cbaba", size = 119948, upload-time = "2025-07-08T10:53:53.115Z" } +sdist = { url = "https://files.pythonhosted.org/packages/72/ab/358923a516216964e150af7176dd3ea13737ddd25503517104f5562e301f/bertopic-0.17.4.tar.gz", hash = "sha256:f92aa560cdf2bcbf9e22c8ee83dd3bfb225b8dc29381dec7327cc0b21bd852ad", size = 121632, upload-time = "2025-12-03T14:19:34.402Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/98/05/2d6b305391efff89c2b4cf19cf847f971ca163eb5c149d0d2ffac0a9c7ed/bertopic-0.17.3-py3-none-any.whl", hash = "sha256:1ac8ccc647dd8f0b424ebcc05d95741394c2a3762d314d28ecd7bf0fdb3930a3", size = 153010, upload-time = "2025-07-08T10:53:50.783Z" }, + { url = "https://files.pythonhosted.org/packages/81/d2/2eb2fec18b3d2cf487d5d1d9b484c6483c7f0f1a3c6f64e2bb9cb7972a3f/bertopic-0.17.4-py3-none-any.whl", hash = "sha256:0dbe7ee22c18fb76efe7f4fbf3a14b51c1e67d9fccc569e3f81e75b8f32646d1", size = 154669, upload-time = "2025-12-03T14:19:32.884Z" }, ] [[package]] name = "black" -version = "25.11.0" +version = "25.12.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, @@ -648,29 +650,34 @@ dependencies = [ { name = "tomli", marker = "python_full_version < '3.11'" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8c/ad/33adf4708633d047950ff2dfdea2e215d84ac50ef95aff14a614e4b6e9b2/black-25.11.0.tar.gz", hash = "sha256:9a323ac32f5dc75ce7470501b887250be5005a01602e931a15e45593f70f6e08", size = 655669, upload-time = "2025-11-10T01:53:50.558Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b3/d2/6caccbc96f9311e8ec3378c296d4f4809429c43a6cd2394e3c390e86816d/black-25.11.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ec311e22458eec32a807f029b2646f661e6859c3f61bc6d9ffb67958779f392e", size = 1743501, upload-time = "2025-11-10T01:59:06.202Z" }, - { url = "https://files.pythonhosted.org/packages/69/35/b986d57828b3f3dccbf922e2864223197ba32e74c5004264b1c62bc9f04d/black-25.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1032639c90208c15711334d681de2e24821af0575573db2810b0763bcd62e0f0", size = 1597308, upload-time = "2025-11-10T01:57:58.633Z" }, - { url = "https://files.pythonhosted.org/packages/39/8e/8b58ef4b37073f52b64a7b2dd8c9a96c84f45d6f47d878d0aa557e9a2d35/black-25.11.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0c0f7c461df55cf32929b002335883946a4893d759f2df343389c4396f3b6b37", size = 1656194, upload-time = "2025-11-10T01:57:10.909Z" }, - { url = "https://files.pythonhosted.org/packages/8d/30/9c2267a7955ecc545306534ab88923769a979ac20a27cf618d370091e5dd/black-25.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:f9786c24d8e9bd5f20dc7a7f0cdd742644656987f6ea6947629306f937726c03", size = 1347996, upload-time = "2025-11-10T01:57:22.391Z" }, - { url = "https://files.pythonhosted.org/packages/c4/62/d304786b75ab0c530b833a89ce7d997924579fb7484ecd9266394903e394/black-25.11.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:895571922a35434a9d8ca67ef926da6bc9ad464522a5fe0db99b394ef1c0675a", size = 1727891, upload-time = "2025-11-10T02:01:40.507Z" }, - { url = "https://files.pythonhosted.org/packages/82/5d/ffe8a006aa522c9e3f430e7b93568a7b2163f4b3f16e8feb6d8c3552761a/black-25.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cb4f4b65d717062191bdec8e4a442539a8ea065e6af1c4f4d36f0cdb5f71e170", size = 1581875, upload-time = "2025-11-10T01:57:51.192Z" }, - { url = "https://files.pythonhosted.org/packages/cb/c8/7c8bda3108d0bb57387ac41b4abb5c08782b26da9f9c4421ef6694dac01a/black-25.11.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d81a44cbc7e4f73a9d6ae449ec2317ad81512d1e7dce7d57f6333fd6259737bc", size = 1642716, upload-time = "2025-11-10T01:56:51.589Z" }, - { url = "https://files.pythonhosted.org/packages/34/b9/f17dea34eecb7cc2609a89627d480fb6caea7b86190708eaa7eb15ed25e7/black-25.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:7eebd4744dfe92ef1ee349dc532defbf012a88b087bb7ddd688ff59a447b080e", size = 1352904, upload-time = "2025-11-10T01:59:26.252Z" }, - { url = "https://files.pythonhosted.org/packages/7f/12/5c35e600b515f35ffd737da7febdb2ab66bb8c24d88560d5e3ef3d28c3fd/black-25.11.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:80e7486ad3535636657aa180ad32a7d67d7c273a80e12f1b4bfa0823d54e8fac", size = 1772831, upload-time = "2025-11-10T02:03:47Z" }, - { url = "https://files.pythonhosted.org/packages/1a/75/b3896bec5a2bb9ed2f989a970ea40e7062f8936f95425879bbe162746fe5/black-25.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6cced12b747c4c76bc09b4db057c319d8545307266f41aaee665540bc0e04e96", size = 1608520, upload-time = "2025-11-10T01:58:46.895Z" }, - { url = "https://files.pythonhosted.org/packages/f3/b5/2bfc18330eddbcfb5aab8d2d720663cd410f51b2ed01375f5be3751595b0/black-25.11.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6cb2d54a39e0ef021d6c5eef442e10fd71fcb491be6413d083a320ee768329dd", size = 1682719, upload-time = "2025-11-10T01:56:55.24Z" }, - { url = "https://files.pythonhosted.org/packages/96/fb/f7dc2793a22cdf74a72114b5ed77fe3349a2e09ef34565857a2f917abdf2/black-25.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:ae263af2f496940438e5be1a0c1020e13b09154f3af4df0835ea7f9fe7bfa409", size = 1362684, upload-time = "2025-11-10T01:57:07.639Z" }, - { url = "https://files.pythonhosted.org/packages/ad/47/3378d6a2ddefe18553d1115e36aea98f4a90de53b6a3017ed861ba1bd3bc/black-25.11.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0a1d40348b6621cc20d3d7530a5b8d67e9714906dfd7346338249ad9c6cedf2b", size = 1772446, upload-time = "2025-11-10T02:02:16.181Z" }, - { url = "https://files.pythonhosted.org/packages/ba/4b/0f00bfb3d1f7e05e25bfc7c363f54dc523bb6ba502f98f4ad3acf01ab2e4/black-25.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:51c65d7d60bb25429ea2bf0731c32b2a2442eb4bd3b2afcb47830f0b13e58bfd", size = 1607983, upload-time = "2025-11-10T02:02:52.502Z" }, - { url = "https://files.pythonhosted.org/packages/99/fe/49b0768f8c9ae57eb74cc10a1f87b4c70453551d8ad498959721cc345cb7/black-25.11.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:936c4dd07669269f40b497440159a221ee435e3fddcf668e0c05244a9be71993", size = 1682481, upload-time = "2025-11-10T01:57:12.35Z" }, - { url = "https://files.pythonhosted.org/packages/55/17/7e10ff1267bfa950cc16f0a411d457cdff79678fbb77a6c73b73a5317904/black-25.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:f42c0ea7f59994490f4dccd64e6b2dd49ac57c7c84f38b8faab50f8759db245c", size = 1363869, upload-time = "2025-11-10T01:58:24.608Z" }, - { url = "https://files.pythonhosted.org/packages/67/c0/cc865ce594d09e4cd4dfca5e11994ebb51604328489f3ca3ae7bb38a7db5/black-25.11.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:35690a383f22dd3e468c85dc4b915217f87667ad9cce781d7b42678ce63c4170", size = 1771358, upload-time = "2025-11-10T02:03:33.331Z" }, - { url = "https://files.pythonhosted.org/packages/37/77/4297114d9e2fd2fc8ab0ab87192643cd49409eb059e2940391e7d2340e57/black-25.11.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:dae49ef7369c6caa1a1833fd5efb7c3024bb7e4499bf64833f65ad27791b1545", size = 1612902, upload-time = "2025-11-10T01:59:33.382Z" }, - { url = "https://files.pythonhosted.org/packages/de/63/d45ef97ada84111e330b2b2d45e1dd163e90bd116f00ac55927fb6bf8adb/black-25.11.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5bd4a22a0b37401c8e492e994bce79e614f91b14d9ea911f44f36e262195fdda", size = 1680571, upload-time = "2025-11-10T01:57:04.239Z" }, - { url = "https://files.pythonhosted.org/packages/ff/4b/5604710d61cdff613584028b4cb4607e56e148801ed9b38ee7970799dab6/black-25.11.0-cp314-cp314-win_amd64.whl", hash = "sha256:aa211411e94fdf86519996b7f5f05e71ba34835d8f0c0f03c00a26271da02664", size = 1382599, upload-time = "2025-11-10T01:57:57.427Z" }, - { url = "https://files.pythonhosted.org/packages/00/5d/aed32636ed30a6e7f9efd6ad14e2a0b0d687ae7c8c7ec4e4a557174b895c/black-25.11.0-py3-none-any.whl", hash = "sha256:e3f562da087791e96cefcd9dda058380a442ab322a02e222add53736451f604b", size = 204918, upload-time = "2025-11-10T01:53:48.917Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/c4/d9/07b458a3f1c525ac392b5edc6b191ff140b596f9d77092429417a54e249d/black-25.12.0.tar.gz", hash = "sha256:8d3dd9cea14bff7ddc0eb243c811cdb1a011ebb4800a5f0335a01a68654796a7", size = 659264, upload-time = "2025-12-08T01:40:52.501Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/37/d5/8d3145999d380e5d09bb00b0f7024bf0a8ccb5c07b5648e9295f02ec1d98/black-25.12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f85ba1ad15d446756b4ab5f3044731bf68b777f8f9ac9cdabd2425b97cd9c4e8", size = 1895720, upload-time = "2025-12-08T01:46:58.197Z" }, + { url = "https://files.pythonhosted.org/packages/06/97/7acc85c4add41098f4f076b21e3e4e383ad6ed0a3da26b2c89627241fc11/black-25.12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:546eecfe9a3a6b46f9d69d8a642585a6eaf348bcbbc4d87a19635570e02d9f4a", size = 1727193, upload-time = "2025-12-08T01:52:26.674Z" }, + { url = "https://files.pythonhosted.org/packages/24/f0/fdf0eb8ba907ddeb62255227d29d349e8256ef03558fbcadfbc26ecfe3b2/black-25.12.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:17dcc893da8d73d8f74a596f64b7c98ef5239c2cd2b053c0f25912c4494bf9ea", size = 1774506, upload-time = "2025-12-08T01:46:25.721Z" }, + { url = "https://files.pythonhosted.org/packages/e4/f5/9203a78efe00d13336786b133c6180a9303d46908a9aa72d1104ca214222/black-25.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:09524b0e6af8ba7a3ffabdfc7a9922fb9adef60fed008c7cd2fc01f3048e6e6f", size = 1416085, upload-time = "2025-12-08T01:46:06.073Z" }, + { url = "https://files.pythonhosted.org/packages/ba/cc/7a6090e6b081c3316282c05c546e76affdce7bf7a3b7d2c3a2a69438bd01/black-25.12.0-cp310-cp310-win_arm64.whl", hash = "sha256:b162653ed89eb942758efeb29d5e333ca5bb90e5130216f8369857db5955a7da", size = 1226038, upload-time = "2025-12-08T01:45:29.388Z" }, + { url = "https://files.pythonhosted.org/packages/60/ad/7ac0d0e1e0612788dbc48e62aef8a8e8feffac7eb3d787db4e43b8462fa8/black-25.12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d0cfa263e85caea2cff57d8f917f9f51adae8e20b610e2b23de35b5b11ce691a", size = 1877003, upload-time = "2025-12-08T01:43:29.967Z" }, + { url = "https://files.pythonhosted.org/packages/e8/dd/a237e9f565f3617a88b49284b59cbca2a4f56ebe68676c1aad0ce36a54a7/black-25.12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1a2f578ae20c19c50a382286ba78bfbeafdf788579b053d8e4980afb079ab9be", size = 1712639, upload-time = "2025-12-08T01:52:46.756Z" }, + { url = "https://files.pythonhosted.org/packages/12/80/e187079df1ea4c12a0c63282ddd8b81d5107db6d642f7d7b75a6bcd6fc21/black-25.12.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d3e1b65634b0e471d07ff86ec338819e2ef860689859ef4501ab7ac290431f9b", size = 1758143, upload-time = "2025-12-08T01:45:29.137Z" }, + { url = "https://files.pythonhosted.org/packages/93/b5/3096ccee4f29dc2c3aac57274326c4d2d929a77e629f695f544e159bfae4/black-25.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:a3fa71e3b8dd9f7c6ac4d818345237dfb4175ed3bf37cd5a581dbc4c034f1ec5", size = 1420698, upload-time = "2025-12-08T01:45:53.379Z" }, + { url = "https://files.pythonhosted.org/packages/7e/39/f81c0ffbc25ffbe61c7d0385bf277e62ffc3e52f5ee668d7369d9854fadf/black-25.12.0-cp311-cp311-win_arm64.whl", hash = "sha256:51e267458f7e650afed8445dc7edb3187143003d52a1b710c7321aef22aa9655", size = 1229317, upload-time = "2025-12-08T01:46:35.606Z" }, + { url = "https://files.pythonhosted.org/packages/d1/bd/26083f805115db17fda9877b3c7321d08c647df39d0df4c4ca8f8450593e/black-25.12.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:31f96b7c98c1ddaeb07dc0f56c652e25bdedaac76d5b68a059d998b57c55594a", size = 1924178, upload-time = "2025-12-08T01:49:51.048Z" }, + { url = "https://files.pythonhosted.org/packages/89/6b/ea00d6651561e2bdd9231c4177f4f2ae19cc13a0b0574f47602a7519b6ca/black-25.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:05dd459a19e218078a1f98178c13f861fe6a9a5f88fc969ca4d9b49eb1809783", size = 1742643, upload-time = "2025-12-08T01:49:59.09Z" }, + { url = "https://files.pythonhosted.org/packages/6d/f3/360fa4182e36e9875fabcf3a9717db9d27a8d11870f21cff97725c54f35b/black-25.12.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c1f68c5eff61f226934be6b5b80296cf6939e5d2f0c2f7d543ea08b204bfaf59", size = 1800158, upload-time = "2025-12-08T01:44:27.301Z" }, + { url = "https://files.pythonhosted.org/packages/f8/08/2c64830cb6616278067e040acca21d4f79727b23077633953081c9445d61/black-25.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:274f940c147ddab4442d316b27f9e332ca586d39c85ecf59ebdea82cc9ee8892", size = 1426197, upload-time = "2025-12-08T01:45:51.198Z" }, + { url = "https://files.pythonhosted.org/packages/d4/60/a93f55fd9b9816b7432cf6842f0e3000fdd5b7869492a04b9011a133ee37/black-25.12.0-cp312-cp312-win_arm64.whl", hash = "sha256:169506ba91ef21e2e0591563deda7f00030cb466e747c4b09cb0a9dae5db2f43", size = 1237266, upload-time = "2025-12-08T01:45:10.556Z" }, + { url = "https://files.pythonhosted.org/packages/c8/52/c551e36bc95495d2aa1a37d50566267aa47608c81a53f91daa809e03293f/black-25.12.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a05ddeb656534c3e27a05a29196c962877c83fa5503db89e68857d1161ad08a5", size = 1923809, upload-time = "2025-12-08T01:46:55.126Z" }, + { url = "https://files.pythonhosted.org/packages/a0/f7/aac9b014140ee56d247e707af8db0aae2e9efc28d4a8aba92d0abd7ae9d1/black-25.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9ec77439ef3e34896995503865a85732c94396edcc739f302c5673a2315e1e7f", size = 1742384, upload-time = "2025-12-08T01:49:37.022Z" }, + { url = "https://files.pythonhosted.org/packages/74/98/38aaa018b2ab06a863974c12b14a6266badc192b20603a81b738c47e902e/black-25.12.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e509c858adf63aa61d908061b52e580c40eae0dfa72415fa47ac01b12e29baf", size = 1798761, upload-time = "2025-12-08T01:46:05.386Z" }, + { url = "https://files.pythonhosted.org/packages/16/3a/a8ac542125f61574a3f015b521ca83b47321ed19bb63fe6d7560f348bfe1/black-25.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:252678f07f5bac4ff0d0e9b261fbb029fa530cfa206d0a636a34ab445ef8ca9d", size = 1429180, upload-time = "2025-12-08T01:45:34.903Z" }, + { url = "https://files.pythonhosted.org/packages/e6/2d/bdc466a3db9145e946762d52cd55b1385509d9f9004fec1c97bdc8debbfb/black-25.12.0-cp313-cp313-win_arm64.whl", hash = "sha256:bc5b1c09fe3c931ddd20ee548511c64ebf964ada7e6f0763d443947fd1c603ce", size = 1239350, upload-time = "2025-12-08T01:46:09.458Z" }, + { url = "https://files.pythonhosted.org/packages/35/46/1d8f2542210c502e2ae1060b2e09e47af6a5e5963cb78e22ec1a11170b28/black-25.12.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:0a0953b134f9335c2434864a643c842c44fba562155c738a2a37a4d61f00cad5", size = 1917015, upload-time = "2025-12-08T01:53:27.987Z" }, + { url = "https://files.pythonhosted.org/packages/41/37/68accadf977672beb8e2c64e080f568c74159c1aaa6414b4cd2aef2d7906/black-25.12.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2355bbb6c3b76062870942d8cc450d4f8ac71f9c93c40122762c8784df49543f", size = 1741830, upload-time = "2025-12-08T01:54:36.861Z" }, + { url = "https://files.pythonhosted.org/packages/ac/76/03608a9d8f0faad47a3af3a3c8c53af3367f6c0dd2d23a84710456c7ac56/black-25.12.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9678bd991cc793e81d19aeeae57966ee02909877cb65838ccffef24c3ebac08f", size = 1791450, upload-time = "2025-12-08T01:44:52.581Z" }, + { url = "https://files.pythonhosted.org/packages/06/99/b2a4bd7dfaea7964974f947e1c76d6886d65fe5d24f687df2d85406b2609/black-25.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:97596189949a8aad13ad12fcbb4ae89330039b96ad6742e6f6b45e75ad5cfd83", size = 1452042, upload-time = "2025-12-08T01:46:13.188Z" }, + { url = "https://files.pythonhosted.org/packages/b2/7c/d9825de75ae5dd7795d007681b752275ea85a1c5d83269b4b9c754c2aaab/black-25.12.0-cp314-cp314-win_arm64.whl", hash = "sha256:778285d9ea197f34704e3791ea9404cd6d07595745907dd2ce3da7a13627b29b", size = 1267446, upload-time = "2025-12-08T01:46:14.497Z" }, + { url = "https://files.pythonhosted.org/packages/68/11/21331aed19145a952ad28fca2756a1433ee9308079bd03bd898e903a2e53/black-25.12.0-py3-none-any.whl", hash = "sha256:48ceb36c16dbc84062740049eef990bb2ce07598272e673c17d1a7720c71c828", size = 206191, upload-time = "2025-12-08T01:40:50.963Z" }, ] [[package]] @@ -701,7 +708,7 @@ wheels = [ [[package]] name = "build" -version = "1.3.0" +version = "1.4.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "(os_name == 'nt' and platform_machine != 'aarch64' and sys_platform == 'linux') or (os_name == 'nt' and sys_platform != 'darwin' and sys_platform != 'linux')" }, @@ -710,27 +717,27 @@ dependencies = [ { name = "pyproject-hooks" }, { name = "tomli", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/25/1c/23e33405a7c9eac261dff640926b8b5adaed6a6eb3e1767d441ed611d0c0/build-1.3.0.tar.gz", hash = "sha256:698edd0ea270bde950f53aed21f3a0135672206f3911e0176261a31e0e07b397", size = 48544, upload-time = "2025-08-01T21:27:09.268Z" } +sdist = { url = "https://files.pythonhosted.org/packages/42/18/94eaffda7b329535d91f00fe605ab1f1e5cd68b2074d03f255c7d250687d/build-1.4.0.tar.gz", hash = "sha256:f1b91b925aa322be454f8330c6fb48b465da993d1e7e7e6fa35027ec49f3c936", size = 50054, upload-time = "2026-01-08T16:41:47.696Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/8c/2b30c12155ad8de0cf641d76a8b396a16d2c36bc6d50b621a62b7c4567c1/build-1.3.0-py3-none-any.whl", hash = "sha256:7145f0b5061ba90a1500d60bd1b13ca0a8a4cebdd0cc16ed8adf1c0e739f43b4", size = 23382, upload-time = "2025-08-01T21:27:07.844Z" }, + { url = "https://files.pythonhosted.org/packages/c5/0d/84a4380f930db0010168e0aa7b7a8fed9ba1835a8fbb1472bc6d0201d529/build-1.4.0-py3-none-any.whl", hash = "sha256:6a07c1b8eb6f2b311b96fcbdbce5dab5fe637ffda0fd83c9cac622e927501596", size = 24141, upload-time = "2026-01-08T16:41:46.453Z" }, ] [[package]] name = "cachetools" -version = "6.2.2" +version = "6.2.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fb/44/ca1675be2a83aeee1886ab745b28cda92093066590233cc501890eb8417a/cachetools-6.2.2.tar.gz", hash = "sha256:8e6d266b25e539df852251cfd6f990b4bc3a141db73b939058d809ebd2590fc6", size = 31571, upload-time = "2025-11-13T17:42:51.465Z" } +sdist = { url = "https://files.pythonhosted.org/packages/bc/1d/ede8680603f6016887c062a2cf4fc8fdba905866a3ab8831aa8aa651320c/cachetools-6.2.4.tar.gz", hash = "sha256:82c5c05585e70b6ba2d3ae09ea60b79548872185d2f24ae1f2709d37299fd607", size = 31731, upload-time = "2025-12-15T18:24:53.744Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e6/46/eb6eca305c77a4489affe1c5d8f4cae82f285d9addd8de4ec084a7184221/cachetools-6.2.2-py3-none-any.whl", hash = "sha256:6c09c98183bf58560c97b2abfcedcbaf6a896a490f534b031b661d3723b45ace", size = 11503, upload-time = "2025-11-13T17:42:50.232Z" }, + { url = "https://files.pythonhosted.org/packages/2c/fc/1d7b80d0eb7b714984ce40efc78859c022cd930e402f599d8ca9e39c78a4/cachetools-6.2.4-py3-none-any.whl", hash = "sha256:69a7a52634fed8b8bf6e24a050fb60bff1c9bd8f6d24572b99c32d4e71e62a51", size = 11551, upload-time = "2025-12-15T18:24:52.332Z" }, ] [[package]] name = "certifi" -version = "2025.11.12" +version = "2026.1.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/8c/58f469717fa48465e4a50c014a0400602d3c437d7c0c468e17ada824da3a/certifi-2025.11.12.tar.gz", hash = "sha256:d8ab5478f2ecd78af242878415affce761ca6bc54a22a27e026d7c25357c3316", size = 160538, upload-time = "2025-11-12T02:54:51.517Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e0/2d/a891ca51311197f6ad14a7ef42e2399f36cf2f9bd44752b3dc4eab60fdc5/certifi-2026.1.4.tar.gz", hash = "sha256:ac726dd470482006e014ad384921ed6438c457018f4b3d204aea4281258b2120", size = 154268, upload-time = "2026-01-04T02:42:41.825Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/70/7d/9bc192684cea499815ff478dfcdc13835ddf401365057044fb721ec6bddb/certifi-2025.11.12-py3-none-any.whl", hash = "sha256:97de8790030bbd5c2d96b7ec782fc2f7820ef8dba6db909ccf95449f2d062d4b", size = 159438, upload-time = "2025-11-12T02:54:49.735Z" }, + { url = "https://files.pythonhosted.org/packages/e6/ad/3cc14f097111b4de0040c83a525973216457bbeeb63739ef1ed275c1c021/certifi-2026.1.4-py3-none-any.whl", hash = "sha256:9943707519e4add1115f44c2bc244f782c0249876bf51b6599fee1ffbedd685c", size = 152900, upload-time = "2026-01-04T02:42:40.15Z" }, ] [[package]] @@ -915,7 +922,7 @@ wheels = [ [[package]] name = "chromadb" -version = "1.3.5" +version = "1.4.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "bcrypt" }, @@ -947,13 +954,13 @@ dependencies = [ { name = "typing-extensions" }, { name = "uvicorn", extra = ["standard"] }, ] -sdist = { url = "https://files.pythonhosted.org/packages/24/b9/78cc62bcfe34cebc61026fa76829bc303bb8d5fcaee5576fae3c64543452/chromadb-1.3.5.tar.gz", hash = "sha256:ca45b9423b083f5dd49373f89af23ff9d94fe6884233cd826e8b1a50abe74166", size = 1921436, upload-time = "2025-11-18T10:08:31.635Z" } +sdist = { url = "https://files.pythonhosted.org/packages/43/54/2bc73eac5d8fd7ffc41f8e6e4dd13ad0fd916f8973f85b1411011ba1e05b/chromadb-1.4.0.tar.gz", hash = "sha256:5b4e6d1ede4faaaf12ec772c3c603ea19f39b255ef0795855b40dd79f00a4183", size = 2001752, upload-time = "2025-12-24T02:58:18.326Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6f/3b/1808d649037164cdf860b248a3468ab4c07b414d08d61ef39c789406cf0b/chromadb-1.3.5-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:d4e5b3bd1bf879a4fffa5644ccb175b98ee18799d8447140f3f893d350068d1b", size = 20613684, upload-time = "2025-11-18T10:08:29.376Z" }, - { url = "https://files.pythonhosted.org/packages/ba/c7/7d251fdc15969bc00dcd70ca973ad6f1e2f1f25e8e097dbec75ffcc7a210/chromadb-1.3.5-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:59d35ea15a5765da751e2bc0804054b7ade81f564119c88a1b3310c18c696d92", size = 19897327, upload-time = "2025-11-18T10:08:26.447Z" }, - { url = "https://files.pythonhosted.org/packages/93/11/6af17eb3eebaf9afd410b03d5bb1a8c1054bd0377502022005c22d0d949f/chromadb-1.3.5-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38e5fad35a4e686668efa956d576e4daea511a42225dbe9c975d9b2889608f53", size = 20476163, upload-time = "2025-11-18T10:08:20.144Z" }, - { url = "https://files.pythonhosted.org/packages/84/66/3ac136143658714adab6a8717d22a44d11e46a01bb7896add9653af39f4f/chromadb-1.3.5-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86d33e9b1153bc1eda48f98fc9c2d54d7dac2a460198a58aec715206d234c7f1", size = 21389003, upload-time = "2025-11-18T10:08:23.293Z" }, - { url = "https://files.pythonhosted.org/packages/e2/2b/d99e8559c6cf6d615e768367cd8c45b168cfa00cb3ac4c6032ddbcfc396d/chromadb-1.3.5-cp39-abi3-win_amd64.whl", hash = "sha256:5ecf6345025abf84095fd9a65bb9855f2019c63e0f42373720aecabadce08cf0", size = 21685628, upload-time = "2025-11-18T10:08:33.573Z" }, + { url = "https://files.pythonhosted.org/packages/a6/d5/7ce34021304bdf1a5eefaaf434d2be078828dd71aa3871d89eeeecedfb19/chromadb-1.4.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:ab4ad96c21d0038f6d8d84b9cac2010ce1f448926e9a2ee35251552f2e85da07", size = 20882057, upload-time = "2025-12-24T02:58:15.916Z" }, + { url = "https://files.pythonhosted.org/packages/76/6d/9fbf794f3672bfaf227b0e8642b1af6e1ef7d5f5b20f7505ac684ff0b155/chromadb-1.4.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:4d3c8abd762f092f73482e3eb1dae560a8a1c2674575d11eaac0dddf35e9cc6d", size = 20148106, upload-time = "2025-12-24T02:58:12.915Z" }, + { url = "https://files.pythonhosted.org/packages/1f/cc/d33e24258027c6a14a49a5abf94c75dd6f82e5ab5ed44fe622c0de303420/chromadb-1.4.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29fe47563c460a6cadbdc481b503c520ab4e424730c97d6a85d488a13009b6ce", size = 20759866, upload-time = "2025-12-24T02:58:06.987Z" }, + { url = "https://files.pythonhosted.org/packages/96/da/048ea86c7cb04a873aaab912be62d90b403a8b15a98ae7781ea777371373/chromadb-1.4.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1942e1ee074c7d1e421ea04391a1fccfd18a4b3b94a8e61e853d88dc6924abfa", size = 21666411, upload-time = "2025-12-24T02:58:10.044Z" }, + { url = "https://files.pythonhosted.org/packages/a0/49/933091cf12ee4ce4527a8e99b778f768f63df67e7d3ed9c20eecc0385169/chromadb-1.4.0-cp39-abi3-win_amd64.whl", hash = "sha256:2ec0485e715357a41078c20ebed65d5d5b941bf2fff418c6f1c64176dc36f837", size = 21930010, upload-time = "2025-12-24T02:58:20.138Z" }, ] [[package]] @@ -1024,101 +1031,101 @@ wheels = [ [[package]] name = "coverage" -version = "7.12.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/89/26/4a96807b193b011588099c3b5c89fbb05294e5b90e71018e065465f34eb6/coverage-7.12.0.tar.gz", hash = "sha256:fc11e0a4e372cb5f282f16ef90d4a585034050ccda536451901abfb19a57f40c", size = 819341, upload-time = "2025-11-18T13:34:20.766Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/26/4a/0dc3de1c172d35abe512332cfdcc43211b6ebce629e4cc42e6cd25ed8f4d/coverage-7.12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:32b75c2ba3f324ee37af3ccee5b30458038c50b349ad9b88cee85096132a575b", size = 217409, upload-time = "2025-11-18T13:31:53.122Z" }, - { url = "https://files.pythonhosted.org/packages/01/c3/086198b98db0109ad4f84241e8e9ea7e5fb2db8c8ffb787162d40c26cc76/coverage-7.12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cb2a1b6ab9fe833714a483a915de350abc624a37149649297624c8d57add089c", size = 217927, upload-time = "2025-11-18T13:31:54.458Z" }, - { url = "https://files.pythonhosted.org/packages/5d/5f/34614dbf5ce0420828fc6c6f915126a0fcb01e25d16cf141bf5361e6aea6/coverage-7.12.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5734b5d913c3755e72f70bf6cc37a0518d4f4745cde760c5d8e12005e62f9832", size = 244678, upload-time = "2025-11-18T13:31:55.805Z" }, - { url = "https://files.pythonhosted.org/packages/55/7b/6b26fb32e8e4a6989ac1d40c4e132b14556131493b1d06bc0f2be169c357/coverage-7.12.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b527a08cdf15753279b7afb2339a12073620b761d79b81cbe2cdebdb43d90daa", size = 246507, upload-time = "2025-11-18T13:31:57.05Z" }, - { url = "https://files.pythonhosted.org/packages/06/42/7d70e6603d3260199b90fb48b537ca29ac183d524a65cc31366b2e905fad/coverage-7.12.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9bb44c889fb68004e94cab71f6a021ec83eac9aeabdbb5a5a88821ec46e1da73", size = 248366, upload-time = "2025-11-18T13:31:58.362Z" }, - { url = "https://files.pythonhosted.org/packages/2d/4a/d86b837923878424c72458c5b25e899a3c5ca73e663082a915f5b3c4d749/coverage-7.12.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4b59b501455535e2e5dde5881739897967b272ba25988c89145c12d772810ccb", size = 245366, upload-time = "2025-11-18T13:31:59.572Z" }, - { url = "https://files.pythonhosted.org/packages/e6/c2/2adec557e0aa9721875f06ced19730fdb7fc58e31b02b5aa56f2ebe4944d/coverage-7.12.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d8842f17095b9868a05837b7b1b73495293091bed870e099521ada176aa3e00e", size = 246408, upload-time = "2025-11-18T13:32:00.784Z" }, - { url = "https://files.pythonhosted.org/packages/5a/4b/8bd1f1148260df11c618e535fdccd1e5aaf646e55b50759006a4f41d8a26/coverage-7.12.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c5a6f20bf48b8866095c6820641e7ffbe23f2ac84a2efc218d91235e404c7777", size = 244416, upload-time = "2025-11-18T13:32:01.963Z" }, - { url = "https://files.pythonhosted.org/packages/0e/13/3a248dd6a83df90414c54a4e121fd081fb20602ca43955fbe1d60e2312a9/coverage-7.12.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:5f3738279524e988d9da2893f307c2093815c623f8d05a8f79e3eff3a7a9e553", size = 244681, upload-time = "2025-11-18T13:32:03.408Z" }, - { url = "https://files.pythonhosted.org/packages/76/30/aa833827465a5e8c938935f5d91ba055f70516941078a703740aaf1aa41f/coverage-7.12.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e0d68c1f7eabbc8abe582d11fa393ea483caf4f44b0af86881174769f185c94d", size = 245300, upload-time = "2025-11-18T13:32:04.686Z" }, - { url = "https://files.pythonhosted.org/packages/38/24/f85b3843af1370fb3739fa7571819b71243daa311289b31214fe3e8c9d68/coverage-7.12.0-cp310-cp310-win32.whl", hash = "sha256:7670d860e18b1e3ee5930b17a7d55ae6287ec6e55d9799982aa103a2cc1fa2ef", size = 220008, upload-time = "2025-11-18T13:32:05.806Z" }, - { url = "https://files.pythonhosted.org/packages/3a/a2/c7da5b9566f7164db9eefa133d17761ecb2c2fde9385d754e5b5c80f710d/coverage-7.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:f999813dddeb2a56aab5841e687b68169da0d3f6fc78ccf50952fa2463746022", size = 220943, upload-time = "2025-11-18T13:32:07.166Z" }, - { url = "https://files.pythonhosted.org/packages/5a/0c/0dfe7f0487477d96432e4815537263363fb6dd7289743a796e8e51eabdf2/coverage-7.12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:aa124a3683d2af98bd9d9c2bfa7a5076ca7e5ab09fdb96b81fa7d89376ae928f", size = 217535, upload-time = "2025-11-18T13:32:08.812Z" }, - { url = "https://files.pythonhosted.org/packages/9b/f5/f9a4a053a5bbff023d3bec259faac8f11a1e5a6479c2ccf586f910d8dac7/coverage-7.12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d93fbf446c31c0140208dcd07c5d882029832e8ed7891a39d6d44bd65f2316c3", size = 218044, upload-time = "2025-11-18T13:32:10.329Z" }, - { url = "https://files.pythonhosted.org/packages/95/c5/84fc3697c1fa10cd8571919bf9693f693b7373278daaf3b73e328d502bc8/coverage-7.12.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:52ca620260bd8cd6027317bdd8b8ba929be1d741764ee765b42c4d79a408601e", size = 248440, upload-time = "2025-11-18T13:32:12.536Z" }, - { url = "https://files.pythonhosted.org/packages/f4/36/2d93fbf6a04670f3874aed397d5a5371948a076e3249244a9e84fb0e02d6/coverage-7.12.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f3433ffd541380f3a0e423cff0f4926d55b0cc8c1d160fdc3be24a4c03aa65f7", size = 250361, upload-time = "2025-11-18T13:32:13.852Z" }, - { url = "https://files.pythonhosted.org/packages/5d/49/66dc65cc456a6bfc41ea3d0758c4afeaa4068a2b2931bf83be6894cf1058/coverage-7.12.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f7bbb321d4adc9f65e402c677cd1c8e4c2d0105d3ce285b51b4d87f1d5db5245", size = 252472, upload-time = "2025-11-18T13:32:15.068Z" }, - { url = "https://files.pythonhosted.org/packages/35/1f/ebb8a18dffd406db9fcd4b3ae42254aedcaf612470e8712f12041325930f/coverage-7.12.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:22a7aade354a72dff3b59c577bfd18d6945c61f97393bc5fb7bd293a4237024b", size = 248592, upload-time = "2025-11-18T13:32:16.328Z" }, - { url = "https://files.pythonhosted.org/packages/da/a8/67f213c06e5ea3b3d4980df7dc344d7fea88240b5fe878a5dcbdfe0e2315/coverage-7.12.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3ff651dcd36d2fea66877cd4a82de478004c59b849945446acb5baf9379a1b64", size = 250167, upload-time = "2025-11-18T13:32:17.687Z" }, - { url = "https://files.pythonhosted.org/packages/f0/00/e52aef68154164ea40cc8389c120c314c747fe63a04b013a5782e989b77f/coverage-7.12.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:31b8b2e38391a56e3cea39d22a23faaa7c3fc911751756ef6d2621d2a9daf742", size = 248238, upload-time = "2025-11-18T13:32:19.2Z" }, - { url = "https://files.pythonhosted.org/packages/1f/a4/4d88750bcf9d6d66f77865e5a05a20e14db44074c25fd22519777cb69025/coverage-7.12.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:297bc2da28440f5ae51c845a47c8175a4db0553a53827886e4fb25c66633000c", size = 247964, upload-time = "2025-11-18T13:32:21.027Z" }, - { url = "https://files.pythonhosted.org/packages/a7/6b/b74693158899d5b47b0bf6238d2c6722e20ba749f86b74454fac0696bb00/coverage-7.12.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6ff7651cc01a246908eac162a6a86fc0dbab6de1ad165dfb9a1e2ec660b44984", size = 248862, upload-time = "2025-11-18T13:32:22.304Z" }, - { url = "https://files.pythonhosted.org/packages/18/de/6af6730227ce0e8ade307b1cc4a08e7f51b419a78d02083a86c04ccceb29/coverage-7.12.0-cp311-cp311-win32.whl", hash = "sha256:313672140638b6ddb2c6455ddeda41c6a0b208298034544cfca138978c6baed6", size = 220033, upload-time = "2025-11-18T13:32:23.714Z" }, - { url = "https://files.pythonhosted.org/packages/e2/a1/e7f63021a7c4fe20994359fcdeae43cbef4a4d0ca36a5a1639feeea5d9e1/coverage-7.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:a1783ed5bd0d5938d4435014626568dc7f93e3cb99bc59188cc18857c47aa3c4", size = 220966, upload-time = "2025-11-18T13:32:25.599Z" }, - { url = "https://files.pythonhosted.org/packages/77/e8/deae26453f37c20c3aa0c4433a1e32cdc169bf415cce223a693117aa3ddd/coverage-7.12.0-cp311-cp311-win_arm64.whl", hash = "sha256:4648158fd8dd9381b5847622df1c90ff314efbfc1df4550092ab6013c238a5fc", size = 219637, upload-time = "2025-11-18T13:32:27.265Z" }, - { url = "https://files.pythonhosted.org/packages/02/bf/638c0427c0f0d47638242e2438127f3c8ee3cfc06c7fdeb16778ed47f836/coverage-7.12.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:29644c928772c78512b48e14156b81255000dcfd4817574ff69def189bcb3647", size = 217704, upload-time = "2025-11-18T13:32:28.906Z" }, - { url = "https://files.pythonhosted.org/packages/08/e1/706fae6692a66c2d6b871a608bbde0da6281903fa0e9f53a39ed441da36a/coverage-7.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8638cbb002eaa5d7c8d04da667813ce1067080b9a91099801a0053086e52b736", size = 218064, upload-time = "2025-11-18T13:32:30.161Z" }, - { url = "https://files.pythonhosted.org/packages/a9/8b/eb0231d0540f8af3ffda39720ff43cb91926489d01524e68f60e961366e4/coverage-7.12.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:083631eeff5eb9992c923e14b810a179798bb598e6a0dd60586819fc23be6e60", size = 249560, upload-time = "2025-11-18T13:32:31.835Z" }, - { url = "https://files.pythonhosted.org/packages/e9/a1/67fb52af642e974d159b5b379e4d4c59d0ebe1288677fbd04bbffe665a82/coverage-7.12.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:99d5415c73ca12d558e07776bd957c4222c687b9f1d26fa0e1b57e3598bdcde8", size = 252318, upload-time = "2025-11-18T13:32:33.178Z" }, - { url = "https://files.pythonhosted.org/packages/41/e5/38228f31b2c7665ebf9bdfdddd7a184d56450755c7e43ac721c11a4b8dab/coverage-7.12.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e949ebf60c717c3df63adb4a1a366c096c8d7fd8472608cd09359e1bd48ef59f", size = 253403, upload-time = "2025-11-18T13:32:34.45Z" }, - { url = "https://files.pythonhosted.org/packages/ec/4b/df78e4c8188f9960684267c5a4897836f3f0f20a20c51606ee778a1d9749/coverage-7.12.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6d907ddccbca819afa2cd014bc69983b146cca2735a0b1e6259b2a6c10be1e70", size = 249984, upload-time = "2025-11-18T13:32:35.747Z" }, - { url = "https://files.pythonhosted.org/packages/ba/51/bb163933d195a345c6f63eab9e55743413d064c291b6220df754075c2769/coverage-7.12.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b1518ecbad4e6173f4c6e6c4a46e49555ea5679bf3feda5edb1b935c7c44e8a0", size = 251339, upload-time = "2025-11-18T13:32:37.352Z" }, - { url = "https://files.pythonhosted.org/packages/15/40/c9b29cdb8412c837cdcbc2cfa054547dd83affe6cbbd4ce4fdb92b6ba7d1/coverage-7.12.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:51777647a749abdf6f6fd8c7cffab12de68ab93aab15efc72fbbb83036c2a068", size = 249489, upload-time = "2025-11-18T13:32:39.212Z" }, - { url = "https://files.pythonhosted.org/packages/c8/da/b3131e20ba07a0de4437a50ef3b47840dfabf9293675b0cd5c2c7f66dd61/coverage-7.12.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:42435d46d6461a3b305cdfcad7cdd3248787771f53fe18305548cba474e6523b", size = 249070, upload-time = "2025-11-18T13:32:40.598Z" }, - { url = "https://files.pythonhosted.org/packages/70/81/b653329b5f6302c08d683ceff6785bc60a34be9ae92a5c7b63ee7ee7acec/coverage-7.12.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5bcead88c8423e1855e64b8057d0544e33e4080b95b240c2a355334bb7ced937", size = 250929, upload-time = "2025-11-18T13:32:42.915Z" }, - { url = "https://files.pythonhosted.org/packages/a3/00/250ac3bca9f252a5fb1338b5ad01331ebb7b40223f72bef5b1b2cb03aa64/coverage-7.12.0-cp312-cp312-win32.whl", hash = "sha256:dcbb630ab034e86d2a0f79aefd2be07e583202f41e037602d438c80044957baa", size = 220241, upload-time = "2025-11-18T13:32:44.665Z" }, - { url = "https://files.pythonhosted.org/packages/64/1c/77e79e76d37ce83302f6c21980b45e09f8aa4551965213a10e62d71ce0ab/coverage-7.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:2fd8354ed5d69775ac42986a691fbf68b4084278710cee9d7c3eaa0c28fa982a", size = 221051, upload-time = "2025-11-18T13:32:46.008Z" }, - { url = "https://files.pythonhosted.org/packages/31/f5/641b8a25baae564f9e52cac0e2667b123de961985709a004e287ee7663cc/coverage-7.12.0-cp312-cp312-win_arm64.whl", hash = "sha256:737c3814903be30695b2de20d22bcc5428fdae305c61ba44cdc8b3252984c49c", size = 219692, upload-time = "2025-11-18T13:32:47.372Z" }, - { url = "https://files.pythonhosted.org/packages/b8/14/771700b4048774e48d2c54ed0c674273702713c9ee7acdfede40c2666747/coverage-7.12.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:47324fffca8d8eae7e185b5bb20c14645f23350f870c1649003618ea91a78941", size = 217725, upload-time = "2025-11-18T13:32:49.22Z" }, - { url = "https://files.pythonhosted.org/packages/17/a7/3aa4144d3bcb719bf67b22d2d51c2d577bf801498c13cb08f64173e80497/coverage-7.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ccf3b2ede91decd2fb53ec73c1f949c3e034129d1e0b07798ff1d02ea0c8fa4a", size = 218098, upload-time = "2025-11-18T13:32:50.78Z" }, - { url = "https://files.pythonhosted.org/packages/fc/9c/b846bbc774ff81091a12a10203e70562c91ae71badda00c5ae5b613527b1/coverage-7.12.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:b365adc70a6936c6b0582dc38746b33b2454148c02349345412c6e743efb646d", size = 249093, upload-time = "2025-11-18T13:32:52.554Z" }, - { url = "https://files.pythonhosted.org/packages/76/b6/67d7c0e1f400b32c883e9342de4a8c2ae7c1a0b57c5de87622b7262e2309/coverage-7.12.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bc13baf85cd8a4cfcf4a35c7bc9d795837ad809775f782f697bf630b7e200211", size = 251686, upload-time = "2025-11-18T13:32:54.862Z" }, - { url = "https://files.pythonhosted.org/packages/cc/75/b095bd4b39d49c3be4bffbb3135fea18a99a431c52dd7513637c0762fecb/coverage-7.12.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:099d11698385d572ceafb3288a5b80fe1fc58bf665b3f9d362389de488361d3d", size = 252930, upload-time = "2025-11-18T13:32:56.417Z" }, - { url = "https://files.pythonhosted.org/packages/6e/f3/466f63015c7c80550bead3093aacabf5380c1220a2a93c35d374cae8f762/coverage-7.12.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:473dc45d69694069adb7680c405fb1e81f60b2aff42c81e2f2c3feaf544d878c", size = 249296, upload-time = "2025-11-18T13:32:58.074Z" }, - { url = "https://files.pythonhosted.org/packages/27/86/eba2209bf2b7e28c68698fc13437519a295b2d228ba9e0ec91673e09fa92/coverage-7.12.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:583f9adbefd278e9de33c33d6846aa8f5d164fa49b47144180a0e037f0688bb9", size = 251068, upload-time = "2025-11-18T13:32:59.646Z" }, - { url = "https://files.pythonhosted.org/packages/ec/55/ca8ae7dbba962a3351f18940b359b94c6bafdd7757945fdc79ec9e452dc7/coverage-7.12.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b2089cc445f2dc0af6f801f0d1355c025b76c24481935303cf1af28f636688f0", size = 249034, upload-time = "2025-11-18T13:33:01.481Z" }, - { url = "https://files.pythonhosted.org/packages/7a/d7/39136149325cad92d420b023b5fd900dabdd1c3a0d1d5f148ef4a8cedef5/coverage-7.12.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:950411f1eb5d579999c5f66c62a40961f126fc71e5e14419f004471957b51508", size = 248853, upload-time = "2025-11-18T13:33:02.935Z" }, - { url = "https://files.pythonhosted.org/packages/fe/b6/76e1add8b87ef60e00643b0b7f8f7bb73d4bf5249a3be19ebefc5793dd25/coverage-7.12.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b1aab7302a87bafebfe76b12af681b56ff446dc6f32ed178ff9c092ca776e6bc", size = 250619, upload-time = "2025-11-18T13:33:04.336Z" }, - { url = "https://files.pythonhosted.org/packages/95/87/924c6dc64f9203f7a3c1832a6a0eee5a8335dbe5f1bdadcc278d6f1b4d74/coverage-7.12.0-cp313-cp313-win32.whl", hash = "sha256:d7e0d0303c13b54db495eb636bc2465b2fb8475d4c8bcec8fe4b5ca454dfbae8", size = 220261, upload-time = "2025-11-18T13:33:06.493Z" }, - { url = "https://files.pythonhosted.org/packages/91/77/dd4aff9af16ff776bf355a24d87eeb48fc6acde54c907cc1ea89b14a8804/coverage-7.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:ce61969812d6a98a981d147d9ac583a36ac7db7766f2e64a9d4d059c2fe29d07", size = 221072, upload-time = "2025-11-18T13:33:07.926Z" }, - { url = "https://files.pythonhosted.org/packages/70/49/5c9dc46205fef31b1b226a6e16513193715290584317fd4df91cdaf28b22/coverage-7.12.0-cp313-cp313-win_arm64.whl", hash = "sha256:bcec6f47e4cb8a4c2dc91ce507f6eefc6a1b10f58df32cdc61dff65455031dfc", size = 219702, upload-time = "2025-11-18T13:33:09.631Z" }, - { url = "https://files.pythonhosted.org/packages/9b/62/f87922641c7198667994dd472a91e1d9b829c95d6c29529ceb52132436ad/coverage-7.12.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:459443346509476170d553035e4a3eed7b860f4fe5242f02de1010501956ce87", size = 218420, upload-time = "2025-11-18T13:33:11.153Z" }, - { url = "https://files.pythonhosted.org/packages/85/dd/1cc13b2395ef15dbb27d7370a2509b4aee77890a464fb35d72d428f84871/coverage-7.12.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:04a79245ab2b7a61688958f7a855275997134bc84f4a03bc240cf64ff132abf6", size = 218773, upload-time = "2025-11-18T13:33:12.569Z" }, - { url = "https://files.pythonhosted.org/packages/74/40/35773cc4bb1e9d4658d4fb669eb4195b3151bef3bbd6f866aba5cd5dac82/coverage-7.12.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:09a86acaaa8455f13d6a99221d9654df249b33937b4e212b4e5a822065f12aa7", size = 260078, upload-time = "2025-11-18T13:33:14.037Z" }, - { url = "https://files.pythonhosted.org/packages/ec/ee/231bb1a6ffc2905e396557585ebc6bdc559e7c66708376d245a1f1d330fc/coverage-7.12.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:907e0df1b71ba77463687a74149c6122c3f6aac56c2510a5d906b2f368208560", size = 262144, upload-time = "2025-11-18T13:33:15.601Z" }, - { url = "https://files.pythonhosted.org/packages/28/be/32f4aa9f3bf0b56f3971001b56508352c7753915345d45fab4296a986f01/coverage-7.12.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9b57e2d0ddd5f0582bae5437c04ee71c46cd908e7bc5d4d0391f9a41e812dd12", size = 264574, upload-time = "2025-11-18T13:33:17.354Z" }, - { url = "https://files.pythonhosted.org/packages/68/7c/00489fcbc2245d13ab12189b977e0cf06ff3351cb98bc6beba8bd68c5902/coverage-7.12.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:58c1c6aa677f3a1411fe6fb28ec3a942e4f665df036a3608816e0847fad23296", size = 259298, upload-time = "2025-11-18T13:33:18.958Z" }, - { url = "https://files.pythonhosted.org/packages/96/b4/f0760d65d56c3bea95b449e02570d4abd2549dc784bf39a2d4721a2d8ceb/coverage-7.12.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4c589361263ab2953e3c4cd2a94db94c4ad4a8e572776ecfbad2389c626e4507", size = 262150, upload-time = "2025-11-18T13:33:20.644Z" }, - { url = "https://files.pythonhosted.org/packages/c5/71/9a9314df00f9326d78c1e5a910f520d599205907432d90d1c1b7a97aa4b1/coverage-7.12.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:91b810a163ccad2e43b1faa11d70d3cf4b6f3d83f9fd5f2df82a32d47b648e0d", size = 259763, upload-time = "2025-11-18T13:33:22.189Z" }, - { url = "https://files.pythonhosted.org/packages/10/34/01a0aceed13fbdf925876b9a15d50862eb8845454301fe3cdd1df08b2182/coverage-7.12.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:40c867af715f22592e0d0fb533a33a71ec9e0f73a6945f722a0c85c8c1cbe3a2", size = 258653, upload-time = "2025-11-18T13:33:24.239Z" }, - { url = "https://files.pythonhosted.org/packages/8d/04/81d8fd64928acf1574bbb0181f66901c6c1c6279c8ccf5f84259d2c68ae9/coverage-7.12.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:68b0d0a2d84f333de875666259dadf28cc67858bc8fd8b3f1eae84d3c2bec455", size = 260856, upload-time = "2025-11-18T13:33:26.365Z" }, - { url = "https://files.pythonhosted.org/packages/f2/76/fa2a37bfaeaf1f766a2d2360a25a5297d4fb567098112f6517475eee120b/coverage-7.12.0-cp313-cp313t-win32.whl", hash = "sha256:73f9e7fbd51a221818fd11b7090eaa835a353ddd59c236c57b2199486b116c6d", size = 220936, upload-time = "2025-11-18T13:33:28.165Z" }, - { url = "https://files.pythonhosted.org/packages/f9/52/60f64d932d555102611c366afb0eb434b34266b1d9266fc2fe18ab641c47/coverage-7.12.0-cp313-cp313t-win_amd64.whl", hash = "sha256:24cff9d1f5743f67db7ba46ff284018a6e9aeb649b67aa1e70c396aa1b7cb23c", size = 222001, upload-time = "2025-11-18T13:33:29.656Z" }, - { url = "https://files.pythonhosted.org/packages/77/df/c303164154a5a3aea7472bf323b7c857fed93b26618ed9fc5c2955566bb0/coverage-7.12.0-cp313-cp313t-win_arm64.whl", hash = "sha256:c87395744f5c77c866d0f5a43d97cc39e17c7f1cb0115e54a2fe67ca75c5d14d", size = 220273, upload-time = "2025-11-18T13:33:31.415Z" }, - { url = "https://files.pythonhosted.org/packages/bf/2e/fc12db0883478d6e12bbd62d481210f0c8daf036102aa11434a0c5755825/coverage-7.12.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:a1c59b7dc169809a88b21a936eccf71c3895a78f5592051b1af8f4d59c2b4f92", size = 217777, upload-time = "2025-11-18T13:33:32.86Z" }, - { url = "https://files.pythonhosted.org/packages/1f/c1/ce3e525d223350c6ec16b9be8a057623f54226ef7f4c2fee361ebb6a02b8/coverage-7.12.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8787b0f982e020adb732b9f051f3e49dd5054cebbc3f3432061278512a2b1360", size = 218100, upload-time = "2025-11-18T13:33:34.532Z" }, - { url = "https://files.pythonhosted.org/packages/15/87/113757441504aee3808cb422990ed7c8bcc2d53a6779c66c5adef0942939/coverage-7.12.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5ea5a9f7dc8877455b13dd1effd3202e0bca72f6f3ab09f9036b1bcf728f69ac", size = 249151, upload-time = "2025-11-18T13:33:36.135Z" }, - { url = "https://files.pythonhosted.org/packages/d9/1d/9529d9bd44049b6b05bb319c03a3a7e4b0a8a802d28fa348ad407e10706d/coverage-7.12.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fdba9f15849534594f60b47c9a30bc70409b54947319a7c4fd0e8e3d8d2f355d", size = 251667, upload-time = "2025-11-18T13:33:37.996Z" }, - { url = "https://files.pythonhosted.org/packages/11/bb/567e751c41e9c03dc29d3ce74b8c89a1e3396313e34f255a2a2e8b9ebb56/coverage-7.12.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a00594770eb715854fb1c57e0dea08cce6720cfbc531accdb9850d7c7770396c", size = 253003, upload-time = "2025-11-18T13:33:39.553Z" }, - { url = "https://files.pythonhosted.org/packages/e4/b3/c2cce2d8526a02fb9e9ca14a263ca6fc074449b33a6afa4892838c903528/coverage-7.12.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5560c7e0d82b42eb1951e4f68f071f8017c824ebfd5a6ebe42c60ac16c6c2434", size = 249185, upload-time = "2025-11-18T13:33:42.086Z" }, - { url = "https://files.pythonhosted.org/packages/0e/a7/967f93bb66e82c9113c66a8d0b65ecf72fc865adfba5a145f50c7af7e58d/coverage-7.12.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d6c2e26b481c9159c2773a37947a9718cfdc58893029cdfb177531793e375cfc", size = 251025, upload-time = "2025-11-18T13:33:43.634Z" }, - { url = "https://files.pythonhosted.org/packages/b9/b2/f2f6f56337bc1af465d5b2dc1ee7ee2141b8b9272f3bf6213fcbc309a836/coverage-7.12.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:6e1a8c066dabcde56d5d9fed6a66bc19a2883a3fe051f0c397a41fc42aedd4cc", size = 248979, upload-time = "2025-11-18T13:33:46.04Z" }, - { url = "https://files.pythonhosted.org/packages/f4/7a/bf4209f45a4aec09d10a01a57313a46c0e0e8f4c55ff2965467d41a92036/coverage-7.12.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:f7ba9da4726e446d8dd8aae5a6cd872511184a5d861de80a86ef970b5dacce3e", size = 248800, upload-time = "2025-11-18T13:33:47.546Z" }, - { url = "https://files.pythonhosted.org/packages/b8/b7/1e01b8696fb0521810f60c5bbebf699100d6754183e6cc0679bf2ed76531/coverage-7.12.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e0f483ab4f749039894abaf80c2f9e7ed77bbf3c737517fb88c8e8e305896a17", size = 250460, upload-time = "2025-11-18T13:33:49.537Z" }, - { url = "https://files.pythonhosted.org/packages/71/ae/84324fb9cb46c024760e706353d9b771a81b398d117d8c1fe010391c186f/coverage-7.12.0-cp314-cp314-win32.whl", hash = "sha256:76336c19a9ef4a94b2f8dc79f8ac2da3f193f625bb5d6f51a328cd19bfc19933", size = 220533, upload-time = "2025-11-18T13:33:51.16Z" }, - { url = "https://files.pythonhosted.org/packages/e2/71/1033629deb8460a8f97f83e6ac4ca3b93952e2b6f826056684df8275e015/coverage-7.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:7c1059b600aec6ef090721f8f633f60ed70afaffe8ecab85b59df748f24b31fe", size = 221348, upload-time = "2025-11-18T13:33:52.776Z" }, - { url = "https://files.pythonhosted.org/packages/0a/5f/ac8107a902f623b0c251abdb749be282dc2ab61854a8a4fcf49e276fce2f/coverage-7.12.0-cp314-cp314-win_arm64.whl", hash = "sha256:172cf3a34bfef42611963e2b661302a8931f44df31629e5b1050567d6b90287d", size = 219922, upload-time = "2025-11-18T13:33:54.316Z" }, - { url = "https://files.pythonhosted.org/packages/79/6e/f27af2d4da367f16077d21ef6fe796c874408219fa6dd3f3efe7751bd910/coverage-7.12.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:aa7d48520a32cb21c7a9b31f81799e8eaec7239db36c3b670be0fa2403828d1d", size = 218511, upload-time = "2025-11-18T13:33:56.343Z" }, - { url = "https://files.pythonhosted.org/packages/67/dd/65fd874aa460c30da78f9d259400d8e6a4ef457d61ab052fd248f0050558/coverage-7.12.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:90d58ac63bc85e0fb919f14d09d6caa63f35a5512a2205284b7816cafd21bb03", size = 218771, upload-time = "2025-11-18T13:33:57.966Z" }, - { url = "https://files.pythonhosted.org/packages/55/e0/7c6b71d327d8068cb79c05f8f45bf1b6145f7a0de23bbebe63578fe5240a/coverage-7.12.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ca8ecfa283764fdda3eae1bdb6afe58bf78c2c3ec2b2edcb05a671f0bba7b3f9", size = 260151, upload-time = "2025-11-18T13:33:59.597Z" }, - { url = "https://files.pythonhosted.org/packages/49/ce/4697457d58285b7200de6b46d606ea71066c6e674571a946a6ea908fb588/coverage-7.12.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:874fe69a0785d96bd066059cd4368022cebbec1a8958f224f0016979183916e6", size = 262257, upload-time = "2025-11-18T13:34:01.166Z" }, - { url = "https://files.pythonhosted.org/packages/2f/33/acbc6e447aee4ceba88c15528dbe04a35fb4d67b59d393d2e0d6f1e242c1/coverage-7.12.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5b3c889c0b8b283a24d721a9eabc8ccafcfc3aebf167e4cd0d0e23bf8ec4e339", size = 264671, upload-time = "2025-11-18T13:34:02.795Z" }, - { url = "https://files.pythonhosted.org/packages/87/ec/e2822a795c1ed44d569980097be839c5e734d4c0c1119ef8e0a073496a30/coverage-7.12.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8bb5b894b3ec09dcd6d3743229dc7f2c42ef7787dc40596ae04c0edda487371e", size = 259231, upload-time = "2025-11-18T13:34:04.397Z" }, - { url = "https://files.pythonhosted.org/packages/72/c5/a7ec5395bb4a49c9b7ad97e63f0c92f6bf4a9e006b1393555a02dae75f16/coverage-7.12.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:79a44421cd5fba96aa57b5e3b5a4d3274c449d4c622e8f76882d76635501fd13", size = 262137, upload-time = "2025-11-18T13:34:06.068Z" }, - { url = "https://files.pythonhosted.org/packages/67/0c/02c08858b764129f4ecb8e316684272972e60777ae986f3865b10940bdd6/coverage-7.12.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:33baadc0efd5c7294f436a632566ccc1f72c867f82833eb59820ee37dc811c6f", size = 259745, upload-time = "2025-11-18T13:34:08.04Z" }, - { url = "https://files.pythonhosted.org/packages/5a/04/4fd32b7084505f3829a8fe45c1a74a7a728cb251aaadbe3bec04abcef06d/coverage-7.12.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:c406a71f544800ef7e9e0000af706b88465f3573ae8b8de37e5f96c59f689ad1", size = 258570, upload-time = "2025-11-18T13:34:09.676Z" }, - { url = "https://files.pythonhosted.org/packages/48/35/2365e37c90df4f5342c4fa202223744119fe31264ee2924f09f074ea9b6d/coverage-7.12.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e71bba6a40883b00c6d571599b4627f50c360b3d0d02bfc658168936be74027b", size = 260899, upload-time = "2025-11-18T13:34:11.259Z" }, - { url = "https://files.pythonhosted.org/packages/05/56/26ab0464ca733fa325e8e71455c58c1c374ce30f7c04cebb88eabb037b18/coverage-7.12.0-cp314-cp314t-win32.whl", hash = "sha256:9157a5e233c40ce6613dead4c131a006adfda70e557b6856b97aceed01b0e27a", size = 221313, upload-time = "2025-11-18T13:34:12.863Z" }, - { url = "https://files.pythonhosted.org/packages/da/1c/017a3e1113ed34d998b27d2c6dba08a9e7cb97d362f0ec988fcd873dcf81/coverage-7.12.0-cp314-cp314t-win_amd64.whl", hash = "sha256:e84da3a0fd233aeec797b981c51af1cabac74f9bd67be42458365b30d11b5291", size = 222423, upload-time = "2025-11-18T13:34:15.14Z" }, - { url = "https://files.pythonhosted.org/packages/4c/36/bcc504fdd5169301b52568802bb1b9cdde2e27a01d39fbb3b4b508ab7c2c/coverage-7.12.0-cp314-cp314t-win_arm64.whl", hash = "sha256:01d24af36fedda51c2b1aca56e4330a3710f83b02a5ff3743a6b015ffa7c9384", size = 220459, upload-time = "2025-11-18T13:34:17.222Z" }, - { url = "https://files.pythonhosted.org/packages/ce/a3/43b749004e3c09452e39bb56347a008f0a0668aad37324a99b5c8ca91d9e/coverage-7.12.0-py3-none-any.whl", hash = "sha256:159d50c0b12e060b15ed3d39f87ed43d4f7f7ad40b8a534f4dd331adbb51104a", size = 209503, upload-time = "2025-11-18T13:34:18.892Z" }, +version = "7.13.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/23/f9/e92df5e07f3fc8d4c7f9a0f146ef75446bf870351cd37b788cf5897f8079/coverage-7.13.1.tar.gz", hash = "sha256:b7593fe7eb5feaa3fbb461ac79aac9f9fc0387a5ca8080b0c6fe2ca27b091afd", size = 825862, upload-time = "2025-12-28T15:42:56.969Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2d/9a/3742e58fd04b233df95c012ee9f3dfe04708a5e1d32613bd2d47d4e1be0d/coverage-7.13.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e1fa280b3ad78eea5be86f94f461c04943d942697e0dac889fa18fff8f5f9147", size = 218633, upload-time = "2025-12-28T15:40:10.165Z" }, + { url = "https://files.pythonhosted.org/packages/7e/45/7e6bdc94d89cd7c8017ce735cf50478ddfe765d4fbf0c24d71d30ea33d7a/coverage-7.13.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c3d8c679607220979434f494b139dfb00131ebf70bb406553d69c1ff01a5c33d", size = 219147, upload-time = "2025-12-28T15:40:12.069Z" }, + { url = "https://files.pythonhosted.org/packages/f7/38/0d6a258625fd7f10773fe94097dc16937a5f0e3e0cdf3adef67d3ac6baef/coverage-7.13.1-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:339dc63b3eba969067b00f41f15ad161bf2946613156fb131266d8debc8e44d0", size = 245894, upload-time = "2025-12-28T15:40:13.556Z" }, + { url = "https://files.pythonhosted.org/packages/27/58/409d15ea487986994cbd4d06376e9860e9b157cfbfd402b1236770ab8dd2/coverage-7.13.1-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:db622b999ffe49cb891f2fff3b340cdc2f9797d01a0a202a0973ba2562501d90", size = 247721, upload-time = "2025-12-28T15:40:15.37Z" }, + { url = "https://files.pythonhosted.org/packages/da/bf/6e8056a83fd7a96c93341f1ffe10df636dd89f26d5e7b9ca511ce3bcf0df/coverage-7.13.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d1443ba9acbb593fa7c1c29e011d7c9761545fe35e7652e85ce7f51a16f7e08d", size = 249585, upload-time = "2025-12-28T15:40:17.226Z" }, + { url = "https://files.pythonhosted.org/packages/f4/15/e1daff723f9f5959acb63cbe35b11203a9df77ee4b95b45fffd38b318390/coverage-7.13.1-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c832ec92c4499ac463186af72f9ed4d8daec15499b16f0a879b0d1c8e5cf4a3b", size = 246597, upload-time = "2025-12-28T15:40:19.028Z" }, + { url = "https://files.pythonhosted.org/packages/74/a6/1efd31c5433743a6ddbc9d37ac30c196bb07c7eab3d74fbb99b924c93174/coverage-7.13.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:562ec27dfa3f311e0db1ba243ec6e5f6ab96b1edfcfc6cf86f28038bc4961ce6", size = 247626, upload-time = "2025-12-28T15:40:20.846Z" }, + { url = "https://files.pythonhosted.org/packages/6d/9f/1609267dd3e749f57fdd66ca6752567d1c13b58a20a809dc409b263d0b5f/coverage-7.13.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:4de84e71173d4dada2897e5a0e1b7877e5eefbfe0d6a44edee6ce31d9b8ec09e", size = 245629, upload-time = "2025-12-28T15:40:22.397Z" }, + { url = "https://files.pythonhosted.org/packages/e2/f6/6815a220d5ec2466383d7cc36131b9fa6ecbe95c50ec52a631ba733f306a/coverage-7.13.1-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:a5a68357f686f8c4d527a2dc04f52e669c2fc1cbde38f6f7eb6a0e58cbd17cae", size = 245901, upload-time = "2025-12-28T15:40:23.836Z" }, + { url = "https://files.pythonhosted.org/packages/ac/58/40576554cd12e0872faf6d2c0eb3bc85f71d78427946ddd19ad65201e2c0/coverage-7.13.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:77cc258aeb29a3417062758975521eae60af6f79e930d6993555eeac6a8eac29", size = 246505, upload-time = "2025-12-28T15:40:25.421Z" }, + { url = "https://files.pythonhosted.org/packages/3b/77/9233a90253fba576b0eee81707b5781d0e21d97478e5377b226c5b096c0f/coverage-7.13.1-cp310-cp310-win32.whl", hash = "sha256:bb4f8c3c9a9f34423dba193f241f617b08ffc63e27f67159f60ae6baf2dcfe0f", size = 221257, upload-time = "2025-12-28T15:40:27.217Z" }, + { url = "https://files.pythonhosted.org/packages/e0/43/e842ff30c1a0a623ec80db89befb84a3a7aad7bfe44a6ea77d5a3e61fedd/coverage-7.13.1-cp310-cp310-win_amd64.whl", hash = "sha256:c8e2706ceb622bc63bac98ebb10ef5da80ed70fbd8a7999a5076de3afaef0fb1", size = 222191, upload-time = "2025-12-28T15:40:28.916Z" }, + { url = "https://files.pythonhosted.org/packages/b4/9b/77baf488516e9ced25fc215a6f75d803493fc3f6a1a1227ac35697910c2a/coverage-7.13.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a55d509a1dc5a5b708b5dad3b5334e07a16ad4c2185e27b40e4dba796ab7f88", size = 218755, upload-time = "2025-12-28T15:40:30.812Z" }, + { url = "https://files.pythonhosted.org/packages/d7/cd/7ab01154e6eb79ee2fab76bf4d89e94c6648116557307ee4ebbb85e5c1bf/coverage-7.13.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4d010d080c4888371033baab27e47c9df7d6fb28d0b7b7adf85a4a49be9298b3", size = 219257, upload-time = "2025-12-28T15:40:32.333Z" }, + { url = "https://files.pythonhosted.org/packages/01/d5/b11ef7863ffbbdb509da0023fad1e9eda1c0eaea61a6d2ea5b17d4ac706e/coverage-7.13.1-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d938b4a840fb1523b9dfbbb454f652967f18e197569c32266d4d13f37244c3d9", size = 249657, upload-time = "2025-12-28T15:40:34.1Z" }, + { url = "https://files.pythonhosted.org/packages/f7/7c/347280982982383621d29b8c544cf497ae07ac41e44b1ca4903024131f55/coverage-7.13.1-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bf100a3288f9bb7f919b87eb84f87101e197535b9bd0e2c2b5b3179633324fee", size = 251581, upload-time = "2025-12-28T15:40:36.131Z" }, + { url = "https://files.pythonhosted.org/packages/82/f6/ebcfed11036ade4c0d75fa4453a6282bdd225bc073862766eec184a4c643/coverage-7.13.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef6688db9bf91ba111ae734ba6ef1a063304a881749726e0d3575f5c10a9facf", size = 253691, upload-time = "2025-12-28T15:40:37.626Z" }, + { url = "https://files.pythonhosted.org/packages/02/92/af8f5582787f5d1a8b130b2dcba785fa5e9a7a8e121a0bb2220a6fdbdb8a/coverage-7.13.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0b609fc9cdbd1f02e51f67f51e5aee60a841ef58a68d00d5ee2c0faf357481a3", size = 249799, upload-time = "2025-12-28T15:40:39.47Z" }, + { url = "https://files.pythonhosted.org/packages/24/aa/0e39a2a3b16eebf7f193863323edbff38b6daba711abaaf807d4290cf61a/coverage-7.13.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c43257717611ff5e9a1d79dce8e47566235ebda63328718d9b65dd640bc832ef", size = 251389, upload-time = "2025-12-28T15:40:40.954Z" }, + { url = "https://files.pythonhosted.org/packages/73/46/7f0c13111154dc5b978900c0ccee2e2ca239b910890e674a77f1363d483e/coverage-7.13.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e09fbecc007f7b6afdfb3b07ce5bd9f8494b6856dd4f577d26c66c391b829851", size = 249450, upload-time = "2025-12-28T15:40:42.489Z" }, + { url = "https://files.pythonhosted.org/packages/ac/ca/e80da6769e8b669ec3695598c58eef7ad98b0e26e66333996aee6316db23/coverage-7.13.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:a03a4f3a19a189919c7055098790285cc5c5b0b3976f8d227aea39dbf9f8bfdb", size = 249170, upload-time = "2025-12-28T15:40:44.279Z" }, + { url = "https://files.pythonhosted.org/packages/af/18/9e29baabdec1a8644157f572541079b4658199cfd372a578f84228e860de/coverage-7.13.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3820778ea1387c2b6a818caec01c63adc5b3750211af6447e8dcfb9b6f08dbba", size = 250081, upload-time = "2025-12-28T15:40:45.748Z" }, + { url = "https://files.pythonhosted.org/packages/00/f8/c3021625a71c3b2f516464d322e41636aea381018319050a8114105872ee/coverage-7.13.1-cp311-cp311-win32.whl", hash = "sha256:ff10896fa55167371960c5908150b434b71c876dfab97b69478f22c8b445ea19", size = 221281, upload-time = "2025-12-28T15:40:47.232Z" }, + { url = "https://files.pythonhosted.org/packages/27/56/c216625f453df6e0559ed666d246fcbaaa93f3aa99eaa5080cea1229aa3d/coverage-7.13.1-cp311-cp311-win_amd64.whl", hash = "sha256:a998cc0aeeea4c6d5622a3754da5a493055d2d95186bad877b0a34ea6e6dbe0a", size = 222215, upload-time = "2025-12-28T15:40:49.19Z" }, + { url = "https://files.pythonhosted.org/packages/5c/9a/be342e76f6e531cae6406dc46af0d350586f24d9b67fdfa6daee02df71af/coverage-7.13.1-cp311-cp311-win_arm64.whl", hash = "sha256:fea07c1a39a22614acb762e3fbbb4011f65eedafcb2948feeef641ac78b4ee5c", size = 220886, upload-time = "2025-12-28T15:40:51.067Z" }, + { url = "https://files.pythonhosted.org/packages/ce/8a/87af46cccdfa78f53db747b09f5f9a21d5fc38d796834adac09b30a8ce74/coverage-7.13.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6f34591000f06e62085b1865c9bc5f7858df748834662a51edadfd2c3bfe0dd3", size = 218927, upload-time = "2025-12-28T15:40:52.814Z" }, + { url = "https://files.pythonhosted.org/packages/82/a8/6e22fdc67242a4a5a153f9438d05944553121c8f4ba70cb072af4c41362e/coverage-7.13.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b67e47c5595b9224599016e333f5ec25392597a89d5744658f837d204e16c63e", size = 219288, upload-time = "2025-12-28T15:40:54.262Z" }, + { url = "https://files.pythonhosted.org/packages/d0/0a/853a76e03b0f7c4375e2ca025df45c918beb367f3e20a0a8e91967f6e96c/coverage-7.13.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3e7b8bd70c48ffb28461ebe092c2345536fb18bbbf19d287c8913699735f505c", size = 250786, upload-time = "2025-12-28T15:40:56.059Z" }, + { url = "https://files.pythonhosted.org/packages/ea/b4/694159c15c52b9f7ec7adf49d50e5f8ee71d3e9ef38adb4445d13dd56c20/coverage-7.13.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c223d078112e90dc0e5c4e35b98b9584164bea9fbbd221c0b21c5241f6d51b62", size = 253543, upload-time = "2025-12-28T15:40:57.585Z" }, + { url = "https://files.pythonhosted.org/packages/96/b2/7f1f0437a5c855f87e17cf5d0dc35920b6440ff2b58b1ba9788c059c26c8/coverage-7.13.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:794f7c05af0763b1bbd1b9e6eff0e52ad068be3b12cd96c87de037b01390c968", size = 254635, upload-time = "2025-12-28T15:40:59.443Z" }, + { url = "https://files.pythonhosted.org/packages/e9/d1/73c3fdb8d7d3bddd9473c9c6a2e0682f09fc3dfbcb9c3f36412a7368bcab/coverage-7.13.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0642eae483cc8c2902e4af7298bf886d605e80f26382124cddc3967c2a3df09e", size = 251202, upload-time = "2025-12-28T15:41:01.328Z" }, + { url = "https://files.pythonhosted.org/packages/66/3c/f0edf75dcc152f145d5598329e864bbbe04ab78660fe3e8e395f9fff010f/coverage-7.13.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9f5e772ed5fef25b3de9f2008fe67b92d46831bd2bc5bdc5dd6bfd06b83b316f", size = 252566, upload-time = "2025-12-28T15:41:03.319Z" }, + { url = "https://files.pythonhosted.org/packages/17/b3/e64206d3c5f7dcbceafd14941345a754d3dbc78a823a6ed526e23b9cdaab/coverage-7.13.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:45980ea19277dc0a579e432aef6a504fe098ef3a9032ead15e446eb0f1191aee", size = 250711, upload-time = "2025-12-28T15:41:06.411Z" }, + { url = "https://files.pythonhosted.org/packages/dc/ad/28a3eb970a8ef5b479ee7f0c484a19c34e277479a5b70269dc652b730733/coverage-7.13.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:e4f18eca6028ffa62adbd185a8f1e1dd242f2e68164dba5c2b74a5204850b4cf", size = 250278, upload-time = "2025-12-28T15:41:08.285Z" }, + { url = "https://files.pythonhosted.org/packages/54/e3/c8f0f1a93133e3e1291ca76cbb63565bd4b5c5df63b141f539d747fff348/coverage-7.13.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f8dca5590fec7a89ed6826fce625595279e586ead52e9e958d3237821fbc750c", size = 252154, upload-time = "2025-12-28T15:41:09.969Z" }, + { url = "https://files.pythonhosted.org/packages/d0/bf/9939c5d6859c380e405b19e736321f1c7d402728792f4c752ad1adcce005/coverage-7.13.1-cp312-cp312-win32.whl", hash = "sha256:ff86d4e85188bba72cfb876df3e11fa243439882c55957184af44a35bd5880b7", size = 221487, upload-time = "2025-12-28T15:41:11.468Z" }, + { url = "https://files.pythonhosted.org/packages/fa/dc/7282856a407c621c2aad74021680a01b23010bb8ebf427cf5eacda2e876f/coverage-7.13.1-cp312-cp312-win_amd64.whl", hash = "sha256:16cc1da46c04fb0fb128b4dc430b78fa2aba8a6c0c9f8eb391fd5103409a6ac6", size = 222299, upload-time = "2025-12-28T15:41:13.386Z" }, + { url = "https://files.pythonhosted.org/packages/10/79/176a11203412c350b3e9578620013af35bcdb79b651eb976f4a4b32044fa/coverage-7.13.1-cp312-cp312-win_arm64.whl", hash = "sha256:8d9bc218650022a768f3775dd7fdac1886437325d8d295d923ebcfef4892ad5c", size = 220941, upload-time = "2025-12-28T15:41:14.975Z" }, + { url = "https://files.pythonhosted.org/packages/a3/a4/e98e689347a1ff1a7f67932ab535cef82eb5e78f32a9e4132e114bbb3a0a/coverage-7.13.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:cb237bfd0ef4d5eb6a19e29f9e528ac67ac3be932ea6b44fb6cc09b9f3ecff78", size = 218951, upload-time = "2025-12-28T15:41:16.653Z" }, + { url = "https://files.pythonhosted.org/packages/32/33/7cbfe2bdc6e2f03d6b240d23dc45fdaf3fd270aaf2d640be77b7f16989ab/coverage-7.13.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1dcb645d7e34dcbcc96cd7c132b1fc55c39263ca62eb961c064eb3928997363b", size = 219325, upload-time = "2025-12-28T15:41:18.609Z" }, + { url = "https://files.pythonhosted.org/packages/59/f6/efdabdb4929487baeb7cb2a9f7dac457d9356f6ad1b255be283d58b16316/coverage-7.13.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3d42df8201e00384736f0df9be2ced39324c3907607d17d50d50116c989d84cd", size = 250309, upload-time = "2025-12-28T15:41:20.629Z" }, + { url = "https://files.pythonhosted.org/packages/12/da/91a52516e9d5aea87d32d1523f9cdcf7a35a3b298e6be05d6509ba3cfab2/coverage-7.13.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fa3edde1aa8807de1d05934982416cb3ec46d1d4d91e280bcce7cca01c507992", size = 252907, upload-time = "2025-12-28T15:41:22.257Z" }, + { url = "https://files.pythonhosted.org/packages/75/38/f1ea837e3dc1231e086db1638947e00d264e7e8c41aa8ecacf6e1e0c05f4/coverage-7.13.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9edd0e01a343766add6817bc448408858ba6b489039eaaa2018474e4001651a4", size = 254148, upload-time = "2025-12-28T15:41:23.87Z" }, + { url = "https://files.pythonhosted.org/packages/7f/43/f4f16b881aaa34954ba446318dea6b9ed5405dd725dd8daac2358eda869a/coverage-7.13.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:985b7836931d033570b94c94713c6dba5f9d3ff26045f72c3e5dbc5fe3361e5a", size = 250515, upload-time = "2025-12-28T15:41:25.437Z" }, + { url = "https://files.pythonhosted.org/packages/84/34/8cba7f00078bd468ea914134e0144263194ce849ec3baad187ffb6203d1c/coverage-7.13.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ffed1e4980889765c84a5d1a566159e363b71d6b6fbaf0bebc9d3c30bc016766", size = 252292, upload-time = "2025-12-28T15:41:28.459Z" }, + { url = "https://files.pythonhosted.org/packages/8c/a4/cffac66c7652d84ee4ac52d3ccb94c015687d3b513f9db04bfcac2ac800d/coverage-7.13.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:8842af7f175078456b8b17f1b73a0d16a65dcbdc653ecefeb00a56b3c8c298c4", size = 250242, upload-time = "2025-12-28T15:41:30.02Z" }, + { url = "https://files.pythonhosted.org/packages/f4/78/9a64d462263dde416f3c0067efade7b52b52796f489b1037a95b0dc389c9/coverage-7.13.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:ccd7a6fca48ca9c131d9b0a2972a581e28b13416fc313fb98b6d24a03ce9a398", size = 250068, upload-time = "2025-12-28T15:41:32.007Z" }, + { url = "https://files.pythonhosted.org/packages/69/c8/a8994f5fece06db7c4a97c8fc1973684e178599b42e66280dded0524ef00/coverage-7.13.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0403f647055de2609be776965108447deb8e384fe4a553c119e3ff6bfbab4784", size = 251846, upload-time = "2025-12-28T15:41:33.946Z" }, + { url = "https://files.pythonhosted.org/packages/cc/f7/91fa73c4b80305c86598a2d4e54ba22df6bf7d0d97500944af7ef155d9f7/coverage-7.13.1-cp313-cp313-win32.whl", hash = "sha256:549d195116a1ba1e1ae2f5ca143f9777800f6636eab917d4f02b5310d6d73461", size = 221512, upload-time = "2025-12-28T15:41:35.519Z" }, + { url = "https://files.pythonhosted.org/packages/45/0b/0768b4231d5a044da8f75e097a8714ae1041246bb765d6b5563bab456735/coverage-7.13.1-cp313-cp313-win_amd64.whl", hash = "sha256:5899d28b5276f536fcf840b18b61a9fce23cc3aec1d114c44c07fe94ebeaa500", size = 222321, upload-time = "2025-12-28T15:41:37.371Z" }, + { url = "https://files.pythonhosted.org/packages/9b/b8/bdcb7253b7e85157282450262008f1366aa04663f3e3e4c30436f596c3e2/coverage-7.13.1-cp313-cp313-win_arm64.whl", hash = "sha256:868a2fae76dfb06e87291bcbd4dcbcc778a8500510b618d50496e520bd94d9b9", size = 220949, upload-time = "2025-12-28T15:41:39.553Z" }, + { url = "https://files.pythonhosted.org/packages/70/52/f2be52cc445ff75ea8397948c96c1b4ee14f7f9086ea62fc929c5ae7b717/coverage-7.13.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:67170979de0dacac3f3097d02b0ad188d8edcea44ccc44aaa0550af49150c7dc", size = 219643, upload-time = "2025-12-28T15:41:41.567Z" }, + { url = "https://files.pythonhosted.org/packages/47/79/c85e378eaa239e2edec0c5523f71542c7793fe3340954eafb0bc3904d32d/coverage-7.13.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f80e2bb21bfab56ed7405c2d79d34b5dc0bc96c2c1d2a067b643a09fb756c43a", size = 219997, upload-time = "2025-12-28T15:41:43.418Z" }, + { url = "https://files.pythonhosted.org/packages/fe/9b/b1ade8bfb653c0bbce2d6d6e90cc6c254cbb99b7248531cc76253cb4da6d/coverage-7.13.1-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f83351e0f7dcdb14d7326c3d8d8c4e915fa685cbfdc6281f9470d97a04e9dfe4", size = 261296, upload-time = "2025-12-28T15:41:45.207Z" }, + { url = "https://files.pythonhosted.org/packages/1f/af/ebf91e3e1a2473d523e87e87fd8581e0aa08741b96265730e2d79ce78d8d/coverage-7.13.1-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bb3f6562e89bad0110afbe64e485aac2462efdce6232cdec7862a095dc3412f6", size = 263363, upload-time = "2025-12-28T15:41:47.163Z" }, + { url = "https://files.pythonhosted.org/packages/c4/8b/fb2423526d446596624ac7fde12ea4262e66f86f5120114c3cfd0bb2befa/coverage-7.13.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:77545b5dcda13b70f872c3b5974ac64c21d05e65b1590b441c8560115dc3a0d1", size = 265783, upload-time = "2025-12-28T15:41:49.03Z" }, + { url = "https://files.pythonhosted.org/packages/9b/26/ef2adb1e22674913b89f0fe7490ecadcef4a71fa96f5ced90c60ec358789/coverage-7.13.1-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a4d240d260a1aed814790bbe1f10a5ff31ce6c21bc78f0da4a1e8268d6c80dbd", size = 260508, upload-time = "2025-12-28T15:41:51.035Z" }, + { url = "https://files.pythonhosted.org/packages/ce/7d/f0f59b3404caf662e7b5346247883887687c074ce67ba453ea08c612b1d5/coverage-7.13.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:d2287ac9360dec3837bfdad969963a5d073a09a85d898bd86bea82aa8876ef3c", size = 263357, upload-time = "2025-12-28T15:41:52.631Z" }, + { url = "https://files.pythonhosted.org/packages/1a/b1/29896492b0b1a047604d35d6fa804f12818fa30cdad660763a5f3159e158/coverage-7.13.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:0d2c11f3ea4db66b5cbded23b20185c35066892c67d80ec4be4bab257b9ad1e0", size = 260978, upload-time = "2025-12-28T15:41:54.589Z" }, + { url = "https://files.pythonhosted.org/packages/48/f2/971de1238a62e6f0a4128d37adadc8bb882ee96afbe03ff1570291754629/coverage-7.13.1-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:3fc6a169517ca0d7ca6846c3c5392ef2b9e38896f61d615cb75b9e7134d4ee1e", size = 259877, upload-time = "2025-12-28T15:41:56.263Z" }, + { url = "https://files.pythonhosted.org/packages/6a/fc/0474efcbb590ff8628830e9aaec5f1831594874360e3251f1fdec31d07a3/coverage-7.13.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d10a2ed46386e850bb3de503a54f9fe8192e5917fcbb143bfef653a9355e9a53", size = 262069, upload-time = "2025-12-28T15:41:58.093Z" }, + { url = "https://files.pythonhosted.org/packages/88/4f/3c159b7953db37a7b44c0eab8a95c37d1aa4257c47b4602c04022d5cb975/coverage-7.13.1-cp313-cp313t-win32.whl", hash = "sha256:75a6f4aa904301dab8022397a22c0039edc1f51e90b83dbd4464b8a38dc87842", size = 222184, upload-time = "2025-12-28T15:41:59.763Z" }, + { url = "https://files.pythonhosted.org/packages/58/a5/6b57d28f81417f9335774f20679d9d13b9a8fb90cd6160957aa3b54a2379/coverage-7.13.1-cp313-cp313t-win_amd64.whl", hash = "sha256:309ef5706e95e62578cda256b97f5e097916a2c26247c287bbe74794e7150df2", size = 223250, upload-time = "2025-12-28T15:42:01.52Z" }, + { url = "https://files.pythonhosted.org/packages/81/7c/160796f3b035acfbb58be80e02e484548595aa67e16a6345e7910ace0a38/coverage-7.13.1-cp313-cp313t-win_arm64.whl", hash = "sha256:92f980729e79b5d16d221038dbf2e8f9a9136afa072f9d5d6ed4cb984b126a09", size = 221521, upload-time = "2025-12-28T15:42:03.275Z" }, + { url = "https://files.pythonhosted.org/packages/aa/8e/ba0e597560c6563fc0adb902fda6526df5d4aa73bb10adf0574d03bd2206/coverage-7.13.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:97ab3647280d458a1f9adb85244e81587505a43c0c7cff851f5116cd2814b894", size = 218996, upload-time = "2025-12-28T15:42:04.978Z" }, + { url = "https://files.pythonhosted.org/packages/6b/8e/764c6e116f4221dc7aa26c4061181ff92edb9c799adae6433d18eeba7a14/coverage-7.13.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8f572d989142e0908e6acf57ad1b9b86989ff057c006d13b76c146ec6a20216a", size = 219326, upload-time = "2025-12-28T15:42:06.691Z" }, + { url = "https://files.pythonhosted.org/packages/4f/a6/6130dc6d8da28cdcbb0f2bf8865aeca9b157622f7c0031e48c6cf9a0e591/coverage-7.13.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d72140ccf8a147e94274024ff6fd8fb7811354cf7ef88b1f0a988ebaa5bc774f", size = 250374, upload-time = "2025-12-28T15:42:08.786Z" }, + { url = "https://files.pythonhosted.org/packages/82/2b/783ded568f7cd6b677762f780ad338bf4b4750205860c17c25f7c708995e/coverage-7.13.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d3c9f051b028810f5a87c88e5d6e9af3c0ff32ef62763bf15d29f740453ca909", size = 252882, upload-time = "2025-12-28T15:42:10.515Z" }, + { url = "https://files.pythonhosted.org/packages/cd/b2/9808766d082e6a4d59eb0cc881a57fc1600eb2c5882813eefff8254f71b5/coverage-7.13.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f398ba4df52d30b1763f62eed9de5620dcde96e6f491f4c62686736b155aa6e4", size = 254218, upload-time = "2025-12-28T15:42:12.208Z" }, + { url = "https://files.pythonhosted.org/packages/44/ea/52a985bb447c871cb4d2e376e401116520991b597c85afdde1ea9ef54f2c/coverage-7.13.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:132718176cc723026d201e347f800cd1a9e4b62ccd3f82476950834dad501c75", size = 250391, upload-time = "2025-12-28T15:42:14.21Z" }, + { url = "https://files.pythonhosted.org/packages/7f/1d/125b36cc12310718873cfc8209ecfbc1008f14f4f5fa0662aa608e579353/coverage-7.13.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9e549d642426e3579b3f4b92d0431543b012dcb6e825c91619d4e93b7363c3f9", size = 252239, upload-time = "2025-12-28T15:42:16.292Z" }, + { url = "https://files.pythonhosted.org/packages/6a/16/10c1c164950cade470107f9f14bbac8485f8fb8515f515fca53d337e4a7f/coverage-7.13.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:90480b2134999301eea795b3a9dbf606c6fbab1b489150c501da84a959442465", size = 250196, upload-time = "2025-12-28T15:42:18.54Z" }, + { url = "https://files.pythonhosted.org/packages/2a/c6/cd860fac08780c6fd659732f6ced1b40b79c35977c1356344e44d72ba6c4/coverage-7.13.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e825dbb7f84dfa24663dd75835e7257f8882629fc11f03ecf77d84a75134b864", size = 250008, upload-time = "2025-12-28T15:42:20.365Z" }, + { url = "https://files.pythonhosted.org/packages/f0/3a/a8c58d3d38f82a5711e1e0a67268362af48e1a03df27c03072ac30feefcf/coverage-7.13.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:623dcc6d7a7ba450bbdbeedbaa0c42b329bdae16491af2282f12a7e809be7eb9", size = 251671, upload-time = "2025-12-28T15:42:22.114Z" }, + { url = "https://files.pythonhosted.org/packages/f0/bc/fd4c1da651d037a1e3d53e8cb3f8182f4b53271ffa9a95a2e211bacc0349/coverage-7.13.1-cp314-cp314-win32.whl", hash = "sha256:6e73ebb44dca5f708dc871fe0b90cf4cff1a13f9956f747cc87b535a840386f5", size = 221777, upload-time = "2025-12-28T15:42:23.919Z" }, + { url = "https://files.pythonhosted.org/packages/4b/50/71acabdc8948464c17e90b5ffd92358579bd0910732c2a1c9537d7536aa6/coverage-7.13.1-cp314-cp314-win_amd64.whl", hash = "sha256:be753b225d159feb397bd0bf91ae86f689bad0da09d3b301478cd39b878ab31a", size = 222592, upload-time = "2025-12-28T15:42:25.619Z" }, + { url = "https://files.pythonhosted.org/packages/f7/c8/a6fb943081bb0cc926499c7907731a6dc9efc2cbdc76d738c0ab752f1a32/coverage-7.13.1-cp314-cp314-win_arm64.whl", hash = "sha256:228b90f613b25ba0019361e4ab81520b343b622fc657daf7e501c4ed6a2366c0", size = 221169, upload-time = "2025-12-28T15:42:27.629Z" }, + { url = "https://files.pythonhosted.org/packages/16/61/d5b7a0a0e0e40d62e59bc8c7aa1afbd86280d82728ba97f0673b746b78e2/coverage-7.13.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:60cfb538fe9ef86e5b2ab0ca8fc8d62524777f6c611dcaf76dc16fbe9b8e698a", size = 219730, upload-time = "2025-12-28T15:42:29.306Z" }, + { url = "https://files.pythonhosted.org/packages/a3/2c/8881326445fd071bb49514d1ce97d18a46a980712b51fee84f9ab42845b4/coverage-7.13.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:57dfc8048c72ba48a8c45e188d811e5efd7e49b387effc8fb17e97936dde5bf6", size = 220001, upload-time = "2025-12-28T15:42:31.319Z" }, + { url = "https://files.pythonhosted.org/packages/b5/d7/50de63af51dfa3a7f91cc37ad8fcc1e244b734232fbc8b9ab0f3c834a5cd/coverage-7.13.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3f2f725aa3e909b3c5fdb8192490bdd8e1495e85906af74fe6e34a2a77ba0673", size = 261370, upload-time = "2025-12-28T15:42:32.992Z" }, + { url = "https://files.pythonhosted.org/packages/e1/2c/d31722f0ec918fd7453b2758312729f645978d212b410cd0f7c2aed88a94/coverage-7.13.1-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9ee68b21909686eeb21dfcba2c3b81fee70dcf38b140dcd5aa70680995fa3aa5", size = 263485, upload-time = "2025-12-28T15:42:34.759Z" }, + { url = "https://files.pythonhosted.org/packages/fa/7a/2c114fa5c5fc08ba0777e4aec4c97e0b4a1afcb69c75f1f54cff78b073ab/coverage-7.13.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:724b1b270cb13ea2e6503476e34541a0b1f62280bc997eab443f87790202033d", size = 265890, upload-time = "2025-12-28T15:42:36.517Z" }, + { url = "https://files.pythonhosted.org/packages/65/d9/f0794aa1c74ceabc780fe17f6c338456bbc4e96bd950f2e969f48ac6fb20/coverage-7.13.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:916abf1ac5cf7eb16bc540a5bf75c71c43a676f5c52fcb9fe75a2bd75fb944e8", size = 260445, upload-time = "2025-12-28T15:42:38.646Z" }, + { url = "https://files.pythonhosted.org/packages/49/23/184b22a00d9bb97488863ced9454068c79e413cb23f472da6cbddc6cfc52/coverage-7.13.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:776483fd35b58d8afe3acbd9988d5de592ab6da2d2a865edfdbc9fdb43e7c486", size = 263357, upload-time = "2025-12-28T15:42:40.788Z" }, + { url = "https://files.pythonhosted.org/packages/7d/bd/58af54c0c9199ea4190284f389005779d7daf7bf3ce40dcd2d2b2f96da69/coverage-7.13.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:b6f3b96617e9852703f5b633ea01315ca45c77e879584f283c44127f0f1ec564", size = 260959, upload-time = "2025-12-28T15:42:42.808Z" }, + { url = "https://files.pythonhosted.org/packages/4b/2a/6839294e8f78a4891bf1df79d69c536880ba2f970d0ff09e7513d6e352e9/coverage-7.13.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:bd63e7b74661fed317212fab774e2a648bc4bb09b35f25474f8e3325d2945cd7", size = 259792, upload-time = "2025-12-28T15:42:44.818Z" }, + { url = "https://files.pythonhosted.org/packages/ba/c3/528674d4623283310ad676c5af7414b9850ab6d55c2300e8aa4b945ec554/coverage-7.13.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:933082f161bbb3e9f90d00990dc956120f608cdbcaeea15c4d897f56ef4fe416", size = 262123, upload-time = "2025-12-28T15:42:47.108Z" }, + { url = "https://files.pythonhosted.org/packages/06/c5/8c0515692fb4c73ac379d8dc09b18eaf0214ecb76ea6e62467ba7a1556ff/coverage-7.13.1-cp314-cp314t-win32.whl", hash = "sha256:18be793c4c87de2965e1c0f060f03d9e5aff66cfeae8e1dbe6e5b88056ec153f", size = 222562, upload-time = "2025-12-28T15:42:49.144Z" }, + { url = "https://files.pythonhosted.org/packages/05/0e/c0a0c4678cb30dac735811db529b321d7e1c9120b79bd728d4f4d6b010e9/coverage-7.13.1-cp314-cp314t-win_amd64.whl", hash = "sha256:0e42e0ec0cd3e0d851cb3c91f770c9301f48647cb2877cb78f74bdaa07639a79", size = 223670, upload-time = "2025-12-28T15:42:51.218Z" }, + { url = "https://files.pythonhosted.org/packages/f5/5f/b177aa0011f354abf03a8f30a85032686d290fdeed4222b27d36b4372a50/coverage-7.13.1-cp314-cp314t-win_arm64.whl", hash = "sha256:eaecf47ef10c72ece9a2a92118257da87e460e113b83cc0d2905cbbe931792b4", size = 221707, upload-time = "2025-12-28T15:42:53.034Z" }, + { url = "https://files.pythonhosted.org/packages/cc/48/d9f421cb8da5afaa1a64570d9989e00fb7955e6acddc5a12979f7666ef60/coverage-7.13.1-py3-none-any.whl", hash = "sha256:2016745cb3ba554469d02819d78958b571792bb68e31302610e898f80dd3a573", size = 210722, upload-time = "2025-12-28T15:42:54.901Z" }, ] [package.optional-dependencies] @@ -1141,7 +1148,7 @@ wheels = [ [[package]] name = "datamodel-code-generator" -version = "0.35.0" +version = "0.52.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "argcomplete" }, @@ -1155,38 +1162,38 @@ dependencies = [ { name = "pyyaml" }, { name = "tomli", marker = "python_full_version < '3.12'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/70/e1/dbf7c2edb1b1db1f4fd472ee92f985ec97d58902512013d9c4584108329c/datamodel_code_generator-0.35.0.tar.gz", hash = "sha256:46805fa2515d3871f6bfafce9aa63128e735a7a6a4cfcbf9c27b3794ee4ea846", size = 459915, upload-time = "2025-10-09T19:26:49.837Z" } +sdist = { url = "https://files.pythonhosted.org/packages/72/8f/7594b293c5da78c7b7685facfc07b96d03af9f833f5f5742a028b6a1d564/datamodel_code_generator-0.52.2.tar.gz", hash = "sha256:54b25789980deafed50c455c25a5d391c177a9f74f0e14b0edeb6db69f1185a9", size = 789610, upload-time = "2026-01-05T17:25:50.211Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/95/ef/0ed17459fe6076219fcd45f69a0bb4bd1cb041b39095ca2946808a9b5f04/datamodel_code_generator-0.35.0-py3-none-any.whl", hash = "sha256:c356d1e4a555f86667a4262db03d4598a30caeda8f51786555fd269c8abb806b", size = 121436, upload-time = "2025-10-09T19:26:48.437Z" }, + { url = "https://files.pythonhosted.org/packages/9e/8d/72a75327e05a26d5fb7fa511a7d78af18198dc2595bbb179da0c9e4b6fbc/datamodel_code_generator-0.52.2-py3-none-any.whl", hash = "sha256:3ee19ec38190b76bb4119aa67ddce25b3447dd4e5c6bfd47b03a9937c50f5aac", size = 249759, upload-time = "2026-01-05T17:25:48.845Z" }, ] [[package]] name = "debugpy" -version = "1.8.17" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/15/ad/71e708ff4ca377c4230530d6a7aa7992592648c122a2cd2b321cf8b35a76/debugpy-1.8.17.tar.gz", hash = "sha256:fd723b47a8c08892b1a16b2c6239a8b96637c62a59b94bb5dab4bac592a58a8e", size = 1644129, upload-time = "2025-09-17T16:33:20.633Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/38/36/b57c6e818d909f6e59c0182252921cf435e0951126a97e11de37e72ab5e1/debugpy-1.8.17-cp310-cp310-macosx_15_0_x86_64.whl", hash = "sha256:c41d2ce8bbaddcc0009cc73f65318eedfa3dbc88a8298081deb05389f1ab5542", size = 2098021, upload-time = "2025-09-17T16:33:22.556Z" }, - { url = "https://files.pythonhosted.org/packages/be/01/0363c7efdd1e9febd090bb13cee4fb1057215b157b2979a4ca5ccb678217/debugpy-1.8.17-cp310-cp310-manylinux_2_34_x86_64.whl", hash = "sha256:1440fd514e1b815edd5861ca394786f90eb24960eb26d6f7200994333b1d79e3", size = 3087399, upload-time = "2025-09-17T16:33:24.292Z" }, - { url = "https://files.pythonhosted.org/packages/79/bc/4a984729674aa9a84856650438b9665f9a1d5a748804ac6f37932ce0d4aa/debugpy-1.8.17-cp310-cp310-win32.whl", hash = "sha256:3a32c0af575749083d7492dc79f6ab69f21b2d2ad4cd977a958a07d5865316e4", size = 5230292, upload-time = "2025-09-17T16:33:26.137Z" }, - { url = "https://files.pythonhosted.org/packages/5d/19/2b9b3092d0cf81a5aa10c86271999453030af354d1a5a7d6e34c574515d7/debugpy-1.8.17-cp310-cp310-win_amd64.whl", hash = "sha256:a3aad0537cf4d9c1996434be68c6c9a6d233ac6f76c2a482c7803295b4e4f99a", size = 5261885, upload-time = "2025-09-17T16:33:27.592Z" }, - { url = "https://files.pythonhosted.org/packages/d8/53/3af72b5c159278c4a0cf4cffa518675a0e73bdb7d1cac0239b815502d2ce/debugpy-1.8.17-cp311-cp311-macosx_15_0_universal2.whl", hash = "sha256:d3fce3f0e3de262a3b67e69916d001f3e767661c6e1ee42553009d445d1cd840", size = 2207154, upload-time = "2025-09-17T16:33:29.457Z" }, - { url = "https://files.pythonhosted.org/packages/8f/6d/204f407df45600e2245b4a39860ed4ba32552330a0b3f5f160ae4cc30072/debugpy-1.8.17-cp311-cp311-manylinux_2_34_x86_64.whl", hash = "sha256:c6bdf134457ae0cac6fb68205776be635d31174eeac9541e1d0c062165c6461f", size = 3170322, upload-time = "2025-09-17T16:33:30.837Z" }, - { url = "https://files.pythonhosted.org/packages/f2/13/1b8f87d39cf83c6b713de2620c31205299e6065622e7dd37aff4808dd410/debugpy-1.8.17-cp311-cp311-win32.whl", hash = "sha256:e79a195f9e059edfe5d8bf6f3749b2599452d3e9380484cd261f6b7cd2c7c4da", size = 5155078, upload-time = "2025-09-17T16:33:33.331Z" }, - { url = "https://files.pythonhosted.org/packages/c2/c5/c012c60a2922cc91caa9675d0ddfbb14ba59e1e36228355f41cab6483469/debugpy-1.8.17-cp311-cp311-win_amd64.whl", hash = "sha256:b532282ad4eca958b1b2d7dbcb2b7218e02cb934165859b918e3b6ba7772d3f4", size = 5179011, upload-time = "2025-09-17T16:33:35.711Z" }, - { url = "https://files.pythonhosted.org/packages/08/2b/9d8e65beb2751876c82e1aceb32f328c43ec872711fa80257c7674f45650/debugpy-1.8.17-cp312-cp312-macosx_15_0_universal2.whl", hash = "sha256:f14467edef672195c6f6b8e27ce5005313cb5d03c9239059bc7182b60c176e2d", size = 2549522, upload-time = "2025-09-17T16:33:38.466Z" }, - { url = "https://files.pythonhosted.org/packages/b4/78/eb0d77f02971c05fca0eb7465b18058ba84bd957062f5eec82f941ac792a/debugpy-1.8.17-cp312-cp312-manylinux_2_34_x86_64.whl", hash = "sha256:24693179ef9dfa20dca8605905a42b392be56d410c333af82f1c5dff807a64cc", size = 4309417, upload-time = "2025-09-17T16:33:41.299Z" }, - { url = "https://files.pythonhosted.org/packages/37/42/c40f1d8cc1fed1e75ea54298a382395b8b937d923fcf41ab0797a554f555/debugpy-1.8.17-cp312-cp312-win32.whl", hash = "sha256:6a4e9dacf2cbb60d2514ff7b04b4534b0139facbf2abdffe0639ddb6088e59cf", size = 5277130, upload-time = "2025-09-17T16:33:43.554Z" }, - { url = "https://files.pythonhosted.org/packages/72/22/84263b205baad32b81b36eac076de0cdbe09fe2d0637f5b32243dc7c925b/debugpy-1.8.17-cp312-cp312-win_amd64.whl", hash = "sha256:e8f8f61c518952fb15f74a302e068b48d9c4691768ade433e4adeea961993464", size = 5319053, upload-time = "2025-09-17T16:33:53.033Z" }, - { url = "https://files.pythonhosted.org/packages/50/76/597e5cb97d026274ba297af8d89138dfd9e695767ba0e0895edb20963f40/debugpy-1.8.17-cp313-cp313-macosx_15_0_universal2.whl", hash = "sha256:857c1dd5d70042502aef1c6d1c2801211f3ea7e56f75e9c335f434afb403e464", size = 2538386, upload-time = "2025-09-17T16:33:54.594Z" }, - { url = "https://files.pythonhosted.org/packages/5f/60/ce5c34fcdfec493701f9d1532dba95b21b2f6394147234dce21160bd923f/debugpy-1.8.17-cp313-cp313-manylinux_2_34_x86_64.whl", hash = "sha256:3bea3b0b12f3946e098cce9b43c3c46e317b567f79570c3f43f0b96d00788088", size = 4292100, upload-time = "2025-09-17T16:33:56.353Z" }, - { url = "https://files.pythonhosted.org/packages/e8/95/7873cf2146577ef71d2a20bf553f12df865922a6f87b9e8ee1df04f01785/debugpy-1.8.17-cp313-cp313-win32.whl", hash = "sha256:e34ee844c2f17b18556b5bbe59e1e2ff4e86a00282d2a46edab73fd7f18f4a83", size = 5277002, upload-time = "2025-09-17T16:33:58.231Z" }, - { url = "https://files.pythonhosted.org/packages/46/11/18c79a1cee5ff539a94ec4aa290c1c069a5580fd5cfd2fb2e282f8e905da/debugpy-1.8.17-cp313-cp313-win_amd64.whl", hash = "sha256:6c5cd6f009ad4fca8e33e5238210dc1e5f42db07d4b6ab21ac7ffa904a196420", size = 5319047, upload-time = "2025-09-17T16:34:00.586Z" }, - { url = "https://files.pythonhosted.org/packages/de/45/115d55b2a9da6de812696064ceb505c31e952c5d89c4ed1d9bb983deec34/debugpy-1.8.17-cp314-cp314-macosx_15_0_universal2.whl", hash = "sha256:045290c010bcd2d82bc97aa2daf6837443cd52f6328592698809b4549babcee1", size = 2536899, upload-time = "2025-09-17T16:34:02.657Z" }, - { url = "https://files.pythonhosted.org/packages/5a/73/2aa00c7f1f06e997ef57dc9b23d61a92120bec1437a012afb6d176585197/debugpy-1.8.17-cp314-cp314-manylinux_2_34_x86_64.whl", hash = "sha256:b69b6bd9dba6a03632534cdf67c760625760a215ae289f7489a452af1031fe1f", size = 4268254, upload-time = "2025-09-17T16:34:04.486Z" }, - { url = "https://files.pythonhosted.org/packages/86/b5/ed3e65c63c68a6634e3ba04bd10255c8e46ec16ebed7d1c79e4816d8a760/debugpy-1.8.17-cp314-cp314-win32.whl", hash = "sha256:5c59b74aa5630f3a5194467100c3b3d1c77898f9ab27e3f7dc5d40fc2f122670", size = 5277203, upload-time = "2025-09-17T16:34:06.65Z" }, - { url = "https://files.pythonhosted.org/packages/b0/26/394276b71c7538445f29e792f589ab7379ae70fd26ff5577dfde71158e96/debugpy-1.8.17-cp314-cp314-win_amd64.whl", hash = "sha256:893cba7bb0f55161de4365584b025f7064e1f88913551bcd23be3260b231429c", size = 5318493, upload-time = "2025-09-17T16:34:08.483Z" }, - { url = "https://files.pythonhosted.org/packages/b0/d0/89247ec250369fc76db477720a26b2fce7ba079ff1380e4ab4529d2fe233/debugpy-1.8.17-py2.py3-none-any.whl", hash = "sha256:60c7dca6571efe660ccb7a9508d73ca14b8796c4ed484c2002abba714226cfef", size = 5283210, upload-time = "2025-09-17T16:34:25.835Z" }, +version = "1.8.19" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/73/75/9e12d4d42349b817cd545b89247696c67917aab907012ae5b64bbfea3199/debugpy-1.8.19.tar.gz", hash = "sha256:eea7e5987445ab0b5ed258093722d5ecb8bb72217c5c9b1e21f64efe23ddebdb", size = 1644590, upload-time = "2025-12-15T21:53:28.044Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bf/98/d57054371887f37d3c959a7a8dc3c76b763acb65f5e78d849d7db7cadc5b/debugpy-1.8.19-cp310-cp310-macosx_15_0_x86_64.whl", hash = "sha256:fce6da15d73be5935b4438435c53adb512326a3e11e4f90793ea87cd9f018254", size = 2098493, upload-time = "2025-12-15T21:53:30.149Z" }, + { url = "https://files.pythonhosted.org/packages/ee/dd/c517b9aa3500157a30e4f4c4f5149f880026bd039d2b940acd2383a85d8e/debugpy-1.8.19-cp310-cp310-manylinux_2_34_x86_64.whl", hash = "sha256:e24b1652a1df1ab04d81e7ead446a91c226de704ff5dde6bd0a0dbaab07aa3f2", size = 3087875, upload-time = "2025-12-15T21:53:31.511Z" }, + { url = "https://files.pythonhosted.org/packages/d8/57/3d5a5b0da9b63445253107ead151eff29190c6ad7440c68d1a59d56613aa/debugpy-1.8.19-cp310-cp310-win32.whl", hash = "sha256:327cb28c3ad9e17bc925efc7f7018195fd4787c2fe4b7af1eec11f1d19bdec62", size = 5239378, upload-time = "2025-12-15T21:53:32.979Z" }, + { url = "https://files.pythonhosted.org/packages/a6/36/7f9053c4c549160c87ae7e43800138f2695578c8b65947114c97250983b6/debugpy-1.8.19-cp310-cp310-win_amd64.whl", hash = "sha256:b7dd275cf2c99e53adb9654f5ae015f70415bbe2bacbe24cfee30d54b6aa03c5", size = 5271129, upload-time = "2025-12-15T21:53:35.085Z" }, + { url = "https://files.pythonhosted.org/packages/80/e2/48531a609b5a2aa94c6b6853afdfec8da05630ab9aaa96f1349e772119e9/debugpy-1.8.19-cp311-cp311-macosx_15_0_universal2.whl", hash = "sha256:c5dcfa21de1f735a4f7ced4556339a109aa0f618d366ede9da0a3600f2516d8b", size = 2207620, upload-time = "2025-12-15T21:53:37.1Z" }, + { url = "https://files.pythonhosted.org/packages/1b/d4/97775c01d56071969f57d93928899e5616a4cfbbf4c8cc75390d3a51c4a4/debugpy-1.8.19-cp311-cp311-manylinux_2_34_x86_64.whl", hash = "sha256:806d6800246244004625d5222d7765874ab2d22f3ba5f615416cf1342d61c488", size = 3170796, upload-time = "2025-12-15T21:53:38.513Z" }, + { url = "https://files.pythonhosted.org/packages/8d/7e/8c7681bdb05be9ec972bbb1245eb7c4c7b0679bb6a9e6408d808bc876d3d/debugpy-1.8.19-cp311-cp311-win32.whl", hash = "sha256:783a519e6dfb1f3cd773a9bda592f4887a65040cb0c7bd38dde410f4e53c40d4", size = 5164287, upload-time = "2025-12-15T21:53:40.857Z" }, + { url = "https://files.pythonhosted.org/packages/f2/a8/aaac7ff12ddf5d68a39e13a423a8490426f5f661384f5ad8d9062761bd8e/debugpy-1.8.19-cp311-cp311-win_amd64.whl", hash = "sha256:14035cbdbb1fe4b642babcdcb5935c2da3b1067ac211c5c5a8fdc0bb31adbcaa", size = 5188269, upload-time = "2025-12-15T21:53:42.359Z" }, + { url = "https://files.pythonhosted.org/packages/4a/15/d762e5263d9e25b763b78be72dc084c7a32113a0bac119e2f7acae7700ed/debugpy-1.8.19-cp312-cp312-macosx_15_0_universal2.whl", hash = "sha256:bccb1540a49cde77edc7ce7d9d075c1dbeb2414751bc0048c7a11e1b597a4c2e", size = 2549995, upload-time = "2025-12-15T21:53:43.773Z" }, + { url = "https://files.pythonhosted.org/packages/a7/88/f7d25c68b18873b7c53d7c156ca7a7ffd8e77073aa0eac170a9b679cf786/debugpy-1.8.19-cp312-cp312-manylinux_2_34_x86_64.whl", hash = "sha256:e9c68d9a382ec754dc05ed1d1b4ed5bd824b9f7c1a8cd1083adb84b3c93501de", size = 4309891, upload-time = "2025-12-15T21:53:45.26Z" }, + { url = "https://files.pythonhosted.org/packages/c5/4f/a65e973aba3865794da65f71971dca01ae66666132c7b2647182d5be0c5f/debugpy-1.8.19-cp312-cp312-win32.whl", hash = "sha256:6599cab8a783d1496ae9984c52cb13b7c4a3bd06a8e6c33446832a5d97ce0bee", size = 5286355, upload-time = "2025-12-15T21:53:46.763Z" }, + { url = "https://files.pythonhosted.org/packages/d8/3a/d3d8b48fec96e3d824e404bf428276fb8419dfa766f78f10b08da1cb2986/debugpy-1.8.19-cp312-cp312-win_amd64.whl", hash = "sha256:66e3d2fd8f2035a8f111eb127fa508469dfa40928a89b460b41fd988684dc83d", size = 5328239, upload-time = "2025-12-15T21:53:48.868Z" }, + { url = "https://files.pythonhosted.org/packages/71/3d/388035a31a59c26f1ecc8d86af607d0c42e20ef80074147cd07b180c4349/debugpy-1.8.19-cp313-cp313-macosx_15_0_universal2.whl", hash = "sha256:91e35db2672a0abaf325f4868fcac9c1674a0d9ad9bb8a8c849c03a5ebba3e6d", size = 2538859, upload-time = "2025-12-15T21:53:50.478Z" }, + { url = "https://files.pythonhosted.org/packages/4a/19/c93a0772d0962294f083dbdb113af1a7427bb632d36e5314297068f55db7/debugpy-1.8.19-cp313-cp313-manylinux_2_34_x86_64.whl", hash = "sha256:85016a73ab84dea1c1f1dcd88ec692993bcbe4532d1b49ecb5f3c688ae50c606", size = 4292575, upload-time = "2025-12-15T21:53:51.821Z" }, + { url = "https://files.pythonhosted.org/packages/5c/56/09e48ab796b0a77e3d7dc250f95251832b8bf6838c9632f6100c98bdf426/debugpy-1.8.19-cp313-cp313-win32.whl", hash = "sha256:b605f17e89ba0ecee994391194285fada89cee111cfcd29d6f2ee11cbdc40976", size = 5286209, upload-time = "2025-12-15T21:53:53.602Z" }, + { url = "https://files.pythonhosted.org/packages/fb/4e/931480b9552c7d0feebe40c73725dd7703dcc578ba9efc14fe0e6d31cfd1/debugpy-1.8.19-cp313-cp313-win_amd64.whl", hash = "sha256:c30639998a9f9cd9699b4b621942c0179a6527f083c72351f95c6ab1728d5b73", size = 5328206, upload-time = "2025-12-15T21:53:55.433Z" }, + { url = "https://files.pythonhosted.org/packages/f6/b9/cbec520c3a00508327476c7fce26fbafef98f412707e511eb9d19a2ef467/debugpy-1.8.19-cp314-cp314-macosx_15_0_universal2.whl", hash = "sha256:1e8c4d1bd230067bf1bbcdbd6032e5a57068638eb28b9153d008ecde288152af", size = 2537372, upload-time = "2025-12-15T21:53:57.318Z" }, + { url = "https://files.pythonhosted.org/packages/88/5e/cf4e4dc712a141e10d58405c58c8268554aec3c35c09cdcda7535ff13f76/debugpy-1.8.19-cp314-cp314-manylinux_2_34_x86_64.whl", hash = "sha256:d40c016c1f538dbf1762936e3aeb43a89b965069d9f60f9e39d35d9d25e6b809", size = 4268729, upload-time = "2025-12-15T21:53:58.712Z" }, + { url = "https://files.pythonhosted.org/packages/82/a3/c91a087ab21f1047db328c1d3eb5d1ff0e52de9e74f9f6f6fa14cdd93d58/debugpy-1.8.19-cp314-cp314-win32.whl", hash = "sha256:0601708223fe1cd0e27c6cce67a899d92c7d68e73690211e6788a4b0e1903f5b", size = 5286388, upload-time = "2025-12-15T21:54:00.687Z" }, + { url = "https://files.pythonhosted.org/packages/17/b8/bfdc30b6e94f1eff09f2dc9cc1f9cd1c6cde3d996bcbd36ce2d9a4956e99/debugpy-1.8.19-cp314-cp314-win_amd64.whl", hash = "sha256:8e19a725f5d486f20e53a1dde2ab8bb2c9607c40c00a42ab646def962b41125f", size = 5327741, upload-time = "2025-12-15T21:54:02.148Z" }, + { url = "https://files.pythonhosted.org/packages/25/3e/e27078370414ef35fafad2c06d182110073daaeb5d3bf734b0b1eeefe452/debugpy-1.8.19-py2.py3-none-any.whl", hash = "sha256:360ffd231a780abbc414ba0f005dad409e71c78637efe8f2bd75837132a41d38", size = 5292321, upload-time = "2025-12-15T21:54:16.024Z" }, ] [[package]] @@ -1262,7 +1269,7 @@ wheels = [ [[package]] name = "docling" -version = "2.63.0" +version = "2.67.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "accelerate" }, @@ -1291,18 +1298,18 @@ dependencies = [ { name = "requests" }, { name = "rtree" }, { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "scipy", version = "1.16.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "scipy", version = "1.17.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "tqdm" }, { name = "typer" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/18/c4/a8b7c66f0902ed4d0bcd87db94d3929539ac5fdff5325978744b30bee6b1/docling-2.63.0.tar.gz", hash = "sha256:5592c25e986ebf58811bcbfdbc8217d1a2074638b5412364968a1f1482994cc8", size = 250895, upload-time = "2025-11-20T14:43:53.131Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a7/eb/1900eaf576ab935d5b630a5ba6eb0e7063a2e1d36542df2f9ba572505d57/docling-2.67.0.tar.gz", hash = "sha256:d8c1992bbc090cee8c3f602fa63ac0c3127a65e3cabdf509d23ebb4e14feaf38", size = 259923, upload-time = "2026-01-09T08:37:37.984Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/69/fd/e5d23f8f12e18a8ada7d977cb86ae5f964b827ae71a42e3ee9f9e2d7d577/docling-2.63.0-py3-none-any.whl", hash = "sha256:59f39b6cf43f10f8c9e429c90f6973245c4c3752d5a03ca3e1732f6fb2905000", size = 268323, upload-time = "2025-11-20T14:43:51.823Z" }, + { url = "https://files.pythonhosted.org/packages/8d/99/fa9ac2cffaa77cf46b6414d0a32490b2580c8f3753ab1c65d983ee27f3a0/docling-2.67.0-py3-none-any.whl", hash = "sha256:d3ea4c0d7a64fce4b8f8b38415e075f6637384eaadb27acbf5621673e0da7669", size = 277794, upload-time = "2026-01-09T08:37:36.52Z" }, ] [[package]] name = "docling-core" -version = "2.52.0" +version = "2.59.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jsonref" }, @@ -1316,9 +1323,9 @@ dependencies = [ { name = "typer" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9e/cd/85cf1d4767feed71cb42f1fd1167a8dd1b153be48f5e2a26cc1aa28c5ee6/docling_core-2.52.0.tar.gz", hash = "sha256:96a90b1c1232855f7efa9c94fb0b8b6daffd65d33ea4ef2ab248b475509c50b1", size = 189781, upload-time = "2025-11-20T14:16:30.12Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/ae/b1216598ab2f2a0fbe5fe974bcd65d9407f26fff0ba89cf6d8d760601a0a/docling_core-2.59.0.tar.gz", hash = "sha256:08900edbaf86a0acdd3d1fb5d47284f3826f68638245ba4a03aba1558d0067d9", size = 231580, upload-time = "2026-01-12T16:40:21.321Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/22/2a/6568c5275300e9c1cc731905d1e26faf8ced36fb0a5237d7102fd56183b3/docling_core-2.52.0-py3-none-any.whl", hash = "sha256:b4502dfc27c8a9f5cc0efc0664d2fa3c67128038505ebc894d0d231a403467d1", size = 190678, upload-time = "2025-11-20T14:16:28.414Z" }, + { url = "https://files.pythonhosted.org/packages/e3/e7/27ba60d253b65415e6e709fda5ddfc1baf82fa9f8fbd8ae14b9bbbba5474/docling_core-2.59.0-py3-none-any.whl", hash = "sha256:014e1075c41fdac0932f25226d6c0972cb9772d866efcf8d04ca2abe246797c1", size = 223304, upload-time = "2026-01-12T16:40:19.488Z" }, ] [package.optional-dependencies] @@ -1327,7 +1334,6 @@ chunking = [ { name = "transformers" }, { name = "tree-sitter" }, { name = "tree-sitter-c" }, - { name = "tree-sitter-java" }, { name = "tree-sitter-javascript" }, { name = "tree-sitter-python" }, { name = "tree-sitter-typescript" }, @@ -1335,7 +1341,7 @@ chunking = [ [[package]] name = "docling-ibm-models" -version = "3.10.2" +version = "3.10.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "accelerate" }, @@ -1355,14 +1361,14 @@ dependencies = [ { name = "tqdm" }, { name = "transformers" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ba/81/e1fddd051c0af6a28d52c01b360867633c8091e594563b1dabb78f3730ab/docling_ibm_models-3.10.2.tar.gz", hash = "sha256:977591cb57f7b442af000614bbdb5cafce9973b2edff6d0b4c3cfafb638ed335", size = 87712, upload-time = "2025-10-28T10:34:38.463Z" } +sdist = { url = "https://files.pythonhosted.org/packages/11/36/6335f0bfa0ed92cd4bddacf0e391e2b41707b4409de327e035f93a9e310d/docling_ibm_models-3.10.3.tar.gz", hash = "sha256:6be756e45df155a367087b93e0e5f2d65905e7e81a5f57c1d3ae57096631655a", size = 87706, upload-time = "2025-12-01T17:04:43.511Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/76/3fe39f06350fd6118babfa0de85b100dbc4939990af4a738ad5003a3ec88/docling_ibm_models-3.10.2-py3-none-any.whl", hash = "sha256:b2ac6fbd9fb0729320ae4970891b96684a2375841b9ba2c316d2389f8b8ef796", size = 87357, upload-time = "2025-10-28T10:34:36.967Z" }, + { url = "https://files.pythonhosted.org/packages/cb/a8/cc3d1e8bc665a7643de1201c6460b3fd7afebb924884d4a048e26f8e5225/docling_ibm_models-3.10.3-py3-none-any.whl", hash = "sha256:e034d1398c99059998da18e38ef80af8a5d975f04de17f6e93efa075fb29cac4", size = 87356, upload-time = "2025-12-01T17:04:41.886Z" }, ] [[package]] name = "docling-parse" -version = "4.7.1" +version = "4.7.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "docling-core" }, @@ -1371,34 +1377,34 @@ dependencies = [ { name = "pywin32", marker = "sys_platform == 'win32'" }, { name = "tabulate" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/10/75/ebabc9abb7153c4e08e2207635b268d8651322432173458e3b7111f99dae/docling_parse-4.7.1.tar.gz", hash = "sha256:90494ecbffb46b574c44ef5ef55f5b4897a9a46a009ddf40fef8b2536894574e", size = 67174375, upload-time = "2025-11-05T18:25:42.742Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/28/73/2e95c851685e26ab1d2958498fb6adfc91ea86cfdada7818965f32603138/docling_parse-4.7.1-cp310-cp310-macosx_13_0_x86_64.whl", hash = "sha256:a0ddff93a3485d7248c2e3b850959c41e8781eb812a73e7bba470bbaf4dde7bf", size = 14737478, upload-time = "2025-11-05T18:24:24.579Z" }, - { url = "https://files.pythonhosted.org/packages/d9/c4/432474b9701b535451983922fa2303d69a12e6cf855186b99da7e5d64d02/docling_parse-4.7.1-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:788465f224b24c9375c67682db57b3e1413ffe2d37561d5d5b972d424c62bc27", size = 14612988, upload-time = "2025-11-05T18:24:27.818Z" }, - { url = "https://files.pythonhosted.org/packages/25/8d/98da05c27011350df6aceb57eb6b046ca895a10bc259efc5af731ed038a4/docling_parse-4.7.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d381a0767530e291053427f9c0b70cb68d11112dc3899e13cd70c9a64579d49", size = 15063003, upload-time = "2025-11-05T18:24:29.725Z" }, - { url = "https://files.pythonhosted.org/packages/f7/d7/2c72c6f2363ab9354365fc1c72b093ddd6429102a2d2729c8c5097364688/docling_parse-4.7.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9bf1cdef21b4420cfeb1224176bb4c9bc0edf7782e234796635ba55fb75dfab9", size = 15135209, upload-time = "2025-11-05T18:24:31.701Z" }, - { url = "https://files.pythonhosted.org/packages/d2/f4/6dff53e036ec71335a2a655b05a67d56863dcfef74e083413a4f8bc36a9e/docling_parse-4.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:613d8d6d1bccf2e70460b534812bae00c0e1efed23c1fe7910a517c8beb10ce3", size = 16142981, upload-time = "2025-11-05T18:24:33.639Z" }, - { url = "https://files.pythonhosted.org/packages/22/18/29f261fc08e7b0e138adf30e2c1bd6eb8958bea9d625833708c573d79b62/docling_parse-4.7.1-cp311-cp311-macosx_13_0_x86_64.whl", hash = "sha256:af5199bed00040e6184f99a9e040a11d0b85b08100c47ceb3c16d6616668510f", size = 14738391, upload-time = "2025-11-05T18:24:35.791Z" }, - { url = "https://files.pythonhosted.org/packages/d0/67/72d89915a941581959750707eb579c84a28105a13f134ad6de41aeef33e1/docling_parse-4.7.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:5d058711998205dbc349b6c7100e0d7734b46ec0bd960f82f07694bfa52f156a", size = 14614881, upload-time = "2025-11-05T18:24:38.135Z" }, - { url = "https://files.pythonhosted.org/packages/c1/2c/cdc92e606cf3755077e361ee239c01dbe0fff5978aa030ce1f6debe8fa06/docling_parse-4.7.1-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:256d4f942045c93d26a397e3cc2739d2fa6045d3b2441b12a3c4cf524cc636f5", size = 14980549, upload-time = "2025-11-05T18:24:40.317Z" }, - { url = "https://files.pythonhosted.org/packages/c2/cc/3cde0ce6261ba2f76001d5b51df32e666fb25cf05aae4006bc7cca23ec9a/docling_parse-4.7.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:673eb110856541e30cf510da43acb90969ef32ddb6e28e53aa8a0fd603c2ccfa", size = 15092011, upload-time = "2025-11-05T18:24:42.91Z" }, - { url = "https://files.pythonhosted.org/packages/be/a3/c033b17d371b06ad5c457599dd384a7695dfd7996266c4372a981c094ec1/docling_parse-4.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:a10525146ae60a4d6cc38b9dfe014f07175c4d8553c8f4dc40793c2e512053b4", size = 16144002, upload-time = "2025-11-05T18:24:46.76Z" }, - { url = "https://files.pythonhosted.org/packages/cc/82/b34bf259a4c30e5985ba4c8171c46e11200c98c7f15ae57af7a91e375aee/docling_parse-4.7.1-cp312-cp312-macosx_13_0_x86_64.whl", hash = "sha256:22ef5777765c23c6d9c264fec24db376e713cbaebff5c2c3a2469c7b0b7d4091", size = 14741116, upload-time = "2025-11-05T18:24:49.053Z" }, - { url = "https://files.pythonhosted.org/packages/69/52/4554076b9c39a46b190eafb5dbb5362c416c2b76febedc3774c0528b8102/docling_parse-4.7.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:d7bbfe58706e9db185c3b0be5a6a4550aa631fdb95edfcba562e2d80b70006dc", size = 14615796, upload-time = "2025-11-05T18:24:50.921Z" }, - { url = "https://files.pythonhosted.org/packages/f8/a7/1dfee55db15b4c40ec1cfe382cf587216fa9eb82ab84060bd2d3ac5033f6/docling_parse-4.7.1-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34e22fec61ee0bc3e0279c5a95ff9748b1320858f4f842d92ffcb9612c5e36f", size = 14979954, upload-time = "2025-11-05T18:24:53.319Z" }, - { url = "https://files.pythonhosted.org/packages/b8/e1/bac7161d29586437d8eb152b67cf8025e29664b37e7c1e2fc35a53624b35/docling_parse-4.7.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1cd331851d9ed135db8fbd5ba73816dfe99ba34e6b3ce7997aad58ce58ae5612", size = 15091614, upload-time = "2025-11-05T18:24:55.406Z" }, - { url = "https://files.pythonhosted.org/packages/ce/9e/ab548db9ad1a29f932fd0a658fa019b5a75065d1e3b364a179d0e2313d70/docling_parse-4.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:922dd46e5add46efba47cc0b01bacc3e3c4f41bae5f8cb3edbcbf709a29aa229", size = 16146366, upload-time = "2025-11-05T18:24:58.027Z" }, - { url = "https://files.pythonhosted.org/packages/cc/a6/b75ca24cce323e9a9fd70142802e8c19fa59398a87c461f4443d55a20195/docling_parse-4.7.1-cp313-cp313-macosx_13_0_x86_64.whl", hash = "sha256:0b4635aceb767f0feb9d98bf2530b8e85b50fc9d82b2891f314d918eaa54eb1c", size = 14741080, upload-time = "2025-11-05T18:25:00.054Z" }, - { url = "https://files.pythonhosted.org/packages/d2/4a/c22452cab8dd075fcbd08543c43d894a0d613df03b6c455660d86b60141e/docling_parse-4.7.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:704ba385a342c4fa1ce7291088920bd4b171e7be7777cb9d55e6aa5fe30eb630", size = 14615772, upload-time = "2025-11-05T18:25:02.379Z" }, - { url = "https://files.pythonhosted.org/packages/25/e4/0b36b5bbeb9ec85083327b00cd0025e9b6208ad63faf0bedb4ef6b167289/docling_parse-4.7.1-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e12a1e8d9c8665fcd9516b68e550c66fcd48af5deb84565676b15b04bf4231a4", size = 14980616, upload-time = "2025-11-05T18:25:04.919Z" }, - { url = "https://files.pythonhosted.org/packages/63/92/730b0e0ee986ec4b7001a7478638ee562dbbb92d18442a74bc2130818860/docling_parse-4.7.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:19f77a7c5ad1fb40370535687550a6d9cb5904dcf934ced4817c6c3e78d51723", size = 15091869, upload-time = "2025-11-05T18:25:07.468Z" }, - { url = "https://files.pythonhosted.org/packages/9f/67/2b4bbe81e9f4e37dabd76acd30617550779208c52b30fbf9d19b40b444ef/docling_parse-4.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:8c39fbdd093fa67117a1264b2c1425749224d358cfd6ddcc483bd9da546f2d96", size = 16146277, upload-time = "2025-11-05T18:25:09.697Z" }, - { url = "https://files.pythonhosted.org/packages/75/4b/d709c1cd5f3d3f881b399e4e8ab567b3e5688c31167cb2484859fde38867/docling_parse-4.7.1-cp314-cp314-macosx_13_0_x86_64.whl", hash = "sha256:cbc37e593c4bc376d9e9e550bf7c3cd9293b66a9575a64b6dcca17b3949c7eb9", size = 14741350, upload-time = "2025-11-05T18:25:13.08Z" }, - { url = "https://files.pythonhosted.org/packages/83/8c/a4ddbb7f3048d6fd1917adb3b0b7b22dea962694dc1c207eac90b8548b9d/docling_parse-4.7.1-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:5aa6a72d67f516447ab7b74b42f1d49029d4807f0c188fb319087f33db781cd4", size = 14616639, upload-time = "2025-11-05T18:25:15.074Z" }, - { url = "https://files.pythonhosted.org/packages/3b/dc/55b330b408820a33af3db0dfac973db32c74361fa44b628f2555eb1caab4/docling_parse-4.7.1-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:95c2eb61afbcc238b3a37dfccfc5c7d6e069c8fa6b8c87f37b4240594446f4a6", size = 14980770, upload-time = "2025-11-05T18:25:17.616Z" }, - { url = "https://files.pythonhosted.org/packages/f0/da/a7b03b4e3c369697a4ed85c99317675895acb74c4c6e1106edd34e12382b/docling_parse-4.7.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50a6cf0d9c6dea0d136703b32708a50edc0c576a9fc96aa39c43605c84acb40b", size = 15091853, upload-time = "2025-11-05T18:25:19.939Z" }, - { url = "https://files.pythonhosted.org/packages/43/3f/8f1165dc52b10b378bd3c63c6362b0e9b0fe8886366250e0fd5044b31e59/docling_parse-4.7.1-cp314-cp314-win_amd64.whl", hash = "sha256:52f67604daf69b785761b7aa96e6035f705b7f51135cf76d825b8de59c0dfa54", size = 16786477, upload-time = "2025-11-05T18:25:22.305Z" }, - { url = "https://files.pythonhosted.org/packages/bd/29/01bed081d633571e095bc565c6e0053699b76383c9c11eba53f2d84a244a/docling_parse-4.7.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:79075b44f3f9b4a2471ace635406d41a562654869019d190f79d17240d0139c6", size = 18059708, upload-time = "2025-11-05T18:25:36.991Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/0f/78/2c7fb2680c308eab60c6e8a47cb00d1a6ed2e6282beca54bb8f8167f1b0d/docling_parse-4.7.2.tar.gz", hash = "sha256:1ce6271686b0e21e0eebb6fc677730460771b48cb7fdc535670d4f5efc901154", size = 67196916, upload-time = "2025-12-02T16:40:27.877Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/07/37/c4b357bde52a9bf6eda3971512b7d2973028f23a66c68bd747f2848e3d79/docling_parse-4.7.2-cp310-cp310-macosx_13_0_x86_64.whl", hash = "sha256:f7b2ccdeee611a44de7fc92bf5dbc1d316aae40ce6ce88ea9667289397c60701", size = 14737518, upload-time = "2025-12-02T16:39:07.325Z" }, + { url = "https://files.pythonhosted.org/packages/bc/e0/16bb808495835030ed079127a27e3e50a804e385773844bc47dd7db1cbc5/docling_parse-4.7.2-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:ba17dae0e88fcac2d5df082b9c4b6ae9deb81e59a3122cf0d1d625be65710c7b", size = 14613211, upload-time = "2025-12-02T16:39:09.771Z" }, + { url = "https://files.pythonhosted.org/packages/32/12/6c90c544b28829d5a85c591cf416daddcf2c9a27c37a4e4056a887bdca95/docling_parse-4.7.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c329841481e6aef53097701d6f9e14f11bfbe2b505b30d78bc09e1602a1dac07", size = 15062991, upload-time = "2025-12-02T16:39:11.834Z" }, + { url = "https://files.pythonhosted.org/packages/05/49/b71a4ed0d8c9afb3cdb6796ca3c4d575bdd53957b446d8e1ae27564f0668/docling_parse-4.7.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4bd7eec8db1232484fef05a9f982eeec8fd301a3946b917c32f4cbe25da63d2f", size = 15135636, upload-time = "2025-12-02T16:39:13.973Z" }, + { url = "https://files.pythonhosted.org/packages/12/e7/06d96dced84436cd1ceb54bbac419f11f803ff9b2ea5cb1079fec932667d/docling_parse-4.7.2-cp310-cp310-win_amd64.whl", hash = "sha256:30402479c2100d90bce834df6fdf1362c77a57ae2d8a0d303528585544ba1ecc", size = 16143143, upload-time = "2025-12-02T16:39:16.271Z" }, + { url = "https://files.pythonhosted.org/packages/8a/46/f3a8fc2f6a705018f6a8baaf807e60f64748960afc5529d1ba95b1d066dc/docling_parse-4.7.2-cp311-cp311-macosx_13_0_x86_64.whl", hash = "sha256:9f9f19f9be4c41c69911d496654bf14b90e112a36ba8179b73479b00a12d0c1c", size = 14738399, upload-time = "2025-12-02T16:39:18.659Z" }, + { url = "https://files.pythonhosted.org/packages/64/d3/ed84b5620bf430e37bb6eb35e623827dab95266a8c467bf3f452db5ce466/docling_parse-4.7.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:386c251245e7e7d317161c343985a8b3eb2773e8e997a77fcd991e1ff6d89d3e", size = 14615076, upload-time = "2025-12-02T16:39:21.119Z" }, + { url = "https://files.pythonhosted.org/packages/7e/8b/3e7451150936f33a8b5651e0045ded19acf24e6dc819f85da0ac67bba7da/docling_parse-4.7.2-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b200b22b1422b036c079fae6277e02eedeb678b8faa0073e96e1e7f1cf4e5693", size = 14981232, upload-time = "2025-12-02T16:39:23.684Z" }, + { url = "https://files.pythonhosted.org/packages/c6/b0/3dfe484ccdcc14a424ed44d114976753c1ff5a762108cc417036910f1368/docling_parse-4.7.2-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:058747f51d2e15eed665070eb0cfeef140167b8d010d7640c82bb42dfd332e1d", size = 15092565, upload-time = "2025-12-02T16:39:26.267Z" }, + { url = "https://files.pythonhosted.org/packages/32/8c/d0c2d9ee6b10d389b5a2188128dec4b19a5f250b2019ef29662b89693a3f/docling_parse-4.7.2-cp311-cp311-win_amd64.whl", hash = "sha256:243a61c65878938bad3d032f9dcf7e285e5b410e0bdca931d3048a667f72b125", size = 16144235, upload-time = "2025-12-02T16:39:29.017Z" }, + { url = "https://files.pythonhosted.org/packages/79/e6/4de5771d10ea788b790b1327c158bbd71e347a2fd92baeaa3ba06b9a6877/docling_parse-4.7.2-cp312-cp312-macosx_13_0_x86_64.whl", hash = "sha256:f98da4346d78af01f0df50b929dd369f5ce53f9c084bba868ca0f7949d8ec71b", size = 14741115, upload-time = "2025-12-02T16:39:31.304Z" }, + { url = "https://files.pythonhosted.org/packages/ce/96/9460e3f02f01ee6dea2993d85aca37fa90bbc0de25ddf94ef253058e8e18/docling_parse-4.7.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:b7fd5c13303635761c5c396eeea0ca8426116c828cce53936db37ea598087ce2", size = 14616018, upload-time = "2025-12-02T16:39:33.344Z" }, + { url = "https://files.pythonhosted.org/packages/e5/7b/c6e0809f4b4e0340a82b6a6cd5f33904e746a4962bcacb7861d8562acd5c/docling_parse-4.7.2-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:acd58f3fdf8d81ebf8ab550706603bcfa531c6d940b119d8686225f91a0b6a7c", size = 14980316, upload-time = "2025-12-02T16:39:35.974Z" }, + { url = "https://files.pythonhosted.org/packages/98/6b/19e3c113b384347364410f0f884fb45fd32f36cdc29f9b0d732b64d00b9e/docling_parse-4.7.2-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:45505266305573d03a7950f38303c400e3d6c068bf9fc09608776b3c06d5d774", size = 15091572, upload-time = "2025-12-02T16:39:38.024Z" }, + { url = "https://files.pythonhosted.org/packages/4b/dc/913ccaec56ff11aa5842fb8f64ae1b70acce68cd92ed68af11b23b7b44c2/docling_parse-4.7.2-cp312-cp312-win_amd64.whl", hash = "sha256:a0cfcd797de3d714cc0564d0971537aea27629472bda7db9833842cb87229cc9", size = 16146497, upload-time = "2025-12-02T16:39:40.283Z" }, + { url = "https://files.pythonhosted.org/packages/2b/51/5fd1e6f3f31b39382b61a6e4d13f2758e57528750b3f87a390a7694eb866/docling_parse-4.7.2-cp313-cp313-macosx_13_0_x86_64.whl", hash = "sha256:186d33bd3ee862cc5e9e37c8f0c07b4031a1c311c833c8b0d642e72877ce647b", size = 14741162, upload-time = "2025-12-02T16:39:42.557Z" }, + { url = "https://files.pythonhosted.org/packages/24/43/aa0d91a7bf1d9e0cafe5405c398ae9828bddbf39ba66159786be1201b892/docling_parse-4.7.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:1184aeafd6d051905ab12cc9834d14e54e7f2eeb8aa9db41172c353ef5404d1f", size = 14616094, upload-time = "2025-12-02T16:39:44.712Z" }, + { url = "https://files.pythonhosted.org/packages/8a/d0/01c2f3c31c828e203abf01317b6e6664faf9a693299e0769d0354f08ab58/docling_parse-4.7.2-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c94a9ca03c8f59955c9a7e8707c33617d69edc8f5557d05b3eaddb43aea3061a", size = 14981616, upload-time = "2025-12-02T16:39:47.387Z" }, + { url = "https://files.pythonhosted.org/packages/e0/5b/25f89921e3dde90f5962a539cb0e33652c90cdb0f96f07224fc042b542f8/docling_parse-4.7.2-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ff2652ab0f357750389e49d3d31a057ae70d4d3a972c3e5f76341a8a5f4a41b0", size = 15092401, upload-time = "2025-12-02T16:39:49.676Z" }, + { url = "https://files.pythonhosted.org/packages/18/2e/7b9665a674b9c2b87e6a5f5deace11ee11aa41cb82d4e9da4846ef70d2d3/docling_parse-4.7.2-cp313-cp313-win_amd64.whl", hash = "sha256:30b9e68bdd59c44db72713fb9786c9430f16c3b222978f0afa9515857986b6d6", size = 16146576, upload-time = "2025-12-02T16:39:52.355Z" }, + { url = "https://files.pythonhosted.org/packages/40/77/57fb6b61d20031e59735fd310ebac2b678c800b89bc3ad935699cfbbc387/docling_parse-4.7.2-cp314-cp314-macosx_13_0_x86_64.whl", hash = "sha256:44d3ea53edd3caa0d64669858583605a6e4ef31df3b12c6c05d923d6a2e0ad5d", size = 14741350, upload-time = "2025-12-02T16:39:54.504Z" }, + { url = "https://files.pythonhosted.org/packages/93/68/eb5e37be1fb8f060951cc542aa21a9e46ac60c87b6a3b2bf0808fe8a7ca9/docling_parse-4.7.2-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:f7834ab19727eb6d0fa6584e05cdbdcf68017bf24e71232a538805f663181a49", size = 14616787, upload-time = "2025-12-02T16:39:57.16Z" }, + { url = "https://files.pythonhosted.org/packages/98/e7/a79392f9500625270466dd035593c480929e32cc257b769403d5f8ac9151/docling_parse-4.7.2-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5e83b2a981c6b402666331ff92d98443492722c659f8867088cad40f2713cdd4", size = 14981588, upload-time = "2025-12-02T16:40:00.187Z" }, + { url = "https://files.pythonhosted.org/packages/a4/8c/0450290ee2ac0a2140c4ebc3200ae7c854ee13edd6419405fe5495a36747/docling_parse-4.7.2-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:30e1a531d183fc8e8ab37d596484d99c48d0cbf00805b4904ce7ff7d39573441", size = 15092383, upload-time = "2025-12-02T16:40:02.483Z" }, + { url = "https://files.pythonhosted.org/packages/63/ad/f8db83c3b57fc12c5c38aa7794f0e0d47fd94ad31bfa6d2392bfca7d13ac/docling_parse-4.7.2-cp314-cp314-win_amd64.whl", hash = "sha256:5884094ac8b03066f018956e9fea215ca396e74c9ba51b93be4940797994ce87", size = 16786739, upload-time = "2025-12-02T16:40:05.105Z" }, + { url = "https://files.pythonhosted.org/packages/e1/62/c635fc1d5d6e11970e5aafae8ab31dc0514dd13dae11b57b089002150a54/docling_parse-4.7.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:8759c64d66594da1d2361513fc6b0778d262710dcc6b9062e08da1f79c336e35", size = 18060135, upload-time = "2025-12-02T16:40:21.562Z" }, ] [[package]] @@ -1460,19 +1466,19 @@ wheels = [ [[package]] name = "faker" -version = "38.2.0" +version = "40.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "tzdata" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/64/27/022d4dbd4c20567b4c294f79a133cc2f05240ea61e0d515ead18c995c249/faker-38.2.0.tar.gz", hash = "sha256:20672803db9c7cb97f9b56c18c54b915b6f1d8991f63d1d673642dc43f5ce7ab", size = 1941469, upload-time = "2025-11-19T16:37:31.892Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/1d/aa43ef59589ddf3647df918143f1bac9eb004cce1c43124ee3347061797d/faker-40.1.0.tar.gz", hash = "sha256:c402212a981a8a28615fea9120d789e3f6062c0c259a82bfb8dff5d273e539d2", size = 1948784, upload-time = "2025-12-29T18:06:00.659Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/17/93/00c94d45f55c336434a15f98d906387e87ce28f9918e4444829a8fda432d/faker-38.2.0-py3-none-any.whl", hash = "sha256:35fe4a0a79dee0dc4103a6083ee9224941e7d3594811a50e3969e547b0d2ee65", size = 1980505, upload-time = "2025-11-19T16:37:30.208Z" }, + { url = "https://files.pythonhosted.org/packages/fc/23/e22da510e1ec1488966330bf76d8ff4bd535cbfc93660eeb7657761a1bb2/faker-40.1.0-py3-none-any.whl", hash = "sha256:a616d35818e2a2387c297de80e2288083bc915e24b7e39d2fb5bc66cce3a929f", size = 1985317, upload-time = "2025-12-29T18:05:58.831Z" }, ] [[package]] name = "fastapi" -version = "0.122.0" +version = "0.128.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-doc" }, @@ -1480,9 +1486,9 @@ dependencies = [ { name = "starlette" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b2/de/3ee97a4f6ffef1fb70bf20561e4f88531633bb5045dc6cebc0f8471f764d/fastapi-0.122.0.tar.gz", hash = "sha256:cd9b5352031f93773228af8b4c443eedc2ac2aa74b27780387b853c3726fb94b", size = 346436, upload-time = "2025-11-24T19:17:47.95Z" } +sdist = { url = "https://files.pythonhosted.org/packages/52/08/8c8508db6c7b9aae8f7175046af41baad690771c9bcde676419965e338c7/fastapi-0.128.0.tar.gz", hash = "sha256:1cc179e1cef10a6be60ffe429f79b829dce99d8de32d7acb7e6c8dfdf7f2645a", size = 365682, upload-time = "2025-12-27T15:21:13.714Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7a/93/aa8072af4ff37b795f6bbf43dcaf61115f40f49935c7dbb180c9afc3f421/fastapi-0.122.0-py3-none-any.whl", hash = "sha256:a456e8915dfc6c8914a50d9651133bd47ec96d331c5b44600baa635538a30d67", size = 110671, upload-time = "2025-11-24T19:17:45.96Z" }, + { url = "https://files.pythonhosted.org/packages/5c/05/5cbb59154b093548acd0f4c7c474a118eda06da25aa75c616b72d8fcd92a/fastapi-0.128.0-py3-none-any.whl", hash = "sha256:aebd93f9716ee3b4f4fcfe13ffb7cf308d99c9f3ab5622d8877441072561582d", size = 103094, upload-time = "2025-12-27T15:21:12.154Z" }, ] [[package]] @@ -1559,11 +1565,11 @@ wheels = [ [[package]] name = "filelock" -version = "3.20.0" +version = "3.20.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/58/46/0028a82567109b5ef6e4d2a1f04a583fb513e6cf9527fcdd09afd817deeb/filelock-3.20.0.tar.gz", hash = "sha256:711e943b4ec6be42e1d4e6690b48dc175c822967466bb31c0c293f34334c13f4", size = 18922, upload-time = "2025-10-08T18:03:50.056Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1d/65/ce7f1b70157833bf3cb851b556a37d4547ceafc158aa9b34b36782f23696/filelock-3.20.3.tar.gz", hash = "sha256:18c57ee915c7ec61cff0ecf7f0f869936c7c30191bb0cf406f1341778d0834e1", size = 19485, upload-time = "2026-01-09T17:55:05.421Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/76/91/7216b27286936c16f5b4d0c530087e4a54eead683e6b0b73dd0c64844af6/filelock-3.20.0-py3-none-any.whl", hash = "sha256:339b4732ffda5cd79b13f4e2711a31b0365ce445d95d243bb996273d072546a2", size = 16054, upload-time = "2025-10-08T18:03:48.35Z" }, + { url = "https://files.pythonhosted.org/packages/b5/36/7fb70f04bf00bc646cd5bb45aa9eddb15e19437a28b8fb2b4a5249fac770/filelock-3.20.3-py3-none-any.whl", hash = "sha256:4b0dda527ee31078689fc205ec4f1c1bf7d56cf88b6dc9426c4f230e46c2dce1", size = 16701, upload-time = "2026-01-09T17:55:04.334Z" }, ] [[package]] @@ -1577,11 +1583,10 @@ wheels = [ [[package]] name = "flatbuffers" -version = "25.9.23" +version = "25.12.19" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9d/1f/3ee70b0a55137442038f2a33469cc5fddd7e0ad2abf83d7497c18a2b6923/flatbuffers-25.9.23.tar.gz", hash = "sha256:676f9fa62750bb50cf531b42a0a2a118ad8f7f797a511eda12881c016f093b12", size = 22067, upload-time = "2025-09-24T05:25:30.106Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ee/1b/00a78aa2e8fbd63f9af08c9c19e6deb3d5d66b4dda677a0f61654680ee89/flatbuffers-25.9.23-py2.py3-none-any.whl", hash = "sha256:255538574d6cb6d0a79a17ec8bc0d30985913b87513a01cce8bcdb6b4c44d0e2", size = 30869, upload-time = "2025-09-24T05:25:28.912Z" }, + { url = "https://files.pythonhosted.org/packages/e8/2d/d2a548598be01649e2d46231d151a6c56d10b964d94043a335ae56ea2d92/flatbuffers-25.12.19-py2.py3-none-any.whl", hash = "sha256:7634f50c427838bb021c2d66a3d1168e9d199b0607e6329399f04846d42e20b4", size = 26661, upload-time = "2025-12-19T23:16:13.622Z" }, ] [[package]] @@ -1707,11 +1712,11 @@ wheels = [ [[package]] name = "fsspec" -version = "2025.10.0" +version = "2026.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/24/7f/2747c0d332b9acfa75dc84447a066fdf812b5a6b8d30472b74d309bfe8cb/fsspec-2025.10.0.tar.gz", hash = "sha256:b6789427626f068f9a83ca4e8a3cc050850b6c0f71f99ddb4f542b8266a26a59", size = 309285, upload-time = "2025-10-30T14:58:44.036Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d5/7d/5df2650c57d47c57232af5ef4b4fdbff182070421e405e0d62c6cdbfaa87/fsspec-2026.1.0.tar.gz", hash = "sha256:e987cb0496a0d81bba3a9d1cee62922fb395e7d4c3b575e57f547953334fe07b", size = 310496, upload-time = "2026-01-09T15:21:35.562Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/eb/02/a6b21098b1d5d6249b7c5ab69dde30108a71e4e819d4a9778f1de1d5b70d/fsspec-2025.10.0-py3-none-any.whl", hash = "sha256:7c7712353ae7d875407f97715f0e1ffcc21e33d5b24556cb1e090ae9409ec61d", size = 200966, upload-time = "2025-10-30T14:58:42.53Z" }, + { url = "https://files.pythonhosted.org/packages/01/c9/97cc5aae1648dcb851958a3ddf73ccd7dbe5650d95203ecb4d7720b4cdbf/fsspec-2026.1.0-py3-none-any.whl", hash = "sha256:cb76aa913c2285a3b49bdd5fc55b1d7c708d7208126b60f2eb8194fe1b4cbdcc", size = 201838, upload-time = "2026-01-09T15:21:34.041Z" }, ] [[package]] @@ -1749,28 +1754,27 @@ wheels = [ [[package]] name = "gitpython" -version = "3.1.45" +version = "3.1.46" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "gitdb" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9a/c8/dd58967d119baab745caec2f9d853297cec1989ec1d63f677d3880632b88/gitpython-3.1.45.tar.gz", hash = "sha256:85b0ee964ceddf211c41b9f27a49086010a190fd8132a24e21f362a4b36a791c", size = 215076, upload-time = "2025-07-24T03:45:54.871Z" } +sdist = { url = "https://files.pythonhosted.org/packages/df/b5/59d16470a1f0dfe8c793f9ef56fd3826093fc52b3bd96d6b9d6c26c7e27b/gitpython-3.1.46.tar.gz", hash = "sha256:400124c7d0ef4ea03f7310ac2fbf7151e09ff97f2a3288d64a440c584a29c37f", size = 215371, upload-time = "2026-01-01T15:37:32.073Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/01/61/d4b89fec821f72385526e1b9d9a3a0385dda4a72b206d28049e2c7cd39b8/gitpython-3.1.45-py3-none-any.whl", hash = "sha256:8908cb2e02fb3b93b7eb0f2827125cb699869470432cc885f019b8fd0fccff77", size = 208168, upload-time = "2025-07-24T03:45:52.517Z" }, + { url = "https://files.pythonhosted.org/packages/6a/09/e21df6aef1e1ffc0c816f0522ddc3f6dcded766c3261813131c78a704470/gitpython-3.1.46-py3-none-any.whl", hash = "sha256:79812ed143d9d25b6d176a10bb511de0f9c67b1fa641d82097b0ab90398a2058", size = 208620, upload-time = "2026-01-01T15:37:30.574Z" }, ] [[package]] name = "google-auth" -version = "2.43.0" +version = "2.47.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "cachetools" }, { name = "pyasn1-modules" }, { name = "rsa" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ff/ef/66d14cf0e01b08d2d51ffc3c20410c4e134a1548fc246a6081eae585a4fe/google_auth-2.43.0.tar.gz", hash = "sha256:88228eee5fc21b62a1b5fe773ca15e67778cb07dc8363adcb4a8827b52d81483", size = 296359, upload-time = "2025-11-06T00:13:36.587Z" } +sdist = { url = "https://files.pythonhosted.org/packages/60/3c/ec64b9a275ca22fa1cd3b6e77fefcf837b0732c890aa32d2bd21313d9b33/google_auth-2.47.0.tar.gz", hash = "sha256:833229070a9dfee1a353ae9877dcd2dec069a8281a4e72e72f77d4a70ff945da", size = 323719, upload-time = "2026-01-06T21:55:31.045Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6f/d1/385110a9ae86d91cc14c5282c61fe9f4dc41c0b9f7d423c6ad77038c4448/google_auth-2.43.0-py2.py3-none-any.whl", hash = "sha256:af628ba6fa493f75c7e9dbe9373d148ca9f4399b5ea29976519e0a3848eddd16", size = 223114, upload-time = "2025-11-06T00:13:35.209Z" }, + { url = "https://files.pythonhosted.org/packages/db/18/79e9008530b79527e0d5f79e7eef08d3b179b7f851cfd3a2f27822fbdfa9/google_auth-2.47.0-py3-none-any.whl", hash = "sha256:c516d68336bfde7cf0da26aab674a36fedcf04b37ac4edd59c597178760c3498", size = 234867, upload-time = "2026-01-06T21:55:28.6Z" }, ] [[package]] @@ -1785,65 +1789,72 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c4/ab/09169d5a4612a5f92490806649ac8d41e3ec9129c636754575b3553f4ea4/googleapis_common_protos-1.72.0-py3-none-any.whl", hash = "sha256:4299c5a82d5ae1a9702ada957347726b167f9f8d1fc352477702a1e851ff4038", size = 297515, upload-time = "2025-11-06T18:29:13.14Z" }, ] +[[package]] +name = "granite-common" +version = "0.3.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jsonschema" }, + { name = "pydantic" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/20/6f/9278b41402e4d93dc22e4796ab68fa4f0e20ae0718499d6c65f767f1a09e/granite_common-0.3.6.tar.gz", hash = "sha256:58b2608dc5c71e8a169cc5f8e5fb2faedca035a3c92629ae1e396c8724e28397", size = 672587, upload-time = "2025-12-12T21:18:15.446Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/b5/376d80040d424533f2d6c1232922672cf52c7bfffd8a2a1503603ede34e8/granite_common-0.3.6-py3-none-any.whl", hash = "sha256:dd3363b618abce5888f5b2b041f1a875dd8598f739e7cac087bdabd4791af5a2", size = 84690, upload-time = "2025-12-12T21:18:13.977Z" }, +] + [[package]] name = "greenlet" -version = "3.2.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/03/b8/704d753a5a45507a7aab61f18db9509302ed3d0a27ac7e0359ec2905b1a6/greenlet-3.2.4.tar.gz", hash = "sha256:0dca0d95ff849f9a364385f36ab49f50065d76964944638be9691e1832e9f86d", size = 188260, upload-time = "2025-08-07T13:24:33.51Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7d/ed/6bfa4109fcb23a58819600392564fea69cdc6551ffd5e69ccf1d52a40cbc/greenlet-3.2.4-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:8c68325b0d0acf8d91dde4e6f930967dd52a5302cd4062932a6b2e7c2969f47c", size = 271061, upload-time = "2025-08-07T13:17:15.373Z" }, - { url = "https://files.pythonhosted.org/packages/2a/fc/102ec1a2fc015b3a7652abab7acf3541d58c04d3d17a8d3d6a44adae1eb1/greenlet-3.2.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:94385f101946790ae13da500603491f04a76b6e4c059dab271b3ce2e283b2590", size = 629475, upload-time = "2025-08-07T13:42:54.009Z" }, - { url = "https://files.pythonhosted.org/packages/c5/26/80383131d55a4ac0fb08d71660fd77e7660b9db6bdb4e8884f46d9f2cc04/greenlet-3.2.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f10fd42b5ee276335863712fa3da6608e93f70629c631bf77145021600abc23c", size = 640802, upload-time = "2025-08-07T13:45:25.52Z" }, - { url = "https://files.pythonhosted.org/packages/9f/7c/e7833dbcd8f376f3326bd728c845d31dcde4c84268d3921afcae77d90d08/greenlet-3.2.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:c8c9e331e58180d0d83c5b7999255721b725913ff6bc6cf39fa2a45841a4fd4b", size = 636703, upload-time = "2025-08-07T13:53:12.622Z" }, - { url = "https://files.pythonhosted.org/packages/e9/49/547b93b7c0428ede7b3f309bc965986874759f7d89e4e04aeddbc9699acb/greenlet-3.2.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:58b97143c9cc7b86fc458f215bd0932f1757ce649e05b640fea2e79b54cedb31", size = 635417, upload-time = "2025-08-07T13:18:25.189Z" }, - { url = "https://files.pythonhosted.org/packages/7f/91/ae2eb6b7979e2f9b035a9f612cf70f1bf54aad4e1d125129bef1eae96f19/greenlet-3.2.4-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c2ca18a03a8cfb5b25bc1cbe20f3d9a4c80d8c3b13ba3df49ac3961af0b1018d", size = 584358, upload-time = "2025-08-07T13:18:23.708Z" }, - { url = "https://files.pythonhosted.org/packages/f7/85/433de0c9c0252b22b16d413c9407e6cb3b41df7389afc366ca204dbc1393/greenlet-3.2.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9fe0a28a7b952a21e2c062cd5756d34354117796c6d9215a87f55e38d15402c5", size = 1113550, upload-time = "2025-08-07T13:42:37.467Z" }, - { url = "https://files.pythonhosted.org/packages/a1/8d/88f3ebd2bc96bf7747093696f4335a0a8a4c5acfcf1b757717c0d2474ba3/greenlet-3.2.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8854167e06950ca75b898b104b63cc646573aa5fef1353d4508ecdd1ee76254f", size = 1137126, upload-time = "2025-08-07T13:18:20.239Z" }, - { url = "https://files.pythonhosted.org/packages/f1/29/74242b7d72385e29bcc5563fba67dad94943d7cd03552bac320d597f29b2/greenlet-3.2.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f47617f698838ba98f4ff4189aef02e7343952df3a615f847bb575c3feb177a7", size = 1544904, upload-time = "2025-11-04T12:42:04.763Z" }, - { url = "https://files.pythonhosted.org/packages/c8/e2/1572b8eeab0f77df5f6729d6ab6b141e4a84ee8eb9bc8c1e7918f94eda6d/greenlet-3.2.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:af41be48a4f60429d5cad9d22175217805098a9ef7c40bfef44f7669fb9d74d8", size = 1611228, upload-time = "2025-11-04T12:42:08.423Z" }, - { url = "https://files.pythonhosted.org/packages/d6/6f/b60b0291d9623c496638c582297ead61f43c4b72eef5e9c926ef4565ec13/greenlet-3.2.4-cp310-cp310-win_amd64.whl", hash = "sha256:73f49b5368b5359d04e18d15828eecc1806033db5233397748f4ca813ff1056c", size = 298654, upload-time = "2025-08-07T13:50:00.469Z" }, - { url = "https://files.pythonhosted.org/packages/a4/de/f28ced0a67749cac23fecb02b694f6473f47686dff6afaa211d186e2ef9c/greenlet-3.2.4-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:96378df1de302bc38e99c3a9aa311967b7dc80ced1dcc6f171e99842987882a2", size = 272305, upload-time = "2025-08-07T13:15:41.288Z" }, - { url = "https://files.pythonhosted.org/packages/09/16/2c3792cba130000bf2a31c5272999113f4764fd9d874fb257ff588ac779a/greenlet-3.2.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1ee8fae0519a337f2329cb78bd7a8e128ec0f881073d43f023c7b8d4831d5246", size = 632472, upload-time = "2025-08-07T13:42:55.044Z" }, - { url = "https://files.pythonhosted.org/packages/ae/8f/95d48d7e3d433e6dae5b1682e4292242a53f22df82e6d3dda81b1701a960/greenlet-3.2.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:94abf90142c2a18151632371140b3dba4dee031633fe614cb592dbb6c9e17bc3", size = 644646, upload-time = "2025-08-07T13:45:26.523Z" }, - { url = "https://files.pythonhosted.org/packages/d5/5e/405965351aef8c76b8ef7ad370e5da58d57ef6068df197548b015464001a/greenlet-3.2.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:4d1378601b85e2e5171b99be8d2dc85f594c79967599328f95c1dc1a40f1c633", size = 640519, upload-time = "2025-08-07T13:53:13.928Z" }, - { url = "https://files.pythonhosted.org/packages/25/5d/382753b52006ce0218297ec1b628e048c4e64b155379331f25a7316eb749/greenlet-3.2.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0db5594dce18db94f7d1650d7489909b57afde4c580806b8d9203b6e79cdc079", size = 639707, upload-time = "2025-08-07T13:18:27.146Z" }, - { url = "https://files.pythonhosted.org/packages/1f/8e/abdd3f14d735b2929290a018ecf133c901be4874b858dd1c604b9319f064/greenlet-3.2.4-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2523e5246274f54fdadbce8494458a2ebdcdbc7b802318466ac5606d3cded1f8", size = 587684, upload-time = "2025-08-07T13:18:25.164Z" }, - { url = "https://files.pythonhosted.org/packages/5d/65/deb2a69c3e5996439b0176f6651e0052542bb6c8f8ec2e3fba97c9768805/greenlet-3.2.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1987de92fec508535687fb807a5cea1560f6196285a4cde35c100b8cd632cc52", size = 1116647, upload-time = "2025-08-07T13:42:38.655Z" }, - { url = "https://files.pythonhosted.org/packages/3f/cc/b07000438a29ac5cfb2194bfc128151d52f333cee74dd7dfe3fb733fc16c/greenlet-3.2.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:55e9c5affaa6775e2c6b67659f3a71684de4c549b3dd9afca3bc773533d284fa", size = 1142073, upload-time = "2025-08-07T13:18:21.737Z" }, - { url = "https://files.pythonhosted.org/packages/67/24/28a5b2fa42d12b3d7e5614145f0bd89714c34c08be6aabe39c14dd52db34/greenlet-3.2.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c9c6de1940a7d828635fbd254d69db79e54619f165ee7ce32fda763a9cb6a58c", size = 1548385, upload-time = "2025-11-04T12:42:11.067Z" }, - { url = "https://files.pythonhosted.org/packages/6a/05/03f2f0bdd0b0ff9a4f7b99333d57b53a7709c27723ec8123056b084e69cd/greenlet-3.2.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:03c5136e7be905045160b1b9fdca93dd6727b180feeafda6818e6496434ed8c5", size = 1613329, upload-time = "2025-11-04T12:42:12.928Z" }, - { url = "https://files.pythonhosted.org/packages/d8/0f/30aef242fcab550b0b3520b8e3561156857c94288f0332a79928c31a52cf/greenlet-3.2.4-cp311-cp311-win_amd64.whl", hash = "sha256:9c40adce87eaa9ddb593ccb0fa6a07caf34015a29bf8d344811665b573138db9", size = 299100, upload-time = "2025-08-07T13:44:12.287Z" }, - { url = "https://files.pythonhosted.org/packages/44/69/9b804adb5fd0671f367781560eb5eb586c4d495277c93bde4307b9e28068/greenlet-3.2.4-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:3b67ca49f54cede0186854a008109d6ee71f66bd57bb36abd6d0a0267b540cdd", size = 274079, upload-time = "2025-08-07T13:15:45.033Z" }, - { url = "https://files.pythonhosted.org/packages/46/e9/d2a80c99f19a153eff70bc451ab78615583b8dac0754cfb942223d2c1a0d/greenlet-3.2.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ddf9164e7a5b08e9d22511526865780a576f19ddd00d62f8a665949327fde8bb", size = 640997, upload-time = "2025-08-07T13:42:56.234Z" }, - { url = "https://files.pythonhosted.org/packages/3b/16/035dcfcc48715ccd345f3a93183267167cdd162ad123cd93067d86f27ce4/greenlet-3.2.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f28588772bb5fb869a8eb331374ec06f24a83a9c25bfa1f38b6993afe9c1e968", size = 655185, upload-time = "2025-08-07T13:45:27.624Z" }, - { url = "https://files.pythonhosted.org/packages/31/da/0386695eef69ffae1ad726881571dfe28b41970173947e7c558d9998de0f/greenlet-3.2.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:5c9320971821a7cb77cfab8d956fa8e39cd07ca44b6070db358ceb7f8797c8c9", size = 649926, upload-time = "2025-08-07T13:53:15.251Z" }, - { url = "https://files.pythonhosted.org/packages/68/88/69bf19fd4dc19981928ceacbc5fd4bb6bc2215d53199e367832e98d1d8fe/greenlet-3.2.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c60a6d84229b271d44b70fb6e5fa23781abb5d742af7b808ae3f6efd7c9c60f6", size = 651839, upload-time = "2025-08-07T13:18:30.281Z" }, - { url = "https://files.pythonhosted.org/packages/19/0d/6660d55f7373b2ff8152401a83e02084956da23ae58cddbfb0b330978fe9/greenlet-3.2.4-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b3812d8d0c9579967815af437d96623f45c0f2ae5f04e366de62a12d83a8fb0", size = 607586, upload-time = "2025-08-07T13:18:28.544Z" }, - { url = "https://files.pythonhosted.org/packages/8e/1a/c953fdedd22d81ee4629afbb38d2f9d71e37d23caace44775a3a969147d4/greenlet-3.2.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:abbf57b5a870d30c4675928c37278493044d7c14378350b3aa5d484fa65575f0", size = 1123281, upload-time = "2025-08-07T13:42:39.858Z" }, - { url = "https://files.pythonhosted.org/packages/3f/c7/12381b18e21aef2c6bd3a636da1088b888b97b7a0362fac2e4de92405f97/greenlet-3.2.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:20fb936b4652b6e307b8f347665e2c615540d4b42b3b4c8a321d8286da7e520f", size = 1151142, upload-time = "2025-08-07T13:18:22.981Z" }, - { url = "https://files.pythonhosted.org/packages/27/45/80935968b53cfd3f33cf99ea5f08227f2646e044568c9b1555b58ffd61c2/greenlet-3.2.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ee7a6ec486883397d70eec05059353b8e83eca9168b9f3f9a361971e77e0bcd0", size = 1564846, upload-time = "2025-11-04T12:42:15.191Z" }, - { url = "https://files.pythonhosted.org/packages/69/02/b7c30e5e04752cb4db6202a3858b149c0710e5453b71a3b2aec5d78a1aab/greenlet-3.2.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:326d234cbf337c9c3def0676412eb7040a35a768efc92504b947b3e9cfc7543d", size = 1633814, upload-time = "2025-11-04T12:42:17.175Z" }, - { url = "https://files.pythonhosted.org/packages/e9/08/b0814846b79399e585f974bbeebf5580fbe59e258ea7be64d9dfb253c84f/greenlet-3.2.4-cp312-cp312-win_amd64.whl", hash = "sha256:a7d4e128405eea3814a12cc2605e0e6aedb4035bf32697f72deca74de4105e02", size = 299899, upload-time = "2025-08-07T13:38:53.448Z" }, - { url = "https://files.pythonhosted.org/packages/49/e8/58c7f85958bda41dafea50497cbd59738c5c43dbbea5ee83d651234398f4/greenlet-3.2.4-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:1a921e542453fe531144e91e1feedf12e07351b1cf6c9e8a3325ea600a715a31", size = 272814, upload-time = "2025-08-07T13:15:50.011Z" }, - { url = "https://files.pythonhosted.org/packages/62/dd/b9f59862e9e257a16e4e610480cfffd29e3fae018a68c2332090b53aac3d/greenlet-3.2.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd3c8e693bff0fff6ba55f140bf390fa92c994083f838fece0f63be121334945", size = 641073, upload-time = "2025-08-07T13:42:57.23Z" }, - { url = "https://files.pythonhosted.org/packages/f7/0b/bc13f787394920b23073ca3b6c4a7a21396301ed75a655bcb47196b50e6e/greenlet-3.2.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:710638eb93b1fa52823aa91bf75326f9ecdfd5e0466f00789246a5280f4ba0fc", size = 655191, upload-time = "2025-08-07T13:45:29.752Z" }, - { url = "https://files.pythonhosted.org/packages/f2/d6/6adde57d1345a8d0f14d31e4ab9c23cfe8e2cd39c3baf7674b4b0338d266/greenlet-3.2.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:c5111ccdc9c88f423426df3fd1811bfc40ed66264d35aa373420a34377efc98a", size = 649516, upload-time = "2025-08-07T13:53:16.314Z" }, - { url = "https://files.pythonhosted.org/packages/7f/3b/3a3328a788d4a473889a2d403199932be55b1b0060f4ddd96ee7cdfcad10/greenlet-3.2.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d76383238584e9711e20ebe14db6c88ddcedc1829a9ad31a584389463b5aa504", size = 652169, upload-time = "2025-08-07T13:18:32.861Z" }, - { url = "https://files.pythonhosted.org/packages/ee/43/3cecdc0349359e1a527cbf2e3e28e5f8f06d3343aaf82ca13437a9aa290f/greenlet-3.2.4-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:23768528f2911bcd7e475210822ffb5254ed10d71f4028387e5a99b4c6699671", size = 610497, upload-time = "2025-08-07T13:18:31.636Z" }, - { url = "https://files.pythonhosted.org/packages/b8/19/06b6cf5d604e2c382a6f31cafafd6f33d5dea706f4db7bdab184bad2b21d/greenlet-3.2.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:00fadb3fedccc447f517ee0d3fd8fe49eae949e1cd0f6a611818f4f6fb7dc83b", size = 1121662, upload-time = "2025-08-07T13:42:41.117Z" }, - { url = "https://files.pythonhosted.org/packages/a2/15/0d5e4e1a66fab130d98168fe984c509249c833c1a3c16806b90f253ce7b9/greenlet-3.2.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:d25c5091190f2dc0eaa3f950252122edbbadbb682aa7b1ef2f8af0f8c0afefae", size = 1149210, upload-time = "2025-08-07T13:18:24.072Z" }, - { url = "https://files.pythonhosted.org/packages/1c/53/f9c440463b3057485b8594d7a638bed53ba531165ef0ca0e6c364b5cc807/greenlet-3.2.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6e343822feb58ac4d0a1211bd9399de2b3a04963ddeec21530fc426cc121f19b", size = 1564759, upload-time = "2025-11-04T12:42:19.395Z" }, - { url = "https://files.pythonhosted.org/packages/47/e4/3bb4240abdd0a8d23f4f88adec746a3099f0d86bfedb623f063b2e3b4df0/greenlet-3.2.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ca7f6f1f2649b89ce02f6f229d7c19f680a6238af656f61e0115b24857917929", size = 1634288, upload-time = "2025-11-04T12:42:21.174Z" }, - { url = "https://files.pythonhosted.org/packages/0b/55/2321e43595e6801e105fcfdee02b34c0f996eb71e6ddffca6b10b7e1d771/greenlet-3.2.4-cp313-cp313-win_amd64.whl", hash = "sha256:554b03b6e73aaabec3745364d6239e9e012d64c68ccd0b8430c64ccc14939a8b", size = 299685, upload-time = "2025-08-07T13:24:38.824Z" }, - { url = "https://files.pythonhosted.org/packages/22/5c/85273fd7cc388285632b0498dbbab97596e04b154933dfe0f3e68156c68c/greenlet-3.2.4-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:49a30d5fda2507ae77be16479bdb62a660fa51b1eb4928b524975b3bde77b3c0", size = 273586, upload-time = "2025-08-07T13:16:08.004Z" }, - { url = "https://files.pythonhosted.org/packages/d1/75/10aeeaa3da9332c2e761e4c50d4c3556c21113ee3f0afa2cf5769946f7a3/greenlet-3.2.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:299fd615cd8fc86267b47597123e3f43ad79c9d8a22bebdce535e53550763e2f", size = 686346, upload-time = "2025-08-07T13:42:59.944Z" }, - { url = "https://files.pythonhosted.org/packages/c0/aa/687d6b12ffb505a4447567d1f3abea23bd20e73a5bed63871178e0831b7a/greenlet-3.2.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:c17b6b34111ea72fc5a4e4beec9711d2226285f0386ea83477cbb97c30a3f3a5", size = 699218, upload-time = "2025-08-07T13:45:30.969Z" }, - { url = "https://files.pythonhosted.org/packages/dc/8b/29aae55436521f1d6f8ff4e12fb676f3400de7fcf27fccd1d4d17fd8fecd/greenlet-3.2.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b4a1870c51720687af7fa3e7cda6d08d801dae660f75a76f3845b642b4da6ee1", size = 694659, upload-time = "2025-08-07T13:53:17.759Z" }, - { url = "https://files.pythonhosted.org/packages/92/2e/ea25914b1ebfde93b6fc4ff46d6864564fba59024e928bdc7de475affc25/greenlet-3.2.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:061dc4cf2c34852b052a8620d40f36324554bc192be474b9e9770e8c042fd735", size = 695355, upload-time = "2025-08-07T13:18:34.517Z" }, - { url = "https://files.pythonhosted.org/packages/72/60/fc56c62046ec17f6b0d3060564562c64c862948c9d4bc8aa807cf5bd74f4/greenlet-3.2.4-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:44358b9bf66c8576a9f57a590d5f5d6e72fa4228b763d0e43fee6d3b06d3a337", size = 657512, upload-time = "2025-08-07T13:18:33.969Z" }, - { url = "https://files.pythonhosted.org/packages/23/6e/74407aed965a4ab6ddd93a7ded3180b730d281c77b765788419484cdfeef/greenlet-3.2.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2917bdf657f5859fbf3386b12d68ede4cf1f04c90c3a6bc1f013dd68a22e2269", size = 1612508, upload-time = "2025-11-04T12:42:23.427Z" }, - { url = "https://files.pythonhosted.org/packages/0d/da/343cd760ab2f92bac1845ca07ee3faea9fe52bee65f7bcb19f16ad7de08b/greenlet-3.2.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:015d48959d4add5d6c9f6c5210ee3803a830dce46356e3bc326d6776bde54681", size = 1680760, upload-time = "2025-11-04T12:42:25.341Z" }, - { url = "https://files.pythonhosted.org/packages/e3/a5/6ddab2b4c112be95601c13428db1d8b6608a8b6039816f2ba09c346c08fc/greenlet-3.2.4-cp314-cp314-win_amd64.whl", hash = "sha256:e37ab26028f12dbb0ff65f29a8d3d44a765c61e729647bf2ddfbbed621726f01", size = 303425, upload-time = "2025-08-07T13:32:27.59Z" }, +version = "3.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/e5/40dbda2736893e3e53d25838e0f19a2b417dfc122b9989c91918db30b5d3/greenlet-3.3.0.tar.gz", hash = "sha256:a82bb225a4e9e4d653dd2fb7b8b2d36e4fb25bc0165422a11e48b88e9e6f78fb", size = 190651, upload-time = "2025-12-04T14:49:44.05Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/32/6a/33d1702184d94106d3cdd7bfb788e19723206fce152e303473ca3b946c7b/greenlet-3.3.0-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:6f8496d434d5cb2dce025773ba5597f71f5410ae499d5dd9533e0653258cdb3d", size = 273658, upload-time = "2025-12-04T14:23:37.494Z" }, + { url = "https://files.pythonhosted.org/packages/d6/b7/2b5805bbf1907c26e434f4e448cd8b696a0b71725204fa21a211ff0c04a7/greenlet-3.3.0-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b96dc7eef78fd404e022e165ec55327f935b9b52ff355b067eb4a0267fc1cffb", size = 574810, upload-time = "2025-12-04T14:50:04.154Z" }, + { url = "https://files.pythonhosted.org/packages/94/38/343242ec12eddf3d8458c73f555c084359883d4ddc674240d9e61ec51fd6/greenlet-3.3.0-cp310-cp310-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:73631cd5cccbcfe63e3f9492aaa664d278fda0ce5c3d43aeda8e77317e38efbd", size = 586248, upload-time = "2025-12-04T14:57:39.35Z" }, + { url = "https://files.pythonhosted.org/packages/f0/d0/0ae86792fb212e4384041e0ef8e7bc66f59a54912ce407d26a966ed2914d/greenlet-3.3.0-cp310-cp310-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b299a0cb979f5d7197442dccc3aee67fce53500cd88951b7e6c35575701c980b", size = 597403, upload-time = "2025-12-04T15:07:10.831Z" }, + { url = "https://files.pythonhosted.org/packages/b6/a8/15d0aa26c0036a15d2659175af00954aaaa5d0d66ba538345bd88013b4d7/greenlet-3.3.0-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7dee147740789a4632cace364816046e43310b59ff8fb79833ab043aefa72fd5", size = 586910, upload-time = "2025-12-04T14:25:59.705Z" }, + { url = "https://files.pythonhosted.org/packages/e1/9b/68d5e3b7ccaba3907e5532cf8b9bf16f9ef5056a008f195a367db0ff32db/greenlet-3.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:39b28e339fc3c348427560494e28d8a6f3561c8d2bcf7d706e1c624ed8d822b9", size = 1547206, upload-time = "2025-12-04T15:04:21.027Z" }, + { url = "https://files.pythonhosted.org/packages/66/bd/e3086ccedc61e49f91e2cfb5ffad9d8d62e5dc85e512a6200f096875b60c/greenlet-3.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b3c374782c2935cc63b2a27ba8708471de4ad1abaa862ffdb1ef45a643ddbb7d", size = 1613359, upload-time = "2025-12-04T14:27:26.548Z" }, + { url = "https://files.pythonhosted.org/packages/f4/6b/d4e73f5dfa888364bbf02efa85616c6714ae7c631c201349782e5b428925/greenlet-3.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:b49e7ed51876b459bd645d83db257f0180e345d3f768a35a85437a24d5a49082", size = 300740, upload-time = "2025-12-04T14:47:52.773Z" }, + { url = "https://files.pythonhosted.org/packages/1f/cb/48e964c452ca2b92175a9b2dca037a553036cb053ba69e284650ce755f13/greenlet-3.3.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:e29f3018580e8412d6aaf5641bb7745d38c85228dacf51a73bd4e26ddf2a6a8e", size = 274908, upload-time = "2025-12-04T14:23:26.435Z" }, + { url = "https://files.pythonhosted.org/packages/28/da/38d7bff4d0277b594ec557f479d65272a893f1f2a716cad91efeb8680953/greenlet-3.3.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a687205fb22794e838f947e2194c0566d3812966b41c78709554aa883183fb62", size = 577113, upload-time = "2025-12-04T14:50:05.493Z" }, + { url = "https://files.pythonhosted.org/packages/3c/f2/89c5eb0faddc3ff014f1c04467d67dee0d1d334ab81fadbf3744847f8a8a/greenlet-3.3.0-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4243050a88ba61842186cb9e63c7dfa677ec146160b0efd73b855a3d9c7fcf32", size = 590338, upload-time = "2025-12-04T14:57:41.136Z" }, + { url = "https://files.pythonhosted.org/packages/80/d7/db0a5085035d05134f8c089643da2b44cc9b80647c39e93129c5ef170d8f/greenlet-3.3.0-cp311-cp311-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:670d0f94cd302d81796e37299bcd04b95d62403883b24225c6b5271466612f45", size = 601098, upload-time = "2025-12-04T15:07:11.898Z" }, + { url = "https://files.pythonhosted.org/packages/dc/a6/e959a127b630a58e23529972dbc868c107f9d583b5a9f878fb858c46bc1a/greenlet-3.3.0-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6cb3a8ec3db4a3b0eb8a3c25436c2d49e3505821802074969db017b87bc6a948", size = 590206, upload-time = "2025-12-04T14:26:01.254Z" }, + { url = "https://files.pythonhosted.org/packages/48/60/29035719feb91798693023608447283b266b12efc576ed013dd9442364bb/greenlet-3.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2de5a0b09eab81fc6a382791b995b1ccf2b172a9fec934747a7a23d2ff291794", size = 1550668, upload-time = "2025-12-04T15:04:22.439Z" }, + { url = "https://files.pythonhosted.org/packages/0a/5f/783a23754b691bfa86bd72c3033aa107490deac9b2ef190837b860996c9f/greenlet-3.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4449a736606bd30f27f8e1ff4678ee193bc47f6ca810d705981cfffd6ce0d8c5", size = 1615483, upload-time = "2025-12-04T14:27:28.083Z" }, + { url = "https://files.pythonhosted.org/packages/1d/d5/c339b3b4bc8198b7caa4f2bd9fd685ac9f29795816d8db112da3d04175bb/greenlet-3.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:7652ee180d16d447a683c04e4c5f6441bae7ba7b17ffd9f6b3aff4605e9e6f71", size = 301164, upload-time = "2025-12-04T14:42:51.577Z" }, + { url = "https://files.pythonhosted.org/packages/f8/0a/a3871375c7b9727edaeeea994bfff7c63ff7804c9829c19309ba2e058807/greenlet-3.3.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:b01548f6e0b9e9784a2c99c5651e5dc89ffcbe870bc5fb2e5ef864e9cc6b5dcb", size = 276379, upload-time = "2025-12-04T14:23:30.498Z" }, + { url = "https://files.pythonhosted.org/packages/43/ab/7ebfe34dce8b87be0d11dae91acbf76f7b8246bf9d6b319c741f99fa59c6/greenlet-3.3.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:349345b770dc88f81506c6861d22a6ccd422207829d2c854ae2af8025af303e3", size = 597294, upload-time = "2025-12-04T14:50:06.847Z" }, + { url = "https://files.pythonhosted.org/packages/a4/39/f1c8da50024feecd0793dbd5e08f526809b8ab5609224a2da40aad3a7641/greenlet-3.3.0-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e8e18ed6995e9e2c0b4ed264d2cf89260ab3ac7e13555b8032b25a74c6d18655", size = 607742, upload-time = "2025-12-04T14:57:42.349Z" }, + { url = "https://files.pythonhosted.org/packages/77/cb/43692bcd5f7a0da6ec0ec6d58ee7cddb606d055ce94a62ac9b1aa481e969/greenlet-3.3.0-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c024b1e5696626890038e34f76140ed1daf858e37496d33f2af57f06189e70d7", size = 622297, upload-time = "2025-12-04T15:07:13.552Z" }, + { url = "https://files.pythonhosted.org/packages/75/b0/6bde0b1011a60782108c01de5913c588cf51a839174538d266de15e4bf4d/greenlet-3.3.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:047ab3df20ede6a57c35c14bf5200fcf04039d50f908270d3f9a7a82064f543b", size = 609885, upload-time = "2025-12-04T14:26:02.368Z" }, + { url = "https://files.pythonhosted.org/packages/49/0e/49b46ac39f931f59f987b7cd9f34bfec8ef81d2a1e6e00682f55be5de9f4/greenlet-3.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2d9ad37fc657b1102ec880e637cccf20191581f75c64087a549e66c57e1ceb53", size = 1567424, upload-time = "2025-12-04T15:04:23.757Z" }, + { url = "https://files.pythonhosted.org/packages/05/f5/49a9ac2dff7f10091935def9165c90236d8f175afb27cbed38fb1d61ab6b/greenlet-3.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:83cd0e36932e0e7f36a64b732a6f60c2fc2df28c351bae79fbaf4f8092fe7614", size = 1636017, upload-time = "2025-12-04T14:27:29.688Z" }, + { url = "https://files.pythonhosted.org/packages/6c/79/3912a94cf27ec503e51ba493692d6db1e3cd8ac7ac52b0b47c8e33d7f4f9/greenlet-3.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:a7a34b13d43a6b78abf828a6d0e87d3385680eaf830cd60d20d52f249faabf39", size = 301964, upload-time = "2025-12-04T14:36:58.316Z" }, + { url = "https://files.pythonhosted.org/packages/02/2f/28592176381b9ab2cafa12829ba7b472d177f3acc35d8fbcf3673d966fff/greenlet-3.3.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:a1e41a81c7e2825822f4e068c48cb2196002362619e2d70b148f20a831c00739", size = 275140, upload-time = "2025-12-04T14:23:01.282Z" }, + { url = "https://files.pythonhosted.org/packages/2c/80/fbe937bf81e9fca98c981fe499e59a3f45df2a04da0baa5c2be0dca0d329/greenlet-3.3.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9f515a47d02da4d30caaa85b69474cec77b7929b2e936ff7fb853d42f4bf8808", size = 599219, upload-time = "2025-12-04T14:50:08.309Z" }, + { url = "https://files.pythonhosted.org/packages/c2/ff/7c985128f0514271b8268476af89aee6866df5eec04ac17dcfbc676213df/greenlet-3.3.0-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7d2d9fd66bfadf230b385fdc90426fcd6eb64db54b40c495b72ac0feb5766c54", size = 610211, upload-time = "2025-12-04T14:57:43.968Z" }, + { url = "https://files.pythonhosted.org/packages/79/07/c47a82d881319ec18a4510bb30463ed6891f2ad2c1901ed5ec23d3de351f/greenlet-3.3.0-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:30a6e28487a790417d036088b3bcb3f3ac7d8babaa7d0139edbaddebf3af9492", size = 624311, upload-time = "2025-12-04T15:07:14.697Z" }, + { url = "https://files.pythonhosted.org/packages/fd/8e/424b8c6e78bd9837d14ff7df01a9829fc883ba2ab4ea787d4f848435f23f/greenlet-3.3.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:087ea5e004437321508a8d6f20efc4cfec5e3c30118e1417ea96ed1d93950527", size = 612833, upload-time = "2025-12-04T14:26:03.669Z" }, + { url = "https://files.pythonhosted.org/packages/b5/ba/56699ff9b7c76ca12f1cdc27a886d0f81f2189c3455ff9f65246780f713d/greenlet-3.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ab97cf74045343f6c60a39913fa59710e4bd26a536ce7ab2397adf8b27e67c39", size = 1567256, upload-time = "2025-12-04T15:04:25.276Z" }, + { url = "https://files.pythonhosted.org/packages/1e/37/f31136132967982d698c71a281a8901daf1a8fbab935dce7c0cf15f942cc/greenlet-3.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5375d2e23184629112ca1ea89a53389dddbffcf417dad40125713d88eb5f96e8", size = 1636483, upload-time = "2025-12-04T14:27:30.804Z" }, + { url = "https://files.pythonhosted.org/packages/7e/71/ba21c3fb8c5dce83b8c01f458a42e99ffdb1963aeec08fff5a18588d8fd7/greenlet-3.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:9ee1942ea19550094033c35d25d20726e4f1c40d59545815e1128ac58d416d38", size = 301833, upload-time = "2025-12-04T14:32:23.929Z" }, + { url = "https://files.pythonhosted.org/packages/d7/7c/f0a6d0ede2c7bf092d00bc83ad5bafb7e6ec9b4aab2fbdfa6f134dc73327/greenlet-3.3.0-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:60c2ef0f578afb3c8d92ea07ad327f9a062547137afe91f38408f08aacab667f", size = 275671, upload-time = "2025-12-04T14:23:05.267Z" }, + { url = "https://files.pythonhosted.org/packages/44/06/dac639ae1a50f5969d82d2e3dd9767d30d6dbdbab0e1a54010c8fe90263c/greenlet-3.3.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a5d554d0712ba1de0a6c94c640f7aeba3f85b3a6e1f2899c11c2c0428da9365", size = 646360, upload-time = "2025-12-04T14:50:10.026Z" }, + { url = "https://files.pythonhosted.org/packages/e0/94/0fb76fe6c5369fba9bf98529ada6f4c3a1adf19e406a47332245ef0eb357/greenlet-3.3.0-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3a898b1e9c5f7307ebbde4102908e6cbfcb9ea16284a3abe15cab996bee8b9b3", size = 658160, upload-time = "2025-12-04T14:57:45.41Z" }, + { url = "https://files.pythonhosted.org/packages/93/79/d2c70cae6e823fac36c3bbc9077962105052b7ef81db2f01ec3b9bf17e2b/greenlet-3.3.0-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:dcd2bdbd444ff340e8d6bdf54d2f206ccddbb3ccfdcd3c25bf4afaa7b8f0cf45", size = 671388, upload-time = "2025-12-04T15:07:15.789Z" }, + { url = "https://files.pythonhosted.org/packages/b8/14/bab308fc2c1b5228c3224ec2bf928ce2e4d21d8046c161e44a2012b5203e/greenlet-3.3.0-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5773edda4dc00e173820722711d043799d3adb4f01731f40619e07ea2750b955", size = 660166, upload-time = "2025-12-04T14:26:05.099Z" }, + { url = "https://files.pythonhosted.org/packages/4b/d2/91465d39164eaa0085177f61983d80ffe746c5a1860f009811d498e7259c/greenlet-3.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ac0549373982b36d5fd5d30beb8a7a33ee541ff98d2b502714a09f1169f31b55", size = 1615193, upload-time = "2025-12-04T15:04:27.041Z" }, + { url = "https://files.pythonhosted.org/packages/42/1b/83d110a37044b92423084d52d5d5a3b3a73cafb51b547e6d7366ff62eff1/greenlet-3.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d198d2d977460358c3b3a4dc844f875d1adb33817f0613f663a656f463764ccc", size = 1683653, upload-time = "2025-12-04T14:27:32.366Z" }, + { url = "https://files.pythonhosted.org/packages/7c/9a/9030e6f9aa8fd7808e9c31ba4c38f87c4f8ec324ee67431d181fe396d705/greenlet-3.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:73f51dd0e0bdb596fb0417e475fa3c5e32d4c83638296e560086b8d7da7c4170", size = 305387, upload-time = "2025-12-04T14:26:51.063Z" }, + { url = "https://files.pythonhosted.org/packages/a0/66/bd6317bc5932accf351fc19f177ffba53712a202f9df10587da8df257c7e/greenlet-3.3.0-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:d6ed6f85fae6cdfdb9ce04c9bf7a08d666cfcfb914e7d006f44f840b46741931", size = 282638, upload-time = "2025-12-04T14:25:20.941Z" }, + { url = "https://files.pythonhosted.org/packages/30/cf/cc81cb030b40e738d6e69502ccbd0dd1bced0588e958f9e757945de24404/greenlet-3.3.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d9125050fcf24554e69c4cacb086b87b3b55dc395a8b3ebe6487b045b2614388", size = 651145, upload-time = "2025-12-04T14:50:11.039Z" }, + { url = "https://files.pythonhosted.org/packages/9c/ea/1020037b5ecfe95ca7df8d8549959baceb8186031da83d5ecceff8b08cd2/greenlet-3.3.0-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:87e63ccfa13c0a0f6234ed0add552af24cc67dd886731f2261e46e241608bee3", size = 654236, upload-time = "2025-12-04T14:57:47.007Z" }, + { url = "https://files.pythonhosted.org/packages/69/cc/1e4bae2e45ca2fa55299f4e85854606a78ecc37fead20d69322f96000504/greenlet-3.3.0-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2662433acbca297c9153a4023fe2161c8dcfdcc91f10433171cf7e7d94ba2221", size = 662506, upload-time = "2025-12-04T15:07:16.906Z" }, + { url = "https://files.pythonhosted.org/packages/57/b9/f8025d71a6085c441a7eaff0fd928bbb275a6633773667023d19179fe815/greenlet-3.3.0-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3c6e9b9c1527a78520357de498b0e709fb9e2f49c3a513afd5a249007261911b", size = 653783, upload-time = "2025-12-04T14:26:06.225Z" }, + { url = "https://files.pythonhosted.org/packages/f6/c7/876a8c7a7485d5d6b5c6821201d542ef28be645aa024cfe1145b35c120c1/greenlet-3.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:286d093f95ec98fdd92fcb955003b8a3d054b4e2cab3e2707a5039e7b50520fd", size = 1614857, upload-time = "2025-12-04T15:04:28.484Z" }, + { url = "https://files.pythonhosted.org/packages/4f/dc/041be1dff9f23dac5f48a43323cd0789cb798342011c19a248d9c9335536/greenlet-3.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c10513330af5b8ae16f023e8ddbfb486ab355d04467c4679c5cfe4659975dd9", size = 1676034, upload-time = "2025-12-04T14:27:33.531Z" }, ] [[package]] @@ -1963,30 +1974,28 @@ wheels = [ [[package]] name = "hdbscan" -version = "0.8.40" +version = "0.8.41" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "joblib" }, { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "scikit-learn" }, + { name = "scikit-learn", version = "1.7.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "scikit-learn", version = "1.8.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "scipy", version = "1.16.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "scipy", version = "1.17.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c1/84/6b010387b795f774e1ec695df3c8660c15abd041783647d5e7e4076bfc6b/hdbscan-0.8.40.tar.gz", hash = "sha256:c9e383ff17beee0591075ff65d524bda5b5a35dfb01d218245a7ba30c8d48a17", size = 6904096, upload-time = "2024-11-18T16:14:05.384Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0c/22/32a66dd4ce72145ec1b792c794b98897be467bdf18c10aa8b48275530b11/hdbscan-0.8.41.tar.gz", hash = "sha256:e41e823e5bb21ff2173f252d226266b1dda82bdbba5d89106eafb251429dff3d", size = 7091384, upload-time = "2025-12-12T15:48:30.807Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/ce/489bb941c77e67f6bdc7b47f28318780fd478db655113a073cf7c89cd8f5/hdbscan-0.8.40-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:811a248e57353a4aa815019176879fd16bace55ed633583a6b47734edcb5397c", size = 813672, upload-time = "2024-11-18T16:14:59.129Z" }, - { url = "https://files.pythonhosted.org/packages/ff/5b/5fb2f22a3b4dd77ae6115e2530c01712b7a00c62bc24bbae743c060a80f0/hdbscan-0.8.40-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0a1b062cdee7f847c1a49e343b1cf0d0c7d570f60aca961c7f5ff3bdd6fe4be7", size = 4219260, upload-time = "2025-10-11T11:47:02.481Z" }, - { url = "https://files.pythonhosted.org/packages/8a/d9/11564d3ebfe7429fb2e54356b07b2e44ac3dca668c47401d98170809a2f6/hdbscan-0.8.40-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cda06a6f4e65c6c34bed083bb8cdf29fdb1ffcb15580829d79b2906c7bdc6dbc", size = 4244270, upload-time = "2024-11-18T16:14:10.512Z" }, - { url = "https://files.pythonhosted.org/packages/97/eb/fd2093176b439d6741e92996f6d1d7273ddb0819de59934bc64fe2b1c308/hdbscan-0.8.40-cp310-cp310-win_amd64.whl", hash = "sha256:9ba82e510508921e0b30a234b639f5d84a7d475746e7db814517c5c4d1589016", size = 730887, upload-time = "2024-11-18T16:18:28.006Z" }, - { url = "https://files.pythonhosted.org/packages/26/6b/88b8c8023c0c0b27589ad83c82084a1b751917a3e09bdf7fcacf7e6bd523/hdbscan-0.8.40-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5e958f0d7a33cd2b5e8e927b47f7360bf8a3e7d72355dd65a701e8aabe407b27", size = 1491349, upload-time = "2024-11-18T16:16:10.666Z" }, - { url = "https://files.pythonhosted.org/packages/e5/72/84bc7b6ea9eb59ca6c5e4d3f32313cdfa8f4ab5cfece6fb6dfef4c9149fc/hdbscan-0.8.40-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b95447c9c2cf6c95f98210c0edee3dc463d0a237e5531076855d9776495c96fc", size = 4459927, upload-time = "2025-10-11T11:55:49.958Z" }, - { url = "https://files.pythonhosted.org/packages/a3/ef/32c8a0b3dc6e6c4e433b85b30c3723d8eb48d115c0185b82ab89e1a0ef89/hdbscan-0.8.40-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e0d6197ee045b173e1f16e6884386f335a56091e373a839dd24f7331a8fa9ed", size = 4576215, upload-time = "2024-11-18T16:14:11.241Z" }, - { url = "https://files.pythonhosted.org/packages/64/b1/96c347c7740efa1ac803be64155159284f92fafcff88c1077344e64eead5/hdbscan-0.8.40-cp311-cp311-win_amd64.whl", hash = "sha256:127cbe8c858dc77adfde33a3e1ce4f3bea810f78b01d2bd47b1147d4b5a50472", size = 732173, upload-time = "2024-11-18T16:18:40.361Z" }, - { url = "https://files.pythonhosted.org/packages/33/ff/4739886abb990dc6feb7b02eafb38a7eaf090fffef6336e70a03d693f433/hdbscan-0.8.40-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:353eaa22e42bee69df095744dbb8b29360e516bd9dcb84580dceeeb755f004cc", size = 1497291, upload-time = "2024-11-18T16:16:54.731Z" }, - { url = "https://files.pythonhosted.org/packages/f0/0f/97a315772abf99b3c230e3d57f3fa426d163ce4d6070241d68a3f0241ea9/hdbscan-0.8.40-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:991e745aa51abfb8abfb0e1525b9309df03a2f67fdd8df96e18f91fe7fe06806", size = 4362227, upload-time = "2025-10-11T11:53:16.452Z" }, - { url = "https://files.pythonhosted.org/packages/c0/cb/6b4254f8a33e075118512e55acf3485c155ea52c6c35d69a985bdc59297c/hdbscan-0.8.40-cp312-cp312-win_amd64.whl", hash = "sha256:1b55a935ed7b329adac52072e1c4028979dfc54312ca08de2deece9c97d6ebb1", size = 726198, upload-time = "2024-11-18T16:18:09.99Z" }, - { url = "https://files.pythonhosted.org/packages/80/2d/ca4d81a5aa5b8ccde1d41b892a2f480c2df238c58f113eee4df50473a3b0/hdbscan-0.8.40-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:32ea7bc4ce8854b5549d341edc841a29766feb62f8c399520e6e0940a41c5e39", size = 4336968, upload-time = "2025-10-11T11:53:10.417Z" }, + { url = "https://files.pythonhosted.org/packages/68/7a/8bc50300f7c1240284b8a69d6c69c59ba37e6349bc9ca097760d3efae077/hdbscan-0.8.41-cp310-cp310-macosx_13_0_x86_64.whl", hash = "sha256:0589ea22e225e4ed6fae8b0a6ac6d18c0aff15165b8ddea7561962e1006b7e63", size = 755274, upload-time = "2025-12-12T15:49:22.72Z" }, + { url = "https://files.pythonhosted.org/packages/10/d9/cf2dc6c14ff2a85f2f48a5c3e034df3690b655f5dc09e2e7db6bc140e0ce/hdbscan-0.8.41-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3970e33b7b370cdca5d0fc5d31171c4b5d588590ec6f04b83e08f743099ba950", size = 4162517, upload-time = "2025-12-12T15:48:27.787Z" }, + { url = "https://files.pythonhosted.org/packages/63/f1/37daced2420b5edaea6fb91875fa4c08e1ef72eaf6f84bc8d4442f45c1dc/hdbscan-0.8.41-cp310-cp310-win_amd64.whl", hash = "sha256:7a689386170d91d1bd9386665b521e4ae66b6a78e0b7e34265ea5b1aa1eb165f", size = 687021, upload-time = "2025-12-12T15:48:57.521Z" }, + { url = "https://files.pythonhosted.org/packages/70/58/5c1cbfac6dd5fd4310da17b09950c68dba1a7c1bdd267eb31468b140bd3a/hdbscan-0.8.41-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:be948fc76d0035d93d309920f14ab6a0185580a63c5a63b05739f08b45dc6c03", size = 1394443, upload-time = "2025-12-12T15:49:56.098Z" }, + { url = "https://files.pythonhosted.org/packages/ef/f1/9a17849751488049003a6af08b270eac1e0135d1d29dfd006bcc4edcca00/hdbscan-0.8.41-cp311-cp311-win_amd64.whl", hash = "sha256:0af3e3bab1eb6b07ea497afc4d2db1b58122974fb052bd21f0ea4b42fcf8d535", size = 687100, upload-time = "2025-12-12T15:49:57.879Z" }, + { url = "https://files.pythonhosted.org/packages/f6/6b/b589c0e903e00108c62f324e99840ab050f1da344fab9cf143ce8ebf1d38/hdbscan-0.8.41-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:07ae4c44098449bd9de12145ad17e92ef699754a43988cbea2dd1a95b89bf142", size = 1393282, upload-time = "2025-12-12T15:52:38.472Z" }, + { url = "https://files.pythonhosted.org/packages/59/ab/6314e52aee546cc14b74fbb575b8713eeec2255880ec99d6490838306d3a/hdbscan-0.8.41-cp312-cp312-win_amd64.whl", hash = "sha256:dce39272d2d4f1dde50dde9cc428cadb84ed16326de872b01761f7ec4f690419", size = 671752, upload-time = "2025-12-12T15:51:33.213Z" }, + { url = "https://files.pythonhosted.org/packages/90/51/0befb66e11c5989b7ec419da2bc652023d30113d4bf4df09cf42a42494d8/hdbscan-0.8.41-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8c2f0111395bd1beba1c095edf6b123ec529c8ebd7f4ccd02aaabd6b016454de", size = 1385747, upload-time = "2025-12-12T15:49:25.195Z" }, + { url = "https://files.pythonhosted.org/packages/37/a6/e208ef8bb6e9e97e4b274951160a5bb754f20a79d5673563222d79b00461/hdbscan-0.8.41-cp313-cp313-win_amd64.whl", hash = "sha256:e90f6b9e2fcc94f9ac09d537f8b414191d1a837d62a355edd78e12820b63f0e2", size = 671718, upload-time = "2025-12-12T15:51:39.379Z" }, ] [[package]] @@ -2131,15 +2140,15 @@ wheels = [ [[package]] name = "hypothesis" -version = "6.148.7" +version = "6.150.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, { name = "sortedcontainers" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/09/5e/6a506e81d4dfefed2e838b6beaaae87b2e411dda3da0a3abf94099f194ae/hypothesis-6.148.7.tar.gz", hash = "sha256:b96e817e715c5b1a278411e3b9baf6d599d5b12207ba25e41a8f066929f6c2a6", size = 471199, upload-time = "2025-12-05T02:12:38.068Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ad/4e/cd3a398b9834386a79f4eb777dc4004ca439c1019d324771ec8196fc8354/hypothesis-6.150.1.tar.gz", hash = "sha256:dc79672b3771e92e6563ca0c56a24135438f319b257a1a1982deb8fbb791be89", size = 474924, upload-time = "2026-01-12T08:45:45.416Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e9/55/fa5607e4a4af96dfa0e7efd81bbd130735cedd21aac70b25e06191bff92f/hypothesis-6.148.7-py3-none-any.whl", hash = "sha256:94dbd58ebf259afa3bafb1d3bf5761ac1bde6f1477de494798cbf7960aabbdee", size = 538127, upload-time = "2025-12-05T02:12:35.54Z" }, + { url = "https://files.pythonhosted.org/packages/22/18/f43815244cd99b54d8ac9f44f9799bb7c0115e48e29bc7a1899c0589ee48/hypothesis-6.150.1-py3-none-any.whl", hash = "sha256:7badb28a0da323d6afaf25eae1c93932cb8ac06193355f5e080d6e6465a51da5", size = 542374, upload-time = "2026-01-12T08:45:41.854Z" }, ] [[package]] @@ -2192,14 +2201,10 @@ name = "ibm-watsonx-ai" version = "1.3.42" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.10.2' and python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'darwin'", - "python_full_version < '3.10.2' and platform_machine == 'x86_64' and sys_platform == 'darwin'", - "python_full_version >= '3.10.2' and python_full_version < '3.11' and platform_machine != 'x86_64' and sys_platform == 'darwin'", - "python_full_version >= '3.10.2' and python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'", - "(python_full_version >= '3.10.2' and python_full_version < '3.11' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.10.2' and python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')", - "python_full_version < '3.10.2' and platform_machine != 'x86_64' and sys_platform == 'darwin'", - "python_full_version < '3.10.2' and platform_machine == 'aarch64' and sys_platform == 'linux'", - "(python_full_version < '3.10.2' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10.2' and sys_platform != 'darwin' and sys_platform != 'linux')", + "python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'darwin'", + "python_full_version < '3.11' and platform_machine != 'x86_64' and sys_platform == 'darwin'", + "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "(python_full_version < '3.11' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')", ] dependencies = [ { name = "cachetools", marker = "python_full_version < '3.11'" }, @@ -2220,15 +2225,19 @@ wheels = [ [[package]] name = "ibm-watsonx-ai" -version = "1.4.7" +version = "1.4.11" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin'", + "python_full_version >= '3.14' and platform_machine == 'x86_64' and sys_platform == 'darwin'", + "python_full_version == '3.13.*' and platform_machine == 'x86_64' and sys_platform == 'darwin'", "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'darwin'", "python_full_version == '3.11.*' and platform_machine == 'x86_64' and sys_platform == 'darwin'", - "python_full_version >= '3.13' and platform_machine != 'x86_64' and sys_platform == 'darwin'", - "python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux'", - "(python_full_version >= '3.13' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and sys_platform != 'darwin' and sys_platform != 'linux')", + "python_full_version >= '3.14' and platform_machine != 'x86_64' and sys_platform == 'darwin'", + "python_full_version >= '3.14' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "(python_full_version >= '3.14' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.14' and sys_platform != 'darwin' and sys_platform != 'linux')", + "python_full_version == '3.13.*' and platform_machine != 'x86_64' and sys_platform == 'darwin'", + "python_full_version == '3.13.*' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "(python_full_version == '3.13.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.13.*' and sys_platform != 'darwin' and sys_platform != 'linux')", "python_full_version == '3.12.*' and platform_machine != 'x86_64' and sys_platform == 'darwin'", "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'", "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')", @@ -2248,9 +2257,9 @@ dependencies = [ { name = "tabulate", marker = "python_full_version >= '3.11'" }, { name = "urllib3", marker = "python_full_version >= '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/65/8d/49096f20333c1782d62a0405d4fe67f82c2499c0282c8b1e2ff2f85aa991/ibm_watsonx_ai-1.4.7.tar.gz", hash = "sha256:046119c62a8b24327699b39d020622d655621fc495c85b54d5db7501ac40ce20", size = 699841, upload-time = "2025-11-18T15:33:45.825Z" } +sdist = { url = "https://files.pythonhosted.org/packages/13/6e/bf09475ddeba9cdbf307fcfb3364deb5358921fb6c7bca034ec4baf725a5/ibm_watsonx_ai-1.4.11.tar.gz", hash = "sha256:a11b96bb3e7730669645b7eaef9ac5852569bca3f19a7cd4b46faa0fce5e8b5b", size = 703106, upload-time = "2025-12-08T18:33:30.931Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f5/b2/16f8be647c66e7741efb6fb0a3c7dc7a3ca9d6c44fe023480679a72fa238/ibm_watsonx_ai-1.4.7-py3-none-any.whl", hash = "sha256:e7ef74790e4e5fe5954098c84f05847395e3cb1e28f6cfeb5fb1f6da57f862c3", size = 1070546, upload-time = "2025-11-18T15:33:44.024Z" }, + { url = "https://files.pythonhosted.org/packages/b0/55/32feb14e27120816014be7f55de9ac7f7af7b722b0379dad2f50725b85fc/ibm_watsonx_ai-1.4.11-py3-none-any.whl", hash = "sha256:52c108f111deb234c0dde43a474d187d97d9f5710239ad4c979fdd211532a9c8", size = 1073791, upload-time = "2025-12-08T18:33:28.229Z" }, ] [[package]] @@ -2273,14 +2282,14 @@ wheels = [ [[package]] name = "importlib-metadata" -version = "8.7.0" +version = "8.7.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "zipp" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/76/66/650a33bd90f786193e4de4b3ad86ea60b53c89b669a5c7be931fac31cdb0/importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000", size = 56641, upload-time = "2025-04-27T15:29:01.736Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/49/3b30cad09e7771a4982d9975a8cbf64f00d4a1ececb53297f1d9a7be1b10/importlib_metadata-8.7.1.tar.gz", hash = "sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb", size = 57107, upload-time = "2025-12-21T10:00:19.278Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/20/b0/36bd937216ec521246249be3bf9855081de4c5e06a0c9b4219dbeda50373/importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd", size = 27656, upload-time = "2025-04-27T15:29:00.214Z" }, + { url = "https://files.pythonhosted.org/packages/fa/5e/f8e9a1d23b9c20a551a8a02ea3637b4642e22c2626e3a13a9a29cdea99eb/importlib_metadata-8.7.1-py3-none-any.whl", hash = "sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151", size = 27865, upload-time = "2025-12-21T10:00:18.329Z" }, ] [[package]] @@ -2322,8 +2331,8 @@ dependencies = [ { name = "appnope", marker = "sys_platform == 'darwin'" }, { name = "comm" }, { name = "debugpy" }, - { name = "ipython", version = "8.37.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "ipython", version = "9.7.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "ipython", version = "8.38.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "ipython", version = "9.9.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "jupyter-client" }, { name = "jupyter-core" }, { name = "matplotlib-inline" }, @@ -2341,17 +2350,13 @@ wheels = [ [[package]] name = "ipython" -version = "8.37.0" +version = "8.38.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.10.2' and python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'darwin'", - "python_full_version < '3.10.2' and platform_machine == 'x86_64' and sys_platform == 'darwin'", - "python_full_version >= '3.10.2' and python_full_version < '3.11' and platform_machine != 'x86_64' and sys_platform == 'darwin'", - "python_full_version >= '3.10.2' and python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'", - "(python_full_version >= '3.10.2' and python_full_version < '3.11' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.10.2' and python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')", - "python_full_version < '3.10.2' and platform_machine != 'x86_64' and sys_platform == 'darwin'", - "python_full_version < '3.10.2' and platform_machine == 'aarch64' and sys_platform == 'linux'", - "(python_full_version < '3.10.2' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10.2' and sys_platform != 'darwin' and sys_platform != 'linux')", + "python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'darwin'", + "python_full_version < '3.11' and platform_machine != 'x86_64' and sys_platform == 'darwin'", + "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "(python_full_version < '3.11' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')", ] dependencies = [ { name = "colorama", marker = "python_full_version < '3.11' and sys_platform == 'win32'" }, @@ -2366,22 +2371,26 @@ dependencies = [ { name = "traitlets", marker = "python_full_version < '3.11'" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/85/31/10ac88f3357fc276dc8a64e8880c82e80e7459326ae1d0a211b40abf6665/ipython-8.37.0.tar.gz", hash = "sha256:ca815841e1a41a1e6b73a0b08f3038af9b2252564d01fc405356d34033012216", size = 5606088, upload-time = "2025-05-31T16:39:09.613Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e5/61/1810830e8b93c72dcd3c0f150c80a00c3deb229562d9423807ec92c3a539/ipython-8.38.0.tar.gz", hash = "sha256:9cfea8c903ce0867cc2f23199ed8545eb741f3a69420bfcf3743ad1cec856d39", size = 5513996, upload-time = "2026-01-05T10:59:06.901Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/91/d0/274fbf7b0b12643cbbc001ce13e6a5b1607ac4929d1b11c72460152c9fc3/ipython-8.37.0-py3-none-any.whl", hash = "sha256:ed87326596b878932dbcb171e3e698845434d8c61b8d8cd474bf663041a9dcf2", size = 831864, upload-time = "2025-05-31T16:39:06.38Z" }, + { url = "https://files.pythonhosted.org/packages/9f/df/db59624f4c71b39717c423409950ac3f2c8b2ce4b0aac843112c7fb3f721/ipython-8.38.0-py3-none-any.whl", hash = "sha256:750162629d800ac65bb3b543a14e7a74b0e88063eac9b92124d4b2aa3f6d8e86", size = 831813, upload-time = "2026-01-05T10:59:04.239Z" }, ] [[package]] name = "ipython" -version = "9.7.0" +version = "9.9.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin'", + "python_full_version >= '3.14' and platform_machine == 'x86_64' and sys_platform == 'darwin'", + "python_full_version == '3.13.*' and platform_machine == 'x86_64' and sys_platform == 'darwin'", "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'darwin'", "python_full_version == '3.11.*' and platform_machine == 'x86_64' and sys_platform == 'darwin'", - "python_full_version >= '3.13' and platform_machine != 'x86_64' and sys_platform == 'darwin'", - "python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux'", - "(python_full_version >= '3.13' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and sys_platform != 'darwin' and sys_platform != 'linux')", + "python_full_version >= '3.14' and platform_machine != 'x86_64' and sys_platform == 'darwin'", + "python_full_version >= '3.14' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "(python_full_version >= '3.14' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.14' and sys_platform != 'darwin' and sys_platform != 'linux')", + "python_full_version == '3.13.*' and platform_machine != 'x86_64' and sys_platform == 'darwin'", + "python_full_version == '3.13.*' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "(python_full_version == '3.13.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.13.*' and sys_platform != 'darwin' and sys_platform != 'linux')", "python_full_version == '3.12.*' and platform_machine != 'x86_64' and sys_platform == 'darwin'", "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'", "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')", @@ -2402,9 +2411,9 @@ dependencies = [ { name = "traitlets", marker = "python_full_version >= '3.11'" }, { name = "typing-extensions", marker = "python_full_version == '3.11.*'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/29/e6/48c74d54039241a456add616464ea28c6ebf782e4110d419411b83dae06f/ipython-9.7.0.tar.gz", hash = "sha256:5f6de88c905a566c6a9d6c400a8fed54a638e1f7543d17aae2551133216b1e4e", size = 4422115, upload-time = "2025-11-05T12:18:54.646Z" } +sdist = { url = "https://files.pythonhosted.org/packages/46/dd/fb08d22ec0c27e73c8bc8f71810709870d51cadaf27b7ddd3f011236c100/ipython-9.9.0.tar.gz", hash = "sha256:48fbed1b2de5e2c7177eefa144aba7fcb82dac514f09b57e2ac9da34ddb54220", size = 4425043, upload-time = "2026-01-05T12:36:46.233Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/05/aa/62893d6a591d337aa59dcc4c6f6c842f1fe20cd72c8c5c1f980255243252/ipython-9.7.0-py3-none-any.whl", hash = "sha256:bce8ac85eb9521adc94e1845b4c03d88365fd6ac2f4908ec4ed1eb1b0a065f9f", size = 618911, upload-time = "2025-11-05T12:18:52.484Z" }, + { url = "https://files.pythonhosted.org/packages/86/92/162cfaee4ccf370465c5af1ce36a9eacec1becb552f2033bb3584e6f640a/ipython-9.9.0-py3-none-any.whl", hash = "sha256:b457fe9165df2b84e8ec909a97abcf2ed88f565970efba16b1f7229c283d252b", size = 621431, upload-time = "2026-01-05T12:36:44.669Z" }, ] [[package]] @@ -2421,11 +2430,11 @@ wheels = [ [[package]] name = "isort" -version = "6.1.0" +version = "7.0.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1e/82/fa43935523efdfcce6abbae9da7f372b627b27142c3419fcf13bf5b0c397/isort-6.1.0.tar.gz", hash = "sha256:9b8f96a14cfee0677e78e941ff62f03769a06d412aabb9e2a90487b3b7e8d481", size = 824325, upload-time = "2025-10-01T16:26:45.027Z" } +sdist = { url = "https://files.pythonhosted.org/packages/63/53/4f3c058e3bace40282876f9b553343376ee687f3c35a525dc79dbd450f88/isort-7.0.0.tar.gz", hash = "sha256:5513527951aadb3ac4292a41a16cbc50dd1642432f5e8c20057d414bdafb4187", size = 805049, upload-time = "2025-10-11T13:30:59.107Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7f/cc/9b681a170efab4868a032631dea1e8446d8ec718a7f657b94d49d1a12643/isort-6.1.0-py3-none-any.whl", hash = "sha256:58d8927ecce74e5087aef019f778d4081a3b6c98f15a80ba35782ca8a2097784", size = 94329, upload-time = "2025-10-01T16:26:43.291Z" }, + { url = "https://files.pythonhosted.org/packages/7f/ed/e3705d6d02b4f7aea715a353c8ce193efd0b5db13e204df895d38734c244/isort-7.0.0-py3-none-any.whl", hash = "sha256:1bcabac8bc3c36c7fb7b98a76c8abb18e0f841a3ba81decac7691008592499c1", size = 94672, upload-time = "2025-10-11T13:30:57.665Z" }, ] [[package]] @@ -2560,20 +2569,20 @@ wheels = [ [[package]] name = "joblib" -version = "1.5.2" +version = "1.5.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e8/5d/447af5ea094b9e4c4054f82e223ada074c552335b9b4b2d14bd9b35a67c4/joblib-1.5.2.tar.gz", hash = "sha256:3faa5c39054b2f03ca547da9b2f52fde67c06240c31853f306aea97f13647b55", size = 331077, upload-time = "2025-08-27T12:15:46.575Z" } +sdist = { url = "https://files.pythonhosted.org/packages/41/f2/d34e8b3a08a9cc79a50b2208a93dce981fe615b64d5a4d4abee421d898df/joblib-1.5.3.tar.gz", hash = "sha256:8561a3269e6801106863fd0d6d84bb737be9e7631e33aaed3fb9ce5953688da3", size = 331603, upload-time = "2025-12-15T08:41:46.427Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/e8/685f47e0d754320684db4425a0967f7d3fa70126bffd76110b7009a0090f/joblib-1.5.2-py3-none-any.whl", hash = "sha256:4e1f0bdbb987e6d843c70cf43714cb276623def372df3c22fe5266b2670bc241", size = 308396, upload-time = "2025-08-27T12:15:45.188Z" }, + { url = "https://files.pythonhosted.org/packages/7b/91/984aca2ec129e2757d1e4e3c81c3fcda9d0f85b74670a094cc443d9ee949/joblib-1.5.3-py3-none-any.whl", hash = "sha256:5fc3c5039fc5ca8c0276333a188bbd59d6b7ab37fe6632daa76bc7f9ec18e713", size = 309071, upload-time = "2025-12-15T08:41:44.973Z" }, ] [[package]] name = "json5" -version = "0.12.1" +version = "0.13.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/12/ae/929aee9619e9eba9015207a9d2c1c54db18311da7eb4dcf6d41ad6f0eb67/json5-0.12.1.tar.gz", hash = "sha256:b2743e77b3242f8d03c143dd975a6ec7c52e2f2afe76ed934e53503dd4ad4990", size = 52191, upload-time = "2025-08-12T19:47:42.583Z" } +sdist = { url = "https://files.pythonhosted.org/packages/77/e8/a3f261a66e4663f22700bc8a17c08cb83e91fbf086726e7a228398968981/json5-0.13.0.tar.gz", hash = "sha256:b1edf8d487721c0bf64d83c28e91280781f6e21f4a797d3261c7c828d4c165bf", size = 52441, upload-time = "2026-01-01T19:42:14.99Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/85/e2/05328bd2621be49a6fed9e3030b1e51a2d04537d3f816d211b9cc53c5262/json5-0.12.1-py3-none-any.whl", hash = "sha256:d9c9b3bc34a5f54d43c35e11ef7cb87d8bdd098c6ace87117a7b7e83e705c1d5", size = 36119, upload-time = "2025-08-12T19:47:41.131Z" }, + { url = "https://files.pythonhosted.org/packages/d7/9e/038522f50ceb7e74f1f991bf1b699f24b0c2bbe7c390dd36ad69f4582258/json5-0.13.0-py3-none-any.whl", hash = "sha256:9a08e1dd65f6a4d4c6fa82d216cf2477349ec2346a38fd70cc11d2557499fbcc", size = 36163, upload-time = "2026-01-01T19:42:13.962Z" }, ] [[package]] @@ -2620,7 +2629,7 @@ wheels = [ [[package]] name = "jsonschema" -version = "4.25.1" +version = "4.26.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "attrs" }, @@ -2628,9 +2637,9 @@ dependencies = [ { name = "referencing" }, { name = "rpds-py" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/74/69/f7185de793a29082a9f3c7728268ffb31cb5095131a9c139a74078e27336/jsonschema-4.25.1.tar.gz", hash = "sha256:e4a9655ce0da0c0b67a085847e00a3a51449e1157f4f75e9fb5aa545e122eb85", size = 357342, upload-time = "2025-08-18T17:03:50.038Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583, upload-time = "2026-01-07T13:41:07.246Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bf/9c/8c95d856233c1f82500c2450b8c68576b4cf1c871db3afac5c34ff84e6fd/jsonschema-4.25.1-py3-none-any.whl", hash = "sha256:3fba0169e345c7175110351d456342c364814cfcf3b964ba4587f22915230a63", size = 90040, upload-time = "2025-08-18T17:03:48.373Z" }, + { url = "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", size = 90630, upload-time = "2026-01-07T13:41:05.306Z" }, ] [[package]] @@ -2647,7 +2656,7 @@ wheels = [ [[package]] name = "jupyter-client" -version = "8.6.3" +version = "8.8.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jupyter-core" }, @@ -2656,9 +2665,9 @@ dependencies = [ { name = "tornado" }, { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/71/22/bf9f12fdaeae18019a468b68952a60fe6dbab5d67cd2a103cac7659b41ca/jupyter_client-8.6.3.tar.gz", hash = "sha256:35b3a0947c4a6e9d589eb97d7d4cd5e90f910ee73101611f01283732bd6d9419", size = 342019, upload-time = "2024-09-17T10:44:17.613Z" } +sdist = { url = "https://files.pythonhosted.org/packages/05/e4/ba649102a3bc3fbca54e7239fb924fd434c766f855693d86de0b1f2bec81/jupyter_client-8.8.0.tar.gz", hash = "sha256:d556811419a4f2d96c869af34e854e3f059b7cc2d6d01a9cd9c85c267691be3e", size = 348020, upload-time = "2026-01-08T13:55:47.938Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/11/85/b0394e0b6fcccd2c1eeefc230978a6f8cb0c5df1e4cd3e7625735a0d7d1e/jupyter_client-8.6.3-py3-none-any.whl", hash = "sha256:e8a19cc986cc45905ac3362915f410f3af85424b4c0905e94fa5f2cb08e8f23f", size = 106105, upload-time = "2024-09-17T10:44:15.218Z" }, + { url = "https://files.pythonhosted.org/packages/2d/0b/ceb7694d864abc0a047649aec263878acb9f792e1fec3e676f22dc9015e3/jupyter_client-8.8.0-py3-none-any.whl", hash = "sha256:f93a5b99c5e23a507b773d3a1136bd6e16c67883ccdbd9a829b0bbdb98cd7d7a", size = 107371, upload-time = "2026-01-08T13:55:45.562Z" }, ] [[package]] @@ -2751,7 +2760,7 @@ wheels = [ [[package]] name = "langchain-chroma" -version = "1.0.0" +version = "1.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "chromadb" }, @@ -2759,14 +2768,14 @@ dependencies = [ { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/6c/b5/1dfb361793b217c7e8e791edebf5ecbbcc7fed2e9fd998b452c32c701c1f/langchain_chroma-1.0.0.tar.gz", hash = "sha256:ee82c6fbd0c94e5e9942e3b9302f69deea6c58c0e957ff16de6066d896d7a34a", size = 217313, upload-time = "2025-10-17T15:18:54.557Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fa/0e/54896830b7331c90788cf96b2c37858977c199da9ecdaf85cf11eb6e6bc1/langchain_chroma-1.1.0.tar.gz", hash = "sha256:8069685e7848041e998d16c8a4964256b031fd20551bf59429173415bc2adc12", size = 220382, upload-time = "2025-12-12T16:23:01.399Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/05/fd/36d8d37a257d7f5961cea9ef0e1a9616a4b4fc259cad2cbf02416de90215/langchain_chroma-1.0.0-py3-none-any.whl", hash = "sha256:b7adb991090f066f8d5e746a2d338de3116b192cec3990deef3052b7ea07324c", size = 12458, upload-time = "2025-10-17T15:18:53.325Z" }, + { url = "https://files.pythonhosted.org/packages/ae/35/2a6d1191acaad043647e28313b0ecd161d61f09d8be37d1996a90d752c13/langchain_chroma-1.1.0-py3-none-any.whl", hash = "sha256:ff65e4a2ccefb0fb9fde2ff38705022ace402f979d557f018f6e623f7288f0fc", size = 12981, upload-time = "2025-12-12T16:23:00.196Z" }, ] [[package]] name = "langchain-classic" -version = "1.0.0" +version = "1.0.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "async-timeout", marker = "python_full_version < '3.11'" }, @@ -2778,9 +2787,9 @@ dependencies = [ { name = "requests" }, { name = "sqlalchemy" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d9/b1/a66babeccb2c05ed89690a534296688c0349bee7a71641e91ecc2afd72fd/langchain_classic-1.0.0.tar.gz", hash = "sha256:a63655609254ebc36d660eb5ad7c06c778b2e6733c615ffdac3eac4fbe2b12c5", size = 10514930, upload-time = "2025-10-17T16:02:47.887Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7c/4b/bd03518418ece4c13192a504449b58c28afee915dc4a6f4b02622458cb1b/langchain_classic-1.0.1.tar.gz", hash = "sha256:40a499684df36b005a1213735dc7f8dca8f5eb67978d6ec763e7a49780864fdc", size = 10516020, upload-time = "2025-12-23T22:55:22.615Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/74/74/246f809a3741c21982f985ca0113ec92d3c84896308561cc4414823f6951/langchain_classic-1.0.0-py3-none-any.whl", hash = "sha256:97f71f150c10123f5511c08873f030e35ede52311d729a7688c721b4e1e01f33", size = 1040701, upload-time = "2025-10-17T16:02:46.35Z" }, + { url = "https://files.pythonhosted.org/packages/83/0f/eab87f017d7fe28e8c11fff614f4cdbfae32baadb77d0f79e9f922af1df2/langchain_classic-1.0.1-py3-none-any.whl", hash = "sha256:131d83a02bb80044c68fedc1ab4ae885d5b8f8c2c742d8ab9e7534ad9cda8e80", size = 1040666, upload-time = "2025-12-23T22:55:21.025Z" }, ] [[package]] @@ -2809,7 +2818,7 @@ wheels = [ [[package]] name = "langchain-core" -version = "1.1.0" +version = "1.2.7" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jsonpatch" }, @@ -2819,55 +2828,56 @@ dependencies = [ { name = "pyyaml" }, { name = "tenacity" }, { name = "typing-extensions" }, + { name = "uuid-utils" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/1e/17/67c1cc2ace919e2b02dd9d783154d7fb3f1495a4ef835d9cd163b7855ac2/langchain_core-1.1.0.tar.gz", hash = "sha256:2b76a82d427922c8bc51c08404af4fc2a29e9f161dfe2297cb05091e810201e7", size = 781995, upload-time = "2025-11-21T21:01:26.958Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/0e/664d8d81b3493e09cbab72448d2f9d693d1fa5aa2bcc488602203a9b6da0/langchain_core-1.2.7.tar.gz", hash = "sha256:e1460639f96c352b4a41c375f25aeb8d16ffc1769499fb1c20503aad59305ced", size = 837039, upload-time = "2026-01-09T17:44:25.505Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/71/1e/e129fc471a2d2a7b3804480a937b5ab9319cab9f4142624fcb115f925501/langchain_core-1.1.0-py3-none-any.whl", hash = "sha256:2c9f27dadc6d21ed4aa46506a37a56e6a7e2d2f9141922dc5c251ba921822ee6", size = 473752, upload-time = "2025-11-21T21:01:25.841Z" }, + { url = "https://files.pythonhosted.org/packages/6e/6f/34a9fba14d191a67f7e2ee3dbce3e9b86d2fa7310e2c7f2c713583481bd2/langchain_core-1.2.7-py3-none-any.whl", hash = "sha256:452f4fef7a3d883357b22600788d37e3d8854ef29da345b7ac7099f33c31828b", size = 490232, upload-time = "2026-01-09T17:44:24.236Z" }, ] [[package]] name = "langchain-huggingface" -version = "1.1.0" +version = "1.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "huggingface-hub" }, { name = "langchain-core" }, { name = "tokenizers" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9f/d7/ffcf97cd977c535df2c621c59eafa82df73f801323f670d88819c23fc304/langchain_huggingface-1.1.0.tar.gz", hash = "sha256:43c3b06413158b0cd1edcdbadf545c24d5f64f180bb71c80dc960959a728c1fd", size = 252295, upload-time = "2025-11-24T14:18:30.366Z" } +sdist = { url = "https://files.pythonhosted.org/packages/67/2c/4fddeb3387baa05b6a95870ad514f649cafb46e0c0ef9caf949d974e55d2/langchain_huggingface-1.2.0.tar.gz", hash = "sha256:18a2d79955271261fb245b233fea6aa29625576e841f2b4f5bee41e51cc70949", size = 255602, upload-time = "2025-12-12T22:19:51.021Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b1/4b/2bdd63464a7bb3aa7911777636cff8e54a2a1edc7b7a85a4acb7decebb23/langchain_huggingface-1.1.0-py3-none-any.whl", hash = "sha256:a3a5218a839062941cb616992bcbc4fe73352454727bafc351a452e76aead1a8", size = 29925, upload-time = "2025-11-24T14:18:29.036Z" }, + { url = "https://files.pythonhosted.org/packages/81/ce/502157ef7390a31cc67e5873ad66e737a25d1d33fcf6936e5c9a0a451409/langchain_huggingface-1.2.0-py3-none-any.whl", hash = "sha256:0ff6a17d3eb36ce2304f446e3285c74b59358703e8f7916c15bfcf9ec7b57bf1", size = 30671, upload-time = "2025-12-12T22:19:50.023Z" }, ] [[package]] name = "langchain-ibm" -version = "1.0.1" +version = "1.0.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "ibm-watsonx-ai", version = "1.3.42", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "ibm-watsonx-ai", version = "1.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "ibm-watsonx-ai", version = "1.4.11", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "langchain-core" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3f/49/e83d41cab8bf5f8e804398c9d755aeaa90686778839b9f7cf81fa674d634/langchain_ibm-1.0.1.tar.gz", hash = "sha256:1418e65dd00f4ed5dc9afc565bd46ef60dffe2b06b3ef9fcd57a98df793c0f23", size = 60230, upload-time = "2025-11-13T13:34:39.982Z" } +sdist = { url = "https://files.pythonhosted.org/packages/36/a0/eb479ef5be8954e6f0202321f10a73928f176a918c6e9b9afa84b5a3c9b0/langchain_ibm-1.0.2.tar.gz", hash = "sha256:c7233e5e53652f76b3e81bb2d6ba29bee867329eed5aa641f4f8f8e1a03cef0a", size = 60833, upload-time = "2025-12-15T16:57:15.398Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/76/6f/01aa98c144e19315e7aa374092c7b775aa1c2eb803d657581224f6d3f08b/langchain_ibm-1.0.1-py3-none-any.whl", hash = "sha256:5cb1e795e61174c1b62b9236c9a1f8f19f59c9d7f5912a1549e8c471970f33b1", size = 49665, upload-time = "2025-11-13T13:34:38.572Z" }, + { url = "https://files.pythonhosted.org/packages/6c/42/f9237c26f8daee8ca51f8b9703ee29f68fcf8ec8cd2dd7220db1ad12e8e9/langchain_ibm-1.0.2-py3-none-any.whl", hash = "sha256:830b8723feb6982bcd29c74979a195cd9e7473c4d27acef3710a2fd3a002901b", size = 50089, upload-time = "2025-12-15T16:57:14.066Z" }, ] [[package]] name = "langchain-text-splitters" -version = "1.0.0" +version = "1.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "langchain-core" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fa/2e/c833dcc379c1c086453708ef5eef7d4d1f808559ca4458bd6569d5d83ad7/langchain_text_splitters-1.0.0.tar.gz", hash = "sha256:d8580a20ad7ed10b432feb273e5758b2cc0902d094919629cec0e1ad691a6744", size = 264257, upload-time = "2025-10-17T14:33:41.743Z" } +sdist = { url = "https://files.pythonhosted.org/packages/41/42/c178dcdc157b473330eb7cc30883ea69b8ec60078c7b85e2d521054c4831/langchain_text_splitters-1.1.0.tar.gz", hash = "sha256:75e58acb7585dc9508f3cd9d9809cb14751283226c2d6e21fb3a9ae57582ca22", size = 272230, upload-time = "2025-12-14T01:15:38.659Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/97/d362353ab04f865af6f81d4d46e7aa428734aa032de0017934b771fc34b7/langchain_text_splitters-1.0.0-py3-none-any.whl", hash = "sha256:f00c8219d3468f2c5bd951b708b6a7dd9bc3c62d0cfb83124c377f7170f33b2e", size = 33851, upload-time = "2025-10-17T14:33:40.46Z" }, + { url = "https://files.pythonhosted.org/packages/d8/1a/a84ed1c046deecf271356b0179c1b9fba95bfdaa6f934e1849dee26fad7b/langchain_text_splitters-1.1.0-py3-none-any.whl", hash = "sha256:f00341fe883358786104a5f881375ac830a4dd40253ecd42b4c10536c6e4693f", size = 34182, upload-time = "2025-12-14T01:15:37.382Z" }, ] [[package]] name = "langgraph" -version = "1.0.3" +version = "1.0.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "langchain-core" }, @@ -2877,9 +2887,9 @@ dependencies = [ { name = "pydantic" }, { name = "xxhash" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a7/55/70f2d11d33b0310d3e48d8e049825b4a34a1c822d48f6448ae548d2cd0f8/langgraph-1.0.3.tar.gz", hash = "sha256:873a6aae6be054ef52a05c463be363a46da9711405b1b14454d595f543b68335", size = 483302, upload-time = "2025-11-10T17:41:45.425Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/47/28f4d4d33d88f69de26f7a54065961ac0c662cec2479b36a2db081ef5cb6/langgraph-1.0.5.tar.gz", hash = "sha256:7f6ae59622386b60fe9fa0ad4c53f42016b668455ed604329e7dc7904adbf3f8", size = 493969, upload-time = "2025-12-12T23:05:48.224Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/84/a3/fdf6ecd0e44cb02d20afe7d0fb64c748a749f4b2e011bf9a785a32642367/langgraph-1.0.3-py3-none-any.whl", hash = "sha256:4a75146f09bd0d127a724876f4244f460c4c66353a993641bd641ed710cd010f", size = 156845, upload-time = "2025-11-10T17:41:43.868Z" }, + { url = "https://files.pythonhosted.org/packages/23/1b/e318ee76e42d28f515d87356ac5bd7a7acc8bad3b8f54ee377bef62e1cbf/langgraph-1.0.5-py3-none-any.whl", hash = "sha256:b4cfd173dca3c389735b47228ad8b295e6f7b3df779aba3a1e0c23871f81281e", size = 157056, upload-time = "2025-12-12T23:05:46.499Z" }, ] [[package]] @@ -2910,20 +2920,20 @@ wheels = [ [[package]] name = "langgraph-sdk" -version = "0.2.10" +version = "0.3.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx" }, { name = "orjson" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/cb/0f/88772be3301cc5ad495e77705538edbcbf7f2ccf38d21555fa26131203aa/langgraph_sdk-0.2.10.tar.gz", hash = "sha256:ab58331504fbea28e6322037aa362929799b4e9106663ac1dbd7c5ac44558933", size = 113432, upload-time = "2025-11-24T21:31:57.268Z" } +sdist = { url = "https://files.pythonhosted.org/packages/66/74/e1fa90bc6194f5409e37ce6d8174e45293b767fe7a11060a3c2a0e15c087/langgraph_sdk-0.3.2.tar.gz", hash = "sha256:3f2ed7b210c0748983b4596157ece9db2c5d6debd9d4878fad4683216a0b6fc4", size = 129502, upload-time = "2026-01-09T21:10:52.627Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8b/cc/ff4ba17253d31981b047f4be52cc51a19fa28dd2dd16a880c0c595bd66bd/langgraph_sdk-0.2.10-py3-none-any.whl", hash = "sha256:9aef403663726085de6851e4e50459df9562069bd316dd0261eb359f776fd0ef", size = 58430, upload-time = "2025-11-24T21:31:56.052Z" }, + { url = "https://files.pythonhosted.org/packages/38/f3/253b46e87bc2cbbe69d71faec213465e01e9028bfa45d9b80a65b169b860/langgraph_sdk-0.3.2-py3-none-any.whl", hash = "sha256:0b0ab967eab59c20989d46f2020da439bd914ed8f4caf3326813be9b70d9037e", size = 66829, upload-time = "2026-01-09T21:10:51.739Z" }, ] [[package]] name = "langsmith" -version = "0.4.47" +version = "0.6.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx" }, @@ -2932,11 +2942,12 @@ dependencies = [ { name = "pydantic" }, { name = "requests" }, { name = "requests-toolbelt" }, + { name = "uuid-utils" }, { name = "zstandard" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ec/dd/d69922b79fb692b9736206574e5ba69b6354080cf1cc9796449d9fe61f9a/langsmith-0.4.47.tar.gz", hash = "sha256:6a576405696ee97147ccb96c9ae5c9437430500a5d118bd447ec2d1f8cf26de1", size = 986584, upload-time = "2025-11-24T16:02:00.914Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0c/8e/3ea7a8e9ce8c530204964207af7f7778597f5a548dc1a489c0c0940561f3/langsmith-0.6.2.tar.gz", hash = "sha256:c2efd7ed61eed3b6fdbf158ea2e9862bc2636f2edc95e90d2faad9462773d097", size = 1739277, upload-time = "2026-01-08T23:17:40.504Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/80/1a/0c84f7096d41d64425d29db549c8d6fe075f925a5f2022e8087d01d862c2/langsmith-0.4.47-py3-none-any.whl", hash = "sha256:b9e514611d4e1570e33595d33ccb1fe6eda9f96c5f961095a138651f746c1ef5", size = 411207, upload-time = "2025-11-24T16:01:59.123Z" }, + { url = "https://files.pythonhosted.org/packages/ed/e0/9d173dd2fa7f85d9ec4989f6f5a1a057d281daa8dada0ff8db0de0cb68aa/langsmith-0.6.2-py3-none-any.whl", hash = "sha256:1ea1a591f52683a5aeebdaa2b58458d72ce9598105dd8b29e16f7373631a6434", size = 282918, upload-time = "2026-01-08T23:17:38.858Z" }, ] [[package]] @@ -2948,14 +2959,101 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3e/76/d661ea2e529c3d464f9efd73f9ac31626b45279eb4306e684054ea20e3d4/latex2mathml-3.78.1-py3-none-any.whl", hash = "sha256:f089b6d75e85b937f99693c93e8c16c0804008672c3dd2a3d25affd36f238100", size = 73892, upload-time = "2025-08-29T23:34:21.98Z" }, ] +[[package]] +name = "latex2sympy2-extended" +version = "1.11.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "antlr4-python3-runtime" }, + { name = "sympy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/30/75/456da2da05f6380ea96e6ea804ab2c03e41fc3ed80052307fe8efe6ea20e/latex2sympy2_extended-1.11.0.tar.gz", hash = "sha256:9695657c81b50abba2636638638618db59f4663ed2a4a12d62cef74a40e28fec", size = 207023, upload-time = "2026-01-10T01:43:21.319Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/61/f75cd1fa54d8434276126034aed54dd120747de9a8fa013cdd79545ccbeb/latex2sympy2_extended-1.11.0-py3-none-any.whl", hash = "sha256:aebb77d52ce269e25028e4bea89ddb14d242ba36bcf7b636496fb5fd9728d234", size = 209050, upload-time = "2026-01-10T01:43:19.458Z" }, +] + +[[package]] +name = "librt" +version = "0.7.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b7/29/47f29026ca17f35cf299290292d5f8331f5077364974b7675a353179afa2/librt-0.7.7.tar.gz", hash = "sha256:81d957b069fed1890953c3b9c3895c7689960f233eea9a1d9607f71ce7f00b2c", size = 145910, upload-time = "2026-01-01T23:52:22.87Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c6/84/2cfb1f3b9b60bab52e16a220c931223fc8e963d0d7bb9132bef012aafc3f/librt-0.7.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e4836c5645f40fbdc275e5670819bde5ab5f2e882290d304e3c6ddab1576a6d0", size = 54709, upload-time = "2026-01-01T23:50:48.326Z" }, + { url = "https://files.pythonhosted.org/packages/19/a1/3127b277e9d3784a8040a54e8396d9ae5c64d6684dc6db4b4089b0eedcfb/librt-0.7.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6ae8aec43117a645a31e5f60e9e3a0797492e747823b9bda6972d521b436b4e8", size = 56658, upload-time = "2026-01-01T23:50:49.74Z" }, + { url = "https://files.pythonhosted.org/packages/3a/e9/b91b093a5c42eb218120445f3fef82e0b977fa2225f4d6fc133d25cdf86a/librt-0.7.7-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:aea05f701ccd2a76b34f0daf47ca5068176ff553510b614770c90d76ac88df06", size = 161026, upload-time = "2026-01-01T23:50:50.853Z" }, + { url = "https://files.pythonhosted.org/packages/c7/cb/1ded77d5976a79d7057af4a010d577ce4f473ff280984e68f4974a3281e5/librt-0.7.7-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7b16ccaeff0ed4355dfb76fe1ea7a5d6d03b5ad27f295f77ee0557bc20a72495", size = 169529, upload-time = "2026-01-01T23:50:52.24Z" }, + { url = "https://files.pythonhosted.org/packages/da/6e/6ca5bdaa701e15f05000ac1a4c5d1475c422d3484bd3d1ca9e8c2f5be167/librt-0.7.7-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c48c7e150c095d5e3cea7452347ba26094be905d6099d24f9319a8b475fcd3e0", size = 183271, upload-time = "2026-01-01T23:50:55.287Z" }, + { url = "https://files.pythonhosted.org/packages/e7/2d/55c0e38073997b4bbb5ddff25b6d1bbba8c2f76f50afe5bb9c844b702f34/librt-0.7.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4dcee2f921a8632636d1c37f1bbdb8841d15666d119aa61e5399c5268e7ce02e", size = 179039, upload-time = "2026-01-01T23:50:56.807Z" }, + { url = "https://files.pythonhosted.org/packages/33/4e/3662a41ae8bb81b226f3968426293517b271d34d4e9fd4b59fc511f1ae40/librt-0.7.7-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:14ef0f4ac3728ffd85bfc58e2f2f48fb4ef4fa871876f13a73a7381d10a9f77c", size = 173505, upload-time = "2026-01-01T23:50:58.291Z" }, + { url = "https://files.pythonhosted.org/packages/f8/5d/cf768deb8bdcbac5f8c21fcb32dd483d038d88c529fd351bbe50590b945d/librt-0.7.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e4ab69fa37f8090f2d971a5d2bc606c7401170dbdae083c393d6cbf439cb45b8", size = 193570, upload-time = "2026-01-01T23:50:59.546Z" }, + { url = "https://files.pythonhosted.org/packages/a1/ea/ee70effd13f1d651976d83a2812391f6203971740705e3c0900db75d4bce/librt-0.7.7-cp310-cp310-win32.whl", hash = "sha256:4bf3cc46d553693382d2abf5f5bd493d71bb0f50a7c0beab18aa13a5545c8900", size = 42600, upload-time = "2026-01-01T23:51:00.694Z" }, + { url = "https://files.pythonhosted.org/packages/f0/eb/dc098730f281cba76c279b71783f5de2edcba3b880c1ab84a093ef826062/librt-0.7.7-cp310-cp310-win_amd64.whl", hash = "sha256:f0c8fe5aeadd8a0e5b0598f8a6ee3533135ca50fd3f20f130f9d72baf5c6ac58", size = 48977, upload-time = "2026-01-01T23:51:01.726Z" }, + { url = "https://files.pythonhosted.org/packages/f0/56/30b5c342518005546df78841cb0820ae85a17e7d07d521c10ef367306d0d/librt-0.7.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a487b71fbf8a9edb72a8c7a456dda0184642d99cd007bc819c0b7ab93676a8ee", size = 54709, upload-time = "2026-01-01T23:51:02.774Z" }, + { url = "https://files.pythonhosted.org/packages/72/78/9f120e3920b22504d4f3835e28b55acc2cc47c9586d2e1b6ba04c3c1bf01/librt-0.7.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f4d4efb218264ecf0f8516196c9e2d1a0679d9fb3bb15df1155a35220062eba8", size = 56663, upload-time = "2026-01-01T23:51:03.838Z" }, + { url = "https://files.pythonhosted.org/packages/1c/ea/7d7a1ee7dfc1151836028eba25629afcf45b56bbc721293e41aa2e9b8934/librt-0.7.7-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:b8bb331aad734b059c4b450cd0a225652f16889e286b2345af5e2c3c625c3d85", size = 161705, upload-time = "2026-01-01T23:51:04.917Z" }, + { url = "https://files.pythonhosted.org/packages/45/a5/952bc840ac8917fbcefd6bc5f51ad02b89721729814f3e2bfcc1337a76d6/librt-0.7.7-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:467dbd7443bda08338fc8ad701ed38cef48194017554f4c798b0a237904b3f99", size = 171029, upload-time = "2026-01-01T23:51:06.09Z" }, + { url = "https://files.pythonhosted.org/packages/fa/bf/c017ff7da82dc9192cf40d5e802a48a25d00e7639b6465cfdcee5893a22c/librt-0.7.7-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50d1d1ee813d2d1a3baf2873634ba506b263032418d16287c92ec1cc9c1a00cb", size = 184704, upload-time = "2026-01-01T23:51:07.549Z" }, + { url = "https://files.pythonhosted.org/packages/77/ec/72f3dd39d2cdfd6402ab10836dc9cbf854d145226062a185b419c4f1624a/librt-0.7.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c7e5070cf3ec92d98f57574da0224f8c73faf1ddd6d8afa0b8c9f6e86997bc74", size = 180719, upload-time = "2026-01-01T23:51:09.062Z" }, + { url = "https://files.pythonhosted.org/packages/78/86/06e7a1a81b246f3313bf515dd9613a1c81583e6fd7843a9f4d625c4e926d/librt-0.7.7-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:bdb9f3d865b2dafe7f9ad7f30ef563c80d0ddd2fdc8cc9b8e4f242f475e34d75", size = 174537, upload-time = "2026-01-01T23:51:10.611Z" }, + { url = "https://files.pythonhosted.org/packages/83/08/f9fb2edc9c7a76e95b2924ce81d545673f5b034e8c5dd92159d1c7dae0c6/librt-0.7.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:8185c8497d45164e256376f9da5aed2bb26ff636c798c9dabe313b90e9f25b28", size = 195238, upload-time = "2026-01-01T23:51:11.762Z" }, + { url = "https://files.pythonhosted.org/packages/ba/56/ea2d2489d3ea1f47b301120e03a099e22de7b32c93df9a211e6ff4f9bf38/librt-0.7.7-cp311-cp311-win32.whl", hash = "sha256:44d63ce643f34a903f09ff7ca355aae019a3730c7afd6a3c037d569beeb5d151", size = 42939, upload-time = "2026-01-01T23:51:13.192Z" }, + { url = "https://files.pythonhosted.org/packages/58/7b/c288f417e42ba2a037f1c0753219e277b33090ed4f72f292fb6fe175db4c/librt-0.7.7-cp311-cp311-win_amd64.whl", hash = "sha256:7d13cc340b3b82134f8038a2bfe7137093693dcad8ba5773da18f95ad6b77a8a", size = 49240, upload-time = "2026-01-01T23:51:14.264Z" }, + { url = "https://files.pythonhosted.org/packages/7c/24/738eb33a6c1516fdb2dfd2a35db6e5300f7616679b573585be0409bc6890/librt-0.7.7-cp311-cp311-win_arm64.whl", hash = "sha256:983de36b5a83fe9222f4f7dcd071f9b1ac6f3f17c0af0238dadfb8229588f890", size = 42613, upload-time = "2026-01-01T23:51:15.268Z" }, + { url = "https://files.pythonhosted.org/packages/56/72/1cd9d752070011641e8aee046c851912d5f196ecd726fffa7aed2070f3e0/librt-0.7.7-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2a85a1fc4ed11ea0eb0a632459ce004a2d14afc085a50ae3463cd3dfe1ce43fc", size = 55687, upload-time = "2026-01-01T23:51:16.291Z" }, + { url = "https://files.pythonhosted.org/packages/50/aa/d5a1d4221c4fe7e76ae1459d24d6037783cb83c7645164c07d7daf1576ec/librt-0.7.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c87654e29a35938baead1c4559858f346f4a2a7588574a14d784f300ffba0efd", size = 57136, upload-time = "2026-01-01T23:51:17.363Z" }, + { url = "https://files.pythonhosted.org/packages/23/6f/0c86b5cb5e7ef63208c8cc22534df10ecc5278efc0d47fb8815577f3ca2f/librt-0.7.7-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:c9faaebb1c6212c20afd8043cd6ed9de0a47d77f91a6b5b48f4e46ed470703fe", size = 165320, upload-time = "2026-01-01T23:51:18.455Z" }, + { url = "https://files.pythonhosted.org/packages/16/37/df4652690c29f645ffe405b58285a4109e9fe855c5bb56e817e3e75840b3/librt-0.7.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1908c3e5a5ef86b23391448b47759298f87f997c3bd153a770828f58c2bb4630", size = 174216, upload-time = "2026-01-01T23:51:19.599Z" }, + { url = "https://files.pythonhosted.org/packages/9a/d6/d3afe071910a43133ec9c0f3e4ce99ee6df0d4e44e4bddf4b9e1c6ed41cc/librt-0.7.7-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dbc4900e95a98fc0729523be9d93a8fedebb026f32ed9ffc08acd82e3e181503", size = 189005, upload-time = "2026-01-01T23:51:21.052Z" }, + { url = "https://files.pythonhosted.org/packages/d5/18/74060a870fe2d9fd9f47824eba6717ce7ce03124a0d1e85498e0e7efc1b2/librt-0.7.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a7ea4e1fbd253e5c68ea0fe63d08577f9d288a73f17d82f652ebc61fa48d878d", size = 183961, upload-time = "2026-01-01T23:51:22.493Z" }, + { url = "https://files.pythonhosted.org/packages/7c/5e/918a86c66304af66a3c1d46d54df1b2d0b8894babc42a14fb6f25511497f/librt-0.7.7-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:ef7699b7a5a244b1119f85c5bbc13f152cd38240cbb2baa19b769433bae98e50", size = 177610, upload-time = "2026-01-01T23:51:23.874Z" }, + { url = "https://files.pythonhosted.org/packages/b2/d7/b5e58dc2d570f162e99201b8c0151acf40a03a39c32ab824dd4febf12736/librt-0.7.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:955c62571de0b181d9e9e0a0303c8bc90d47670a5eff54cf71bf5da61d1899cf", size = 199272, upload-time = "2026-01-01T23:51:25.341Z" }, + { url = "https://files.pythonhosted.org/packages/18/87/8202c9bd0968bdddc188ec3811985f47f58ed161b3749299f2c0dd0f63fb/librt-0.7.7-cp312-cp312-win32.whl", hash = "sha256:1bcd79be209313b270b0e1a51c67ae1af28adad0e0c7e84c3ad4b5cb57aaa75b", size = 43189, upload-time = "2026-01-01T23:51:26.799Z" }, + { url = "https://files.pythonhosted.org/packages/61/8d/80244b267b585e7aa79ffdac19f66c4861effc3a24598e77909ecdd0850e/librt-0.7.7-cp312-cp312-win_amd64.whl", hash = "sha256:4353ee891a1834567e0302d4bd5e60f531912179578c36f3d0430f8c5e16b456", size = 49462, upload-time = "2026-01-01T23:51:27.813Z" }, + { url = "https://files.pythonhosted.org/packages/2d/1f/75db802d6a4992d95e8a889682601af9b49d5a13bbfa246d414eede1b56c/librt-0.7.7-cp312-cp312-win_arm64.whl", hash = "sha256:a76f1d679beccccdf8c1958e732a1dfcd6e749f8821ee59d7bec009ac308c029", size = 42828, upload-time = "2026-01-01T23:51:28.804Z" }, + { url = "https://files.pythonhosted.org/packages/8d/5e/d979ccb0a81407ec47c14ea68fb217ff4315521730033e1dd9faa4f3e2c1/librt-0.7.7-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8f4a0b0a3c86ba9193a8e23bb18f100d647bf192390ae195d84dfa0a10fb6244", size = 55746, upload-time = "2026-01-01T23:51:29.828Z" }, + { url = "https://files.pythonhosted.org/packages/f5/2c/3b65861fb32f802c3783d6ac66fc5589564d07452a47a8cf9980d531cad3/librt-0.7.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5335890fea9f9e6c4fdf8683061b9ccdcbe47c6dc03ab8e9b68c10acf78be78d", size = 57174, upload-time = "2026-01-01T23:51:31.226Z" }, + { url = "https://files.pythonhosted.org/packages/50/df/030b50614b29e443607220097ebaf438531ea218c7a9a3e21ea862a919cd/librt-0.7.7-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:9b4346b1225be26def3ccc6c965751c74868f0578cbcba293c8ae9168483d811", size = 165834, upload-time = "2026-01-01T23:51:32.278Z" }, + { url = "https://files.pythonhosted.org/packages/5d/e1/bd8d1eacacb24be26a47f157719553bbd1b3fe812c30dddf121c0436fd0b/librt-0.7.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a10b8eebdaca6e9fdbaf88b5aefc0e324b763a5f40b1266532590d5afb268a4c", size = 174819, upload-time = "2026-01-01T23:51:33.461Z" }, + { url = "https://files.pythonhosted.org/packages/46/7d/91d6c3372acf54a019c1ad8da4c9ecf4fc27d039708880bf95f48dbe426a/librt-0.7.7-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:067be973d90d9e319e6eb4ee2a9b9307f0ecd648b8a9002fa237289a4a07a9e7", size = 189607, upload-time = "2026-01-01T23:51:34.604Z" }, + { url = "https://files.pythonhosted.org/packages/fa/ac/44604d6d3886f791fbd1c6ae12d5a782a8f4aca927484731979f5e92c200/librt-0.7.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:23d2299ed007812cccc1ecef018db7d922733382561230de1f3954db28433977", size = 184586, upload-time = "2026-01-01T23:51:35.845Z" }, + { url = "https://files.pythonhosted.org/packages/5c/26/d8a6e4c17117b7f9b83301319d9a9de862ae56b133efb4bad8b3aa0808c9/librt-0.7.7-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:6b6f8ea465524aa4c7420c7cc4ca7d46fe00981de8debc67b1cc2e9957bb5b9d", size = 178251, upload-time = "2026-01-01T23:51:37.018Z" }, + { url = "https://files.pythonhosted.org/packages/99/ab/98d857e254376f8e2f668e807daccc1f445e4b4fc2f6f9c1cc08866b0227/librt-0.7.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f8df32a99cc46eb0ee90afd9ada113ae2cafe7e8d673686cf03ec53e49635439", size = 199853, upload-time = "2026-01-01T23:51:38.195Z" }, + { url = "https://files.pythonhosted.org/packages/7c/55/4523210d6ae5134a5da959900be43ad8bab2e4206687b6620befddb5b5fd/librt-0.7.7-cp313-cp313-win32.whl", hash = "sha256:86f86b3b785487c7760247bcdac0b11aa8bf13245a13ed05206286135877564b", size = 43247, upload-time = "2026-01-01T23:51:39.629Z" }, + { url = "https://files.pythonhosted.org/packages/25/40/3ec0fed5e8e9297b1cf1a3836fb589d3de55f9930e3aba988d379e8ef67c/librt-0.7.7-cp313-cp313-win_amd64.whl", hash = "sha256:4862cb2c702b1f905c0503b72d9d4daf65a7fdf5a9e84560e563471e57a56949", size = 49419, upload-time = "2026-01-01T23:51:40.674Z" }, + { url = "https://files.pythonhosted.org/packages/1c/7a/aab5f0fb122822e2acbc776addf8b9abfb4944a9056c00c393e46e543177/librt-0.7.7-cp313-cp313-win_arm64.whl", hash = "sha256:0996c83b1cb43c00e8c87835a284f9057bc647abd42b5871e5f941d30010c832", size = 42828, upload-time = "2026-01-01T23:51:41.731Z" }, + { url = "https://files.pythonhosted.org/packages/69/9c/228a5c1224bd23809a635490a162e9cbdc68d99f0eeb4a696f07886b8206/librt-0.7.7-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:23daa1ab0512bafdd677eb1bfc9611d8ffbe2e328895671e64cb34166bc1b8c8", size = 55188, upload-time = "2026-01-01T23:51:43.14Z" }, + { url = "https://files.pythonhosted.org/packages/ba/c2/0e7c6067e2b32a156308205e5728f4ed6478c501947e9142f525afbc6bd2/librt-0.7.7-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:558a9e5a6f3cc1e20b3168fb1dc802d0d8fa40731f6e9932dcc52bbcfbd37111", size = 56895, upload-time = "2026-01-01T23:51:44.534Z" }, + { url = "https://files.pythonhosted.org/packages/0e/77/de50ff70c80855eb79d1d74035ef06f664dd073fb7fb9d9fb4429651b8eb/librt-0.7.7-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:2567cb48dc03e5b246927ab35cbb343376e24501260a9b5e30b8e255dca0d1d2", size = 163724, upload-time = "2026-01-01T23:51:45.571Z" }, + { url = "https://files.pythonhosted.org/packages/6e/19/f8e4bf537899bdef9e0bb9f0e4b18912c2d0f858ad02091b6019864c9a6d/librt-0.7.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6066c638cdf85ff92fc6f932d2d73c93a0e03492cdfa8778e6d58c489a3d7259", size = 172470, upload-time = "2026-01-01T23:51:46.823Z" }, + { url = "https://files.pythonhosted.org/packages/42/4c/dcc575b69d99076768e8dd6141d9aecd4234cba7f0e09217937f52edb6ed/librt-0.7.7-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a609849aca463074c17de9cda173c276eb8fee9e441053529e7b9e249dc8b8ee", size = 186806, upload-time = "2026-01-01T23:51:48.009Z" }, + { url = "https://files.pythonhosted.org/packages/fe/f8/4094a2b7816c88de81239a83ede6e87f1138477d7ee956c30f136009eb29/librt-0.7.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:add4e0a000858fe9bb39ed55f31085506a5c38363e6eb4a1e5943a10c2bfc3d1", size = 181809, upload-time = "2026-01-01T23:51:49.35Z" }, + { url = "https://files.pythonhosted.org/packages/1b/ac/821b7c0ab1b5a6cd9aee7ace8309c91545a2607185101827f79122219a7e/librt-0.7.7-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:a3bfe73a32bd0bdb9a87d586b05a23c0a1729205d79df66dee65bb2e40d671ba", size = 175597, upload-time = "2026-01-01T23:51:50.636Z" }, + { url = "https://files.pythonhosted.org/packages/71/f9/27f6bfbcc764805864c04211c6ed636fe1d58f57a7b68d1f4ae5ed74e0e0/librt-0.7.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:0ecce0544d3db91a40f8b57ae26928c02130a997b540f908cefd4d279d6c5848", size = 196506, upload-time = "2026-01-01T23:51:52.535Z" }, + { url = "https://files.pythonhosted.org/packages/46/ba/c9b9c6fc931dd7ea856c573174ccaf48714905b1a7499904db2552e3bbaf/librt-0.7.7-cp314-cp314-win32.whl", hash = "sha256:8f7a74cf3a80f0c3b0ec75b0c650b2f0a894a2cec57ef75f6f72c1e82cdac61d", size = 39747, upload-time = "2026-01-01T23:51:53.683Z" }, + { url = "https://files.pythonhosted.org/packages/c5/69/cd1269337c4cde3ee70176ee611ab0058aa42fc8ce5c9dce55f48facfcd8/librt-0.7.7-cp314-cp314-win_amd64.whl", hash = "sha256:3d1fe2e8df3268dd6734dba33ededae72ad5c3a859b9577bc00b715759c5aaab", size = 45971, upload-time = "2026-01-01T23:51:54.697Z" }, + { url = "https://files.pythonhosted.org/packages/79/fd/e0844794423f5583108c5991313c15e2b400995f44f6ec6871f8aaf8243c/librt-0.7.7-cp314-cp314-win_arm64.whl", hash = "sha256:2987cf827011907d3dfd109f1be0d61e173d68b1270107bb0e89f2fca7f2ed6b", size = 39075, upload-time = "2026-01-01T23:51:55.726Z" }, + { url = "https://files.pythonhosted.org/packages/42/02/211fd8f7c381e7b2a11d0fdfcd410f409e89967be2e705983f7c6342209a/librt-0.7.7-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8e92c8de62b40bfce91d5e12c6e8b15434da268979b1af1a6589463549d491e6", size = 57368, upload-time = "2026-01-01T23:51:56.706Z" }, + { url = "https://files.pythonhosted.org/packages/4c/b6/aca257affae73ece26041ae76032153266d110453173f67d7603058e708c/librt-0.7.7-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f683dcd49e2494a7535e30f779aa1ad6e3732a019d80abe1309ea91ccd3230e3", size = 59238, upload-time = "2026-01-01T23:51:58.066Z" }, + { url = "https://files.pythonhosted.org/packages/96/47/7383a507d8e0c11c78ca34c9d36eab9000db5989d446a2f05dc40e76c64f/librt-0.7.7-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:9b15e5d17812d4d629ff576699954f74e2cc24a02a4fc401882dd94f81daba45", size = 183870, upload-time = "2026-01-01T23:51:59.204Z" }, + { url = "https://files.pythonhosted.org/packages/a4/b8/50f3d8eec8efdaf79443963624175c92cec0ba84827a66b7fcfa78598e51/librt-0.7.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c084841b879c4d9b9fa34e5d5263994f21aea7fd9c6add29194dbb41a6210536", size = 194608, upload-time = "2026-01-01T23:52:00.419Z" }, + { url = "https://files.pythonhosted.org/packages/23/d9/1b6520793aadb59d891e3b98ee057a75de7f737e4a8b4b37fdbecb10d60f/librt-0.7.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:10c8fb9966f84737115513fecbaf257f9553d067a7dd45a69c2c7e5339e6a8dc", size = 206776, upload-time = "2026-01-01T23:52:01.705Z" }, + { url = "https://files.pythonhosted.org/packages/ff/db/331edc3bba929d2756fa335bfcf736f36eff4efcb4f2600b545a35c2ae58/librt-0.7.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:9b5fb1ecb2c35362eab2dbd354fd1efa5a8440d3e73a68be11921042a0edc0ff", size = 203206, upload-time = "2026-01-01T23:52:03.315Z" }, + { url = "https://files.pythonhosted.org/packages/b2/e1/6af79ec77204e85f6f2294fc171a30a91bb0e35d78493532ed680f5d98be/librt-0.7.7-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:d1454899909d63cc9199a89fcc4f81bdd9004aef577d4ffc022e600c412d57f3", size = 196697, upload-time = "2026-01-01T23:52:04.857Z" }, + { url = "https://files.pythonhosted.org/packages/f3/46/de55ecce4b2796d6d243295c221082ca3a944dc2fb3a52dcc8660ce7727d/librt-0.7.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:7ef28f2e7a016b29792fe0a2dd04dec75725b32a1264e390c366103f834a9c3a", size = 217193, upload-time = "2026-01-01T23:52:06.159Z" }, + { url = "https://files.pythonhosted.org/packages/41/61/33063e271949787a2f8dd33c5260357e3d512a114fc82ca7890b65a76e2d/librt-0.7.7-cp314-cp314t-win32.whl", hash = "sha256:5e419e0db70991b6ba037b70c1d5bbe92b20ddf82f31ad01d77a347ed9781398", size = 40277, upload-time = "2026-01-01T23:52:07.625Z" }, + { url = "https://files.pythonhosted.org/packages/06/21/1abd972349f83a696ea73159ac964e63e2d14086fdd9bc7ca878c25fced4/librt-0.7.7-cp314-cp314t-win_amd64.whl", hash = "sha256:d6b7d93657332c817b8d674ef6bf1ab7796b4f7ce05e420fd45bd258a72ac804", size = 46765, upload-time = "2026-01-01T23:52:08.647Z" }, + { url = "https://files.pythonhosted.org/packages/51/0e/b756c7708143a63fca65a51ca07990fa647db2cc8fcd65177b9e96680255/librt-0.7.7-cp314-cp314t-win_arm64.whl", hash = "sha256:142c2cd91794b79fd0ce113bd658993b7ede0fe93057668c2f98a45ca00b7e91", size = 39724, upload-time = "2026-01-01T23:52:09.745Z" }, +] + [[package]] name = "litellm" -version = "1.80.5" +version = "1.80.15" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohttp" }, { name = "click" }, { name = "fastuuid" }, + { name = "grpcio" }, { name = "httpx" }, { name = "importlib-metadata" }, { name = "jinja2" }, @@ -2966,21 +3064,21 @@ dependencies = [ { name = "tiktoken" }, { name = "tokenizers" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/85/b8/357544534bef87dd2858432f3cbd3a0e5cc267caebca5ea86b03618786c5/litellm-1.80.5.tar.gz", hash = "sha256:922791c264845d9ed59e540c8fa74a74d237c1b209568a05ffeacd8b51770deb", size = 11885764, upload-time = "2025-11-22T23:41:42.25Z" } +sdist = { url = "https://files.pythonhosted.org/packages/12/41/9b28df3e4739df83ddb32dfb2bccb12ad271d986494c9fd60e4927a0a6c3/litellm-1.80.15.tar.gz", hash = "sha256:759d09f33c9c6028c58dcdf71781b17b833ee926525714e09a408602be27f54e", size = 13376508, upload-time = "2026-01-11T18:31:44.95Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bd/af/1d4693746ff9fbbe27a6e7d6394b801acf234e00c83f45ad1cb5bf2eaa6c/litellm-1.80.5-py3-none-any.whl", hash = "sha256:2ac5f4e88cd57ae056e00da8f872e1c2956653750929fba2fd9b007b400fdb77", size = 10671970, upload-time = "2025-11-22T23:41:39.923Z" }, + { url = "https://files.pythonhosted.org/packages/df/3b/b1bd693721ccb3c9a37c8233d019a643ac57bef5a93f279e5a63839ee4db/litellm-1.80.15-py3-none-any.whl", hash = "sha256:f354e49456985a235b9ed99df1c19d686d30501f96e68882dcc5b29b1e7c59d9", size = 11670707, upload-time = "2026-01-11T18:31:41.67Z" }, ] [[package]] name = "llm-sandbox" -version = "0.3.25" +version = "0.3.31" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pydantic" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f7/65/ed58ffdeac4145823a9d6afca4d2e81beb3e3cf0e610727807f58819bf7e/llm_sandbox-0.3.25.tar.gz", hash = "sha256:656930f38c6b280fb32128f171be8f7fb60bf2d175729eaa224e2038e75937e5", size = 518964, upload-time = "2025-11-14T07:52:06.18Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e5/40/6932046e2fa378aad14bae10bbb3455d1b4f761c541d8491b41685b31f8a/llm_sandbox-0.3.31.tar.gz", hash = "sha256:e7bf2bbbb925e8fc1ea659c9d86b9a525443cfebc9a36a21ab8fe02a339f23f3", size = 585962, upload-time = "2025-12-06T04:45:09.319Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/06/a9/277c9864b36f77d9c23b5757bc103ec8b421bdfe23e2ca197785860bbc4e/llm_sandbox-0.3.25-py3-none-any.whl", hash = "sha256:57a5ebf3ef00c8b5aee179937c8cf51ff11e7a729fd217f848c236cc2825143a", size = 78279, upload-time = "2025-11-14T07:52:05.171Z" }, + { url = "https://files.pythonhosted.org/packages/82/e9/de7f5d64d9797cd74bb8e52028d5bcd6972c3a3e73378eee4fcb43c1fcf3/llm_sandbox-0.3.31-py3-none-any.whl", hash = "sha256:641cc5c5c32d32da6e025574b650a9245cfa88132ca695a0a638f63f4c608b07", size = 103503, upload-time = "2025-12-06T04:45:07.981Z" }, ] [package.optional-dependencies] @@ -2994,30 +3092,30 @@ podman = [ [[package]] name = "llvmlite" -version = "0.45.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/99/8d/5baf1cef7f9c084fb35a8afbde88074f0d6a727bc63ef764fe0e7543ba40/llvmlite-0.45.1.tar.gz", hash = "sha256:09430bb9d0bb58fc45a45a57c7eae912850bedc095cd0810a57de109c69e1c32", size = 185600, upload-time = "2025-10-01T17:59:52.046Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cf/6d/585c84ddd9d2a539a3c3487792b3cf3f988e28ec4fa281bf8b0e055e1166/llvmlite-0.45.1-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:1b1af0c910af0978aa55fa4f60bbb3e9f39b41e97c2a6d94d199897be62ba07a", size = 43043523, upload-time = "2025-10-01T18:02:58.621Z" }, - { url = "https://files.pythonhosted.org/packages/ae/34/992bd12d3ff245e0801bcf6013961daa8c19c9b9c2e61cb4b8bce94566f9/llvmlite-0.45.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02a164db2d79088bbd6e0d9633b4fe4021d6379d7e4ac7cc85ed5f44b06a30c5", size = 37253122, upload-time = "2025-10-01T18:03:55.159Z" }, - { url = "https://files.pythonhosted.org/packages/a6/7b/6d7585998a5991fa74dc925aae57913ba8c7c2efff909de9d34cc1cd3c27/llvmlite-0.45.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f2d47f34e4029e6df3395de34cc1c66440a8d72712993a6e6168db228686711b", size = 56288210, upload-time = "2025-10-01T18:00:41.978Z" }, - { url = "https://files.pythonhosted.org/packages/b5/e2/a4abea058633bfc82eb08fd69ce242c118fdb9b0abad1fdcbe0bc6aedab5/llvmlite-0.45.1-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f7319e5f9f90720578a7f56fbc805bdfb4bc071b507c7611f170d631c3c0f1e0", size = 55140958, upload-time = "2025-10-01T18:01:55.694Z" }, - { url = "https://files.pythonhosted.org/packages/74/c0/233468e96ed287b953239c3b24b1d69df47c6ba9262bfdca98eda7e83a04/llvmlite-0.45.1-cp310-cp310-win_amd64.whl", hash = "sha256:4edb62e685867799e336723cb9787ec6598d51d0b1ed9af0f38e692aa757e898", size = 38132232, upload-time = "2025-10-01T18:04:41.538Z" }, - { url = "https://files.pythonhosted.org/packages/04/ad/9bdc87b2eb34642c1cfe6bcb4f5db64c21f91f26b010f263e7467e7536a3/llvmlite-0.45.1-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:60f92868d5d3af30b4239b50e1717cb4e4e54f6ac1c361a27903b318d0f07f42", size = 43043526, upload-time = "2025-10-01T18:03:15.051Z" }, - { url = "https://files.pythonhosted.org/packages/a5/ea/c25c6382f452a943b4082da5e8c1665ce29a62884e2ec80608533e8e82d5/llvmlite-0.45.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:98baab513e19beb210f1ef39066288784839a44cd504e24fff5d17f1b3cf0860", size = 37253118, upload-time = "2025-10-01T18:04:06.783Z" }, - { url = "https://files.pythonhosted.org/packages/fe/af/85fc237de98b181dbbe8647324331238d6c52a3554327ccdc83ced28efba/llvmlite-0.45.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3adc2355694d6a6fbcc024d59bb756677e7de506037c878022d7b877e7613a36", size = 56288209, upload-time = "2025-10-01T18:01:00.168Z" }, - { url = "https://files.pythonhosted.org/packages/0a/df/3daf95302ff49beff4230065e3178cd40e71294968e8d55baf4a9e560814/llvmlite-0.45.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2f3377a6db40f563058c9515dedcc8a3e562d8693a106a28f2ddccf2c8fcf6ca", size = 55140958, upload-time = "2025-10-01T18:02:11.199Z" }, - { url = "https://files.pythonhosted.org/packages/a4/56/4c0d503fe03bac820ecdeb14590cf9a248e120f483bcd5c009f2534f23f0/llvmlite-0.45.1-cp311-cp311-win_amd64.whl", hash = "sha256:f9c272682d91e0d57f2a76c6d9ebdfccc603a01828cdbe3d15273bdca0c3363a", size = 38132232, upload-time = "2025-10-01T18:04:52.181Z" }, - { url = "https://files.pythonhosted.org/packages/e2/7c/82cbd5c656e8991bcc110c69d05913be2229302a92acb96109e166ae31fb/llvmlite-0.45.1-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:28e763aba92fe9c72296911e040231d486447c01d4f90027c8e893d89d49b20e", size = 43043524, upload-time = "2025-10-01T18:03:30.666Z" }, - { url = "https://files.pythonhosted.org/packages/9d/bc/5314005bb2c7ee9f33102c6456c18cc81745d7055155d1218f1624463774/llvmlite-0.45.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1a53f4b74ee9fd30cb3d27d904dadece67a7575198bd80e687ee76474620735f", size = 37253123, upload-time = "2025-10-01T18:04:18.177Z" }, - { url = "https://files.pythonhosted.org/packages/96/76/0f7154952f037cb320b83e1c952ec4a19d5d689cf7d27cb8a26887d7bbc1/llvmlite-0.45.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5b3796b1b1e1c14dcae34285d2f4ea488402fbd2c400ccf7137603ca3800864f", size = 56288211, upload-time = "2025-10-01T18:01:24.079Z" }, - { url = "https://files.pythonhosted.org/packages/00/b1/0b581942be2683ceb6862d558979e87387e14ad65a1e4db0e7dd671fa315/llvmlite-0.45.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:779e2f2ceefef0f4368548685f0b4adde34e5f4b457e90391f570a10b348d433", size = 55140958, upload-time = "2025-10-01T18:02:30.482Z" }, - { url = "https://files.pythonhosted.org/packages/33/94/9ba4ebcf4d541a325fd8098ddc073b663af75cc8b065b6059848f7d4dce7/llvmlite-0.45.1-cp312-cp312-win_amd64.whl", hash = "sha256:9e6c9949baf25d9aa9cd7cf0f6d011b9ca660dd17f5ba2b23bdbdb77cc86b116", size = 38132231, upload-time = "2025-10-01T18:05:03.664Z" }, - { url = "https://files.pythonhosted.org/packages/1d/e2/c185bb7e88514d5025f93c6c4092f6120c6cea8fe938974ec9860fb03bbb/llvmlite-0.45.1-cp313-cp313-macosx_10_15_x86_64.whl", hash = "sha256:d9ea9e6f17569a4253515cc01dade70aba536476e3d750b2e18d81d7e670eb15", size = 43043524, upload-time = "2025-10-01T18:03:43.249Z" }, - { url = "https://files.pythonhosted.org/packages/09/b8/b5437b9ecb2064e89ccf67dccae0d02cd38911705112dd0dcbfa9cd9a9de/llvmlite-0.45.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:c9f3cadee1630ce4ac18ea38adebf2a4f57a89bd2740ce83746876797f6e0bfb", size = 37253121, upload-time = "2025-10-01T18:04:30.557Z" }, - { url = "https://files.pythonhosted.org/packages/f7/97/ad1a907c0173a90dd4df7228f24a3ec61058bc1a9ff8a0caec20a0cc622e/llvmlite-0.45.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:57c48bf2e1083eedbc9406fb83c4e6483017879714916fe8be8a72a9672c995a", size = 56288210, upload-time = "2025-10-01T18:01:40.26Z" }, - { url = "https://files.pythonhosted.org/packages/32/d8/c99c8ac7a326e9735401ead3116f7685a7ec652691aeb2615aa732b1fc4a/llvmlite-0.45.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3aa3dfceda4219ae39cf18806c60eeb518c1680ff834b8b311bd784160b9ce40", size = 55140957, upload-time = "2025-10-01T18:02:46.244Z" }, - { url = "https://files.pythonhosted.org/packages/09/56/ed35668130e32dbfad2eb37356793b0a95f23494ab5be7d9bf5cb75850ee/llvmlite-0.45.1-cp313-cp313-win_amd64.whl", hash = "sha256:080e6f8d0778a8239cd47686d402cb66eb165e421efa9391366a9b7e5810a38b", size = 38132232, upload-time = "2025-10-01T18:05:14.477Z" }, +version = "0.46.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/74/cd/08ae687ba099c7e3d21fe2ea536500563ef1943c5105bf6ab4ee3829f68e/llvmlite-0.46.0.tar.gz", hash = "sha256:227c9fd6d09dce2783c18b754b7cd9d9b3b3515210c46acc2d3c5badd9870ceb", size = 193456, upload-time = "2025-12-08T18:15:36.295Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3d/a4/3959e1c61c5ca9db7921e5fd115b344c29b9d57a5dadd87bef97963ca1a5/llvmlite-0.46.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4323177e936d61ae0f73e653e2e614284d97d14d5dd12579adc92b6c2b0597b0", size = 37232766, upload-time = "2025-12-08T18:14:34.765Z" }, + { url = "https://files.pythonhosted.org/packages/c2/a5/a4d916f1015106e1da876028606a8e87fd5d5c840f98c87bc2d5153b6a2f/llvmlite-0.46.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0a2d461cb89537b7c20feb04c46c32e12d5ad4f0896c9dfc0f60336219ff248e", size = 56275176, upload-time = "2025-12-08T18:14:37.944Z" }, + { url = "https://files.pythonhosted.org/packages/79/7f/a7f2028805dac8c1a6fae7bda4e739b7ebbcd45b29e15bf6d21556fcd3d5/llvmlite-0.46.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b1f6595a35b7b39c3518b85a28bf18f45e075264e4b2dce3f0c2a4f232b4a910", size = 55128629, upload-time = "2025-12-08T18:14:41.674Z" }, + { url = "https://files.pythonhosted.org/packages/b2/bc/4689e1ba0c073c196b594471eb21be0aa51d9e64b911728aa13cd85ef0ae/llvmlite-0.46.0-cp310-cp310-win_amd64.whl", hash = "sha256:e7a34d4aa6f9a97ee006b504be6d2b8cb7f755b80ab2f344dda1ef992f828559", size = 38138651, upload-time = "2025-12-08T18:14:45.845Z" }, + { url = "https://files.pythonhosted.org/packages/7a/a1/2ad4b2367915faeebe8447f0a057861f646dbf5fbbb3561db42c65659cf3/llvmlite-0.46.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:82f3d39b16f19aa1a56d5fe625883a6ab600d5cc9ea8906cca70ce94cabba067", size = 37232766, upload-time = "2025-12-08T18:14:48.836Z" }, + { url = "https://files.pythonhosted.org/packages/12/b5/99cf8772fdd846c07da4fd70f07812a3c8fd17ea2409522c946bb0f2b277/llvmlite-0.46.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a3df43900119803bbc52720e758c76f316a9a0f34612a886862dfe0a5591a17e", size = 56275175, upload-time = "2025-12-08T18:14:51.604Z" }, + { url = "https://files.pythonhosted.org/packages/38/f2/ed806f9c003563732da156139c45d970ee435bd0bfa5ed8de87ba972b452/llvmlite-0.46.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:de183fefc8022d21b0aa37fc3e90410bc3524aed8617f0ff76732fc6c3af5361", size = 55128630, upload-time = "2025-12-08T18:14:55.107Z" }, + { url = "https://files.pythonhosted.org/packages/19/0c/8f5a37a65fc9b7b17408508145edd5f86263ad69c19d3574e818f533a0eb/llvmlite-0.46.0-cp311-cp311-win_amd64.whl", hash = "sha256:e8b10bc585c58bdffec9e0c309bb7d51be1f2f15e169a4b4d42f2389e431eb93", size = 38138652, upload-time = "2025-12-08T18:14:58.171Z" }, + { url = "https://files.pythonhosted.org/packages/2b/f8/4db016a5e547d4e054ff2f3b99203d63a497465f81ab78ec8eb2ff7b2304/llvmlite-0.46.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6b9588ad4c63b4f0175a3984b85494f0c927c6b001e3a246a3a7fb3920d9a137", size = 37232767, upload-time = "2025-12-08T18:15:00.737Z" }, + { url = "https://files.pythonhosted.org/packages/aa/85/4890a7c14b4fa54400945cb52ac3cd88545bbdb973c440f98ca41591cdc5/llvmlite-0.46.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3535bd2bb6a2d7ae4012681ac228e5132cdb75fefb1bcb24e33f2f3e0c865ed4", size = 56275176, upload-time = "2025-12-08T18:15:03.936Z" }, + { url = "https://files.pythonhosted.org/packages/6a/07/3d31d39c1a1a08cd5337e78299fca77e6aebc07c059fbd0033e3edfab45c/llvmlite-0.46.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4cbfd366e60ff87ea6cc62f50bc4cd800ebb13ed4c149466f50cf2163a473d1e", size = 55128630, upload-time = "2025-12-08T18:15:07.196Z" }, + { url = "https://files.pythonhosted.org/packages/2a/6b/d139535d7590a1bba1ceb68751bef22fadaa5b815bbdf0e858e3875726b2/llvmlite-0.46.0-cp312-cp312-win_amd64.whl", hash = "sha256:398b39db462c39563a97b912d4f2866cd37cba60537975a09679b28fbbc0fb38", size = 38138940, upload-time = "2025-12-08T18:15:10.162Z" }, + { url = "https://files.pythonhosted.org/packages/e6/ff/3eba7eb0aed4b6fca37125387cd417e8c458e750621fce56d2c541f67fa8/llvmlite-0.46.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:30b60892d034bc560e0ec6654737aaa74e5ca327bd8114d82136aa071d611172", size = 37232767, upload-time = "2025-12-08T18:15:13.22Z" }, + { url = "https://files.pythonhosted.org/packages/0e/54/737755c0a91558364b9200702c3c9c15d70ed63f9b98a2c32f1c2aa1f3ba/llvmlite-0.46.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6cc19b051753368a9c9f31dc041299059ee91aceec81bd57b0e385e5d5bf1a54", size = 56275176, upload-time = "2025-12-08T18:15:16.339Z" }, + { url = "https://files.pythonhosted.org/packages/e6/91/14f32e1d70905c1c0aa4e6609ab5d705c3183116ca02ac6df2091868413a/llvmlite-0.46.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bca185892908f9ede48c0acd547fe4dc1bafefb8a4967d47db6cf664f9332d12", size = 55128629, upload-time = "2025-12-08T18:15:19.493Z" }, + { url = "https://files.pythonhosted.org/packages/4a/a7/d526ae86708cea531935ae777b6dbcabe7db52718e6401e0fb9c5edea80e/llvmlite-0.46.0-cp313-cp313-win_amd64.whl", hash = "sha256:67438fd30e12349ebb054d86a5a1a57fd5e87d264d2451bcfafbbbaa25b82a35", size = 38138941, upload-time = "2025-12-08T18:15:22.536Z" }, + { url = "https://files.pythonhosted.org/packages/95/ae/af0ffb724814cc2ea64445acad05f71cff5f799bb7efb22e47ee99340dbc/llvmlite-0.46.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:d252edfb9f4ac1fcf20652258e3f102b26b03eef738dc8a6ffdab7d7d341d547", size = 37232768, upload-time = "2025-12-08T18:15:25.055Z" }, + { url = "https://files.pythonhosted.org/packages/c9/19/5018e5352019be753b7b07f7759cdabb69ca5779fea2494be8839270df4c/llvmlite-0.46.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:379fdd1c59badeff8982cb47e4694a6143bec3bb49aa10a466e095410522064d", size = 56275173, upload-time = "2025-12-08T18:15:28.109Z" }, + { url = "https://files.pythonhosted.org/packages/9f/c9/d57877759d707e84c082163c543853245f91b70c804115a5010532890f18/llvmlite-0.46.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2e8cbfff7f6db0fa2c771ad24154e2a7e457c2444d7673e6de06b8b698c3b269", size = 55128628, upload-time = "2025-12-08T18:15:31.098Z" }, + { url = "https://files.pythonhosted.org/packages/30/a8/e61a8c2b3cc7a597073d9cde1fcbb567e9d827f1db30c93cf80422eac70d/llvmlite-0.46.0-cp314-cp314-win_amd64.whl", hash = "sha256:7821eda3ec1f18050f981819756631d60b6d7ab1a6cf806d9efefbe3f4082d61", size = 39153056, upload-time = "2025-12-08T18:15:33.938Z" }, ] [[package]] @@ -3179,11 +3277,11 @@ wheels = [ [[package]] name = "marko" -version = "2.2.1" +version = "2.2.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f4/60/f5ce3c467b29fbf8f654c56e23ddde6febf52b8fab4b8e949f46aa8e1c12/marko-2.2.1.tar.gz", hash = "sha256:e29d7e071a3b0cb2f7cc4c500d55f893dc5a45d85a8298dde6cb4e4dffd794d3", size = 143474, upload-time = "2025-10-13T03:13:42.101Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e3/2f/050b6d485f052ddf17d76a41f9334d6fb2a8a85df35347a12d97ed3bc5c1/marko-2.2.2.tar.gz", hash = "sha256:6940308e655f63733ca518c47a68ec9510279dbb916c83616e4c4b5829f052e8", size = 143641, upload-time = "2026-01-05T11:04:41.935Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/73/de/65dfc670e50c9db92b750db1d7c87292b8f3ba9be2c1154594d1a7d1afb4/marko-2.2.1-py3-none-any.whl", hash = "sha256:31e9a18b35c113e506ace5594716fa3df2872f8955908e279bc551f3eb1f0db8", size = 42688, upload-time = "2025-10-13T03:13:40.452Z" }, + { url = "https://files.pythonhosted.org/packages/83/f8/36d79bac5701e6786f9880c61bbe57574760a13c1af84ab71e5ed21faecc/marko-2.2.2-py3-none-any.whl", hash = "sha256:f064ae8c10416285ad1d96048dc11e98ef04e662d3342ae416f662b70aa7959e", size = 42701, upload-time = "2026-01-05T11:04:40.75Z" }, ] [[package]] @@ -3273,14 +3371,26 @@ wheels = [ [[package]] name = "marshmallow" -version = "3.26.1" +version = "3.26.2" 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, upload-time = "2025-02-03T15:32:25.093Z" } +sdist = { url = "https://files.pythonhosted.org/packages/55/79/de6c16cc902f4fc372236926b0ce2ab7845268dcc30fb2fbb7f71b418631/marshmallow-3.26.2.tar.gz", hash = "sha256:bbe2adb5a03e6e3571b573f42527c6fe926e17467833660bebd11593ab8dfd57", size = 222095, upload-time = "2025-12-22T06:53:53.309Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/34/75/51952c7b2d3873b44a0028b1bd26a25078c18f92f256608e8d1dc61b39fd/marshmallow-3.26.1-py3-none-any.whl", hash = "sha256:3350409f20a70a7e4e11a27661187b77cdcaeb20abca41c1454fe33636bea09c", size = 50878, upload-time = "2025-02-03T15:32:22.295Z" }, + { url = "https://files.pythonhosted.org/packages/be/2f/5108cb3ee4ba6501748c4908b908e55f42a5b66245b4cfe0c99326e1ef6e/marshmallow-3.26.2-py3-none-any.whl", hash = "sha256:013fa8a3c4c276c24d26d84ce934dc964e2aa794345a0f8c7e5a7191482c8a73", size = 50964, upload-time = "2025-12-22T06:53:51.801Z" }, +] + +[[package]] +name = "math-verify" +version = "0.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "latex2sympy2-extended" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4f/12/b8d13b581e110ac2f724a2351a8361a70fa36d057eb945d6379e8747c256/math_verify-0.9.0.tar.gz", hash = "sha256:45ac6c61344ba056b9e99a660a4bc8d044ed408f730aed68c60435aa5eec4645", size = 60329, upload-time = "2026-01-10T01:48:33.056Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/76/6b4969bccc842b6567f7e6ee015684b9428a9b7fcbdf479e73716f43597f/math_verify-0.9.0-py3-none-any.whl", hash = "sha256:3703e7c4885354027fa84409d762a596a2906d1fd4deb78361876bd905a76194", size = 29967, upload-time = "2026-01-10T01:48:31.674Z" }, ] [[package]] @@ -3318,29 +3428,33 @@ wheels = [ [[package]] name = "mellea" -version = "0.0.6" +version = "0.2.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "ansicolors" }, { name = "click" }, { name = "fastapi" }, + { name = "granite-common" }, { name = "huggingface-hub" }, { name = "jinja2" }, { name = "json5" }, + { name = "llm-sandbox", extra = ["docker"] }, + { name = "math-verify" }, { name = "mistletoe" }, { name = "ollama" }, { name = "openai" }, { name = "pillow" }, { name = "pydantic" }, { name = "requests" }, + { name = "rouge-score" }, { name = "typer" }, { name = "types-requests" }, { name = "types-tqdm" }, { name = "uvicorn" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c2/44/a17baf5168e818e3c394f584c9e44dad69ca30d05ba90084f054483bb192/mellea-0.0.6.tar.gz", hash = "sha256:e2a0d13d8197acff501d03d2cf0e05b47041e6e5cfb570673ec93e343ef4be72", size = 133365, upload-time = "2025-09-18T00:40:24.94Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2f/8d/e9032cacc0b5f54236074a8e1a1333813db4f0613baea17e5d815305135b/mellea-0.2.4.tar.gz", hash = "sha256:973a20bf7a3f25a7e85a563d79a166b6bb8a12142d93a46f71df26632362db65", size = 208578, upload-time = "2026-01-08T17:25:40.282Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/27/62/b359f5966f492e2e21203d3e5248da1247a65ea329e0388431cf4c704d15/mellea-0.0.6-py3-none-any.whl", hash = "sha256:6cafb7647df255a2a8d1118d8a9a466d5bc1575a43c5437e3a27f57d0b76ff12", size = 205415, upload-time = "2025-09-18T00:40:23.486Z" }, + { url = "https://files.pythonhosted.org/packages/e0/96/b1482a8aec3419981e7f40eb9f7388a924c3023a77aaf032d39ddbe062ac/mellea-0.2.4-py3-none-any.whl", hash = "sha256:549b1b57f081a5837ea30692075ab29fd224a0600f4fa6da99b56be95b9c1759", size = 308071, upload-time = "2026-01-08T17:25:38.84Z" }, ] [[package]] @@ -3368,23 +3482,23 @@ wheels = [ [[package]] name = "mistletoe" -version = "1.5.0" +version = "1.5.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/71/99/a42e378cc63df214c80e1487237964b6ffc23a847085c13ebf71f623e9aa/mistletoe-1.5.0.tar.gz", hash = "sha256:68b73be68bf8075ec882e035176d5c379d0d7f3401392c6f39c2cc9cd3264d44", size = 111620, upload-time = "2025-10-18T16:37:09.523Z" } +sdist = { url = "https://files.pythonhosted.org/packages/31/ae/d33647e2a26a8899224f36afc5e7b7a670af30f1fd87231e9f07ca19d673/mistletoe-1.5.1.tar.gz", hash = "sha256:c5571ce6ca9cfdc7ce9151c3ae79acb418e067812000907616427197648030a3", size = 111769, upload-time = "2025-12-07T16:19:01.066Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/c2/bd0fc48dc323035cd65d21c45a3bb5c7b054001bf4ea75e461beb7656e07/mistletoe-1.5.0-py3-none-any.whl", hash = "sha256:d4e77b991b998c5efe3c4eab4e1b472263b5349688acd50d79bd9a6c317a9df1", size = 55262, upload-time = "2025-10-18T16:37:08.376Z" }, + { url = "https://files.pythonhosted.org/packages/20/60/0980fefdc4d12c18c1bbab9d62852f27aded8839233c7b0a9827aaf395f5/mistletoe-1.5.1-py3-none-any.whl", hash = "sha256:d3e97664798261503f685f6a6281b092628367cf3128fc68a015a993b0c4feb3", size = 55331, upload-time = "2025-12-07T16:18:59.65Z" }, ] [[package]] name = "mistune" -version = "3.1.4" +version = "3.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d7/02/a7fb8b21d4d55ac93cdcde9d3638da5dd0ebdd3a4fed76c7725e10b81cbe/mistune-3.1.4.tar.gz", hash = "sha256:b5a7f801d389f724ec702840c11d8fc48f2b33519102fc7ee739e8177b672164", size = 94588, upload-time = "2025-08-29T07:20:43.594Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9d/55/d01f0c4b45ade6536c51170b9043db8b2ec6ddf4a35c7ea3f5f559ac935b/mistune-3.2.0.tar.gz", hash = "sha256:708487c8a8cdd99c9d90eb3ed4c3ed961246ff78ac82f03418f5183ab70e398a", size = 95467, upload-time = "2025-12-23T11:36:34.994Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7a/f0/8282d9641415e9e33df173516226b404d367a0fc55e1a60424a152913abc/mistune-3.1.4-py3-none-any.whl", hash = "sha256:93691da911e5d9d2e23bc54472892aff676df27a75274962ff9edc210364266d", size = 53481, upload-time = "2025-08-29T07:20:42.218Z" }, + { url = "https://files.pythonhosted.org/packages/9b/f7/4a5e785ec9fbd65146a27b6b70b6cdc161a66f2024e4b04ac06a67f5578b/mistune-3.2.0-py3-none-any.whl", hash = "sha256:febdc629a3c78616b94393c6580551e0e34cc289987ec6c35ed3f4be42d0eee1", size = 53598, upload-time = "2025-12-23T11:36:33.211Z" }, ] [[package]] @@ -3427,7 +3541,7 @@ wheels = [ [[package]] name = "mkdocs-awesome-nav" -version = "3.2.0" +version = "3.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "mkdocs" }, @@ -3435,9 +3549,9 @@ dependencies = [ { name = "pydantic" }, { name = "wcmatch" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fc/59/d39d2ab15f667a07d978110efd67da14f77d3316ec69b29d69ebe8babdbd/mkdocs_awesome_nav-3.2.0.tar.gz", hash = "sha256:ec0eab7bbe94532d9b3f18ae9a54599b670069856b93df9f6ced1b4a62dfe510", size = 8947, upload-time = "2025-09-10T21:39:21.129Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1f/96/d581c9ceae7d8c08c7456d1d2befbf3e9c7a52a6b03aebbe6dc1d46ffdac/mkdocs_awesome_nav-3.3.0.tar.gz", hash = "sha256:330edf4129995a7be8a9751edd8739d312fddf591c6cbd95ec80f12f4bfe8578", size = 9112, upload-time = "2025-12-02T20:10:46.11Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f4/2a/dab5925d2d66a1620400e9b64127e576859a0d5dc0cb5ebda1c4f64601ef/mkdocs_awesome_nav-3.2.0-py3-none-any.whl", hash = "sha256:1acf17aaa0f13a5506d3bdb39965bae3136df18a9a2e1106190ebaae2c14d1b6", size = 12662, upload-time = "2025-09-10T21:39:20.302Z" }, + { url = "https://files.pythonhosted.org/packages/de/98/dc154a7f0bb25827d988dc31992917606d1ca9f5349077db045bb5cd9dbc/mkdocs_awesome_nav-3.3.0-py3-none-any.whl", hash = "sha256:bf80f14bff9571df79129bbdf103b4198589ee7616f8a29720ae2e7ac2455930", size = 12891, upload-time = "2025-12-02T20:10:45.054Z" }, ] [[package]] @@ -3512,7 +3626,7 @@ wheels = [ [[package]] name = "mkdocs-material" -version = "9.7.0" +version = "9.7.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "babel" }, @@ -3527,9 +3641,9 @@ dependencies = [ { name = "pymdown-extensions" }, { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9c/3b/111b84cd6ff28d9e955b5f799ef217a17bc1684ac346af333e6100e413cb/mkdocs_material-9.7.0.tar.gz", hash = "sha256:602b359844e906ee402b7ed9640340cf8a474420d02d8891451733b6b02314ec", size = 4094546, upload-time = "2025-11-11T08:49:09.73Z" } +sdist = { url = "https://files.pythonhosted.org/packages/27/e2/2ffc356cd72f1473d07c7719d82a8f2cbd261666828614ecb95b12169f41/mkdocs_material-9.7.1.tar.gz", hash = "sha256:89601b8f2c3e6c6ee0a918cc3566cb201d40bf37c3cd3c2067e26fadb8cce2b8", size = 4094392, upload-time = "2025-12-18T09:49:00.308Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/04/87/eefe8d5e764f4cf50ed91b943f8e8f96b5efd65489d8303b7a36e2e79834/mkdocs_material-9.7.0-py3-none-any.whl", hash = "sha256:da2866ea53601125ff5baa8aa06404c6e07af3c5ce3d5de95e3b52b80b442887", size = 9283770, upload-time = "2025-11-11T08:49:06.26Z" }, + { url = "https://files.pythonhosted.org/packages/3e/32/ed071cb721aca8c227718cffcf7bd539620e9799bbf2619e90c757bfd030/mkdocs_material-9.7.1-py3-none-any.whl", hash = "sha256:3f6100937d7d731f87f1e3e3b021c97f7239666b9ba1151ab476cabb96c60d5c", size = 9297166, upload-time = "2025-12-18T09:48:56.664Z" }, ] [[package]] @@ -3543,7 +3657,7 @@ wheels = [ [[package]] name = "mkdocstrings" -version = "0.30.1" +version = "1.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jinja2" }, @@ -3553,9 +3667,9 @@ dependencies = [ { name = "mkdocs-autorefs" }, { name = "pymdown-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c5/33/2fa3243439f794e685d3e694590d28469a9b8ea733af4b48c250a3ffc9a0/mkdocstrings-0.30.1.tar.gz", hash = "sha256:84a007aae9b707fb0aebfc9da23db4b26fc9ab562eb56e335e9ec480cb19744f", size = 106350, upload-time = "2025-09-19T10:49:26.446Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e5/13/10bbf9d56565fd91b91e6f5a8cd9b9d8a2b101c4e8ad6eeafa35a706301d/mkdocstrings-1.0.0.tar.gz", hash = "sha256:351a006dbb27aefce241ade110d3cd040c1145b7a3eb5fd5ac23f03ed67f401a", size = 101086, upload-time = "2025-11-27T15:39:40.534Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7b/2c/f0dc4e1ee7f618f5bff7e05898d20bf8b6e7fa612038f768bfa295f136a4/mkdocstrings-0.30.1-py3-none-any.whl", hash = "sha256:41bd71f284ca4d44a668816193e4025c950b002252081e387433656ae9a70a82", size = 36704, upload-time = "2025-09-19T10:49:24.805Z" }, + { url = "https://files.pythonhosted.org/packages/ec/fc/80aa31b79133634721cf7855d37b76ea49773599214896f2ff10be03de2a/mkdocstrings-1.0.0-py3-none-any.whl", hash = "sha256:4c50eb960bff6e05dfc631f6bc00dfabffbcb29c5ff25f676d64daae05ed82fa", size = 35135, upload-time = "2025-11-27T15:39:39.301Z" }, ] [package.optional-dependencies] @@ -3565,7 +3679,7 @@ python = [ [[package]] name = "mkdocstrings-python" -version = "1.19.0" +version = "2.0.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "griffe" }, @@ -3573,9 +3687,9 @@ dependencies = [ { name = "mkdocstrings" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/75/1c/3af8413919b0839b96a78f60e8bd0dfd26c844d3717eeb77f80b43f5be1c/mkdocstrings_python-1.19.0.tar.gz", hash = "sha256:917aac66cf121243c11db5b89f66b0ded6c53ec0de5318ff5e22424eb2f2e57c", size = 204010, upload-time = "2025-11-10T13:30:55.915Z" } +sdist = { url = "https://files.pythonhosted.org/packages/24/75/d30af27a2906f00eb90143470272376d728521997800f5dce5b340ba35bc/mkdocstrings_python-2.0.1.tar.gz", hash = "sha256:843a562221e6a471fefdd4b45cc6c22d2607ccbad632879234fa9692e9cf7732", size = 199345, upload-time = "2025-12-03T14:26:11.755Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/98/5c/2597cef67b6947b15c47f8dba967a0baf19fbdfdc86f6e4a8ba7af8b581a/mkdocstrings_python-1.19.0-py3-none-any.whl", hash = "sha256:395c1032af8f005234170575cc0c5d4d20980846623b623b35594281be4a3059", size = 143417, upload-time = "2025-11-10T13:30:54.164Z" }, + { url = "https://files.pythonhosted.org/packages/81/06/c5f8deba7d2cbdfa7967a716ae801aa9ca5f734b8f54fd473ef77a088dbe/mkdocstrings_python-2.0.1-py3-none-any.whl", hash = "sha256:66ecff45c5f8b71bf174e11d49afc845c2dfc7fc0ab17a86b6b337e0f24d8d90", size = 105055, upload-time = "2025-12-03T14:26:10.184Z" }, ] [[package]] @@ -3899,47 +4013,48 @@ wheels = [ [[package]] name = "mypy" -version = "1.18.2" +version = "1.19.1" source = { registry = "https://pypi.org/simple" } dependencies = [ + { name = "librt", marker = "platform_python_implementation != 'PyPy'" }, { name = "mypy-extensions" }, { name = "pathspec" }, { name = "tomli", marker = "python_full_version < '3.11'" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c0/77/8f0d0001ffad290cef2f7f216f96c814866248a0b92a722365ed54648e7e/mypy-1.18.2.tar.gz", hash = "sha256:06a398102a5f203d7477b2923dda3634c36727fa5c237d8f859ef90c42a9924b", size = 3448846, upload-time = "2025-09-19T00:11:10.519Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/03/6f/657961a0743cff32e6c0611b63ff1c1970a0b482ace35b069203bf705187/mypy-1.18.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c1eab0cf6294dafe397c261a75f96dc2c31bffe3b944faa24db5def4e2b0f77c", size = 12807973, upload-time = "2025-09-19T00:10:35.282Z" }, - { url = "https://files.pythonhosted.org/packages/10/e9/420822d4f661f13ca8900f5fa239b40ee3be8b62b32f3357df9a3045a08b/mypy-1.18.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7a780ca61fc239e4865968ebc5240bb3bf610ef59ac398de9a7421b54e4a207e", size = 11896527, upload-time = "2025-09-19T00:10:55.791Z" }, - { url = "https://files.pythonhosted.org/packages/aa/73/a05b2bbaa7005f4642fcfe40fb73f2b4fb6bb44229bd585b5878e9a87ef8/mypy-1.18.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:448acd386266989ef11662ce3c8011fd2a7b632e0ec7d61a98edd8e27472225b", size = 12507004, upload-time = "2025-09-19T00:11:05.411Z" }, - { url = "https://files.pythonhosted.org/packages/4f/01/f6e4b9f0d031c11ccbd6f17da26564f3a0f3c4155af344006434b0a05a9d/mypy-1.18.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f9e171c465ad3901dc652643ee4bffa8e9fef4d7d0eece23b428908c77a76a66", size = 13245947, upload-time = "2025-09-19T00:10:46.923Z" }, - { url = "https://files.pythonhosted.org/packages/d7/97/19727e7499bfa1ae0773d06afd30ac66a58ed7437d940c70548634b24185/mypy-1.18.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:592ec214750bc00741af1f80cbf96b5013d81486b7bb24cb052382c19e40b428", size = 13499217, upload-time = "2025-09-19T00:09:39.472Z" }, - { url = "https://files.pythonhosted.org/packages/9f/4f/90dc8c15c1441bf31cf0f9918bb077e452618708199e530f4cbd5cede6ff/mypy-1.18.2-cp310-cp310-win_amd64.whl", hash = "sha256:7fb95f97199ea11769ebe3638c29b550b5221e997c63b14ef93d2e971606ebed", size = 9766753, upload-time = "2025-09-19T00:10:49.161Z" }, - { url = "https://files.pythonhosted.org/packages/88/87/cafd3ae563f88f94eec33f35ff722d043e09832ea8530ef149ec1efbaf08/mypy-1.18.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:807d9315ab9d464125aa9fcf6d84fde6e1dc67da0b6f80e7405506b8ac72bc7f", size = 12731198, upload-time = "2025-09-19T00:09:44.857Z" }, - { url = "https://files.pythonhosted.org/packages/0f/e0/1e96c3d4266a06d4b0197ace5356d67d937d8358e2ee3ffac71faa843724/mypy-1.18.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:776bb00de1778caf4db739c6e83919c1d85a448f71979b6a0edd774ea8399341", size = 11817879, upload-time = "2025-09-19T00:09:47.131Z" }, - { url = "https://files.pythonhosted.org/packages/72/ef/0c9ba89eb03453e76bdac5a78b08260a848c7bfc5d6603634774d9cd9525/mypy-1.18.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1379451880512ffce14505493bd9fe469e0697543717298242574882cf8cdb8d", size = 12427292, upload-time = "2025-09-19T00:10:22.472Z" }, - { url = "https://files.pythonhosted.org/packages/1a/52/ec4a061dd599eb8179d5411d99775bec2a20542505988f40fc2fee781068/mypy-1.18.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1331eb7fd110d60c24999893320967594ff84c38ac6d19e0a76c5fd809a84c86", size = 13163750, upload-time = "2025-09-19T00:09:51.472Z" }, - { url = "https://files.pythonhosted.org/packages/c4/5f/2cf2ceb3b36372d51568f2208c021870fe7834cf3186b653ac6446511839/mypy-1.18.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3ca30b50a51e7ba93b00422e486cbb124f1c56a535e20eff7b2d6ab72b3b2e37", size = 13351827, upload-time = "2025-09-19T00:09:58.311Z" }, - { url = "https://files.pythonhosted.org/packages/c8/7d/2697b930179e7277529eaaec1513f8de622818696857f689e4a5432e5e27/mypy-1.18.2-cp311-cp311-win_amd64.whl", hash = "sha256:664dc726e67fa54e14536f6e1224bcfce1d9e5ac02426d2326e2bb4e081d1ce8", size = 9757983, upload-time = "2025-09-19T00:10:09.071Z" }, - { url = "https://files.pythonhosted.org/packages/07/06/dfdd2bc60c66611dd8335f463818514733bc763e4760dee289dcc33df709/mypy-1.18.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:33eca32dd124b29400c31d7cf784e795b050ace0e1f91b8dc035672725617e34", size = 12908273, upload-time = "2025-09-19T00:10:58.321Z" }, - { url = "https://files.pythonhosted.org/packages/81/14/6a9de6d13a122d5608e1a04130724caf9170333ac5a924e10f670687d3eb/mypy-1.18.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a3c47adf30d65e89b2dcd2fa32f3aeb5e94ca970d2c15fcb25e297871c8e4764", size = 11920910, upload-time = "2025-09-19T00:10:20.043Z" }, - { url = "https://files.pythonhosted.org/packages/5f/a9/b29de53e42f18e8cc547e38daa9dfa132ffdc64f7250e353f5c8cdd44bee/mypy-1.18.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d6c838e831a062f5f29d11c9057c6009f60cb294fea33a98422688181fe2893", size = 12465585, upload-time = "2025-09-19T00:10:33.005Z" }, - { url = "https://files.pythonhosted.org/packages/77/ae/6c3d2c7c61ff21f2bee938c917616c92ebf852f015fb55917fd6e2811db2/mypy-1.18.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01199871b6110a2ce984bde85acd481232d17413868c9807e95c1b0739a58914", size = 13348562, upload-time = "2025-09-19T00:10:11.51Z" }, - { url = "https://files.pythonhosted.org/packages/4d/31/aec68ab3b4aebdf8f36d191b0685d99faa899ab990753ca0fee60fb99511/mypy-1.18.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a2afc0fa0b0e91b4599ddfe0f91e2c26c2b5a5ab263737e998d6817874c5f7c8", size = 13533296, upload-time = "2025-09-19T00:10:06.568Z" }, - { url = "https://files.pythonhosted.org/packages/9f/83/abcb3ad9478fca3ebeb6a5358bb0b22c95ea42b43b7789c7fb1297ca44f4/mypy-1.18.2-cp312-cp312-win_amd64.whl", hash = "sha256:d8068d0afe682c7c4897c0f7ce84ea77f6de953262b12d07038f4d296d547074", size = 9828828, upload-time = "2025-09-19T00:10:28.203Z" }, - { url = "https://files.pythonhosted.org/packages/5f/04/7f462e6fbba87a72bc8097b93f6842499c428a6ff0c81dd46948d175afe8/mypy-1.18.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:07b8b0f580ca6d289e69209ec9d3911b4a26e5abfde32228a288eb79df129fcc", size = 12898728, upload-time = "2025-09-19T00:10:01.33Z" }, - { url = "https://files.pythonhosted.org/packages/99/5b/61ed4efb64f1871b41fd0b82d29a64640f3516078f6c7905b68ab1ad8b13/mypy-1.18.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ed4482847168439651d3feee5833ccedbf6657e964572706a2adb1f7fa4dfe2e", size = 11910758, upload-time = "2025-09-19T00:10:42.607Z" }, - { url = "https://files.pythonhosted.org/packages/3c/46/d297d4b683cc89a6e4108c4250a6a6b717f5fa96e1a30a7944a6da44da35/mypy-1.18.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c3ad2afadd1e9fea5cf99a45a822346971ede8685cc581ed9cd4d42eaf940986", size = 12475342, upload-time = "2025-09-19T00:11:00.371Z" }, - { url = "https://files.pythonhosted.org/packages/83/45/4798f4d00df13eae3bfdf726c9244bcb495ab5bd588c0eed93a2f2dd67f3/mypy-1.18.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a431a6f1ef14cf8c144c6b14793a23ec4eae3db28277c358136e79d7d062f62d", size = 13338709, upload-time = "2025-09-19T00:11:03.358Z" }, - { url = "https://files.pythonhosted.org/packages/d7/09/479f7358d9625172521a87a9271ddd2441e1dab16a09708f056e97007207/mypy-1.18.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7ab28cc197f1dd77a67e1c6f35cd1f8e8b73ed2217e4fc005f9e6a504e46e7ba", size = 13529806, upload-time = "2025-09-19T00:10:26.073Z" }, - { url = "https://files.pythonhosted.org/packages/71/cf/ac0f2c7e9d0ea3c75cd99dff7aec1c9df4a1376537cb90e4c882267ee7e9/mypy-1.18.2-cp313-cp313-win_amd64.whl", hash = "sha256:0e2785a84b34a72ba55fb5daf079a1003a34c05b22238da94fcae2bbe46f3544", size = 9833262, upload-time = "2025-09-19T00:10:40.035Z" }, - { url = "https://files.pythonhosted.org/packages/5a/0c/7d5300883da16f0063ae53996358758b2a2df2a09c72a5061fa79a1f5006/mypy-1.18.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:62f0e1e988ad41c2a110edde6c398383a889d95b36b3e60bcf155f5164c4fdce", size = 12893775, upload-time = "2025-09-19T00:10:03.814Z" }, - { url = "https://files.pythonhosted.org/packages/50/df/2cffbf25737bdb236f60c973edf62e3e7b4ee1c25b6878629e88e2cde967/mypy-1.18.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8795a039bab805ff0c1dfdb8cd3344642c2b99b8e439d057aba30850b8d3423d", size = 11936852, upload-time = "2025-09-19T00:10:51.631Z" }, - { url = "https://files.pythonhosted.org/packages/be/50/34059de13dd269227fb4a03be1faee6e2a4b04a2051c82ac0a0b5a773c9a/mypy-1.18.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6ca1e64b24a700ab5ce10133f7ccd956a04715463d30498e64ea8715236f9c9c", size = 12480242, upload-time = "2025-09-19T00:11:07.955Z" }, - { url = "https://files.pythonhosted.org/packages/5b/11/040983fad5132d85914c874a2836252bbc57832065548885b5bb5b0d4359/mypy-1.18.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d924eef3795cc89fecf6bedc6ed32b33ac13e8321344f6ddbf8ee89f706c05cb", size = 13326683, upload-time = "2025-09-19T00:09:55.572Z" }, - { url = "https://files.pythonhosted.org/packages/e9/ba/89b2901dd77414dd7a8c8729985832a5735053be15b744c18e4586e506ef/mypy-1.18.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:20c02215a080e3a2be3aa50506c67242df1c151eaba0dcbc1e4e557922a26075", size = 13514749, upload-time = "2025-09-19T00:10:44.827Z" }, - { url = "https://files.pythonhosted.org/packages/25/bc/cc98767cffd6b2928ba680f3e5bc969c4152bf7c2d83f92f5a504b92b0eb/mypy-1.18.2-cp314-cp314-win_amd64.whl", hash = "sha256:749b5f83198f1ca64345603118a6f01a4e99ad4bf9d103ddc5a3200cc4614adf", size = 9982959, upload-time = "2025-09-19T00:10:37.344Z" }, - { url = "https://files.pythonhosted.org/packages/87/e3/be76d87158ebafa0309946c4a73831974d4d6ab4f4ef40c3b53a385a66fd/mypy-1.18.2-py3-none-any.whl", hash = "sha256:22a1748707dd62b58d2ae53562ffc4d7f8bcc727e8ac7cbc69c053ddc874d47e", size = 2352367, upload-time = "2025-09-19T00:10:15.489Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/f5/db/4efed9504bc01309ab9c2da7e352cc223569f05478012b5d9ece38fd44d2/mypy-1.19.1.tar.gz", hash = "sha256:19d88bb05303fe63f71dd2c6270daca27cb9401c4ca8255fe50d1d920e0eb9ba", size = 3582404, upload-time = "2025-12-15T05:03:48.42Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2f/63/e499890d8e39b1ff2df4c0c6ce5d371b6844ee22b8250687a99fd2f657a8/mypy-1.19.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5f05aa3d375b385734388e844bc01733bd33c644ab48e9684faa54e5389775ec", size = 13101333, upload-time = "2025-12-15T05:03:03.28Z" }, + { url = "https://files.pythonhosted.org/packages/72/4b/095626fc136fba96effc4fd4a82b41d688ab92124f8c4f7564bffe5cf1b0/mypy-1.19.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:022ea7279374af1a5d78dfcab853fe6a536eebfda4b59deab53cd21f6cd9f00b", size = 12164102, upload-time = "2025-12-15T05:02:33.611Z" }, + { url = "https://files.pythonhosted.org/packages/0c/5b/952928dd081bf88a83a5ccd49aaecfcd18fd0d2710c7ff07b8fb6f7032b9/mypy-1.19.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee4c11e460685c3e0c64a4c5de82ae143622410950d6be863303a1c4ba0e36d6", size = 12765799, upload-time = "2025-12-15T05:03:28.44Z" }, + { url = "https://files.pythonhosted.org/packages/2a/0d/93c2e4a287f74ef11a66fb6d49c7a9f05e47b0a4399040e6719b57f500d2/mypy-1.19.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de759aafbae8763283b2ee5869c7255391fbc4de3ff171f8f030b5ec48381b74", size = 13522149, upload-time = "2025-12-15T05:02:36.011Z" }, + { url = "https://files.pythonhosted.org/packages/7b/0e/33a294b56aaad2b338d203e3a1d8b453637ac36cb278b45005e0901cf148/mypy-1.19.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ab43590f9cd5108f41aacf9fca31841142c786827a74ab7cc8a2eacb634e09a1", size = 13810105, upload-time = "2025-12-15T05:02:40.327Z" }, + { url = "https://files.pythonhosted.org/packages/0e/fd/3e82603a0cb66b67c5e7abababce6bf1a929ddf67bf445e652684af5c5a0/mypy-1.19.1-cp310-cp310-win_amd64.whl", hash = "sha256:2899753e2f61e571b3971747e302d5f420c3fd09650e1951e99f823bc3089dac", size = 10057200, upload-time = "2025-12-15T05:02:51.012Z" }, + { url = "https://files.pythonhosted.org/packages/ef/47/6b3ebabd5474d9cdc170d1342fbf9dddc1b0ec13ec90bf9004ee6f391c31/mypy-1.19.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d8dfc6ab58ca7dda47d9237349157500468e404b17213d44fc1cb77bce532288", size = 13028539, upload-time = "2025-12-15T05:03:44.129Z" }, + { url = "https://files.pythonhosted.org/packages/5c/a6/ac7c7a88a3c9c54334f53a941b765e6ec6c4ebd65d3fe8cdcfbe0d0fd7db/mypy-1.19.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e3f276d8493c3c97930e354b2595a44a21348b320d859fb4a2b9f66da9ed27ab", size = 12083163, upload-time = "2025-12-15T05:03:37.679Z" }, + { url = "https://files.pythonhosted.org/packages/67/af/3afa9cf880aa4a2c803798ac24f1d11ef72a0c8079689fac5cfd815e2830/mypy-1.19.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2abb24cf3f17864770d18d673c85235ba52456b36a06b6afc1e07c1fdcd3d0e6", size = 12687629, upload-time = "2025-12-15T05:02:31.526Z" }, + { url = "https://files.pythonhosted.org/packages/2d/46/20f8a7114a56484ab268b0ab372461cb3a8f7deed31ea96b83a4e4cfcfca/mypy-1.19.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a009ffa5a621762d0c926a078c2d639104becab69e79538a494bcccb62cc0331", size = 13436933, upload-time = "2025-12-15T05:03:15.606Z" }, + { url = "https://files.pythonhosted.org/packages/5b/f8/33b291ea85050a21f15da910002460f1f445f8007adb29230f0adea279cb/mypy-1.19.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f7cee03c9a2e2ee26ec07479f38ea9c884e301d42c6d43a19d20fb014e3ba925", size = 13661754, upload-time = "2025-12-15T05:02:26.731Z" }, + { url = "https://files.pythonhosted.org/packages/fd/a3/47cbd4e85bec4335a9cd80cf67dbc02be21b5d4c9c23ad6b95d6c5196bac/mypy-1.19.1-cp311-cp311-win_amd64.whl", hash = "sha256:4b84a7a18f41e167f7995200a1d07a4a6810e89d29859df936f1c3923d263042", size = 10055772, upload-time = "2025-12-15T05:03:26.179Z" }, + { url = "https://files.pythonhosted.org/packages/06/8a/19bfae96f6615aa8a0604915512e0289b1fad33d5909bf7244f02935d33a/mypy-1.19.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a8174a03289288c1f6c46d55cef02379b478bfbc8e358e02047487cad44c6ca1", size = 13206053, upload-time = "2025-12-15T05:03:46.622Z" }, + { url = "https://files.pythonhosted.org/packages/a5/34/3e63879ab041602154ba2a9f99817bb0c85c4df19a23a1443c8986e4d565/mypy-1.19.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ffcebe56eb09ff0c0885e750036a095e23793ba6c2e894e7e63f6d89ad51f22e", size = 12219134, upload-time = "2025-12-15T05:03:24.367Z" }, + { url = "https://files.pythonhosted.org/packages/89/cc/2db6f0e95366b630364e09845672dbee0cbf0bbe753a204b29a944967cd9/mypy-1.19.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b64d987153888790bcdb03a6473d321820597ab8dd9243b27a92153c4fa50fd2", size = 12731616, upload-time = "2025-12-15T05:02:44.725Z" }, + { url = "https://files.pythonhosted.org/packages/00/be/dd56c1fd4807bc1eba1cf18b2a850d0de7bacb55e158755eb79f77c41f8e/mypy-1.19.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c35d298c2c4bba75feb2195655dfea8124d855dfd7343bf8b8c055421eaf0cf8", size = 13620847, upload-time = "2025-12-15T05:03:39.633Z" }, + { url = "https://files.pythonhosted.org/packages/6d/42/332951aae42b79329f743bf1da088cd75d8d4d9acc18fbcbd84f26c1af4e/mypy-1.19.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:34c81968774648ab5ac09c29a375fdede03ba253f8f8287847bd480782f73a6a", size = 13834976, upload-time = "2025-12-15T05:03:08.786Z" }, + { url = "https://files.pythonhosted.org/packages/6f/63/e7493e5f90e1e085c562bb06e2eb32cae27c5057b9653348d38b47daaecc/mypy-1.19.1-cp312-cp312-win_amd64.whl", hash = "sha256:b10e7c2cd7870ba4ad9b2d8a6102eb5ffc1f16ca35e3de6bfa390c1113029d13", size = 10118104, upload-time = "2025-12-15T05:03:10.834Z" }, + { url = "https://files.pythonhosted.org/packages/de/9f/a6abae693f7a0c697dbb435aac52e958dc8da44e92e08ba88d2e42326176/mypy-1.19.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e3157c7594ff2ef1634ee058aafc56a82db665c9438fd41b390f3bde1ab12250", size = 13201927, upload-time = "2025-12-15T05:02:29.138Z" }, + { url = "https://files.pythonhosted.org/packages/9a/a4/45c35ccf6e1c65afc23a069f50e2c66f46bd3798cbe0d680c12d12935caa/mypy-1.19.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdb12f69bcc02700c2b47e070238f42cb87f18c0bc1fc4cdb4fb2bc5fd7a3b8b", size = 12206730, upload-time = "2025-12-15T05:03:01.325Z" }, + { url = "https://files.pythonhosted.org/packages/05/bb/cdcf89678e26b187650512620eec8368fded4cfd99cfcb431e4cdfd19dec/mypy-1.19.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f859fb09d9583a985be9a493d5cfc5515b56b08f7447759a0c5deaf68d80506e", size = 12724581, upload-time = "2025-12-15T05:03:20.087Z" }, + { url = "https://files.pythonhosted.org/packages/d1/32/dd260d52babf67bad8e6770f8e1102021877ce0edea106e72df5626bb0ec/mypy-1.19.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c9a6538e0415310aad77cb94004ca6482330fece18036b5f360b62c45814c4ef", size = 13616252, upload-time = "2025-12-15T05:02:49.036Z" }, + { url = "https://files.pythonhosted.org/packages/71/d0/5e60a9d2e3bd48432ae2b454b7ef2b62a960ab51292b1eda2a95edd78198/mypy-1.19.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:da4869fc5e7f62a88f3fe0b5c919d1d9f7ea3cef92d3689de2823fd27e40aa75", size = 13840848, upload-time = "2025-12-15T05:02:55.95Z" }, + { url = "https://files.pythonhosted.org/packages/98/76/d32051fa65ecf6cc8c6610956473abdc9b4c43301107476ac03559507843/mypy-1.19.1-cp313-cp313-win_amd64.whl", hash = "sha256:016f2246209095e8eda7538944daa1d60e1e8134d98983b9fc1e92c1fc0cb8dd", size = 10135510, upload-time = "2025-12-15T05:02:58.438Z" }, + { url = "https://files.pythonhosted.org/packages/de/eb/b83e75f4c820c4247a58580ef86fcd35165028f191e7e1ba57128c52782d/mypy-1.19.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:06e6170bd5836770e8104c8fdd58e5e725cfeb309f0a6c681a811f557e97eac1", size = 13199744, upload-time = "2025-12-15T05:03:30.823Z" }, + { url = "https://files.pythonhosted.org/packages/94/28/52785ab7bfa165f87fcbb61547a93f98bb20e7f82f90f165a1f69bce7b3d/mypy-1.19.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:804bd67b8054a85447c8954215a906d6eff9cabeabe493fb6334b24f4bfff718", size = 12215815, upload-time = "2025-12-15T05:02:42.323Z" }, + { url = "https://files.pythonhosted.org/packages/0a/c6/bdd60774a0dbfb05122e3e925f2e9e846c009e479dcec4821dad881f5b52/mypy-1.19.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21761006a7f497cb0d4de3d8ef4ca70532256688b0523eee02baf9eec895e27b", size = 12740047, upload-time = "2025-12-15T05:03:33.168Z" }, + { url = "https://files.pythonhosted.org/packages/32/2a/66ba933fe6c76bd40d1fe916a83f04fed253152f451a877520b3c4a5e41e/mypy-1.19.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:28902ee51f12e0f19e1e16fbe2f8f06b6637f482c459dd393efddd0ec7f82045", size = 13601998, upload-time = "2025-12-15T05:03:13.056Z" }, + { url = "https://files.pythonhosted.org/packages/e3/da/5055c63e377c5c2418760411fd6a63ee2b96cf95397259038756c042574f/mypy-1.19.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:481daf36a4c443332e2ae9c137dfee878fcea781a2e3f895d54bd3002a900957", size = 13807476, upload-time = "2025-12-15T05:03:17.977Z" }, + { url = "https://files.pythonhosted.org/packages/cd/09/4ebd873390a063176f06b0dbf1f7783dd87bd120eae7727fa4ae4179b685/mypy-1.19.1-cp314-cp314-win_amd64.whl", hash = "sha256:8bb5c6f6d043655e055be9b542aa5f3bdd30e4f3589163e85f93f3640060509f", size = 10281872, upload-time = "2025-12-15T05:03:05.549Z" }, + { url = "https://files.pythonhosted.org/packages/8d/f4/4ce9a05ce5ded1de3ec1c1d96cf9f9504a04e54ce0ed55cfa38619a32b8d/mypy-1.19.1-py3-none-any.whl", hash = "sha256:f1235f5ea01b7db5468d53ece6aaddf1ad0b88d9e7462b86ef96fe04995d7247", size = 2471239, upload-time = "2025-12-15T05:03:07.248Z" }, ] [[package]] @@ -3953,11 +4068,11 @@ wheels = [ [[package]] name = "narwhals" -version = "2.12.0" +version = "2.15.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/93/f8/e1c28f24b641871c14ccae7ba6381f3c7827789a06e947ce975ae8a9075a/narwhals-2.12.0.tar.gz", hash = "sha256:075b6d56f3a222613793e025744b129439ecdff9292ea6615dd983af7ba6ea44", size = 590404, upload-time = "2025-11-17T10:53:28.381Z" } +sdist = { url = "https://files.pythonhosted.org/packages/47/6d/b57c64e5038a8cf071bce391bb11551657a74558877ac961e7fa905ece27/narwhals-2.15.0.tar.gz", hash = "sha256:a9585975b99d95084268445a1fdd881311fa26ef1caa18020d959d5b2ff9a965", size = 603479, upload-time = "2026-01-06T08:10:13.27Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0b/9a/c6f79de7ba3a0a8473129936b7b90aa461d3d46fec6f1627672b1dccf4e9/narwhals-2.12.0-py3-none-any.whl", hash = "sha256:baeba5d448a30b04c299a696bd9ee5ff73e4742143e06c49ca316b46539a7cbb", size = 425014, upload-time = "2025-11-17T10:53:26.65Z" }, + { url = "https://files.pythonhosted.org/packages/3d/2e/cf2ffeb386ac3763526151163ad7da9f1b586aac96d2b4f7de1eaebf0c61/narwhals-2.15.0-py3-none-any.whl", hash = "sha256:cbfe21ca19d260d9fd67f995ec75c44592d1f106933b03ddd375df7ac841f9d6", size = 432856, upload-time = "2026-01-06T08:10:11.511Z" }, ] [[package]] @@ -3971,7 +4086,7 @@ wheels = [ [[package]] name = "nbclient" -version = "0.10.2" +version = "0.10.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jupyter-client" }, @@ -3979,9 +4094,9 @@ dependencies = [ { name = "nbformat" }, { name = "traitlets" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/87/66/7ffd18d58eae90d5721f9f39212327695b749e23ad44b3881744eaf4d9e8/nbclient-0.10.2.tar.gz", hash = "sha256:90b7fc6b810630db87a6d0c2250b1f0ab4cf4d3c27a299b0cde78a4ed3fd9193", size = 62424, upload-time = "2024-12-19T10:32:27.164Z" } +sdist = { url = "https://files.pythonhosted.org/packages/56/91/1c1d5a4b9a9ebba2b4e32b8c852c2975c872aec1fe42ab5e516b2cecd193/nbclient-0.10.4.tar.gz", hash = "sha256:1e54091b16e6da39e297b0ece3e10f6f29f4ac4e8ee515d29f8a7099bd6553c9", size = 62554, upload-time = "2025-12-23T07:45:46.369Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/34/6d/e7fa07f03a4a7b221d94b4d586edb754a9b0dc3c9e2c93353e9fa4e0d117/nbclient-0.10.2-py3-none-any.whl", hash = "sha256:4ffee11e788b4a27fabeb7955547e4318a5298f34342a4bfd01f2e1faaeadc3d", size = 25434, upload-time = "2024-12-19T10:32:24.139Z" }, + { url = "https://files.pythonhosted.org/packages/83/a0/5b0c2f11142ed1dddec842457d3f65eaf71a0080894eb6f018755b319c3a/nbclient-0.10.4-py3-none-any.whl", hash = "sha256:9162df5a7373d70d606527300a95a975a47c137776cd942e52d9c7e29ff83440", size = 25465, upload-time = "2025-12-23T07:45:44.51Z" }, ] [[package]] @@ -4048,7 +4163,7 @@ dependencies = [ { name = "nltk" }, { name = "pydantic" }, { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "scipy", version = "1.16.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "scipy", version = "1.17.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "sentence-transformers" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b5/4a/7865a872ff5fdf4a81c65dff1e939d155c4e446464cb96af2ecb64148ce1/nestful_wrapper-0.1.7.tar.gz", hash = "sha256:65437ecabedf2e0e36239d9be67d293d3e6f64a8cd36124dcd571cc28db66bf5", size = 12943860, upload-time = "2025-10-09T04:30:06.644Z" } @@ -4061,14 +4176,10 @@ name = "networkx" version = "3.4.2" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.10.2' and python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'darwin'", - "python_full_version < '3.10.2' and platform_machine == 'x86_64' and sys_platform == 'darwin'", - "python_full_version >= '3.10.2' and python_full_version < '3.11' and platform_machine != 'x86_64' and sys_platform == 'darwin'", - "python_full_version >= '3.10.2' and python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'", - "(python_full_version >= '3.10.2' and python_full_version < '3.11' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.10.2' and python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')", - "python_full_version < '3.10.2' and platform_machine != 'x86_64' and sys_platform == 'darwin'", - "python_full_version < '3.10.2' and platform_machine == 'aarch64' and sys_platform == 'linux'", - "(python_full_version < '3.10.2' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10.2' and sys_platform != 'darwin' and sys_platform != 'linux')", + "python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'darwin'", + "python_full_version < '3.11' and platform_machine != 'x86_64' and sys_platform == 'darwin'", + "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "(python_full_version < '3.11' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')", ] sdist = { url = "https://files.pythonhosted.org/packages/fd/1d/06475e1cd5264c0b870ea2cc6fdb3e37177c1e565c43f56ff17a10e3937f/networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1", size = 2151368, upload-time = "2024-10-21T12:39:38.695Z" } wheels = [ @@ -4077,15 +4188,19 @@ wheels = [ [[package]] name = "networkx" -version = "3.6" +version = "3.6.1" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin'", + "python_full_version >= '3.14' and platform_machine == 'x86_64' and sys_platform == 'darwin'", + "python_full_version == '3.13.*' and platform_machine == 'x86_64' and sys_platform == 'darwin'", "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'darwin'", "python_full_version == '3.11.*' and platform_machine == 'x86_64' and sys_platform == 'darwin'", - "python_full_version >= '3.13' and platform_machine != 'x86_64' and sys_platform == 'darwin'", - "python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux'", - "(python_full_version >= '3.13' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and sys_platform != 'darwin' and sys_platform != 'linux')", + "python_full_version >= '3.14' and platform_machine != 'x86_64' and sys_platform == 'darwin'", + "python_full_version >= '3.14' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "(python_full_version >= '3.14' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.14' and sys_platform != 'darwin' and sys_platform != 'linux')", + "python_full_version == '3.13.*' and platform_machine != 'x86_64' and sys_platform == 'darwin'", + "python_full_version == '3.13.*' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "(python_full_version == '3.13.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.13.*' and sys_platform != 'darwin' and sys_platform != 'linux')", "python_full_version == '3.12.*' and platform_machine != 'x86_64' and sys_platform == 'darwin'", "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'", "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')", @@ -4093,9 +4208,9 @@ resolution-markers = [ "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'", "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux')", ] -sdist = { url = "https://files.pythonhosted.org/packages/e8/fc/7b6fd4d22c8c4dc5704430140d8b3f520531d4fe7328b8f8d03f5a7950e8/networkx-3.6.tar.gz", hash = "sha256:285276002ad1f7f7da0f7b42f004bcba70d381e936559166363707fdad3d72ad", size = 2511464, upload-time = "2025-11-24T03:03:47.158Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6a/51/63fe664f3908c97be9d2e4f1158eb633317598cfa6e1fc14af5383f17512/networkx-3.6.1.tar.gz", hash = "sha256:26b7c357accc0c8cde558ad486283728b65b6a95d85ee1cd66bafab4c8168509", size = 2517025, upload-time = "2025-12-08T17:02:39.908Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/07/c7/d64168da60332c17d24c0d2f08bdf3987e8d1ae9d84b5bbd0eec2eb26a55/networkx-3.6-py3-none-any.whl", hash = "sha256:cdb395b105806062473d3be36458d8f1459a4e4b98e236a66c3a48996e07684f", size = 2063713, upload-time = "2025-11-24T03:03:45.21Z" }, + { url = "https://files.pythonhosted.org/packages/9e/c9/b2622292ea83fbb4ec318f5b9ab867d0a28ab43c5717bb85b0a5f6b3b0a4/networkx-3.6.1-py3-none-any.whl", hash = "sha256:d47fbf302e7d9cbbb9e2555a0d267983d2aa476bac30e90dfbe5669bd57f3762", size = 2068504, upload-time = "2025-12-08T17:02:38.159Z" }, ] [[package]] @@ -4133,44 +4248,44 @@ wheels = [ [[package]] name = "nodeenv" -version = "1.9.1" +version = "1.10.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437, upload-time = "2024-06-04T18:44:11.171Z" } +sdist = { url = "https://files.pythonhosted.org/packages/24/bf/d1bda4f6168e0b2e9e5958945e01910052158313224ada5ce1fb2e1113b8/nodeenv-1.10.0.tar.gz", hash = "sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb", size = 55611, upload-time = "2025-12-20T14:08:54.006Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314, upload-time = "2024-06-04T18:44:08.352Z" }, + { url = "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827", size = 23438, upload-time = "2025-12-20T14:08:52.782Z" }, ] [[package]] name = "numba" -version = "0.62.1" +version = "0.63.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "llvmlite" }, { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a3/20/33dbdbfe60e5fd8e3dbfde299d106279a33d9f8308346022316781368591/numba-0.62.1.tar.gz", hash = "sha256:7b774242aa890e34c21200a1fc62e5b5757d5286267e71103257f4e2af0d5161", size = 2749817, upload-time = "2025-09-29T10:46:31.551Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f5/27/a5a9a58f267ec3b72f609789b2a8eefd6156bd7117e41cc9b7cf5de30490/numba-0.62.1-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:a323df9d36a0da1ca9c592a6baaddd0176d9f417ef49a65bb81951dce69d941a", size = 2684281, upload-time = "2025-09-29T10:43:31.863Z" }, - { url = "https://files.pythonhosted.org/packages/3a/9d/ffc091c0bfd7b80f66df3887a7061b6af80c8c2649902444026ee1454391/numba-0.62.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1e1f4781d3f9f7c23f16eb04e76ca10b5a3516e959634bd226fc48d5d8e7a0a", size = 2687311, upload-time = "2025-09-29T10:43:54.441Z" }, - { url = "https://files.pythonhosted.org/packages/a1/13/9a27bcd0baeea236116070c7df458414336f25e9dd5a872b066cf36b74bf/numba-0.62.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:14432af305ea68627a084cd702124fd5d0c1f5b8a413b05f4e14757202d1cf6c", size = 3734548, upload-time = "2025-09-29T10:42:38.232Z" }, - { url = "https://files.pythonhosted.org/packages/a7/00/17a1ac4a60253c784ce59549375e047da98330b82de7df6ac7f4ecc90902/numba-0.62.1-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f180922adf159ae36c2fe79fb94ffaa74cf5cb3688cb72dba0a904b91e978507", size = 3441277, upload-time = "2025-09-29T10:43:06.124Z" }, - { url = "https://files.pythonhosted.org/packages/86/94/20ae0ff78612c4697eaf942a639db01dd4e2d90f634ac41fa3e015c961fc/numba-0.62.1-cp310-cp310-win_amd64.whl", hash = "sha256:f41834909d411b4b8d1c68f745144136f21416547009c1e860cc2098754b4ca7", size = 2745647, upload-time = "2025-09-29T10:44:15.282Z" }, - { url = "https://files.pythonhosted.org/packages/dd/5f/8b3491dd849474f55e33c16ef55678ace1455c490555337899c35826836c/numba-0.62.1-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:f43e24b057714e480fe44bc6031de499e7cf8150c63eb461192caa6cc8530bc8", size = 2684279, upload-time = "2025-09-29T10:43:37.213Z" }, - { url = "https://files.pythonhosted.org/packages/bf/18/71969149bfeb65a629e652b752b80167fe8a6a6f6e084f1f2060801f7f31/numba-0.62.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:57cbddc53b9ee02830b828a8428757f5c218831ccc96490a314ef569d8342b7b", size = 2687330, upload-time = "2025-09-29T10:43:59.601Z" }, - { url = "https://files.pythonhosted.org/packages/0e/7d/403be3fecae33088027bc8a95dc80a2fda1e3beff3e0e5fc4374ada3afbe/numba-0.62.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:604059730c637c7885386521bb1b0ddcbc91fd56131a6dcc54163d6f1804c872", size = 3739727, upload-time = "2025-09-29T10:42:45.922Z" }, - { url = "https://files.pythonhosted.org/packages/e0/c3/3d910d08b659a6d4c62ab3cd8cd93c4d8b7709f55afa0d79a87413027ff6/numba-0.62.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d6c540880170bee817011757dc9049dba5a29db0c09b4d2349295991fe3ee55f", size = 3445490, upload-time = "2025-09-29T10:43:12.692Z" }, - { url = "https://files.pythonhosted.org/packages/5b/82/9d425c2f20d9f0a37f7cb955945a553a00fa06a2b025856c3550227c5543/numba-0.62.1-cp311-cp311-win_amd64.whl", hash = "sha256:03de6d691d6b6e2b76660ba0f38f37b81ece8b2cc524a62f2a0cfae2bfb6f9da", size = 2745550, upload-time = "2025-09-29T10:44:20.571Z" }, - { url = "https://files.pythonhosted.org/packages/5e/fa/30fa6873e9f821c0ae755915a3ca444e6ff8d6a7b6860b669a3d33377ac7/numba-0.62.1-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:1b743b32f8fa5fff22e19c2e906db2f0a340782caf024477b97801b918cf0494", size = 2685346, upload-time = "2025-09-29T10:43:43.677Z" }, - { url = "https://files.pythonhosted.org/packages/a9/d5/504ce8dc46e0dba2790c77e6b878ee65b60fe3e7d6d0006483ef6fde5a97/numba-0.62.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:90fa21b0142bcf08ad8e32a97d25d0b84b1e921bc9423f8dda07d3652860eef6", size = 2688139, upload-time = "2025-09-29T10:44:04.894Z" }, - { url = "https://files.pythonhosted.org/packages/50/5f/6a802741176c93f2ebe97ad90751894c7b0c922b52ba99a4395e79492205/numba-0.62.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6ef84d0ac19f1bf80431347b6f4ce3c39b7ec13f48f233a48c01e2ec06ecbc59", size = 3796453, upload-time = "2025-09-29T10:42:52.771Z" }, - { url = "https://files.pythonhosted.org/packages/7e/df/efd21527d25150c4544eccc9d0b7260a5dec4b7e98b5a581990e05a133c0/numba-0.62.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9315cc5e441300e0ca07c828a627d92a6802bcbf27c5487f31ae73783c58da53", size = 3496451, upload-time = "2025-09-29T10:43:19.279Z" }, - { url = "https://files.pythonhosted.org/packages/80/44/79bfdab12a02796bf4f1841630355c82b5a69933b1d50eb15c7fa37dabe8/numba-0.62.1-cp312-cp312-win_amd64.whl", hash = "sha256:44e3aa6228039992f058f5ebfcfd372c83798e9464297bdad8cc79febcf7891e", size = 2745552, upload-time = "2025-09-29T10:44:26.399Z" }, - { url = "https://files.pythonhosted.org/packages/22/76/501ea2c07c089ef1386868f33dff2978f43f51b854e34397b20fc55e0a58/numba-0.62.1-cp313-cp313-macosx_10_15_x86_64.whl", hash = "sha256:b72489ba8411cc9fdcaa2458d8f7677751e94f0109eeb53e5becfdc818c64afb", size = 2685766, upload-time = "2025-09-29T10:43:49.161Z" }, - { url = "https://files.pythonhosted.org/packages/80/68/444986ed95350c0611d5c7b46828411c222ce41a0c76707c36425d27ce29/numba-0.62.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:44a1412095534a26fb5da2717bc755b57da5f3053965128fe3dc286652cc6a92", size = 2688741, upload-time = "2025-09-29T10:44:10.07Z" }, - { url = "https://files.pythonhosted.org/packages/78/7e/bf2e3634993d57f95305c7cee4c9c6cb3c9c78404ee7b49569a0dfecfe33/numba-0.62.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8c9460b9e936c5bd2f0570e20a0a5909ee6e8b694fd958b210e3bde3a6dba2d7", size = 3804576, upload-time = "2025-09-29T10:42:59.53Z" }, - { url = "https://files.pythonhosted.org/packages/e8/b6/8a1723fff71f63bbb1354bdc60a1513a068acc0f5322f58da6f022d20247/numba-0.62.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:728f91a874192df22d74e3fd42c12900b7ce7190b1aad3574c6c61b08313e4c5", size = 3503367, upload-time = "2025-09-29T10:43:26.326Z" }, - { url = "https://files.pythonhosted.org/packages/9c/ec/9d414e7a80d6d1dc4af0e07c6bfe293ce0b04ea4d0ed6c45dad9bd6e72eb/numba-0.62.1-cp313-cp313-win_amd64.whl", hash = "sha256:bbf3f88b461514287df66bc8d0307e949b09f2b6f67da92265094e8fa1282dd8", size = 2745529, upload-time = "2025-09-29T10:44:31.738Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/dc/60/0145d479b2209bd8fdae5f44201eceb8ce5a23e0ed54c71f57db24618665/numba-0.63.1.tar.gz", hash = "sha256:b320aa675d0e3b17b40364935ea52a7b1c670c9037c39cf92c49502a75902f4b", size = 2761666, upload-time = "2025-12-10T02:57:39.002Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5e/ce/5283d4ffa568f795bb0fd61ee1f0efc0c6094b94209259167fc8d4276bde/numba-0.63.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c6d6bf5bf00f7db629305caaec82a2ffb8abe2bf45eaad0d0738dc7de4113779", size = 2680810, upload-time = "2025-12-10T02:56:55.269Z" }, + { url = "https://files.pythonhosted.org/packages/0f/72/a8bda517e26d912633b32626333339b7c769ea73a5c688365ea5f88fd07e/numba-0.63.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:08653d0dfc9cc9c4c9a8fba29ceb1f2d5340c3b86c4a7e5e07e42b643bc6a2f4", size = 3739735, upload-time = "2025-12-10T02:56:57.922Z" }, + { url = "https://files.pythonhosted.org/packages/ca/17/1913b7c1173b2db30fb7a9696892a7c4c59aeee777a9af6859e9e01bac51/numba-0.63.1-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f09eebf5650246ce2a4e9a8d38270e2d4b0b0ae978103bafb38ed7adc5ea906e", size = 3446707, upload-time = "2025-12-10T02:56:59.837Z" }, + { url = "https://files.pythonhosted.org/packages/b4/77/703db56c3061e9fdad5e79c91452947fdeb2ec0bdfe4affe9b144e7025e0/numba-0.63.1-cp310-cp310-win_amd64.whl", hash = "sha256:f8bba17421d865d8c0f7be2142754ebce53e009daba41c44cf6909207d1a8d7d", size = 2747374, upload-time = "2025-12-10T02:57:07.908Z" }, + { url = "https://files.pythonhosted.org/packages/70/90/5f8614c165d2e256fbc6c57028519db6f32e4982475a372bbe550ea0454c/numba-0.63.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b33db00f18ccc790ee9911ce03fcdfe9d5124637d1ecc266f5ae0df06e02fec3", size = 2680501, upload-time = "2025-12-10T02:57:09.797Z" }, + { url = "https://files.pythonhosted.org/packages/dc/9d/d0afc4cf915edd8eadd9b2ab5b696242886ee4f97720d9322650d66a88c6/numba-0.63.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7d31ea186a78a7c0f6b1b2a3fe68057fdb291b045c52d86232b5383b6cf4fc25", size = 3744945, upload-time = "2025-12-10T02:57:11.697Z" }, + { url = "https://files.pythonhosted.org/packages/05/a9/d82f38f2ab73f3be6f838a826b545b80339762ee8969c16a8bf1d39395a8/numba-0.63.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ed3bb2fbdb651d6aac394388130a7001aab6f4541837123a4b4ab8b02716530c", size = 3450827, upload-time = "2025-12-10T02:57:13.709Z" }, + { url = "https://files.pythonhosted.org/packages/18/3f/a9b106e93c5bd7434e65f044bae0d204e20aa7f7f85d72ceb872c7c04216/numba-0.63.1-cp311-cp311-win_amd64.whl", hash = "sha256:1ecbff7688f044b1601be70113e2fb1835367ee0b28ffa8f3adf3a05418c5c87", size = 2747262, upload-time = "2025-12-10T02:57:15.664Z" }, + { url = "https://files.pythonhosted.org/packages/14/9c/c0974cd3d00ff70d30e8ff90522ba5fbb2bcee168a867d2321d8d0457676/numba-0.63.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2819cd52afa5d8d04e057bdfd54367575105f8829350d8fb5e4066fb7591cc71", size = 2680981, upload-time = "2025-12-10T02:57:17.579Z" }, + { url = "https://files.pythonhosted.org/packages/cb/70/ea2bc45205f206b7a24ee68a159f5097c9ca7e6466806e7c213587e0c2b1/numba-0.63.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5cfd45dbd3d409e713b1ccfdc2ee72ca82006860254429f4ef01867fdba5845f", size = 3801656, upload-time = "2025-12-10T02:57:19.106Z" }, + { url = "https://files.pythonhosted.org/packages/0d/82/4f4ba4fd0f99825cbf3cdefd682ca3678be1702b63362011de6e5f71f831/numba-0.63.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:69a599df6976c03b7ecf15d05302696f79f7e6d10d620367407517943355bcb0", size = 3501857, upload-time = "2025-12-10T02:57:20.721Z" }, + { url = "https://files.pythonhosted.org/packages/af/fd/6540456efa90b5f6604a86ff50dabefb187e43557e9081adcad3be44f048/numba-0.63.1-cp312-cp312-win_amd64.whl", hash = "sha256:bbad8c63e4fc7eb3cdb2c2da52178e180419f7969f9a685f283b313a70b92af3", size = 2750282, upload-time = "2025-12-10T02:57:22.474Z" }, + { url = "https://files.pythonhosted.org/packages/57/f7/e19e6eff445bec52dde5bed1ebb162925a8e6f988164f1ae4b3475a73680/numba-0.63.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:0bd4fd820ef7442dcc07da184c3f54bb41d2bdb7b35bacf3448e73d081f730dc", size = 2680954, upload-time = "2025-12-10T02:57:24.145Z" }, + { url = "https://files.pythonhosted.org/packages/e9/6c/1e222edba1e20e6b113912caa9b1665b5809433cbcb042dfd133c6f1fd38/numba-0.63.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:53de693abe4be3bd4dee38e1c55f01c55ff644a6a3696a3670589e6e4c39cde2", size = 3809736, upload-time = "2025-12-10T02:57:25.836Z" }, + { url = "https://files.pythonhosted.org/packages/76/0a/590bad11a8b3feeac30a24d01198d46bdb76ad15c70d3a530691ce3cae58/numba-0.63.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:81227821a72a763c3d4ac290abbb4371d855b59fdf85d5af22a47c0e86bf8c7e", size = 3508854, upload-time = "2025-12-10T02:57:27.438Z" }, + { url = "https://files.pythonhosted.org/packages/4e/f5/3800384a24eed1e4d524669cdbc0b9b8a628800bb1e90d7bd676e5f22581/numba-0.63.1-cp313-cp313-win_amd64.whl", hash = "sha256:eb227b07c2ac37b09432a9bda5142047a2d1055646e089d4a240a2643e508102", size = 2750228, upload-time = "2025-12-10T02:57:30.36Z" }, + { url = "https://files.pythonhosted.org/packages/36/2f/53be2aa8a55ee2608ebe1231789cbb217f6ece7f5e1c685d2f0752e95a5b/numba-0.63.1-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:f180883e5508940cc83de8a8bea37fc6dd20fbe4e5558d4659b8b9bef5ff4731", size = 2681153, upload-time = "2025-12-10T02:57:32.016Z" }, + { url = "https://files.pythonhosted.org/packages/13/91/53e59c86759a0648282368d42ba732c29524a745fd555ed1fb1df83febbe/numba-0.63.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f0938764afa82a47c0e895637a6c55547a42c9e1d35cac42285b1fa60a8b02bb", size = 3778718, upload-time = "2025-12-10T02:57:33.764Z" }, + { url = "https://files.pythonhosted.org/packages/6c/0c/2be19eba50b0b7636f6d1f69dfb2825530537708a234ba1ff34afc640138/numba-0.63.1-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f90a929fa5094e062d4e0368ede1f4497d5e40f800e80aa5222c4734236a2894", size = 3478712, upload-time = "2025-12-10T02:57:35.518Z" }, + { url = "https://files.pythonhosted.org/packages/0d/5f/4d0c9e756732577a52211f31da13a3d943d185f7fb90723f56d79c696caa/numba-0.63.1-cp314-cp314-win_amd64.whl", hash = "sha256:8d6d5ce85f572ed4e1a135dbb8c0114538f9dd0e3657eeb0bb64ab204cbe2a8f", size = 2752161, upload-time = "2025-12-10T02:57:37.12Z" }, ] [[package]] @@ -4178,14 +4293,10 @@ name = "numpy" version = "2.2.6" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.10.2' and python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'darwin'", - "python_full_version < '3.10.2' and platform_machine == 'x86_64' and sys_platform == 'darwin'", - "python_full_version >= '3.10.2' and python_full_version < '3.11' and platform_machine != 'x86_64' and sys_platform == 'darwin'", - "python_full_version >= '3.10.2' and python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'", - "(python_full_version >= '3.10.2' and python_full_version < '3.11' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.10.2' and python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')", - "python_full_version < '3.10.2' and platform_machine != 'x86_64' and sys_platform == 'darwin'", - "python_full_version < '3.10.2' and platform_machine == 'aarch64' and sys_platform == 'linux'", - "(python_full_version < '3.10.2' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10.2' and sys_platform != 'darwin' and sys_platform != 'linux')", + "python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'darwin'", + "python_full_version < '3.11' and platform_machine != 'x86_64' and sys_platform == 'darwin'", + "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "(python_full_version < '3.11' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')", ] sdist = { url = "https://files.pythonhosted.org/packages/76/21/7d2a95e4bba9dc13d043ee156a356c0a8f0c6309dff6b21b4d71a073b8a8/numpy-2.2.6.tar.gz", hash = "sha256:e29554e2bef54a90aa5cc07da6ce955accb83f21ab5de01a62c8478897b264fd", size = 20276440, upload-time = "2025-05-17T22:38:04.611Z" } wheels = [ @@ -4250,12 +4361,16 @@ name = "numpy" version = "2.3.5" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin'", + "python_full_version >= '3.14' and platform_machine == 'x86_64' and sys_platform == 'darwin'", + "python_full_version == '3.13.*' and platform_machine == 'x86_64' and sys_platform == 'darwin'", "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'darwin'", "python_full_version == '3.11.*' and platform_machine == 'x86_64' and sys_platform == 'darwin'", - "python_full_version >= '3.13' and platform_machine != 'x86_64' and sys_platform == 'darwin'", - "python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux'", - "(python_full_version >= '3.13' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and sys_platform != 'darwin' and sys_platform != 'linux')", + "python_full_version >= '3.14' and platform_machine != 'x86_64' and sys_platform == 'darwin'", + "python_full_version >= '3.14' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "(python_full_version >= '3.14' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.14' and sys_platform != 'darwin' and sys_platform != 'linux')", + "python_full_version == '3.13.*' and platform_machine != 'x86_64' and sys_platform == 'darwin'", + "python_full_version == '3.13.*' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "(python_full_version == '3.13.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.13.*' and sys_platform != 'darwin' and sys_platform != 'linux')", "python_full_version == '3.12.*' and platform_machine != 'x86_64' and sys_platform == 'darwin'", "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'", "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')", @@ -4377,7 +4492,7 @@ name = "nvidia-cudnn-cu12" version = "9.10.2.21" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-cublas-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, + { name = "nvidia-cublas-cu12", marker = "platform_machine != 'x86_64' or sys_platform != 'darwin'" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/ba/51/e123d997aa098c61d029f76663dedbfb9bc8dcf8c60cbd6adbe42f76d049/nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:949452be657fa16687d0930933f032835951ef0892b37d2d53824d1a84dc97a8", size = 706758467, upload-time = "2025-06-06T21:54:08.597Z" }, @@ -4388,7 +4503,7 @@ name = "nvidia-cufft-cu12" version = "11.3.3.83" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-nvjitlink-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, + { name = "nvidia-nvjitlink-cu12", marker = "platform_machine != 'x86_64' or sys_platform != 'darwin'" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/1f/13/ee4e00f30e676b66ae65b4f08cb5bcbb8392c03f54f2d5413ea99a5d1c80/nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d2dd21ec0b88cf61b62e6b43564355e5222e4a3fb394cac0db101f2dd0d4f74", size = 193118695, upload-time = "2025-03-07T01:45:27.821Z" }, @@ -4415,9 +4530,9 @@ name = "nvidia-cusolver-cu12" version = "11.7.3.90" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-cublas-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, - { name = "nvidia-cusparse-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, - { name = "nvidia-nvjitlink-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, + { name = "nvidia-cublas-cu12", marker = "platform_machine != 'x86_64' or sys_platform != 'darwin'" }, + { name = "nvidia-cusparse-cu12", marker = "platform_machine != 'x86_64' or sys_platform != 'darwin'" }, + { name = "nvidia-nvjitlink-cu12", marker = "platform_machine != 'x86_64' or sys_platform != 'darwin'" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/85/48/9a13d2975803e8cf2777d5ed57b87a0b6ca2cc795f9a4f59796a910bfb80/nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:4376c11ad263152bd50ea295c05370360776f8c3427b30991df774f9fb26c450", size = 267506905, upload-time = "2025-03-07T01:47:16.273Z" }, @@ -4428,7 +4543,7 @@ name = "nvidia-cusparse-cu12" version = "12.5.8.93" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-nvjitlink-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, + { name = "nvidia-nvjitlink-cu12", marker = "platform_machine != 'x86_64' or sys_platform != 'darwin'" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/c2/f5/e1854cb2f2bcd4280c44736c93550cc300ff4b8c95ebe370d0aa7d2b473d/nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ec05d76bbbd8b61b06a80e1eaf8cf4959c3d4ce8e711b65ebd0443bb0ebb13b", size = 288216466, upload-time = "2025-03-07T01:48:13.779Z" }, @@ -4485,16 +4600,16 @@ wheels = [ [[package]] name = "ocrmac" -version = "1.0.0" +version = "1.0.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "click", marker = "sys_platform == 'darwin'" }, - { name = "pillow", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-vision", marker = "sys_platform == 'darwin'" }, + { name = "click" }, + { name = "pillow" }, + { name = "pyobjc-framework-vision" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/dd/dc/de3e9635774b97d9766f6815bbb3f5ec9bce347115f10d9abbf2733a9316/ocrmac-1.0.0.tar.gz", hash = "sha256:5b299e9030c973d1f60f82db000d6c2e5ff271601878c7db0885e850597d1d2e", size = 1463997, upload-time = "2024-11-07T12:00:00.197Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5e/07/3e15ab404f75875c5e48c47163300eb90b7409044d8711fc3aaf52503f2e/ocrmac-1.0.1.tar.gz", hash = "sha256:507fe5e4cbd67b2d03f6729a52bbc11f9d0b58241134eb958a5daafd4b9d93d9", size = 1454317, upload-time = "2026-01-08T16:44:26.412Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/f4/eef75cb750ff3e40240c8cbc713d68f8fc12b10eef016f7d4966eb05b065/ocrmac-1.0.0-py2.py3-none-any.whl", hash = "sha256:0b5a072aa23a9ead48132cb2d595b680aa6c3c5a6cb69525155e35ca95610c3a", size = 12100, upload-time = "2024-11-07T11:59:58.383Z" }, + { url = "https://files.pythonhosted.org/packages/37/15/7cc16507a2aca927abe395f1c545f17ae76b1f8ed44f43ebe4e8670ee203/ocrmac-1.0.1-py3-none-any.whl", hash = "sha256:1cef25426f7ae6bbd57fe3dc5553b25461ae8ad0d2b428a9bbadbf5907349024", size = 9955, upload-time = "2026-01-08T16:44:25.555Z" }, ] [[package]] @@ -4563,7 +4678,7 @@ wheels = [ [[package]] name = "openai" -version = "2.8.1" +version = "2.15.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -4575,9 +4690,9 @@ dependencies = [ { name = "tqdm" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d5/e4/42591e356f1d53c568418dc7e30dcda7be31dd5a4d570bca22acb0525862/openai-2.8.1.tar.gz", hash = "sha256:cb1b79eef6e809f6da326a7ef6038719e35aa944c42d081807bfa1be8060f15f", size = 602490, upload-time = "2025-11-17T22:39:59.549Z" } +sdist = { url = "https://files.pythonhosted.org/packages/94/f4/4690ecb5d70023ce6bfcfeabfe717020f654bde59a775058ec6ac4692463/openai-2.15.0.tar.gz", hash = "sha256:42eb8cbb407d84770633f31bf727d4ffb4138711c670565a41663d9439174fba", size = 627383, upload-time = "2026-01-09T22:10:08.603Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/55/4f/dbc0c124c40cb390508a82770fb9f6e3ed162560181a85089191a851c59a/openai-2.8.1-py3-none-any.whl", hash = "sha256:c6c3b5a04994734386e8dad3c00a393f56d3b68a27cd2e8acae91a59e4122463", size = 1022688, upload-time = "2025-11-17T22:39:57.675Z" }, + { url = "https://files.pythonhosted.org/packages/b5/df/c306f7375d42bafb379934c2df4c2fa3964656c8c782bac75ee10c102818/openai-2.15.0-py3-none-any.whl", hash = "sha256:6ae23b932cd7230f7244e52954daa6602716d6b9bf235401a107af731baea6c3", size = 1067879, upload-time = "2026-01-09T22:10:06.446Z" }, ] [[package]] @@ -4612,32 +4727,32 @@ wheels = [ [[package]] name = "opentelemetry-api" -version = "1.38.0" +version = "1.39.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "importlib-metadata" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/08/d8/0f354c375628e048bd0570645b310797299754730079853095bf000fba69/opentelemetry_api-1.38.0.tar.gz", hash = "sha256:f4c193b5e8acb0912b06ac5b16321908dd0843d75049c091487322284a3eea12", size = 65242, upload-time = "2025-10-16T08:35:50.25Z" } +sdist = { url = "https://files.pythonhosted.org/packages/97/b9/3161be15bb8e3ad01be8be5a968a9237c3027c5be504362ff800fca3e442/opentelemetry_api-1.39.1.tar.gz", hash = "sha256:fbde8c80e1b937a2c61f20347e91c0c18a1940cecf012d62e65a7caf08967c9c", size = 65767, upload-time = "2025-12-11T13:32:39.182Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ae/a2/d86e01c28300bd41bab8f18afd613676e2bd63515417b77636fc1add426f/opentelemetry_api-1.38.0-py3-none-any.whl", hash = "sha256:2891b0197f47124454ab9f0cf58f3be33faca394457ac3e09daba13ff50aa582", size = 65947, upload-time = "2025-10-16T08:35:30.23Z" }, + { url = "https://files.pythonhosted.org/packages/cf/df/d3f1ddf4bb4cb50ed9b1139cc7b1c54c34a1e7ce8fd1b9a37c0d1551a6bd/opentelemetry_api-1.39.1-py3-none-any.whl", hash = "sha256:2edd8463432a7f8443edce90972169b195e7d6a05500cd29e6d13898187c9950", size = 66356, upload-time = "2025-12-11T13:32:17.304Z" }, ] [[package]] name = "opentelemetry-exporter-otlp-proto-common" -version = "1.38.0" +version = "1.39.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-proto" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/19/83/dd4660f2956ff88ed071e9e0e36e830df14b8c5dc06722dbde1841accbe8/opentelemetry_exporter_otlp_proto_common-1.38.0.tar.gz", hash = "sha256:e333278afab4695aa8114eeb7bf4e44e65c6607d54968271a249c180b2cb605c", size = 20431, upload-time = "2025-10-16T08:35:53.285Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e9/9d/22d241b66f7bbde88a3bfa6847a351d2c46b84de23e71222c6aae25c7050/opentelemetry_exporter_otlp_proto_common-1.39.1.tar.gz", hash = "sha256:763370d4737a59741c89a67b50f9e39271639ee4afc999dadfe768541c027464", size = 20409, upload-time = "2025-12-11T13:32:40.885Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/9e/55a41c9601191e8cd8eb626b54ee6827b9c9d4a46d736f32abc80d8039fc/opentelemetry_exporter_otlp_proto_common-1.38.0-py3-none-any.whl", hash = "sha256:03cb76ab213300fe4f4c62b7d8f17d97fcfd21b89f0b5ce38ea156327ddda74a", size = 18359, upload-time = "2025-10-16T08:35:34.099Z" }, + { url = "https://files.pythonhosted.org/packages/8c/02/ffc3e143d89a27ac21fd557365b98bd0653b98de8a101151d5805b5d4c33/opentelemetry_exporter_otlp_proto_common-1.39.1-py3-none-any.whl", hash = "sha256:08f8a5862d64cc3435105686d0216c1365dc5701f86844a8cd56597d0c764fde", size = 18366, upload-time = "2025-12-11T13:32:20.2Z" }, ] [[package]] name = "opentelemetry-exporter-otlp-proto-grpc" -version = "1.38.0" +version = "1.39.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "googleapis-common-protos" }, @@ -4648,184 +4763,184 @@ dependencies = [ { name = "opentelemetry-sdk" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a2/c0/43222f5b97dc10812bc4f0abc5dc7cd0a2525a91b5151d26c9e2e958f52e/opentelemetry_exporter_otlp_proto_grpc-1.38.0.tar.gz", hash = "sha256:2473935e9eac71f401de6101d37d6f3f0f1831db92b953c7dcc912536158ebd6", size = 24676, upload-time = "2025-10-16T08:35:53.83Z" } +sdist = { url = "https://files.pythonhosted.org/packages/53/48/b329fed2c610c2c32c9366d9dc597202c9d1e58e631c137ba15248d8850f/opentelemetry_exporter_otlp_proto_grpc-1.39.1.tar.gz", hash = "sha256:772eb1c9287485d625e4dbe9c879898e5253fea111d9181140f51291b5fec3ad", size = 24650, upload-time = "2025-12-11T13:32:41.429Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/28/f0/bd831afbdba74ca2ce3982142a2fad707f8c487e8a3b6fef01f1d5945d1b/opentelemetry_exporter_otlp_proto_grpc-1.38.0-py3-none-any.whl", hash = "sha256:7c49fd9b4bd0dbe9ba13d91f764c2d20b0025649a6e4ac35792fb8d84d764bc7", size = 19695, upload-time = "2025-10-16T08:35:35.053Z" }, + { url = "https://files.pythonhosted.org/packages/81/a3/cc9b66575bd6597b98b886a2067eea2693408d2d5f39dad9ab7fc264f5f3/opentelemetry_exporter_otlp_proto_grpc-1.39.1-py3-none-any.whl", hash = "sha256:fa1c136a05c7e9b4c09f739469cbdb927ea20b34088ab1d959a849b5cc589c18", size = 19766, upload-time = "2025-12-11T13:32:21.027Z" }, ] [[package]] name = "opentelemetry-proto" -version = "1.38.0" +version = "1.39.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "protobuf" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/51/14/f0c4f0f6371b9cb7f9fa9ee8918bfd59ac7040c7791f1e6da32a1839780d/opentelemetry_proto-1.38.0.tar.gz", hash = "sha256:88b161e89d9d372ce723da289b7da74c3a8354a8e5359992be813942969ed468", size = 46152, upload-time = "2025-10-16T08:36:01.612Z" } +sdist = { url = "https://files.pythonhosted.org/packages/49/1d/f25d76d8260c156c40c97c9ed4511ec0f9ce353f8108ca6e7561f82a06b2/opentelemetry_proto-1.39.1.tar.gz", hash = "sha256:6c8e05144fc0d3ed4d22c2289c6b126e03bcd0e6a7da0f16cedd2e1c2772e2c8", size = 46152, upload-time = "2025-12-11T13:32:48.681Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b6/6a/82b68b14efca5150b2632f3692d627afa76b77378c4999f2648979409528/opentelemetry_proto-1.38.0-py3-none-any.whl", hash = "sha256:b6ebe54d3217c42e45462e2a1ae28c3e2bf2ec5a5645236a490f55f45f1a0a18", size = 72535, upload-time = "2025-10-16T08:35:45.749Z" }, + { url = "https://files.pythonhosted.org/packages/51/95/b40c96a7b5203005a0b03d8ce8cd212ff23f1793d5ba289c87a097571b18/opentelemetry_proto-1.39.1-py3-none-any.whl", hash = "sha256:22cdc78efd3b3765d09e68bfbd010d4fc254c9818afd0b6b423387d9dee46007", size = 72535, upload-time = "2025-12-11T13:32:33.866Z" }, ] [[package]] name = "opentelemetry-sdk" -version = "1.38.0" +version = "1.39.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, { name = "opentelemetry-semantic-conventions" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/85/cb/f0eee1445161faf4c9af3ba7b848cc22a50a3d3e2515051ad8628c35ff80/opentelemetry_sdk-1.38.0.tar.gz", hash = "sha256:93df5d4d871ed09cb4272305be4d996236eedb232253e3ab864c8620f051cebe", size = 171942, upload-time = "2025-10-16T08:36:02.257Z" } +sdist = { url = "https://files.pythonhosted.org/packages/eb/fb/c76080c9ba07e1e8235d24cdcc4d125ef7aa3edf23eb4e497c2e50889adc/opentelemetry_sdk-1.39.1.tar.gz", hash = "sha256:cf4d4563caf7bff906c9f7967e2be22d0d6b349b908be0d90fb21c8e9c995cc6", size = 171460, upload-time = "2025-12-11T13:32:49.369Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2f/2e/e93777a95d7d9c40d270a371392b6d6f1ff170c2a3cb32d6176741b5b723/opentelemetry_sdk-1.38.0-py3-none-any.whl", hash = "sha256:1c66af6564ecc1553d72d811a01df063ff097cdc82ce188da9951f93b8d10f6b", size = 132349, upload-time = "2025-10-16T08:35:46.995Z" }, + { url = "https://files.pythonhosted.org/packages/7c/98/e91cf858f203d86f4eccdf763dcf01cf03f1dae80c3750f7e635bfa206b6/opentelemetry_sdk-1.39.1-py3-none-any.whl", hash = "sha256:4d5482c478513ecb0a5d938dcc61394e647066e0cc2676bee9f3af3f3f45f01c", size = 132565, upload-time = "2025-12-11T13:32:35.069Z" }, ] [[package]] name = "opentelemetry-semantic-conventions" -version = "0.59b0" +version = "0.60b1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/40/bc/8b9ad3802cd8ac6583a4eb7de7e5d7db004e89cb7efe7008f9c8a537ee75/opentelemetry_semantic_conventions-0.59b0.tar.gz", hash = "sha256:7a6db3f30d70202d5bf9fa4b69bc866ca6a30437287de6c510fb594878aed6b0", size = 129861, upload-time = "2025-10-16T08:36:03.346Z" } +sdist = { url = "https://files.pythonhosted.org/packages/91/df/553f93ed38bf22f4b999d9be9c185adb558982214f33eae539d3b5cd0858/opentelemetry_semantic_conventions-0.60b1.tar.gz", hash = "sha256:87c228b5a0669b748c76d76df6c364c369c28f1c465e50f661e39737e84bc953", size = 137935, upload-time = "2025-12-11T13:32:50.487Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/24/7d/c88d7b15ba8fe5c6b8f93be50fc11795e9fc05386c44afaf6b76fe191f9b/opentelemetry_semantic_conventions-0.59b0-py3-none-any.whl", hash = "sha256:35d3b8833ef97d614136e253c1da9342b4c3c083bbaf29ce31d572a1c3825eed", size = 207954, upload-time = "2025-10-16T08:35:48.054Z" }, + { url = "https://files.pythonhosted.org/packages/7a/5e/5958555e09635d09b75de3c4f8b9cae7335ca545d77392ffe7331534c402/opentelemetry_semantic_conventions-0.60b1-py3-none-any.whl", hash = "sha256:9fa8c8b0c110da289809292b0591220d3a7b53c1526a23021e977d68597893fb", size = 219982, upload-time = "2025-12-11T13:32:36.955Z" }, ] [[package]] name = "orjson" -version = "3.11.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c6/fe/ed708782d6709cc60eb4c2d8a361a440661f74134675c72990f2c48c785f/orjson-3.11.4.tar.gz", hash = "sha256:39485f4ab4c9b30a3943cfe99e1a213c4776fb69e8abd68f66b83d5a0b0fdc6d", size = 5945188, upload-time = "2025-10-24T15:50:38.027Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/30/5aed63d5af1c8b02fbd2a8d83e2a6c8455e30504c50dbf08c8b51403d873/orjson-3.11.4-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:e3aa2118a3ece0d25489cbe48498de8a5d580e42e8d9979f65bf47900a15aba1", size = 243870, upload-time = "2025-10-24T15:48:28.908Z" }, - { url = "https://files.pythonhosted.org/packages/44/1f/da46563c08bef33c41fd63c660abcd2184b4d2b950c8686317d03b9f5f0c/orjson-3.11.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a69ab657a4e6733133a3dca82768f2f8b884043714e8d2b9ba9f52b6efef5c44", size = 130622, upload-time = "2025-10-24T15:48:31.361Z" }, - { url = "https://files.pythonhosted.org/packages/02/bd/b551a05d0090eab0bf8008a13a14edc0f3c3e0236aa6f5b697760dd2817b/orjson-3.11.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3740bffd9816fc0326ddc406098a3a8f387e42223f5f455f2a02a9f834ead80c", size = 129344, upload-time = "2025-10-24T15:48:32.71Z" }, - { url = "https://files.pythonhosted.org/packages/87/6c/9ddd5e609f443b2548c5e7df3c44d0e86df2c68587a0e20c50018cdec535/orjson-3.11.4-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:65fd2f5730b1bf7f350c6dc896173d3460d235c4be007af73986d7cd9a2acd23", size = 136633, upload-time = "2025-10-24T15:48:34.128Z" }, - { url = "https://files.pythonhosted.org/packages/95/f2/9f04f2874c625a9fb60f6918c33542320661255323c272e66f7dcce14df2/orjson-3.11.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fdc3ae730541086158d549c97852e2eea6820665d4faf0f41bf99df41bc11ea", size = 137695, upload-time = "2025-10-24T15:48:35.654Z" }, - { url = "https://files.pythonhosted.org/packages/d2/c2/c7302afcbdfe8a891baae0e2cee091583a30e6fa613e8bdf33b0e9c8a8c7/orjson-3.11.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e10b4d65901da88845516ce9f7f9736f9638d19a1d483b3883dc0182e6e5edba", size = 136879, upload-time = "2025-10-24T15:48:37.483Z" }, - { url = "https://files.pythonhosted.org/packages/c6/3a/b31c8f0182a3e27f48e703f46e61bb769666cd0dac4700a73912d07a1417/orjson-3.11.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb6a03a678085f64b97f9d4a9ae69376ce91a3a9e9b56a82b1580d8e1d501aff", size = 136374, upload-time = "2025-10-24T15:48:38.624Z" }, - { url = "https://files.pythonhosted.org/packages/29/d0/fd9ab96841b090d281c46df566b7f97bc6c8cd9aff3f3ebe99755895c406/orjson-3.11.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2c82e4f0b1c712477317434761fbc28b044c838b6b1240d895607441412371ac", size = 140519, upload-time = "2025-10-24T15:48:39.756Z" }, - { url = "https://files.pythonhosted.org/packages/d6/ce/36eb0f15978bb88e33a3480e1a3fb891caa0f189ba61ce7713e0ccdadabf/orjson-3.11.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:d58c166a18f44cc9e2bad03a327dc2d1a3d2e85b847133cfbafd6bfc6719bd79", size = 406522, upload-time = "2025-10-24T15:48:41.198Z" }, - { url = "https://files.pythonhosted.org/packages/85/11/e8af3161a288f5c6a00c188fc729c7ba193b0cbc07309a1a29c004347c30/orjson-3.11.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:94f206766bf1ea30e1382e4890f763bd1eefddc580e08fec1ccdc20ddd95c827", size = 149790, upload-time = "2025-10-24T15:48:42.664Z" }, - { url = "https://files.pythonhosted.org/packages/ea/96/209d52db0cf1e10ed48d8c194841e383e23c2ced5a2ee766649fe0e32d02/orjson-3.11.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:41bf25fb39a34cf8edb4398818523277ee7096689db352036a9e8437f2f3ee6b", size = 140040, upload-time = "2025-10-24T15:48:44.042Z" }, - { url = "https://files.pythonhosted.org/packages/ef/0e/526db1395ccb74c3d59ac1660b9a325017096dc5643086b38f27662b4add/orjson-3.11.4-cp310-cp310-win32.whl", hash = "sha256:fa9627eba4e82f99ca6d29bc967f09aba446ee2b5a1ea728949ede73d313f5d3", size = 135955, upload-time = "2025-10-24T15:48:45.495Z" }, - { url = "https://files.pythonhosted.org/packages/e6/69/18a778c9de3702b19880e73c9866b91cc85f904b885d816ba1ab318b223c/orjson-3.11.4-cp310-cp310-win_amd64.whl", hash = "sha256:23ef7abc7fca96632d8174ac115e668c1e931b8fe4dde586e92a500bf1914dcc", size = 131577, upload-time = "2025-10-24T15:48:46.609Z" }, - { url = "https://files.pythonhosted.org/packages/63/1d/1ea6005fffb56715fd48f632611e163d1604e8316a5bad2288bee9a1c9eb/orjson-3.11.4-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:5e59d23cd93ada23ec59a96f215139753fbfe3a4d989549bcb390f8c00370b39", size = 243498, upload-time = "2025-10-24T15:48:48.101Z" }, - { url = "https://files.pythonhosted.org/packages/37/d7/ffed10c7da677f2a9da307d491b9eb1d0125b0307019c4ad3d665fd31f4f/orjson-3.11.4-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:5c3aedecfc1beb988c27c79d52ebefab93b6c3921dbec361167e6559aba2d36d", size = 128961, upload-time = "2025-10-24T15:48:49.571Z" }, - { url = "https://files.pythonhosted.org/packages/a2/96/3e4d10a18866d1368f73c8c44b7fe37cc8a15c32f2a7620be3877d4c55a3/orjson-3.11.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da9e5301f1c2caa2a9a4a303480d79c9ad73560b2e7761de742ab39fe59d9175", size = 130321, upload-time = "2025-10-24T15:48:50.713Z" }, - { url = "https://files.pythonhosted.org/packages/eb/1f/465f66e93f434f968dd74d5b623eb62c657bdba2332f5a8be9f118bb74c7/orjson-3.11.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8873812c164a90a79f65368f8f96817e59e35d0cc02786a5356f0e2abed78040", size = 129207, upload-time = "2025-10-24T15:48:52.193Z" }, - { url = "https://files.pythonhosted.org/packages/28/43/d1e94837543321c119dff277ae8e348562fe8c0fafbb648ef7cb0c67e521/orjson-3.11.4-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5d7feb0741ebb15204e748f26c9638e6665a5fa93c37a2c73d64f1669b0ddc63", size = 136323, upload-time = "2025-10-24T15:48:54.806Z" }, - { url = "https://files.pythonhosted.org/packages/bf/04/93303776c8890e422a5847dd012b4853cdd88206b8bbd3edc292c90102d1/orjson-3.11.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:01ee5487fefee21e6910da4c2ee9eef005bee568a0879834df86f888d2ffbdd9", size = 137440, upload-time = "2025-10-24T15:48:56.326Z" }, - { url = "https://files.pythonhosted.org/packages/1e/ef/75519d039e5ae6b0f34d0336854d55544ba903e21bf56c83adc51cd8bf82/orjson-3.11.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d40d46f348c0321df01507f92b95a377240c4ec31985225a6668f10e2676f9a", size = 136680, upload-time = "2025-10-24T15:48:57.476Z" }, - { url = "https://files.pythonhosted.org/packages/b5/18/bf8581eaae0b941b44efe14fee7b7862c3382fbc9a0842132cfc7cf5ecf4/orjson-3.11.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95713e5fc8af84d8edc75b785d2386f653b63d62b16d681687746734b4dfc0be", size = 136160, upload-time = "2025-10-24T15:48:59.631Z" }, - { url = "https://files.pythonhosted.org/packages/c4/35/a6d582766d351f87fc0a22ad740a641b0a8e6fc47515e8614d2e4790ae10/orjson-3.11.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ad73ede24f9083614d6c4ca9a85fe70e33be7bf047ec586ee2363bc7418fe4d7", size = 140318, upload-time = "2025-10-24T15:49:00.834Z" }, - { url = "https://files.pythonhosted.org/packages/76/b3/5a4801803ab2e2e2d703bce1a56540d9f99a9143fbec7bf63d225044fef8/orjson-3.11.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:842289889de515421f3f224ef9c1f1efb199a32d76d8d2ca2706fa8afe749549", size = 406330, upload-time = "2025-10-24T15:49:02.327Z" }, - { url = "https://files.pythonhosted.org/packages/80/55/a8f682f64833e3a649f620eafefee175cbfeb9854fc5b710b90c3bca45df/orjson-3.11.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:3b2427ed5791619851c52a1261b45c233930977e7de8cf36de05636c708fa905", size = 149580, upload-time = "2025-10-24T15:49:03.517Z" }, - { url = "https://files.pythonhosted.org/packages/ad/e4/c132fa0c67afbb3eb88274fa98df9ac1f631a675e7877037c611805a4413/orjson-3.11.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3c36e524af1d29982e9b190573677ea02781456b2e537d5840e4538a5ec41907", size = 139846, upload-time = "2025-10-24T15:49:04.761Z" }, - { url = "https://files.pythonhosted.org/packages/54/06/dc3491489efd651fef99c5908e13951abd1aead1257c67f16135f95ce209/orjson-3.11.4-cp311-cp311-win32.whl", hash = "sha256:87255b88756eab4a68ec61837ca754e5d10fa8bc47dc57f75cedfeaec358d54c", size = 135781, upload-time = "2025-10-24T15:49:05.969Z" }, - { url = "https://files.pythonhosted.org/packages/79/b7/5e5e8d77bd4ea02a6ac54c42c818afb01dd31961be8a574eb79f1d2cfb1e/orjson-3.11.4-cp311-cp311-win_amd64.whl", hash = "sha256:e2d5d5d798aba9a0e1fede8d853fa899ce2cb930ec0857365f700dffc2c7af6a", size = 131391, upload-time = "2025-10-24T15:49:07.355Z" }, - { url = "https://files.pythonhosted.org/packages/0f/dc/9484127cc1aa213be398ed735f5f270eedcb0c0977303a6f6ddc46b60204/orjson-3.11.4-cp311-cp311-win_arm64.whl", hash = "sha256:6bb6bb41b14c95d4f2702bce9975fda4516f1db48e500102fc4d8119032ff045", size = 126252, upload-time = "2025-10-24T15:49:08.869Z" }, - { url = "https://files.pythonhosted.org/packages/63/51/6b556192a04595b93e277a9ff71cd0cc06c21a7df98bcce5963fa0f5e36f/orjson-3.11.4-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:d4371de39319d05d3f482f372720b841c841b52f5385bd99c61ed69d55d9ab50", size = 243571, upload-time = "2025-10-24T15:49:10.008Z" }, - { url = "https://files.pythonhosted.org/packages/1c/2c/2602392ddf2601d538ff11848b98621cd465d1a1ceb9db9e8043181f2f7b/orjson-3.11.4-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:e41fd3b3cac850eaae78232f37325ed7d7436e11c471246b87b2cd294ec94853", size = 128891, upload-time = "2025-10-24T15:49:11.297Z" }, - { url = "https://files.pythonhosted.org/packages/4e/47/bf85dcf95f7a3a12bf223394a4f849430acd82633848d52def09fa3f46ad/orjson-3.11.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:600e0e9ca042878c7fdf189cf1b028fe2c1418cc9195f6cb9824eb6ed99cb938", size = 130137, upload-time = "2025-10-24T15:49:12.544Z" }, - { url = "https://files.pythonhosted.org/packages/b4/4d/a0cb31007f3ab6f1fd2a1b17057c7c349bc2baf8921a85c0180cc7be8011/orjson-3.11.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7bbf9b333f1568ef5da42bc96e18bf30fd7f8d54e9ae066d711056add508e415", size = 129152, upload-time = "2025-10-24T15:49:13.754Z" }, - { url = "https://files.pythonhosted.org/packages/f7/ef/2811def7ce3d8576b19e3929fff8f8f0d44bc5eb2e0fdecb2e6e6cc6c720/orjson-3.11.4-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4806363144bb6e7297b8e95870e78d30a649fdc4e23fc84daa80c8ebd366ce44", size = 136834, upload-time = "2025-10-24T15:49:15.307Z" }, - { url = "https://files.pythonhosted.org/packages/00/d4/9aee9e54f1809cec8ed5abd9bc31e8a9631d19460e3b8470145d25140106/orjson-3.11.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad355e8308493f527d41154e9053b86a5be892b3b359a5c6d5d95cda23601cb2", size = 137519, upload-time = "2025-10-24T15:49:16.557Z" }, - { url = "https://files.pythonhosted.org/packages/db/ea/67bfdb5465d5679e8ae8d68c11753aaf4f47e3e7264bad66dc2f2249e643/orjson-3.11.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c8a7517482667fb9f0ff1b2f16fe5829296ed7a655d04d68cd9711a4d8a4e708", size = 136749, upload-time = "2025-10-24T15:49:17.796Z" }, - { url = "https://files.pythonhosted.org/packages/01/7e/62517dddcfce6d53a39543cd74d0dccfcbdf53967017c58af68822100272/orjson-3.11.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97eb5942c7395a171cbfecc4ef6701fc3c403e762194683772df4c54cfbb2210", size = 136325, upload-time = "2025-10-24T15:49:19.347Z" }, - { url = "https://files.pythonhosted.org/packages/18/ae/40516739f99ab4c7ec3aaa5cc242d341fcb03a45d89edeeaabc5f69cb2cf/orjson-3.11.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:149d95d5e018bdd822e3f38c103b1a7c91f88d38a88aada5c4e9b3a73a244241", size = 140204, upload-time = "2025-10-24T15:49:20.545Z" }, - { url = "https://files.pythonhosted.org/packages/82/18/ff5734365623a8916e3a4037fcef1cd1782bfc14cf0992afe7940c5320bf/orjson-3.11.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:624f3951181eb46fc47dea3d221554e98784c823e7069edb5dbd0dc826ac909b", size = 406242, upload-time = "2025-10-24T15:49:21.884Z" }, - { url = "https://files.pythonhosted.org/packages/e1/43/96436041f0a0c8c8deca6a05ebeaf529bf1de04839f93ac5e7c479807aec/orjson-3.11.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:03bfa548cf35e3f8b3a96c4e8e41f753c686ff3d8e182ce275b1751deddab58c", size = 150013, upload-time = "2025-10-24T15:49:23.185Z" }, - { url = "https://files.pythonhosted.org/packages/1b/48/78302d98423ed8780479a1e682b9aecb869e8404545d999d34fa486e573e/orjson-3.11.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:525021896afef44a68148f6ed8a8bf8375553d6066c7f48537657f64823565b9", size = 139951, upload-time = "2025-10-24T15:49:24.428Z" }, - { url = "https://files.pythonhosted.org/packages/4a/7b/ad613fdcdaa812f075ec0875143c3d37f8654457d2af17703905425981bf/orjson-3.11.4-cp312-cp312-win32.whl", hash = "sha256:b58430396687ce0f7d9eeb3dd47761ca7d8fda8e9eb92b3077a7a353a75efefa", size = 136049, upload-time = "2025-10-24T15:49:25.973Z" }, - { url = "https://files.pythonhosted.org/packages/b9/3c/9cf47c3ff5f39b8350fb21ba65d789b6a1129d4cbb3033ba36c8a9023520/orjson-3.11.4-cp312-cp312-win_amd64.whl", hash = "sha256:c6dbf422894e1e3c80a177133c0dda260f81428f9de16d61041949f6a2e5c140", size = 131461, upload-time = "2025-10-24T15:49:27.259Z" }, - { url = "https://files.pythonhosted.org/packages/c6/3b/e2425f61e5825dc5b08c2a5a2b3af387eaaca22a12b9c8c01504f8614c36/orjson-3.11.4-cp312-cp312-win_arm64.whl", hash = "sha256:d38d2bc06d6415852224fcc9c0bfa834c25431e466dc319f0edd56cca81aa96e", size = 126167, upload-time = "2025-10-24T15:49:28.511Z" }, - { url = "https://files.pythonhosted.org/packages/23/15/c52aa7112006b0f3d6180386c3a46ae057f932ab3425bc6f6ac50431cca1/orjson-3.11.4-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:2d6737d0e616a6e053c8b4acc9eccea6b6cce078533666f32d140e4f85002534", size = 243525, upload-time = "2025-10-24T15:49:29.737Z" }, - { url = "https://files.pythonhosted.org/packages/ec/38/05340734c33b933fd114f161f25a04e651b0c7c33ab95e9416ade5cb44b8/orjson-3.11.4-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:afb14052690aa328cc118a8e09f07c651d301a72e44920b887c519b313d892ff", size = 128871, upload-time = "2025-10-24T15:49:31.109Z" }, - { url = "https://files.pythonhosted.org/packages/55/b9/ae8d34899ff0c012039b5a7cb96a389b2476e917733294e498586b45472d/orjson-3.11.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38aa9e65c591febb1b0aed8da4d469eba239d434c218562df179885c94e1a3ad", size = 130055, upload-time = "2025-10-24T15:49:33.382Z" }, - { url = "https://files.pythonhosted.org/packages/33/aa/6346dd5073730451bee3681d901e3c337e7ec17342fb79659ec9794fc023/orjson-3.11.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f2cf4dfaf9163b0728d061bebc1e08631875c51cd30bf47cb9e3293bfbd7dcd5", size = 129061, upload-time = "2025-10-24T15:49:34.935Z" }, - { url = "https://files.pythonhosted.org/packages/39/e4/8eea51598f66a6c853c380979912d17ec510e8e66b280d968602e680b942/orjson-3.11.4-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:89216ff3dfdde0e4070932e126320a1752c9d9a758d6a32ec54b3b9334991a6a", size = 136541, upload-time = "2025-10-24T15:49:36.923Z" }, - { url = "https://files.pythonhosted.org/packages/9a/47/cb8c654fa9adcc60e99580e17c32b9e633290e6239a99efa6b885aba9dbc/orjson-3.11.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9daa26ca8e97fae0ce8aa5d80606ef8f7914e9b129b6b5df9104266f764ce436", size = 137535, upload-time = "2025-10-24T15:49:38.307Z" }, - { url = "https://files.pythonhosted.org/packages/43/92/04b8cc5c2b729f3437ee013ce14a60ab3d3001465d95c184758f19362f23/orjson-3.11.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c8b2769dc31883c44a9cd126560327767f848eb95f99c36c9932f51090bfce9", size = 136703, upload-time = "2025-10-24T15:49:40.795Z" }, - { url = "https://files.pythonhosted.org/packages/aa/fd/d0733fcb9086b8be4ebcfcda2d0312865d17d0d9884378b7cffb29d0763f/orjson-3.11.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1469d254b9884f984026bd9b0fa5bbab477a4bfe558bba6848086f6d43eb5e73", size = 136293, upload-time = "2025-10-24T15:49:42.347Z" }, - { url = "https://files.pythonhosted.org/packages/c2/d7/3c5514e806837c210492d72ae30ccf050ce3f940f45bf085bab272699ef4/orjson-3.11.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:68e44722541983614e37117209a194e8c3ad07838ccb3127d96863c95ec7f1e0", size = 140131, upload-time = "2025-10-24T15:49:43.638Z" }, - { url = "https://files.pythonhosted.org/packages/9c/dd/ba9d32a53207babf65bd510ac4d0faaa818bd0df9a9c6f472fe7c254f2e3/orjson-3.11.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:8e7805fda9672c12be2f22ae124dcd7b03928d6c197544fe12174b86553f3196", size = 406164, upload-time = "2025-10-24T15:49:45.498Z" }, - { url = "https://files.pythonhosted.org/packages/8e/f9/f68ad68f4af7c7bde57cd514eaa2c785e500477a8bc8f834838eb696a685/orjson-3.11.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:04b69c14615fb4434ab867bf6f38b2d649f6f300af30a6705397e895f7aec67a", size = 149859, upload-time = "2025-10-24T15:49:46.981Z" }, - { url = "https://files.pythonhosted.org/packages/b6/d2/7f847761d0c26818395b3d6b21fb6bc2305d94612a35b0a30eae65a22728/orjson-3.11.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:639c3735b8ae7f970066930e58cf0ed39a852d417c24acd4a25fc0b3da3c39a6", size = 139926, upload-time = "2025-10-24T15:49:48.321Z" }, - { url = "https://files.pythonhosted.org/packages/9f/37/acd14b12dc62db9a0e1d12386271b8661faae270b22492580d5258808975/orjson-3.11.4-cp313-cp313-win32.whl", hash = "sha256:6c13879c0d2964335491463302a6ca5ad98105fc5db3565499dcb80b1b4bd839", size = 136007, upload-time = "2025-10-24T15:49:49.938Z" }, - { url = "https://files.pythonhosted.org/packages/c0/a9/967be009ddf0a1fffd7a67de9c36656b28c763659ef91352acc02cbe364c/orjson-3.11.4-cp313-cp313-win_amd64.whl", hash = "sha256:09bf242a4af98732db9f9a1ec57ca2604848e16f132e3f72edfd3c5c96de009a", size = 131314, upload-time = "2025-10-24T15:49:51.248Z" }, - { url = "https://files.pythonhosted.org/packages/cb/db/399abd6950fbd94ce125cb8cd1a968def95174792e127b0642781e040ed4/orjson-3.11.4-cp313-cp313-win_arm64.whl", hash = "sha256:a85f0adf63319d6c1ba06fb0dbf997fced64a01179cf17939a6caca662bf92de", size = 126152, upload-time = "2025-10-24T15:49:52.922Z" }, - { url = "https://files.pythonhosted.org/packages/25/e3/54ff63c093cc1697e758e4fceb53164dd2661a7d1bcd522260ba09f54533/orjson-3.11.4-cp314-cp314-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:42d43a1f552be1a112af0b21c10a5f553983c2a0938d2bbb8ecd8bc9fb572803", size = 243501, upload-time = "2025-10-24T15:49:54.288Z" }, - { url = "https://files.pythonhosted.org/packages/ac/7d/e2d1076ed2e8e0ae9badca65bf7ef22710f93887b29eaa37f09850604e09/orjson-3.11.4-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:26a20f3fbc6c7ff2cb8e89c4c5897762c9d88cf37330c6a117312365d6781d54", size = 128862, upload-time = "2025-10-24T15:49:55.961Z" }, - { url = "https://files.pythonhosted.org/packages/9f/37/ca2eb40b90621faddfa9517dfe96e25f5ae4d8057a7c0cdd613c17e07b2c/orjson-3.11.4-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e3f20be9048941c7ffa8fc523ccbd17f82e24df1549d1d1fe9317712d19938e", size = 130047, upload-time = "2025-10-24T15:49:57.406Z" }, - { url = "https://files.pythonhosted.org/packages/c7/62/1021ed35a1f2bad9040f05fa4cc4f9893410df0ba3eaa323ccf899b1c90a/orjson-3.11.4-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:aac364c758dc87a52e68e349924d7e4ded348dedff553889e4d9f22f74785316", size = 129073, upload-time = "2025-10-24T15:49:58.782Z" }, - { url = "https://files.pythonhosted.org/packages/e8/3f/f84d966ec2a6fd5f73b1a707e7cd876813422ae4bf9f0145c55c9c6a0f57/orjson-3.11.4-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d5c54a6d76e3d741dcc3f2707f8eeb9ba2a791d3adbf18f900219b62942803b1", size = 136597, upload-time = "2025-10-24T15:50:00.12Z" }, - { url = "https://files.pythonhosted.org/packages/32/78/4fa0aeca65ee82bbabb49e055bd03fa4edea33f7c080c5c7b9601661ef72/orjson-3.11.4-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f28485bdca8617b79d44627f5fb04336897041dfd9fa66d383a49d09d86798bc", size = 137515, upload-time = "2025-10-24T15:50:01.57Z" }, - { url = "https://files.pythonhosted.org/packages/c1/9d/0c102e26e7fde40c4c98470796d050a2ec1953897e2c8ab0cb95b0759fa2/orjson-3.11.4-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bfc2a484cad3585e4ba61985a6062a4c2ed5c7925db6d39f1fa267c9d166487f", size = 136703, upload-time = "2025-10-24T15:50:02.944Z" }, - { url = "https://files.pythonhosted.org/packages/df/ac/2de7188705b4cdfaf0b6c97d2f7849c17d2003232f6e70df98602173f788/orjson-3.11.4-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e34dbd508cb91c54f9c9788923daca129fe5b55c5b4eebe713bf5ed3791280cf", size = 136311, upload-time = "2025-10-24T15:50:04.441Z" }, - { url = "https://files.pythonhosted.org/packages/e0/52/847fcd1a98407154e944feeb12e3b4d487a0e264c40191fb44d1269cbaa1/orjson-3.11.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b13c478fa413d4b4ee606ec8e11c3b2e52683a640b006bb586b3041c2ca5f606", size = 140127, upload-time = "2025-10-24T15:50:07.398Z" }, - { url = "https://files.pythonhosted.org/packages/c1/ae/21d208f58bdb847dd4d0d9407e2929862561841baa22bdab7aea10ca088e/orjson-3.11.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:724ca721ecc8a831b319dcd72cfa370cc380db0bf94537f08f7edd0a7d4e1780", size = 406201, upload-time = "2025-10-24T15:50:08.796Z" }, - { url = "https://files.pythonhosted.org/packages/8d/55/0789d6de386c8366059db098a628e2ad8798069e94409b0d8935934cbcb9/orjson-3.11.4-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:977c393f2e44845ce1b540e19a786e9643221b3323dae190668a98672d43fb23", size = 149872, upload-time = "2025-10-24T15:50:10.234Z" }, - { url = "https://files.pythonhosted.org/packages/cc/1d/7ff81ea23310e086c17b41d78a72270d9de04481e6113dbe2ac19118f7fb/orjson-3.11.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:1e539e382cf46edec157ad66b0b0872a90d829a6b71f17cb633d6c160a223155", size = 139931, upload-time = "2025-10-24T15:50:11.623Z" }, - { url = "https://files.pythonhosted.org/packages/77/92/25b886252c50ed64be68c937b562b2f2333b45afe72d53d719e46a565a50/orjson-3.11.4-cp314-cp314-win32.whl", hash = "sha256:d63076d625babab9db5e7836118bdfa086e60f37d8a174194ae720161eb12394", size = 136065, upload-time = "2025-10-24T15:50:13.025Z" }, - { url = "https://files.pythonhosted.org/packages/63/b8/718eecf0bb7e9d64e4956afaafd23db9f04c776d445f59fe94f54bdae8f0/orjson-3.11.4-cp314-cp314-win_amd64.whl", hash = "sha256:0a54d6635fa3aaa438ae32e8570b9f0de36f3f6562c308d2a2a452e8b0592db1", size = 131310, upload-time = "2025-10-24T15:50:14.46Z" }, - { url = "https://files.pythonhosted.org/packages/1a/bf/def5e25d4d8bfce296a9a7c8248109bf58622c21618b590678f945a2c59c/orjson-3.11.4-cp314-cp314-win_arm64.whl", hash = "sha256:78b999999039db3cf58f6d230f524f04f75f129ba3d1ca2ed121f8657e575d3d", size = 126151, upload-time = "2025-10-24T15:50:15.878Z" }, +version = "3.11.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/04/b8/333fdb27840f3bf04022d21b654a35f58e15407183aeb16f3b41aa053446/orjson-3.11.5.tar.gz", hash = "sha256:82393ab47b4fe44ffd0a7659fa9cfaacc717eb617c93cde83795f14af5c2e9d5", size = 5972347, upload-time = "2025-12-06T15:55:39.458Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/19/b22cf9dad4db20c8737041046054cbd4f38bb5a2d0e4bb60487832ce3d76/orjson-3.11.5-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:df9eadb2a6386d5ea2bfd81309c505e125cfc9ba2b1b99a97e60985b0b3665d1", size = 245719, upload-time = "2025-12-06T15:53:43.877Z" }, + { url = "https://files.pythonhosted.org/packages/03/2e/b136dd6bf30ef5143fbe76a4c142828b55ccc618be490201e9073ad954a1/orjson-3.11.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ccc70da619744467d8f1f49a8cadae5ec7bbe054e5232d95f92ed8737f8c5870", size = 132467, upload-time = "2025-12-06T15:53:45.379Z" }, + { url = "https://files.pythonhosted.org/packages/ae/fc/ae99bfc1e1887d20a0268f0e2686eb5b13d0ea7bbe01de2b566febcd2130/orjson-3.11.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:073aab025294c2f6fc0807201c76fdaed86f8fc4be52c440fb78fbb759a1ac09", size = 130702, upload-time = "2025-12-06T15:53:46.659Z" }, + { url = "https://files.pythonhosted.org/packages/6e/43/ef7912144097765997170aca59249725c3ab8ef6079f93f9d708dd058df5/orjson-3.11.5-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:835f26fa24ba0bb8c53ae2a9328d1706135b74ec653ed933869b74b6909e63fd", size = 135907, upload-time = "2025-12-06T15:53:48.487Z" }, + { url = "https://files.pythonhosted.org/packages/3f/da/24d50e2d7f4092ddd4d784e37a3fa41f22ce8ed97abc9edd222901a96e74/orjson-3.11.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:667c132f1f3651c14522a119e4dd631fad98761fa960c55e8e7430bb2a1ba4ac", size = 139935, upload-time = "2025-12-06T15:53:49.88Z" }, + { url = "https://files.pythonhosted.org/packages/02/4a/b4cb6fcbfff5b95a3a019a8648255a0fac9b221fbf6b6e72be8df2361feb/orjson-3.11.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:42e8961196af655bb5e63ce6c60d25e8798cd4dfbc04f4203457fa3869322c2e", size = 137541, upload-time = "2025-12-06T15:53:51.226Z" }, + { url = "https://files.pythonhosted.org/packages/a5/99/a11bd129f18c2377c27b2846a9d9be04acec981f770d711ba0aaea563984/orjson-3.11.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75412ca06e20904c19170f8a24486c4e6c7887dea591ba18a1ab572f1300ee9f", size = 139031, upload-time = "2025-12-06T15:53:52.309Z" }, + { url = "https://files.pythonhosted.org/packages/64/29/d7b77d7911574733a036bb3e8ad7053ceb2b7d6ea42208b9dbc55b23b9ed/orjson-3.11.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6af8680328c69e15324b5af3ae38abbfcf9cbec37b5346ebfd52339c3d7e8a18", size = 141622, upload-time = "2025-12-06T15:53:53.606Z" }, + { url = "https://files.pythonhosted.org/packages/93/41/332db96c1de76b2feda4f453e91c27202cd092835936ce2b70828212f726/orjson-3.11.5-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:a86fe4ff4ea523eac8f4b57fdac319faf037d3c1be12405e6a7e86b3fbc4756a", size = 413800, upload-time = "2025-12-06T15:53:54.866Z" }, + { url = "https://files.pythonhosted.org/packages/76/e1/5a0d148dd1f89ad2f9651df67835b209ab7fcb1118658cf353425d7563e9/orjson-3.11.5-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e607b49b1a106ee2086633167033afbd63f76f2999e9236f638b06b112b24ea7", size = 151198, upload-time = "2025-12-06T15:53:56.383Z" }, + { url = "https://files.pythonhosted.org/packages/0d/96/8db67430d317a01ae5cf7971914f6775affdcfe99f5bff9ef3da32492ecc/orjson-3.11.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7339f41c244d0eea251637727f016b3d20050636695bc78345cce9029b189401", size = 141984, upload-time = "2025-12-06T15:53:57.746Z" }, + { url = "https://files.pythonhosted.org/packages/71/49/40d21e1aa1ac569e521069228bb29c9b5a350344ccf922a0227d93c2ed44/orjson-3.11.5-cp310-cp310-win32.whl", hash = "sha256:8be318da8413cdbbce77b8c5fac8d13f6eb0f0db41b30bb598631412619572e8", size = 135272, upload-time = "2025-12-06T15:53:59.769Z" }, + { url = "https://files.pythonhosted.org/packages/c4/7e/d0e31e78be0c100e08be64f48d2850b23bcb4d4c70d114f4e43b39f6895a/orjson-3.11.5-cp310-cp310-win_amd64.whl", hash = "sha256:b9f86d69ae822cabc2a0f6c099b43e8733dda788405cba2665595b7e8dd8d167", size = 133360, upload-time = "2025-12-06T15:54:01.25Z" }, + { url = "https://files.pythonhosted.org/packages/fd/68/6b3659daec3a81aed5ab47700adb1a577c76a5452d35b91c88efee89987f/orjson-3.11.5-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9c8494625ad60a923af6b2b0bd74107146efe9b55099e20d7740d995f338fcd8", size = 245318, upload-time = "2025-12-06T15:54:02.355Z" }, + { url = "https://files.pythonhosted.org/packages/e9/00/92db122261425f61803ccf0830699ea5567439d966cbc35856fe711bfe6b/orjson-3.11.5-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:7bb2ce0b82bc9fd1168a513ddae7a857994b780b2945a8c51db4ab1c4b751ebc", size = 129491, upload-time = "2025-12-06T15:54:03.877Z" }, + { url = "https://files.pythonhosted.org/packages/94/4f/ffdcb18356518809d944e1e1f77589845c278a1ebbb5a8297dfefcc4b4cb/orjson-3.11.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67394d3becd50b954c4ecd24ac90b5051ee7c903d167459f93e77fc6f5b4c968", size = 132167, upload-time = "2025-12-06T15:54:04.944Z" }, + { url = "https://files.pythonhosted.org/packages/97/c6/0a8caff96f4503f4f7dd44e40e90f4d14acf80d3b7a97cb88747bb712d3e/orjson-3.11.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:298d2451f375e5f17b897794bcc3e7b821c0f32b4788b9bcae47ada24d7f3cf7", size = 130516, upload-time = "2025-12-06T15:54:06.274Z" }, + { url = "https://files.pythonhosted.org/packages/4d/63/43d4dc9bd9954bff7052f700fdb501067f6fb134a003ddcea2a0bb3854ed/orjson-3.11.5-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa5e4244063db8e1d87e0f54c3f7522f14b2dc937e65d5241ef0076a096409fd", size = 135695, upload-time = "2025-12-06T15:54:07.702Z" }, + { url = "https://files.pythonhosted.org/packages/87/6f/27e2e76d110919cb7fcb72b26166ee676480a701bcf8fc53ac5d0edce32f/orjson-3.11.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1db2088b490761976c1b2e956d5d4e6409f3732e9d79cfa69f876c5248d1baf9", size = 139664, upload-time = "2025-12-06T15:54:08.828Z" }, + { url = "https://files.pythonhosted.org/packages/d4/f8/5966153a5f1be49b5fbb8ca619a529fde7bc71aa0a376f2bb83fed248bcd/orjson-3.11.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c2ed66358f32c24e10ceea518e16eb3549e34f33a9d51f99ce23b0251776a1ef", size = 137289, upload-time = "2025-12-06T15:54:09.898Z" }, + { url = "https://files.pythonhosted.org/packages/a7/34/8acb12ff0299385c8bbcbb19fbe40030f23f15a6de57a9c587ebf71483fb/orjson-3.11.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c2021afda46c1ed64d74b555065dbd4c2558d510d8cec5ea6a53001b3e5e82a9", size = 138784, upload-time = "2025-12-06T15:54:11.022Z" }, + { url = "https://files.pythonhosted.org/packages/ee/27/910421ea6e34a527f73d8f4ee7bdffa48357ff79c7b8d6eb6f7b82dd1176/orjson-3.11.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b42ffbed9128e547a1647a3e50bc88ab28ae9daa61713962e0d3dd35e820c125", size = 141322, upload-time = "2025-12-06T15:54:12.427Z" }, + { url = "https://files.pythonhosted.org/packages/87/a3/4b703edd1a05555d4bb1753d6ce44e1a05b7a6d7c164d5b332c795c63d70/orjson-3.11.5-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:8d5f16195bb671a5dd3d1dbea758918bada8f6cc27de72bd64adfbd748770814", size = 413612, upload-time = "2025-12-06T15:54:13.858Z" }, + { url = "https://files.pythonhosted.org/packages/1b/36/034177f11d7eeea16d3d2c42a1883b0373978e08bc9dad387f5074c786d8/orjson-3.11.5-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:c0e5d9f7a0227df2927d343a6e3859bebf9208b427c79bd31949abcc2fa32fa5", size = 150993, upload-time = "2025-12-06T15:54:15.189Z" }, + { url = "https://files.pythonhosted.org/packages/44/2f/ea8b24ee046a50a7d141c0227c4496b1180b215e728e3b640684f0ea448d/orjson-3.11.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:23d04c4543e78f724c4dfe656b3791b5f98e4c9253e13b2636f1af5d90e4a880", size = 141774, upload-time = "2025-12-06T15:54:16.451Z" }, + { url = "https://files.pythonhosted.org/packages/8a/12/cc440554bf8200eb23348a5744a575a342497b65261cd65ef3b28332510a/orjson-3.11.5-cp311-cp311-win32.whl", hash = "sha256:c404603df4865f8e0afe981aa3c4b62b406e6d06049564d58934860b62b7f91d", size = 135109, upload-time = "2025-12-06T15:54:17.73Z" }, + { url = "https://files.pythonhosted.org/packages/a3/83/e0c5aa06ba73a6760134b169f11fb970caa1525fa4461f94d76e692299d9/orjson-3.11.5-cp311-cp311-win_amd64.whl", hash = "sha256:9645ef655735a74da4990c24ffbd6894828fbfa117bc97c1edd98c282ecb52e1", size = 133193, upload-time = "2025-12-06T15:54:19.426Z" }, + { url = "https://files.pythonhosted.org/packages/cb/35/5b77eaebc60d735e832c5b1a20b155667645d123f09d471db0a78280fb49/orjson-3.11.5-cp311-cp311-win_arm64.whl", hash = "sha256:1cbf2735722623fcdee8e712cbaaab9e372bbcb0c7924ad711b261c2eccf4a5c", size = 126830, upload-time = "2025-12-06T15:54:20.836Z" }, + { url = "https://files.pythonhosted.org/packages/ef/a4/8052a029029b096a78955eadd68ab594ce2197e24ec50e6b6d2ab3f4e33b/orjson-3.11.5-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:334e5b4bff9ad101237c2d799d9fd45737752929753bf4faf4b207335a416b7d", size = 245347, upload-time = "2025-12-06T15:54:22.061Z" }, + { url = "https://files.pythonhosted.org/packages/64/67/574a7732bd9d9d79ac620c8790b4cfe0717a3d5a6eb2b539e6e8995e24a0/orjson-3.11.5-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:ff770589960a86eae279f5d8aa536196ebda8273a2a07db2a54e82b93bc86626", size = 129435, upload-time = "2025-12-06T15:54:23.615Z" }, + { url = "https://files.pythonhosted.org/packages/52/8d/544e77d7a29d90cf4d9eecd0ae801c688e7f3d1adfa2ebae5e1e94d38ab9/orjson-3.11.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed24250e55efbcb0b35bed7caaec8cedf858ab2f9f2201f17b8938c618c8ca6f", size = 132074, upload-time = "2025-12-06T15:54:24.694Z" }, + { url = "https://files.pythonhosted.org/packages/6e/57/b9f5b5b6fbff9c26f77e785baf56ae8460ef74acdb3eae4931c25b8f5ba9/orjson-3.11.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a66d7769e98a08a12a139049aac2f0ca3adae989817f8c43337455fbc7669b85", size = 130520, upload-time = "2025-12-06T15:54:26.185Z" }, + { url = "https://files.pythonhosted.org/packages/f6/6d/d34970bf9eb33f9ec7c979a262cad86076814859e54eb9a059a52f6dc13d/orjson-3.11.5-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:86cfc555bfd5794d24c6a1903e558b50644e5e68e6471d66502ce5cb5fdef3f9", size = 136209, upload-time = "2025-12-06T15:54:27.264Z" }, + { url = "https://files.pythonhosted.org/packages/e7/39/bc373b63cc0e117a105ea12e57280f83ae52fdee426890d57412432d63b3/orjson-3.11.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a230065027bc2a025e944f9d4714976a81e7ecfa940923283bca7bbc1f10f626", size = 139837, upload-time = "2025-12-06T15:54:28.75Z" }, + { url = "https://files.pythonhosted.org/packages/cb/aa/7c4818c8d7d324da220f4f1af55c343956003aa4d1ce1857bdc1d396ba69/orjson-3.11.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b29d36b60e606df01959c4b982729c8845c69d1963f88686608be9ced96dbfaa", size = 137307, upload-time = "2025-12-06T15:54:29.856Z" }, + { url = "https://files.pythonhosted.org/packages/46/bf/0993b5a056759ba65145effe3a79dd5a939d4a070eaa5da2ee3180fbb13f/orjson-3.11.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c74099c6b230d4261fdc3169d50efc09abf38ace1a42ea2f9994b1d79153d477", size = 139020, upload-time = "2025-12-06T15:54:31.024Z" }, + { url = "https://files.pythonhosted.org/packages/65/e8/83a6c95db3039e504eda60fc388f9faedbb4f6472f5aba7084e06552d9aa/orjson-3.11.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e697d06ad57dd0c7a737771d470eedc18e68dfdefcdd3b7de7f33dfda5b6212e", size = 141099, upload-time = "2025-12-06T15:54:32.196Z" }, + { url = "https://files.pythonhosted.org/packages/b9/b4/24fdc024abfce31c2f6812973b0a693688037ece5dc64b7a60c1ce69e2f2/orjson-3.11.5-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:e08ca8a6c851e95aaecc32bc44a5aa75d0ad26af8cdac7c77e4ed93acf3d5b69", size = 413540, upload-time = "2025-12-06T15:54:33.361Z" }, + { url = "https://files.pythonhosted.org/packages/d9/37/01c0ec95d55ed0c11e4cae3e10427e479bba40c77312b63e1f9665e0737d/orjson-3.11.5-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e8b5f96c05fce7d0218df3fdfeb962d6b8cfff7e3e20264306b46dd8b217c0f3", size = 151530, upload-time = "2025-12-06T15:54:34.6Z" }, + { url = "https://files.pythonhosted.org/packages/f9/d4/f9ebc57182705bb4bbe63f5bbe14af43722a2533135e1d2fb7affa0c355d/orjson-3.11.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ddbfdb5099b3e6ba6d6ea818f61997bb66de14b411357d24c4612cf1ebad08ca", size = 141863, upload-time = "2025-12-06T15:54:35.801Z" }, + { url = "https://files.pythonhosted.org/packages/0d/04/02102b8d19fdcb009d72d622bb5781e8f3fae1646bf3e18c53d1bc8115b5/orjson-3.11.5-cp312-cp312-win32.whl", hash = "sha256:9172578c4eb09dbfcf1657d43198de59b6cef4054de385365060ed50c458ac98", size = 135255, upload-time = "2025-12-06T15:54:37.209Z" }, + { url = "https://files.pythonhosted.org/packages/d4/fb/f05646c43d5450492cb387de5549f6de90a71001682c17882d9f66476af5/orjson-3.11.5-cp312-cp312-win_amd64.whl", hash = "sha256:2b91126e7b470ff2e75746f6f6ee32b9ab67b7a93c8ba1d15d3a0caaf16ec875", size = 133252, upload-time = "2025-12-06T15:54:38.401Z" }, + { url = "https://files.pythonhosted.org/packages/dc/a6/7b8c0b26ba18c793533ac1cd145e131e46fcf43952aa94c109b5b913c1f0/orjson-3.11.5-cp312-cp312-win_arm64.whl", hash = "sha256:acbc5fac7e06777555b0722b8ad5f574739e99ffe99467ed63da98f97f9ca0fe", size = 126777, upload-time = "2025-12-06T15:54:39.515Z" }, + { url = "https://files.pythonhosted.org/packages/10/43/61a77040ce59f1569edf38f0b9faadc90c8cf7e9bec2e0df51d0132c6bb7/orjson-3.11.5-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:3b01799262081a4c47c035dd77c1301d40f568f77cc7ec1bb7db5d63b0a01629", size = 245271, upload-time = "2025-12-06T15:54:40.878Z" }, + { url = "https://files.pythonhosted.org/packages/55/f9/0f79be617388227866d50edd2fd320cb8fb94dc1501184bb1620981a0aba/orjson-3.11.5-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:61de247948108484779f57a9f406e4c84d636fa5a59e411e6352484985e8a7c3", size = 129422, upload-time = "2025-12-06T15:54:42.403Z" }, + { url = "https://files.pythonhosted.org/packages/77/42/f1bf1549b432d4a78bfa95735b79b5dac75b65b5bb815bba86ad406ead0a/orjson-3.11.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:894aea2e63d4f24a7f04a1908307c738d0dce992e9249e744b8f4e8dd9197f39", size = 132060, upload-time = "2025-12-06T15:54:43.531Z" }, + { url = "https://files.pythonhosted.org/packages/25/49/825aa6b929f1a6ed244c78acd7b22c1481fd7e5fda047dc8bf4c1a807eb6/orjson-3.11.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ddc21521598dbe369d83d4d40338e23d4101dad21dae0e79fa20465dbace019f", size = 130391, upload-time = "2025-12-06T15:54:45.059Z" }, + { url = "https://files.pythonhosted.org/packages/42/ec/de55391858b49e16e1aa8f0bbbb7e5997b7345d8e984a2dec3746d13065b/orjson-3.11.5-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7cce16ae2f5fb2c53c3eafdd1706cb7b6530a67cc1c17abe8ec747f5cd7c0c51", size = 135964, upload-time = "2025-12-06T15:54:46.576Z" }, + { url = "https://files.pythonhosted.org/packages/1c/40/820bc63121d2d28818556a2d0a09384a9f0262407cf9fa305e091a8048df/orjson-3.11.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e46c762d9f0e1cfb4ccc8515de7f349abbc95b59cb5a2bd68df5973fdef913f8", size = 139817, upload-time = "2025-12-06T15:54:48.084Z" }, + { url = "https://files.pythonhosted.org/packages/09/c7/3a445ca9a84a0d59d26365fd8898ff52bdfcdcb825bcc6519830371d2364/orjson-3.11.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d7345c759276b798ccd6d77a87136029e71e66a8bbf2d2755cbdde1d82e78706", size = 137336, upload-time = "2025-12-06T15:54:49.426Z" }, + { url = "https://files.pythonhosted.org/packages/9a/b3/dc0d3771f2e5d1f13368f56b339c6782f955c6a20b50465a91acb79fe961/orjson-3.11.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75bc2e59e6a2ac1dd28901d07115abdebc4563b5b07dd612bf64260a201b1c7f", size = 138993, upload-time = "2025-12-06T15:54:50.939Z" }, + { url = "https://files.pythonhosted.org/packages/d1/a2/65267e959de6abe23444659b6e19c888f242bf7725ff927e2292776f6b89/orjson-3.11.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:54aae9b654554c3b4edd61896b978568c6daa16af96fa4681c9b5babd469f863", size = 141070, upload-time = "2025-12-06T15:54:52.414Z" }, + { url = "https://files.pythonhosted.org/packages/63/c9/da44a321b288727a322c6ab17e1754195708786a04f4f9d2220a5076a649/orjson-3.11.5-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:4bdd8d164a871c4ec773f9de0f6fe8769c2d6727879c37a9666ba4183b7f8228", size = 413505, upload-time = "2025-12-06T15:54:53.67Z" }, + { url = "https://files.pythonhosted.org/packages/7f/17/68dc14fa7000eefb3d4d6d7326a190c99bb65e319f02747ef3ebf2452f12/orjson-3.11.5-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:a261fef929bcf98a60713bf5e95ad067cea16ae345d9a35034e73c3990e927d2", size = 151342, upload-time = "2025-12-06T15:54:55.113Z" }, + { url = "https://files.pythonhosted.org/packages/c4/c5/ccee774b67225bed630a57478529fc026eda33d94fe4c0eac8fe58d4aa52/orjson-3.11.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c028a394c766693c5c9909dec76b24f37e6a1b91999e8d0c0d5feecbe93c3e05", size = 141823, upload-time = "2025-12-06T15:54:56.331Z" }, + { url = "https://files.pythonhosted.org/packages/67/80/5d00e4155d0cd7390ae2087130637671da713959bb558db9bac5e6f6b042/orjson-3.11.5-cp313-cp313-win32.whl", hash = "sha256:2cc79aaad1dfabe1bd2d50ee09814a1253164b3da4c00a78c458d82d04b3bdef", size = 135236, upload-time = "2025-12-06T15:54:57.507Z" }, + { url = "https://files.pythonhosted.org/packages/95/fe/792cc06a84808dbdc20ac6eab6811c53091b42f8e51ecebf14b540e9cfe4/orjson-3.11.5-cp313-cp313-win_amd64.whl", hash = "sha256:ff7877d376add4e16b274e35a3f58b7f37b362abf4aa31863dadacdd20e3a583", size = 133167, upload-time = "2025-12-06T15:54:58.71Z" }, + { url = "https://files.pythonhosted.org/packages/46/2c/d158bd8b50e3b1cfdcf406a7e463f6ffe3f0d167b99634717acdaf5e299f/orjson-3.11.5-cp313-cp313-win_arm64.whl", hash = "sha256:59ac72ea775c88b163ba8d21b0177628bd015c5dd060647bbab6e22da3aad287", size = 126712, upload-time = "2025-12-06T15:54:59.892Z" }, + { url = "https://files.pythonhosted.org/packages/c2/60/77d7b839e317ead7bb225d55bb50f7ea75f47afc489c81199befc5435b50/orjson-3.11.5-cp314-cp314-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:e446a8ea0a4c366ceafc7d97067bfd55292969143b57e3c846d87fc701e797a0", size = 245252, upload-time = "2025-12-06T15:55:01.127Z" }, + { url = "https://files.pythonhosted.org/packages/f1/aa/d4639163b400f8044cef0fb9aa51b0337be0da3a27187a20d1166e742370/orjson-3.11.5-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:53deb5addae9c22bbe3739298f5f2196afa881ea75944e7720681c7080909a81", size = 129419, upload-time = "2025-12-06T15:55:02.723Z" }, + { url = "https://files.pythonhosted.org/packages/30/94/9eabf94f2e11c671111139edf5ec410d2f21e6feee717804f7e8872d883f/orjson-3.11.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82cd00d49d6063d2b8791da5d4f9d20539c5951f965e45ccf4e96d33505ce68f", size = 132050, upload-time = "2025-12-06T15:55:03.918Z" }, + { url = "https://files.pythonhosted.org/packages/3d/c8/ca10f5c5322f341ea9a9f1097e140be17a88f88d1cfdd29df522970d9744/orjson-3.11.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3fd15f9fc8c203aeceff4fda211157fad114dde66e92e24097b3647a08f4ee9e", size = 130370, upload-time = "2025-12-06T15:55:05.173Z" }, + { url = "https://files.pythonhosted.org/packages/25/d4/e96824476d361ee2edd5c6290ceb8d7edf88d81148a6ce172fc00278ca7f/orjson-3.11.5-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9df95000fbe6777bf9820ae82ab7578e8662051bb5f83d71a28992f539d2cda7", size = 136012, upload-time = "2025-12-06T15:55:06.402Z" }, + { url = "https://files.pythonhosted.org/packages/85/8e/9bc3423308c425c588903f2d103cfcfe2539e07a25d6522900645a6f257f/orjson-3.11.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:92a8d676748fca47ade5bc3da7430ed7767afe51b2f8100e3cd65e151c0eaceb", size = 139809, upload-time = "2025-12-06T15:55:07.656Z" }, + { url = "https://files.pythonhosted.org/packages/e9/3c/b404e94e0b02a232b957c54643ce68d0268dacb67ac33ffdee24008c8b27/orjson-3.11.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aa0f513be38b40234c77975e68805506cad5d57b3dfd8fe3baa7f4f4051e15b4", size = 137332, upload-time = "2025-12-06T15:55:08.961Z" }, + { url = "https://files.pythonhosted.org/packages/51/30/cc2d69d5ce0ad9b84811cdf4a0cd5362ac27205a921da524ff42f26d65e0/orjson-3.11.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa1863e75b92891f553b7922ce4ee10ed06db061e104f2b7815de80cdcb135ad", size = 138983, upload-time = "2025-12-06T15:55:10.595Z" }, + { url = "https://files.pythonhosted.org/packages/0e/87/de3223944a3e297d4707d2fe3b1ffb71437550e165eaf0ca8bbe43ccbcb1/orjson-3.11.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d4be86b58e9ea262617b8ca6251a2f0d63cc132a6da4b5fcc8e0a4128782c829", size = 141069, upload-time = "2025-12-06T15:55:11.832Z" }, + { url = "https://files.pythonhosted.org/packages/65/30/81d5087ae74be33bcae3ff2d80f5ccaa4a8fedc6d39bf65a427a95b8977f/orjson-3.11.5-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:b923c1c13fa02084eb38c9c065afd860a5cff58026813319a06949c3af5732ac", size = 413491, upload-time = "2025-12-06T15:55:13.314Z" }, + { url = "https://files.pythonhosted.org/packages/d0/6f/f6058c21e2fc1efaf918986dbc2da5cd38044f1a2d4b7b91ad17c4acf786/orjson-3.11.5-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:1b6bd351202b2cd987f35a13b5e16471cf4d952b42a73c391cc537974c43ef6d", size = 151375, upload-time = "2025-12-06T15:55:14.715Z" }, + { url = "https://files.pythonhosted.org/packages/54/92/c6921f17d45e110892899a7a563a925b2273d929959ce2ad89e2525b885b/orjson-3.11.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:bb150d529637d541e6af06bbe3d02f5498d628b7f98267ff87647584293ab439", size = 141850, upload-time = "2025-12-06T15:55:15.94Z" }, + { url = "https://files.pythonhosted.org/packages/88/86/cdecb0140a05e1a477b81f24739da93b25070ee01ce7f7242f44a6437594/orjson-3.11.5-cp314-cp314-win32.whl", hash = "sha256:9cc1e55c884921434a84a0c3dd2699eb9f92e7b441d7f53f3941079ec6ce7499", size = 135278, upload-time = "2025-12-06T15:55:17.202Z" }, + { url = "https://files.pythonhosted.org/packages/e4/97/b638d69b1e947d24f6109216997e38922d54dcdcdb1b11c18d7efd2d3c59/orjson-3.11.5-cp314-cp314-win_amd64.whl", hash = "sha256:a4f3cb2d874e03bc7767c8f88adaa1a9a05cecea3712649c3b58589ec7317310", size = 133170, upload-time = "2025-12-06T15:55:18.468Z" }, + { url = "https://files.pythonhosted.org/packages/8f/dd/f4fff4a6fe601b4f8f3ba3aa6da8ac33d17d124491a3b804c662a70e1636/orjson-3.11.5-cp314-cp314-win_arm64.whl", hash = "sha256:38b22f476c351f9a1c43e5b07d8b5a02eb24a6ab8e75f700f7d479d4568346a5", size = 126713, upload-time = "2025-12-06T15:55:19.738Z" }, ] [[package]] name = "ormsgpack" -version = "1.12.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6c/67/d5ef41c3b4a94400be801984ef7c7fc9623e1a82b643e74eeec367e7462b/ormsgpack-1.12.0.tar.gz", hash = "sha256:94be818fdbb0285945839b88763b269987787cb2f7ef280cad5d6ec815b7e608", size = 49959, upload-time = "2025-11-04T18:30:10.083Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4d/0c/8f45fcd22c95190b05d4fba71375d8f783a9e3b0b6aaf476812b693e3868/ormsgpack-1.12.0-cp310-cp310-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:e08904c232358b94a682ccfbb680bc47d3fd5c424bb7dccb65974dd20c95e8e1", size = 369156, upload-time = "2025-11-04T18:29:17.629Z" }, - { url = "https://files.pythonhosted.org/packages/d2/f8/c7adc093d4ceb05e38786906815f868210f808326c27f8124b4d9466a26b/ormsgpack-1.12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9ed7a4b0037d69c8ba7e670e03ee65ae8d5c5114a409e73c5770d7fb5e4b895", size = 195743, upload-time = "2025-11-04T18:29:18.964Z" }, - { url = "https://files.pythonhosted.org/packages/fe/b8/bf002648fa6c150ed6157837b00303edafb35f65c352429463f83214de18/ormsgpack-1.12.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:db2928525b684f3f2af0367aef7ae8d20cde37fc5349c700017129d493a755aa", size = 206472, upload-time = "2025-11-04T18:29:19.951Z" }, - { url = "https://files.pythonhosted.org/packages/23/d6/1f445947c95a931bb189b7864a3f9dcbeebe7dcbc1b3c7387427b3779228/ormsgpack-1.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45f911d9c5b23d11e49ff03fc8f9566745a2b1a7d9033733a1c0a2fa9301cd60", size = 207959, upload-time = "2025-11-04T18:29:21.282Z" }, - { url = "https://files.pythonhosted.org/packages/5c/9c/dd8ccd7553a5c1d0b4b69a0541611983a2f9bc2e8c5708a4bfffc0100468/ormsgpack-1.12.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:98c54ae6fd682b2aceb264505af9b2255f3df9d84e6e4369bc44d2110f1f311d", size = 377659, upload-time = "2025-11-04T18:29:22.561Z" }, - { url = "https://files.pythonhosted.org/packages/3d/08/3282d8f6330e742d4cdbcfbe2c100b403ae5526ae82a1c1b6a11b7967e37/ormsgpack-1.12.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:857ab987c3502de08258cc4baf0e87267cb2c80931601084e13df3c355b1ab9d", size = 471391, upload-time = "2025-11-04T18:29:23.663Z" }, - { url = "https://files.pythonhosted.org/packages/18/d4/94a2fbfd4837754bda7a099b6a23b9d40aba9e76e1af8b8fb8133612eb54/ormsgpack-1.12.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:27579d45dc502ee736238e1024559cb0a01aa72a3b68827448b8edf6a2dcdc9c", size = 381501, upload-time = "2025-11-04T18:29:24.771Z" }, - { url = "https://files.pythonhosted.org/packages/d8/c6/1a9fa122cb5deb10b067bbaa43165b12291a914cc0ce364988ff17bbf405/ormsgpack-1.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:c78379d054760875540cf2e81f28da1bb78d09fda3eabdbeb6c53b3e297158cb", size = 112715, upload-time = "2025-11-04T18:29:26.016Z" }, - { url = "https://files.pythonhosted.org/packages/1a/ba/3cae83cf36420c1c8dd294f16c852c03313aafe2439a165c4c6ac611b1d0/ormsgpack-1.12.0-cp311-cp311-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:c40d86d77391b18dd34de5295e3de2b8ad818bcab9c9def4121c8ec5c9714ae4", size = 369159, upload-time = "2025-11-04T18:29:27.057Z" }, - { url = "https://files.pythonhosted.org/packages/97/d4/5e176309e01a8b9098d80201aac1eb7db9336c3b5b4fa6254a2bbb0d0fa0/ormsgpack-1.12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:777b7fab364dc0f200bb382a98a385c8222ffa6a2333d627d763797326202c86", size = 195744, upload-time = "2025-11-04T18:29:28.069Z" }, - { url = "https://files.pythonhosted.org/packages/4f/83/6d80c8c5571639c000a39f38f77752dfaf9d9e552d775331e8d280f66a4e/ormsgpack-1.12.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b5b5089ad9dd5b3d3013b245a55e4abaea2f8ad70f4a78e1b002127b02340004", size = 206474, upload-time = "2025-11-04T18:29:29.034Z" }, - { url = "https://files.pythonhosted.org/packages/5e/e6/940311e48dc0cfc3e212bd7007a21ed0825158638057687d804f2c5c2cca/ormsgpack-1.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:deaf0c87cace7bc08fbf68c5cc66605b593df6427e9f4de235b2da358787e008", size = 207959, upload-time = "2025-11-04T18:29:30.315Z" }, - { url = "https://files.pythonhosted.org/packages/1a/e3/fbe94b0a311815343b86a95a0627e4901b11ff6fd522679ca29a2a88c99b/ormsgpack-1.12.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f62d476fe28bc5675d9aff30341bfa9f41d7de332c5b63fbbe9aaf6bb7ec74d4", size = 377666, upload-time = "2025-11-04T18:29:31.38Z" }, - { url = "https://files.pythonhosted.org/packages/a3/3b/229cfa28076798ffb619aaa854b842de3f2ed5ea4e6509bf34d14c038c4d/ormsgpack-1.12.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:ded7810095b887e28434f32f5a345d354e88cf851bab3c5435aeb86a718618d2", size = 471394, upload-time = "2025-11-04T18:29:32.521Z" }, - { url = "https://files.pythonhosted.org/packages/6b/bd/4eae4ab35586e4175c07acb5f98aec83aa9d8987f71ea0443aa900191bdf/ormsgpack-1.12.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f72a1dea0c4ae7c4101dcfbe8133f274a9d769d0b87fe5188db4fab07ffabaee", size = 381506, upload-time = "2025-11-04T18:29:33.533Z" }, - { url = "https://files.pythonhosted.org/packages/dd/51/f9d56d6d015cbfa1ce9a4358ca30a41744644f0cf606e060d7203efe5af8/ormsgpack-1.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:8f479bfef847255d7d0b12c7a198f6a21490155da2da3062e082ba370893d4a1", size = 112707, upload-time = "2025-11-04T18:29:34.898Z" }, - { url = "https://files.pythonhosted.org/packages/f4/07/bb189ef7072979f2f96e8716e952172efdce9c54930aa0814bec73aee19b/ormsgpack-1.12.0-cp311-cp311-win_arm64.whl", hash = "sha256:3583ca410e4502144b2594170542e4bbef7b15643fd1208703ae820f11029036", size = 106533, upload-time = "2025-11-04T18:29:36.112Z" }, - { url = "https://files.pythonhosted.org/packages/a2/f2/c1036b2775fcc0cfa5fd618c53bcd3b862ee07298fb627f03af4c7982f84/ormsgpack-1.12.0-cp312-cp312-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:e0c1e08b64d99076fee155276097489b82cc56e8d5951c03c721a65a32f44494", size = 369538, upload-time = "2025-11-04T18:29:37.125Z" }, - { url = "https://files.pythonhosted.org/packages/d9/ca/526c4ae02f3cb34621af91bf8282a10d666757c2e0c6ff391ff5d403d607/ormsgpack-1.12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3fd43bcb299131690b8e0677af172020b2ada8e625169034b42ac0c13adf84aa", size = 195872, upload-time = "2025-11-04T18:29:38.34Z" }, - { url = "https://files.pythonhosted.org/packages/7f/0f/83bb7968e9715f6a85be53d041b1e6324a05428f56b8b980dac866886871/ormsgpack-1.12.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f0149d595341e22ead340bf281b2995c4cc7dc8d522a6b5f575fe17aa407604", size = 206469, upload-time = "2025-11-04T18:29:39.749Z" }, - { url = "https://files.pythonhosted.org/packages/02/e3/9e93ca1065f2d4af035804a842b1ff3025bab580c7918239bb225cd1fee2/ormsgpack-1.12.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f19a1b27d169deb553c80fd10b589fc2be1fc14cee779fae79fcaf40db04de2b", size = 208273, upload-time = "2025-11-04T18:29:40.769Z" }, - { url = "https://files.pythonhosted.org/packages/b3/d8/6d6ef901b3a8b8f3ab8836b135a56eb7f66c559003e251d9530bedb12627/ormsgpack-1.12.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6f28896942d655064940dfe06118b7ce1e3468d051483148bf02c99ec157483a", size = 377839, upload-time = "2025-11-04T18:29:42.092Z" }, - { url = "https://files.pythonhosted.org/packages/4c/72/fcb704bfa4c2c3a37b647d597cc45a13cffc9d50baac635a9ad620731d29/ormsgpack-1.12.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:9396efcfa48b4abbc06e44c5dbc3c4574a8381a80cb4cd01eea15d28b38c554e", size = 471446, upload-time = "2025-11-04T18:29:43.133Z" }, - { url = "https://files.pythonhosted.org/packages/84/f8/402e4e3eb997c2ee534c99bec4b5bb359c2a1f9edadf043e254a71e11378/ormsgpack-1.12.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:96586ed537a5fb386a162c4f9f7d8e6f76e07b38a990d50c73f11131e00ff040", size = 381783, upload-time = "2025-11-04T18:29:44.466Z" }, - { url = "https://files.pythonhosted.org/packages/f0/8d/5897b700360bc00911b70ae5ef1134ee7abf5baa81a92a4be005917d3dfd/ormsgpack-1.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:e70387112fb3870e4844de090014212cdcf1342f5022047aecca01ec7de05d7a", size = 112943, upload-time = "2025-11-04T18:29:45.468Z" }, - { url = "https://files.pythonhosted.org/packages/5b/44/1e73649f79bb96d6cf9e5bcbac68b6216d238bba80af351c4c0cbcf7ee15/ormsgpack-1.12.0-cp312-cp312-win_arm64.whl", hash = "sha256:d71290a23de5d4829610c42665d816c661ecad8979883f3f06b2e3ab9639962e", size = 106688, upload-time = "2025-11-04T18:29:46.411Z" }, - { url = "https://files.pythonhosted.org/packages/2e/e8/35f11ce9313111488b26b3035e4cbe55caa27909c0b6c8b5b5cd59f9661e/ormsgpack-1.12.0-cp313-cp313-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:766f2f3b512d85cd375b26a8b1329b99843560b50b93d3880718e634ad4a5de5", size = 369574, upload-time = "2025-11-04T18:29:47.431Z" }, - { url = "https://files.pythonhosted.org/packages/61/b0/77461587f412d4e598d3687bafe23455ed0f26269f44be20252eddaa624e/ormsgpack-1.12.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84b285b1f3f185aad7da45641b873b30acfd13084cf829cf668c4c6480a81583", size = 195893, upload-time = "2025-11-04T18:29:48.735Z" }, - { url = "https://files.pythonhosted.org/packages/c6/67/e197ceb04c3b550589e5407fc9fdae10f4e2e2eba5fdac921a269e02e974/ormsgpack-1.12.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e23604fc79fe110292cb365f4c8232e64e63a34f470538be320feae3921f271b", size = 206503, upload-time = "2025-11-04T18:29:49.99Z" }, - { url = "https://files.pythonhosted.org/packages/0b/b1/7fa8ba82a25cef678983c7976f85edeef5014f5c26495f338258e6a3cf1c/ormsgpack-1.12.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc32b156c113a0fae2975051417d8d9a7a5247c34b2d7239410c46b75ce9348a", size = 208257, upload-time = "2025-11-04T18:29:51.007Z" }, - { url = "https://files.pythonhosted.org/packages/ce/b1/759e999390000d2589e6d0797f7265e6ec28378547075d28d3736248ab63/ormsgpack-1.12.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:94ac500dd10c20fa8b8a23bc55606250bfe711bf9716828d9f3d44dfd1f25668", size = 377852, upload-time = "2025-11-04T18:29:52.103Z" }, - { url = "https://files.pythonhosted.org/packages/51/e7/0af737c94272494d9d84a3c29cc42c973ef7fd2342917020906596db863c/ormsgpack-1.12.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:c5201ff7ec24f721f813a182885a17064cffdbe46b2412685a52e6374a872c8f", size = 471456, upload-time = "2025-11-04T18:29:53.336Z" }, - { url = "https://files.pythonhosted.org/packages/f4/ba/c81f0aa4f19fbf457213395945b672e6fde3ce777e3587456e7f0fca2147/ormsgpack-1.12.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a9740bb3839c9368aacae1cbcfc474ee6976458f41cc135372b7255d5206c953", size = 381813, upload-time = "2025-11-04T18:29:54.394Z" }, - { url = "https://files.pythonhosted.org/packages/ce/15/429c72d64323503fd42cc4ca8398930ded8aa8b3470df8a86b3bbae7a35c/ormsgpack-1.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:8ed37f29772432048b58174e920a1d4c4cde0404a5d448d3d8bbcc95d86a6918", size = 112949, upload-time = "2025-11-04T18:29:55.371Z" }, - { url = "https://files.pythonhosted.org/packages/55/b9/e72c451a40f8c57bfc229e0b8e536ecea7203c8f0a839676df2ffb605c62/ormsgpack-1.12.0-cp313-cp313-win_arm64.whl", hash = "sha256:b03994bbec5d6d42e03d6604e327863f885bde67aa61e06107ce1fa5bdd3e71d", size = 106689, upload-time = "2025-11-04T18:29:56.262Z" }, - { url = "https://files.pythonhosted.org/packages/13/16/13eab1a75da531b359105fdee90dda0b6bd1ca0a09880250cf91d8bdfdea/ormsgpack-1.12.0-cp314-cp314-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:0f3981ba3cba80656012090337e548e597799e14b41e3d0b595ab5ab05a23d7f", size = 369620, upload-time = "2025-11-04T18:29:57.255Z" }, - { url = "https://files.pythonhosted.org/packages/a0/c1/cbcc38b7af4ce58d8893e56d3595c0c8dcd117093bf048f889cf351bdba0/ormsgpack-1.12.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:901f6f55184d6776dbd5183cbce14caf05bf7f467eef52faf9b094686980bf71", size = 195925, upload-time = "2025-11-04T18:29:58.34Z" }, - { url = "https://files.pythonhosted.org/packages/5c/59/4fa4dc0681490e12b75333440a1c0fd9741b0ebff272b1db4a29d35c2021/ormsgpack-1.12.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e13b15412571422b711b40f45e3fe6d993ea3314b5e97d1a853fe99226c5effc", size = 206594, upload-time = "2025-11-04T18:29:59.329Z" }, - { url = "https://files.pythonhosted.org/packages/39/67/249770896bc32bb91b22c30256961f935d0915cbcf6e289a7fc961d9b14c/ormsgpack-1.12.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91fa8a452553a62e5fb3fbab471e7faf7b3bec3c87a2f355ebf3d7aab290fe4f", size = 208307, upload-time = "2025-11-04T18:30:00.377Z" }, - { url = "https://files.pythonhosted.org/packages/07/0a/e041a248cd72f2f4c07e155913e0a3ede4c86cf21a40ae6cd79f135f2847/ormsgpack-1.12.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:74ec101f69624695eec4ce7c953192d97748254abe78fb01b591f06d529e1952", size = 377844, upload-time = "2025-11-04T18:30:01.389Z" }, - { url = "https://files.pythonhosted.org/packages/d8/71/6f7773e4ffda73a358ce4bba69b3e8bee9d40a7a06315e4c1cd7a3ea9d02/ormsgpack-1.12.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:9bbf7896580848326c1f9bd7531f264e561f98db7e08e15aa75963d83832c717", size = 471572, upload-time = "2025-11-04T18:30:02.486Z" }, - { url = "https://files.pythonhosted.org/packages/65/29/af6769a4289c07acc71e7bda1d64fb31800563147d73142686e185e82348/ormsgpack-1.12.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7567917da613b8f8d591c1674e411fd3404bea41ef2b9a0e0a1e049c0f9406d7", size = 381842, upload-time = "2025-11-04T18:30:03.799Z" }, - { url = "https://files.pythonhosted.org/packages/0b/dd/0a86195ee7a1a96c088aefc8504385e881cf56f4563ed81bafe21cbf1fb0/ormsgpack-1.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:4e418256c5d8622b8bc92861936f7c6a0131355e7bcad88a42102ae8227f8a1c", size = 113008, upload-time = "2025-11-04T18:30:04.777Z" }, - { url = "https://files.pythonhosted.org/packages/4c/57/fafc79e32f3087f6f26f509d80b8167516326bfea38d30502627c01617e0/ormsgpack-1.12.0-cp314-cp314-win_arm64.whl", hash = "sha256:433ace29aa02713554f714c62a4e4dcad0c9e32674ba4f66742c91a4c3b1b969", size = 106648, upload-time = "2025-11-04T18:30:05.708Z" }, - { url = "https://files.pythonhosted.org/packages/b3/cf/5d58d9b132128d2fe5d586355dde76af386554abef00d608f66b913bff1f/ormsgpack-1.12.0-cp314-cp314t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:e57164be4ca34b64e210ec515059193280ac84df4d6f31a6fcbfb2fc8436de55", size = 369803, upload-time = "2025-11-04T18:30:06.728Z" }, - { url = "https://files.pythonhosted.org/packages/67/42/968a2da361eaff2e4cbb17c82c7599787babf16684110ad70409646cc1e4/ormsgpack-1.12.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:904f96289deaa92fc6440b122edc27c5bdc28234edd63717f6d853d88c823a83", size = 195991, upload-time = "2025-11-04T18:30:07.713Z" }, - { url = "https://files.pythonhosted.org/packages/03/f0/9696c6c6cf8ad35170f0be8d0ef3523cc258083535f6c8071cb8235ebb8b/ormsgpack-1.12.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b291d086e524a1062d57d1b7b5a8bcaaf29caebf0212fec12fd86240bd33633", size = 208316, upload-time = "2025-11-04T18:30:08.663Z" }, +version = "1.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fe/96/34c40d621996c2f377a18decbd3c59f031dde73c3ba47d1e1e8f29a05aaa/ormsgpack-1.12.1.tar.gz", hash = "sha256:a3877fde1e4f27a39f92681a0aab6385af3a41d0c25375d33590ae20410ea2ac", size = 39476, upload-time = "2025-12-14T07:57:43.248Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b5/da/caf25cc54d6870089a0b5614c4c5914dd3fae45f9f7f84a32445ad0612e3/ormsgpack-1.12.1-cp310-cp310-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:62e3614cab63fa5aa42f5f0ca3cd12899f0bfc5eb8a5a0ebab09d571c89d427d", size = 376182, upload-time = "2025-12-14T07:56:46.094Z" }, + { url = "https://files.pythonhosted.org/packages/fc/02/ccc9170c6bee86f428707f15b5ad68d42c71d43856e1b8e37cdfea50af5b/ormsgpack-1.12.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:86d9fbf85c05c69c33c229d2eba7c8c3500a56596cd8348131c918acd040d6af", size = 202339, upload-time = "2025-12-14T07:56:47.609Z" }, + { url = "https://files.pythonhosted.org/packages/86/c7/10309a5a6421adaedab710a72470143d664bb0a043cc095c1311878325a0/ormsgpack-1.12.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8d246e66f09d8e0f96e770829149ee83206e90ed12f5987998bb7be84aec99fe", size = 210720, upload-time = "2025-12-14T07:56:48.66Z" }, + { url = "https://files.pythonhosted.org/packages/1b/b4/92a0f7a00c5f0c71b51dc3112e53b1ca937b9891a08979d06524db11b799/ormsgpack-1.12.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cfc2c830a1ed2d00de713d08c9e62efa699e8fd29beafa626aaebe466f583ebb", size = 211264, upload-time = "2025-12-14T07:56:49.976Z" }, + { url = "https://files.pythonhosted.org/packages/33/fa/5cce85c8e58fcaa048c75fbbe37816a1b3fb58ba4289a7dedc4f4ed9ce82/ormsgpack-1.12.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:bc892757d8f9eea5208268a527cf93c98409802f6a9f7c8d71a7b8f9ba5cb944", size = 386076, upload-time = "2025-12-14T07:56:51.74Z" }, + { url = "https://files.pythonhosted.org/packages/88/d0/f18d258c733eb22eadad748659f7984d0b6a851fb3deefcb33f50e9a947a/ormsgpack-1.12.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:0de1dbcf11ea739ac4a882b43d5c2055e6d99ce64e8d6502e25d6d881700c017", size = 479570, upload-time = "2025-12-14T07:56:52.912Z" }, + { url = "https://files.pythonhosted.org/packages/3f/3a/b362dff090f4740090fe51d512f24b1e320d1f96497ebf9248e2a04ac88f/ormsgpack-1.12.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d5065dfb9ec4db93241c60847624d9aeef4ccb449c26a018c216b55c69be83c0", size = 387859, upload-time = "2025-12-14T07:56:53.968Z" }, + { url = "https://files.pythonhosted.org/packages/7c/8a/d948965598b2b7872800076da5c02573aa72f716be57a3d4fe60490b2a2a/ormsgpack-1.12.1-cp310-cp310-win_amd64.whl", hash = "sha256:7d17103c4726181d7000c61b751c881f1b6f401d146df12da028fc730227df19", size = 115906, upload-time = "2025-12-14T07:56:55.068Z" }, + { url = "https://files.pythonhosted.org/packages/57/e2/f5b89365c8dc8025c27d31316038f1c103758ddbf87dc0fa8e3f78f66907/ormsgpack-1.12.1-cp311-cp311-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:4038f59ae0e19dac5e5d9aae4ec17ff84a79e046342ee73ccdecf3547ecf0d34", size = 376180, upload-time = "2025-12-14T07:56:56.521Z" }, + { url = "https://files.pythonhosted.org/packages/ca/87/3f694e06f5e32c6d65066f53b4a025282a5072b6b336c17560b00e04606d/ormsgpack-1.12.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16c63b0c5a3eec467e4bb33a14dabba076b7d934dff62898297b5c0b5f7c3cb3", size = 202338, upload-time = "2025-12-14T07:56:57.585Z" }, + { url = "https://files.pythonhosted.org/packages/e5/f5/6d95d7b7c11f97a92522082fc7e5d1ab34537929f1e13f4c369f392f19d0/ormsgpack-1.12.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:74fd6a8e037eb310dda865298e8d122540af00fe5658ec18b97a1d34f4012e4d", size = 210720, upload-time = "2025-12-14T07:56:58.968Z" }, + { url = "https://files.pythonhosted.org/packages/2b/9d/9a49a2686f8b7165dcb2342b8554951263c30c0f0825f1fcc2d56e736a6b/ormsgpack-1.12.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58ad60308e233dd824a1859eabb5fe092e123e885eafa4ad5789322329c80fb5", size = 211264, upload-time = "2025-12-14T07:57:00.099Z" }, + { url = "https://files.pythonhosted.org/packages/02/31/2fdc36eaeca2182900b96fc7b19755f293283fe681750e3d295733d62f0e/ormsgpack-1.12.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:35127464c941c1219acbe1a220e48d55e7933373d12257202f4042f7044b4c90", size = 386081, upload-time = "2025-12-14T07:57:01.177Z" }, + { url = "https://files.pythonhosted.org/packages/f0/65/0a765432f08ae26b4013c6a9aed97be17a9ef85f1600948a474b518e27dd/ormsgpack-1.12.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:c48d1c50794692d1e6e3f8c3bb65f5c3acfaae9347e506484a65d60b3d91fb50", size = 479572, upload-time = "2025-12-14T07:57:02.738Z" }, + { url = "https://files.pythonhosted.org/packages/4e/4f/f2f15ebef786ad71cea420bf8692448fbddf04d1bf3feaa68bd5ee3172e6/ormsgpack-1.12.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b512b2ad6feaaefdc26e05431ed2843e42483041e354e167c53401afaa83d919", size = 387862, upload-time = "2025-12-14T07:57:03.842Z" }, + { url = "https://files.pythonhosted.org/packages/15/eb/86fbef1d605fa91ecef077f93f9d0e34fc39b23475dfe3ffb92f6c8db28d/ormsgpack-1.12.1-cp311-cp311-win_amd64.whl", hash = "sha256:93f30db95e101a9616323bfc50807ad00e7f6197cea2216d2d24af42afc77d88", size = 115900, upload-time = "2025-12-14T07:57:05.137Z" }, + { url = "https://files.pythonhosted.org/packages/5b/67/7ba1a46e6a6e263fc42a4fafc24afc1ab21a66116553cad670426f0bd9ef/ormsgpack-1.12.1-cp311-cp311-win_arm64.whl", hash = "sha256:d75b5fa14f6abffce2c392ee03b4731199d8a964c81ee8645c4c79af0e80fd50", size = 109868, upload-time = "2025-12-14T07:57:06.834Z" }, + { url = "https://files.pythonhosted.org/packages/17/fe/ab9167ca037406b5703add24049cf3e18021a3b16133ea20615b1f160ea4/ormsgpack-1.12.1-cp312-cp312-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:4d7fb0e1b6fbc701d75269f7405a4f79230a6ce0063fb1092e4f6577e312f86d", size = 376725, upload-time = "2025-12-14T07:57:07.894Z" }, + { url = "https://files.pythonhosted.org/packages/c7/ea/2820e65f506894c459b840d1091ae6e327fde3d5a3f3b002a11a1b9bdf7d/ormsgpack-1.12.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:43a9353e2db5b024c91a47d864ef15eaa62d81824cfc7740fed4cef7db738694", size = 202466, upload-time = "2025-12-14T07:57:09.049Z" }, + { url = "https://files.pythonhosted.org/packages/45/8b/def01c13339c5bbec2ee1469ef53e7fadd66c8d775df974ee4def1572515/ormsgpack-1.12.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fc8fe866b7706fc25af0adf1f600bc06ece5b15ca44e34641327198b821e5c3c", size = 210748, upload-time = "2025-12-14T07:57:10.074Z" }, + { url = "https://files.pythonhosted.org/packages/5d/d2/bf350c92f7f067dd9484499705f2d8366d8d9008a670e3d1d0add1908f85/ormsgpack-1.12.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:813755b5f598a78242042e05dfd1ada4e769e94b98c9ab82554550f97ff4d641", size = 211510, upload-time = "2025-12-14T07:57:11.165Z" }, + { url = "https://files.pythonhosted.org/packages/74/92/9d689bcb95304a6da26c4d59439c350940c25d1b35f146d402ccc6344c51/ormsgpack-1.12.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8eea2a13536fae45d78f93f2cc846c9765c7160c85f19cfefecc20873c137cdd", size = 386237, upload-time = "2025-12-14T07:57:12.306Z" }, + { url = "https://files.pythonhosted.org/packages/17/fe/bd3107547f8b6129265dd957f40b9cd547d2445db2292aacb13335a7ea89/ormsgpack-1.12.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:7a02ebda1a863cbc604740e76faca8eee1add322db2dcbe6cf32669fffdff65c", size = 479589, upload-time = "2025-12-14T07:57:13.475Z" }, + { url = "https://files.pythonhosted.org/packages/c1/7c/e8e5cc9edb967d44f6f85e9ebdad440b59af3fae00b137a4327dc5aed9bb/ormsgpack-1.12.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3c0bd63897c439931cdf29348e5e6e8c330d529830e848d10767615c0f3d1b82", size = 388077, upload-time = "2025-12-14T07:57:14.551Z" }, + { url = "https://files.pythonhosted.org/packages/35/6b/5031797e43b58506f28a8760b26dc23f2620fb4f2200c4c1b3045603e67e/ormsgpack-1.12.1-cp312-cp312-win_amd64.whl", hash = "sha256:362f2e812f8d7035dc25a009171e09d7cc97cb30d3c9e75a16aeae00ca3c1dcf", size = 116190, upload-time = "2025-12-14T07:57:15.575Z" }, + { url = "https://files.pythonhosted.org/packages/1e/fd/9f43ea6425e383a6b2dbfafebb06fd60e8d68c700ef715adfbcdb499f75d/ormsgpack-1.12.1-cp312-cp312-win_arm64.whl", hash = "sha256:6190281e381db2ed0045052208f47a995ccf61eed48f1215ae3cce3fbccd59c5", size = 109990, upload-time = "2025-12-14T07:57:16.419Z" }, + { url = "https://files.pythonhosted.org/packages/11/42/f110dfe7cf23a52a82e23eb23d9a6a76ae495447d474686dfa758f3d71d6/ormsgpack-1.12.1-cp313-cp313-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:9663d6b3ecc917c063d61a99169ce196a80f3852e541ae404206836749459279", size = 376746, upload-time = "2025-12-14T07:57:17.699Z" }, + { url = "https://files.pythonhosted.org/packages/11/76/b386e508a8ae207daec240201a81adb26467bf99b163560724e86bd9ff33/ormsgpack-1.12.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32e85cfbaf01a94a92520e7fe7851cfcfe21a5698299c28ab86194895f9b9233", size = 202489, upload-time = "2025-12-14T07:57:18.807Z" }, + { url = "https://files.pythonhosted.org/packages/ea/0e/5db7a63f387149024572daa3d9512fe8fb14bf4efa0722d6d491bed280e7/ormsgpack-1.12.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:dabfd2c24b59c7c69870a5ecee480dfae914a42a0c2e7c9d971cf531e2ba471a", size = 210757, upload-time = "2025-12-14T07:57:19.893Z" }, + { url = "https://files.pythonhosted.org/packages/64/79/3a9899e57cb57430bd766fc1b4c9ad410cb2ba6070bc8cf6301e7d385768/ormsgpack-1.12.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51bbf2b64afeded34ccd8e25402e4bca038757913931fa0d693078d75563f6f9", size = 211518, upload-time = "2025-12-14T07:57:20.972Z" }, + { url = "https://files.pythonhosted.org/packages/d7/cd/4f41710ae9fe50d7fcbe476793b3c487746d0e1cc194cc0fee42ff6d989b/ormsgpack-1.12.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9959a71dde1bd0ced84af17facc06a8afada495a34e9cb1bad8e9b20d4c59cef", size = 386251, upload-time = "2025-12-14T07:57:22.099Z" }, + { url = "https://files.pythonhosted.org/packages/bf/54/ba0c97d6231b1f01daafaa520c8cce1e1b7fceaae6fdc1c763925874a7de/ormsgpack-1.12.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:e9be0e3b62d758f21f5b20e0e06b3a240ec546c4a327bf771f5825462aa74714", size = 479607, upload-time = "2025-12-14T07:57:23.525Z" }, + { url = "https://files.pythonhosted.org/packages/18/75/19a9a97a462776d525baf41cfb7072734528775f0a3d5fbfab3aa7756b9b/ormsgpack-1.12.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a29d49ab7fdd77ea787818e60cb4ef491708105b9c4c9b0f919201625eb036b5", size = 388062, upload-time = "2025-12-14T07:57:24.616Z" }, + { url = "https://files.pythonhosted.org/packages/a8/6a/ec26e3f44e9632ecd2f43638b7b37b500eaea5d79cab984ad0b94be14f82/ormsgpack-1.12.1-cp313-cp313-win_amd64.whl", hash = "sha256:c418390b47a1d367e803f6c187f77e4d67c7ae07ba962e3a4a019001f4b0291a", size = 116195, upload-time = "2025-12-14T07:57:25.626Z" }, + { url = "https://files.pythonhosted.org/packages/7d/64/bfa5f4a34d0f15c6aba1b73e73f7441a66d635bd03249d334a4796b7a924/ormsgpack-1.12.1-cp313-cp313-win_arm64.whl", hash = "sha256:cfa22c91cffc10a7fbd43729baff2de7d9c28cef2509085a704168ae31f02568", size = 109986, upload-time = "2025-12-14T07:57:26.569Z" }, + { url = "https://files.pythonhosted.org/packages/87/0e/78e5697164e3223b9b216c13e99f1acbc1ee9833490d68842b13da8ba883/ormsgpack-1.12.1-cp314-cp314-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:b93c91efb1a70751a1902a5b43b27bd8fd38e0ca0365cf2cde2716423c15c3a6", size = 376758, upload-time = "2025-12-14T07:57:27.641Z" }, + { url = "https://files.pythonhosted.org/packages/2c/0e/3a3cbb64703263d7bbaed7effa3ce78cb9add360a60aa7c544d7df28b641/ormsgpack-1.12.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cf0ea0389167b5fa8d2933dd3f33e887ec4ba68f89c25214d7eec4afd746d22", size = 202487, upload-time = "2025-12-14T07:57:29.051Z" }, + { url = "https://files.pythonhosted.org/packages/d7/2c/807ebe2b77995599bbb1dec8c3f450d5d7dddee14ce3e1e71dc60e2e2a74/ormsgpack-1.12.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f4c29af837f35af3375070689e781161e7cf019eb2f7cd641734ae45cd001c0d", size = 210853, upload-time = "2025-12-14T07:57:30.508Z" }, + { url = "https://files.pythonhosted.org/packages/25/57/2cdfc354e3ad8e847628f511f4d238799d90e9e090941e50b9d5ba955ae2/ormsgpack-1.12.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:336fc65aa0fe65896a3dabaae31e332a0a98b4a00ad7b0afde21a7505fd23ff3", size = 211545, upload-time = "2025-12-14T07:57:31.585Z" }, + { url = "https://files.pythonhosted.org/packages/76/1d/c6fda560e4a8ff865b3aec8a86f7c95ab53f4532193a6ae4ab9db35f85aa/ormsgpack-1.12.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:940f60aabfefe71dd6b82cb33f4ff10b2e7f5fcfa5f103cdb0a23b6aae4c713c", size = 386333, upload-time = "2025-12-14T07:57:32.957Z" }, + { url = "https://files.pythonhosted.org/packages/fc/3e/715081b36fceb8b497c68b87d384e1cc6d9c9c130ce3b435634d3d785b86/ormsgpack-1.12.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:596ad9e1b6d4c95595c54aaf49b1392609ca68f562ce06f4f74a5bc4053bcda4", size = 479701, upload-time = "2025-12-14T07:57:34.686Z" }, + { url = "https://files.pythonhosted.org/packages/6d/cf/01ad04def42b3970fc1a302c07f4b46339edf62ef9650247097260471f40/ormsgpack-1.12.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:575210e8fcbc7b0375026ba040a5eef223e9f66a4453d9623fc23282ae09c3c8", size = 388148, upload-time = "2025-12-14T07:57:35.771Z" }, + { url = "https://files.pythonhosted.org/packages/15/91/1fff2fc2b5943c740028f339154e7103c8f2edf1a881d9fbba2ce11c3b1d/ormsgpack-1.12.1-cp314-cp314-win_amd64.whl", hash = "sha256:647daa3718572280893456be44c60aea6690b7f2edc54c55648ee66e8f06550f", size = 116201, upload-time = "2025-12-14T07:57:36.763Z" }, + { url = "https://files.pythonhosted.org/packages/ed/66/142b542aed3f96002c7d1c33507ca6e1e0d0a42b9253ab27ef7ed5793bd9/ormsgpack-1.12.1-cp314-cp314-win_arm64.whl", hash = "sha256:a8b3ab762a6deaf1b6490ab46dda0c51528cf8037e0246c40875c6fe9e37b699", size = 110029, upload-time = "2025-12-14T07:57:37.703Z" }, + { url = "https://files.pythonhosted.org/packages/38/b3/ef4494438c90359e1547eaed3c5ec46e2c431d59a3de2af4e70ebd594c49/ormsgpack-1.12.1-cp314-cp314t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:12087214e436c1f6c28491949571abea759a63111908c4f7266586d78144d7a8", size = 376777, upload-time = "2025-12-14T07:57:38.795Z" }, + { url = "https://files.pythonhosted.org/packages/05/a0/1149a7163f8b0dfbc64bf9099b6f16d102ad3b03bcc11afee198d751da2d/ormsgpack-1.12.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e6d54c14cf86ef13f10ccade94d1e7de146aa9b17d371e18b16e95f329393b7", size = 202490, upload-time = "2025-12-14T07:57:40.168Z" }, + { url = "https://files.pythonhosted.org/packages/68/82/f2ec5e758d6a7106645cca9bb7137d98bce5d363789fa94075be6572057c/ormsgpack-1.12.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f3584d07882b7ea2a1a589f795a3af97fe4c2932b739408e6d1d9d286cad862", size = 211733, upload-time = "2025-12-14T07:57:42.253Z" }, ] [[package]] @@ -4906,16 +5021,16 @@ wheels = [ [[package]] name = "pandas-stubs" -version = "2.3.2.250926" +version = "2.3.3.251219" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "types-pytz" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/1b/3b/32be58a125db39d0b5f62cc93795f32b5bb2915bd5c4a46f0e35171985e2/pandas_stubs-2.3.2.250926.tar.gz", hash = "sha256:c64b9932760ceefb96a3222b953e6a251321a9832a28548be6506df473a66406", size = 102147, upload-time = "2025-09-26T19:50:39.522Z" } +sdist = { url = "https://files.pythonhosted.org/packages/95/ee/5407e9e63d22a47774f9246ca80b24f82c36f26efd39f9e3c5b584b915aa/pandas_stubs-2.3.3.251219.tar.gz", hash = "sha256:dc2883e6daff49d380d1b5a2e864983ab9be8cd9a661fa861e3dea37559a5af4", size = 106899, upload-time = "2025-12-19T15:49:53.766Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/40/96/1e4a035eaf4dce9610aac6e43026d0c6baa05773daf6d21e635a4fe19e21/pandas_stubs-2.3.2.250926-py3-none-any.whl", hash = "sha256:81121818453dcfe00f45c852f4dceee043640b813830f6e7bd084a4ef7ff7270", size = 159995, upload-time = "2025-09-26T19:50:38.241Z" }, + { url = "https://files.pythonhosted.org/packages/64/20/69f2a39792a653fd64d916cd563ed79ec6e5dcfa6408c4674021d810afcf/pandas_stubs-2.3.3.251219-py3-none-any.whl", hash = "sha256:ccc6337febb51d6d8a08e4c96b479478a0da0ef704b5e08bd212423fe1cb549c", size = 163667, upload-time = "2025-12-19T15:49:52.072Z" }, ] [[package]] @@ -4938,11 +5053,11 @@ wheels = [ [[package]] name = "pathspec" -version = "0.12.1" +version = "1.0.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043, upload-time = "2023-12-10T22:30:45Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4c/b2/bb8e495d5262bfec41ab5cb18f522f1012933347fb5d9e62452d446baca2/pathspec-1.0.3.tar.gz", hash = "sha256:bac5cf97ae2c2876e2d25ebb15078eb04d76e4b98921ee31c6f85ade8b59444d", size = 130841, upload-time = "2026-01-09T15:46:46.009Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload-time = "2023-12-10T22:30:43.14Z" }, + { url = "https://files.pythonhosted.org/packages/32/2b/121e912bd60eebd623f873fd090de0e84f322972ab25a7f9044c056804ed/pathspec-1.0.3-py3-none-any.whl", hash = "sha256:e80767021c1cc524aa3fb14bedda9c34406591343cc42797b386ce7b9354fb6c", size = 55021, upload-time = "2026-01-09T15:46:44.652Z" }, ] [[package]] @@ -5061,24 +5176,24 @@ wheels = [ [[package]] name = "platformdirs" -version = "4.5.0" +version = "4.5.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/61/33/9611380c2bdb1225fdef633e2a9610622310fed35ab11dac9620972ee088/platformdirs-4.5.0.tar.gz", hash = "sha256:70ddccdd7c99fc5942e9fc25636a8b34d04c24b335100223152c2803e4063312", size = 21632, upload-time = "2025-10-08T17:44:48.791Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cf/86/0248f086a84f01b37aaec0fa567b397df1a119f73c16f6c7a9aac73ea309/platformdirs-4.5.1.tar.gz", hash = "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda", size = 21715, upload-time = "2025-12-05T13:52:58.638Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/73/cb/ac7874b3e5d58441674fb70742e6c374b28b0c7cb988d37d991cde47166c/platformdirs-4.5.0-py3-none-any.whl", hash = "sha256:e578a81bb873cbb89a41fcc904c7ef523cc18284b7e3b3ccf06aca1403b7ebd3", size = 18651, upload-time = "2025-10-08T17:44:47.223Z" }, + { url = "https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31", size = 18731, upload-time = "2025-12-05T13:52:56.823Z" }, ] [[package]] name = "plotly" -version = "6.5.0" +version = "6.5.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "narwhals" }, { name = "packaging" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/94/05/1199e2a03ce6637960bc1e951ca0f928209a48cfceb57355806a88f214cf/plotly-6.5.0.tar.gz", hash = "sha256:d5d38224883fd38c1409bef7d6a8dc32b74348d39313f3c52ca998b8e447f5c8", size = 7013624, upload-time = "2025-11-17T18:39:24.523Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/ff/a4938b75e95114451efdb34db6b41930253e67efc8dc737bd592ef2e419d/plotly-6.5.1.tar.gz", hash = "sha256:b0478c8d5ada0c8756bce15315bcbfec7d3ab8d24614e34af9aff7bfcfea9281", size = 7014606, upload-time = "2026-01-07T20:11:41.644Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/c3/3031c931098de393393e1f93a38dc9ed6805d86bb801acc3cf2d5bd1e6b7/plotly-6.5.0-py3-none-any.whl", hash = "sha256:5ac851e100367735250206788a2b1325412aa4a4917a4fe3e6f0bc5aa6f3d90a", size = 9893174, upload-time = "2025-11-17T18:39:20.351Z" }, + { url = "https://files.pythonhosted.org/packages/e9/8e/24e0bb90b2d75af84820693260c5534e9ed351afdda67ed6f393a141a0e2/plotly-6.5.1-py3-none-any.whl", hash = "sha256:5adad4f58c360612b6c5ce11a308cdbc4fd38ceb1d40594a614f0062e227abe1", size = 9894981, upload-time = "2026-01-07T20:11:38.124Z" }, ] [[package]] @@ -5106,15 +5221,15 @@ wheels = [ [[package]] name = "polyfactory" -version = "3.0.0" +version = "3.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "faker" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/45/c6/2f795ede27e82bf6ed10a6b689f975017e36bf497915bfeafc3932b1a613/polyfactory-3.0.0.tar.gz", hash = "sha256:c1be54fc10fe738415eef2248bcfc687e3d74634f5e73b70e4ba58952df31363", size = 304234, upload-time = "2025-11-15T11:23:02.003Z" } +sdist = { url = "https://files.pythonhosted.org/packages/97/92/e90639b1d2abe982749eba7e734571a343ea062f7d486498b1c2b852f019/polyfactory-3.2.0.tar.gz", hash = "sha256:879242f55208f023eee1de48522de5cb1f9fd2d09b2314e999a9592829d596d1", size = 346878, upload-time = "2025-12-21T11:18:51.017Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/54/86/742c4eb237cdb5e7e6dfb1474f5f0dac967896185f832c012dff4d7bdce2/polyfactory-3.0.0-py3-none-any.whl", hash = "sha256:ad9ba3b3f04c292d30673587946e1c5d18e249074368b09abb7c9455288cfc37", size = 61237, upload-time = "2025-11-15T11:23:00.524Z" }, + { url = "https://files.pythonhosted.org/packages/d9/21/93363d7b802aa904f8d4169bc33e0e316d06d26ee68d40fe0355057da98c/polyfactory-3.2.0-py3-none-any.whl", hash = "sha256:5945799cce4c56cd44ccad96fb0352996914553cc3efaa5a286930599f569571", size = 62181, upload-time = "2025-12-21T11:18:49.311Z" }, ] [[package]] @@ -5135,7 +5250,7 @@ wheels = [ [[package]] name = "pre-commit" -version = "4.5.0" +version = "4.5.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cfgv" }, @@ -5144,9 +5259,9 @@ dependencies = [ { name = "pyyaml" }, { name = "virtualenv" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f4/9b/6a4ffb4ed980519da959e1cf3122fc6cb41211daa58dbae1c73c0e519a37/pre_commit-4.5.0.tar.gz", hash = "sha256:dc5a065e932b19fc1d4c653c6939068fe54325af8e741e74e88db4d28a4dd66b", size = 198428, upload-time = "2025-11-22T21:02:42.304Z" } +sdist = { url = "https://files.pythonhosted.org/packages/40/f1/6d86a29246dfd2e9b6237f0b5823717f60cad94d47ddc26afa916d21f525/pre_commit-4.5.1.tar.gz", hash = "sha256:eb545fcff725875197837263e977ea257a402056661f09dae08e4b149b030a61", size = 198232, upload-time = "2025-12-16T21:14:33.552Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5d/c4/b2d28e9d2edf4f1713eb3c29307f1a63f3d67cf09bdda29715a36a68921a/pre_commit-4.5.0-py2.py3-none-any.whl", hash = "sha256:25e2ce09595174d9c97860a95609f9f852c0614ba602de3561e267547f2335e1", size = 226429, upload-time = "2025-11-22T21:02:40.836Z" }, + { url = "https://files.pythonhosted.org/packages/5d/19/fd3ef348460c80af7bb4669ea7926651d1f95c23ff2df18b9d24bab4f3fa/pre_commit-4.5.1-py2.py3-none-any.whl", hash = "sha256:3b3afd891e97337708c1674210f8eba659b52a38ea5f822ff142d10786221f77", size = 226437, upload-time = "2025-12-16T21:14:32.409Z" }, ] [[package]] @@ -5277,43 +5392,45 @@ wheels = [ [[package]] name = "protobuf" -version = "6.33.1" +version = "6.33.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0a/03/a1440979a3f74f16cab3b75b0da1a1a7f922d56a8ddea96092391998edc0/protobuf-6.33.1.tar.gz", hash = "sha256:97f65757e8d09870de6fd973aeddb92f85435607235d20b2dfed93405d00c85b", size = 443432, upload-time = "2025-11-13T16:44:18.895Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cc/5c/f912bdebdd4af4160da6a2c2b1b3aaa1b8c578d0243ba8f694f93c7095f0/protobuf-6.33.3.tar.gz", hash = "sha256:c8794debeb402963fddff41a595e1f649bcd76616ba56c835645cab4539e810e", size = 444318, upload-time = "2026-01-09T23:05:02.79Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/06/f1/446a9bbd2c60772ca36556bac8bfde40eceb28d9cc7838755bc41e001d8f/protobuf-6.33.1-cp310-abi3-win32.whl", hash = "sha256:f8d3fdbc966aaab1d05046d0240dd94d40f2a8c62856d41eaa141ff64a79de6b", size = 425593, upload-time = "2025-11-13T16:44:06.275Z" }, - { url = "https://files.pythonhosted.org/packages/a6/79/8780a378c650e3df849b73de8b13cf5412f521ca2ff9b78a45c247029440/protobuf-6.33.1-cp310-abi3-win_amd64.whl", hash = "sha256:923aa6d27a92bf44394f6abf7ea0500f38769d4b07f4be41cb52bd8b1123b9ed", size = 436883, upload-time = "2025-11-13T16:44:09.222Z" }, - { url = "https://files.pythonhosted.org/packages/cd/93/26213ff72b103ae55bb0d73e7fb91ea570ef407c3ab4fd2f1f27cac16044/protobuf-6.33.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:fe34575f2bdde76ac429ec7b570235bf0c788883e70aee90068e9981806f2490", size = 427522, upload-time = "2025-11-13T16:44:10.475Z" }, - { url = "https://files.pythonhosted.org/packages/c2/32/df4a35247923393aa6b887c3b3244a8c941c32a25681775f96e2b418f90e/protobuf-6.33.1-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:f8adba2e44cde2d7618996b3fc02341f03f5bc3f2748be72dc7b063319276178", size = 324445, upload-time = "2025-11-13T16:44:11.869Z" }, - { url = "https://files.pythonhosted.org/packages/8e/d0/d796e419e2ec93d2f3fa44888861c3f88f722cde02b7c3488fcc6a166820/protobuf-6.33.1-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:0f4cf01222c0d959c2b399142deb526de420be8236f22c71356e2a544e153c53", size = 339161, upload-time = "2025-11-13T16:44:12.778Z" }, - { url = "https://files.pythonhosted.org/packages/1d/2a/3c5f05a4af06649547027d288747f68525755de692a26a7720dced3652c0/protobuf-6.33.1-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:8fd7d5e0eb08cd5b87fd3df49bc193f5cfd778701f47e11d127d0afc6c39f1d1", size = 323171, upload-time = "2025-11-13T16:44:14.035Z" }, - { url = "https://files.pythonhosted.org/packages/08/b4/46310463b4f6ceef310f8348786f3cff181cea671578e3d9743ba61a459e/protobuf-6.33.1-py3-none-any.whl", hash = "sha256:d595a9fd694fdeb061a62fbe10eb039cc1e444df81ec9bb70c7fc59ebcb1eafa", size = 170477, upload-time = "2025-11-13T16:44:17.633Z" }, + { url = "https://files.pythonhosted.org/packages/2a/56/2a41b9dcc3b92fa672bb89610608f4fd4f71bec075d314956710503b29f5/protobuf-6.33.3-cp310-abi3-win32.whl", hash = "sha256:b4046f9f2ede57ad5b1d9917baafcbcad42f8151a73c755a1e2ec9557b0a764f", size = 425597, upload-time = "2026-01-09T23:04:50.11Z" }, + { url = "https://files.pythonhosted.org/packages/23/07/1f1300fe7d204fd7aaabd9a0aafd54e6358de833b783f5bd161614e8e1e4/protobuf-6.33.3-cp310-abi3-win_amd64.whl", hash = "sha256:1fd18f030ae9df97712fbbb0849b6e54c63e3edd9b88d8c3bb4771f84d8db7a4", size = 436945, upload-time = "2026-01-09T23:04:51.921Z" }, + { url = "https://files.pythonhosted.org/packages/ec/5d/0ef28dded98973a26443a6a7bc49bff6206be8c57dc1d1e28e6c1147b879/protobuf-6.33.3-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:648b7b0144222eb06cf529a3d7b01333c5f30b4196773b682d388f04db373759", size = 427594, upload-time = "2026-01-09T23:04:53.358Z" }, + { url = "https://files.pythonhosted.org/packages/c5/46/551c69b6ff1957bd703654342bfb776bb97db400bc80afc56fbb64e7c11d/protobuf-6.33.3-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:08a6ca12f60ba99097dd3625ef4275280f99c9037990e47ce9368826b159b890", size = 324469, upload-time = "2026-01-09T23:04:54.332Z" }, + { url = "https://files.pythonhosted.org/packages/ca/6d/ade1cca06c64a421ee9745e082671465ead28164c809efaf2c15bc93f9a0/protobuf-6.33.3-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:642fce7187526c98683c79a3ad68e5d646a5ef5eb004582fe123fc9a33a9456b", size = 339242, upload-time = "2026-01-09T23:04:55.347Z" }, + { url = "https://files.pythonhosted.org/packages/38/8c/6522b8e543ece46f645911c3cebe361d8460134c0fee02ddcf70ebf32999/protobuf-6.33.3-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:6fa9b5f4baa12257542273e5e6f3c3d3867b30bc2770c14ad9ac8315264bf986", size = 323298, upload-time = "2026-01-09T23:04:56.866Z" }, + { url = "https://files.pythonhosted.org/packages/a6/b9/067b8a843569d5605ba6f7c039b9319720a974f82216cd623e13186d3078/protobuf-6.33.3-py3-none-any.whl", hash = "sha256:c2bf221076b0d463551efa2e1319f08d4cffcc5f0d864614ccd3d0e77a637794", size = 170518, upload-time = "2026-01-09T23:05:01.227Z" }, ] [[package]] name = "psutil" -version = "7.1.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e1/88/bdd0a41e5857d5d703287598cbf08dad90aed56774ea52ae071bae9071b6/psutil-7.1.3.tar.gz", hash = "sha256:6c86281738d77335af7aec228328e944b30930899ea760ecf33a4dba66be5e74", size = 489059, upload-time = "2025-11-02T12:25:54.619Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bd/93/0c49e776b8734fef56ec9c5c57f923922f2cf0497d62e0f419465f28f3d0/psutil-7.1.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0005da714eee687b4b8decd3d6cc7c6db36215c9e74e5ad2264b90c3df7d92dc", size = 239751, upload-time = "2025-11-02T12:25:58.161Z" }, - { url = "https://files.pythonhosted.org/packages/6f/8d/b31e39c769e70780f007969815195a55c81a63efebdd4dbe9e7a113adb2f/psutil-7.1.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:19644c85dcb987e35eeeaefdc3915d059dac7bd1167cdcdbf27e0ce2df0c08c0", size = 240368, upload-time = "2025-11-02T12:26:00.491Z" }, - { url = "https://files.pythonhosted.org/packages/62/61/23fd4acc3c9eebbf6b6c78bcd89e5d020cfde4acf0a9233e9d4e3fa698b4/psutil-7.1.3-cp313-cp313t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:95ef04cf2e5ba0ab9eaafc4a11eaae91b44f4ef5541acd2ee91d9108d00d59a7", size = 287134, upload-time = "2025-11-02T12:26:02.613Z" }, - { url = "https://files.pythonhosted.org/packages/30/1c/f921a009ea9ceb51aa355cb0cc118f68d354db36eae18174bab63affb3e6/psutil-7.1.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1068c303be3a72f8e18e412c5b2a8f6d31750fb152f9cb106b54090296c9d251", size = 289904, upload-time = "2025-11-02T12:26:05.207Z" }, - { url = "https://files.pythonhosted.org/packages/a6/82/62d68066e13e46a5116df187d319d1724b3f437ddd0f958756fc052677f4/psutil-7.1.3-cp313-cp313t-win_amd64.whl", hash = "sha256:18349c5c24b06ac5612c0428ec2a0331c26443d259e2a0144a9b24b4395b58fa", size = 249642, upload-time = "2025-11-02T12:26:07.447Z" }, - { url = "https://files.pythonhosted.org/packages/df/ad/c1cd5fe965c14a0392112f68362cfceb5230819dbb5b1888950d18a11d9f/psutil-7.1.3-cp313-cp313t-win_arm64.whl", hash = "sha256:c525ffa774fe4496282fb0b1187725793de3e7c6b29e41562733cae9ada151ee", size = 245518, upload-time = "2025-11-02T12:26:09.719Z" }, - { url = "https://files.pythonhosted.org/packages/2e/bb/6670bded3e3236eb4287c7bcdc167e9fae6e1e9286e437f7111caed2f909/psutil-7.1.3-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:b403da1df4d6d43973dc004d19cee3b848e998ae3154cc8097d139b77156c353", size = 239843, upload-time = "2025-11-02T12:26:11.968Z" }, - { url = "https://files.pythonhosted.org/packages/b8/66/853d50e75a38c9a7370ddbeefabdd3d3116b9c31ef94dc92c6729bc36bec/psutil-7.1.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ad81425efc5e75da3f39b3e636293360ad8d0b49bed7df824c79764fb4ba9b8b", size = 240369, upload-time = "2025-11-02T12:26:14.358Z" }, - { url = "https://files.pythonhosted.org/packages/41/bd/313aba97cb5bfb26916dc29cf0646cbe4dd6a89ca69e8c6edce654876d39/psutil-7.1.3-cp314-cp314t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8f33a3702e167783a9213db10ad29650ebf383946e91bc77f28a5eb083496bc9", size = 288210, upload-time = "2025-11-02T12:26:16.699Z" }, - { url = "https://files.pythonhosted.org/packages/c2/fa/76e3c06e760927a0cfb5705eb38164254de34e9bd86db656d4dbaa228b04/psutil-7.1.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fac9cd332c67f4422504297889da5ab7e05fd11e3c4392140f7370f4208ded1f", size = 291182, upload-time = "2025-11-02T12:26:18.848Z" }, - { url = "https://files.pythonhosted.org/packages/0f/1d/5774a91607035ee5078b8fd747686ebec28a962f178712de100d00b78a32/psutil-7.1.3-cp314-cp314t-win_amd64.whl", hash = "sha256:3792983e23b69843aea49c8f5b8f115572c5ab64c153bada5270086a2123c7e7", size = 250466, upload-time = "2025-11-02T12:26:21.183Z" }, - { url = "https://files.pythonhosted.org/packages/00/ca/e426584bacb43a5cb1ac91fae1937f478cd8fbe5e4ff96574e698a2c77cd/psutil-7.1.3-cp314-cp314t-win_arm64.whl", hash = "sha256:31d77fcedb7529f27bb3a0472bea9334349f9a04160e8e6e5020f22c59893264", size = 245756, upload-time = "2025-11-02T12:26:23.148Z" }, - { url = "https://files.pythonhosted.org/packages/ef/94/46b9154a800253e7ecff5aaacdf8ebf43db99de4a2dfa18575b02548654e/psutil-7.1.3-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2bdbcd0e58ca14996a42adf3621a6244f1bb2e2e528886959c72cf1e326677ab", size = 238359, upload-time = "2025-11-02T12:26:25.284Z" }, - { url = "https://files.pythonhosted.org/packages/68/3a/9f93cff5c025029a36d9a92fef47220ab4692ee7f2be0fba9f92813d0cb8/psutil-7.1.3-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:bc31fa00f1fbc3c3802141eede66f3a2d51d89716a194bf2cd6fc68310a19880", size = 239171, upload-time = "2025-11-02T12:26:27.23Z" }, - { url = "https://files.pythonhosted.org/packages/ce/b1/5f49af514f76431ba4eea935b8ad3725cdeb397e9245ab919dbc1d1dc20f/psutil-7.1.3-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3bb428f9f05c1225a558f53e30ccbad9930b11c3fc206836242de1091d3e7dd3", size = 263261, upload-time = "2025-11-02T12:26:29.48Z" }, - { url = "https://files.pythonhosted.org/packages/e0/95/992c8816a74016eb095e73585d747e0a8ea21a061ed3689474fabb29a395/psutil-7.1.3-cp36-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:56d974e02ca2c8eb4812c3f76c30e28836fffc311d55d979f1465c1feeb2b68b", size = 264635, upload-time = "2025-11-02T12:26:31.74Z" }, - { url = "https://files.pythonhosted.org/packages/55/4c/c3ed1a622b6ae2fd3c945a366e64eb35247a31e4db16cf5095e269e8eb3c/psutil-7.1.3-cp37-abi3-win_amd64.whl", hash = "sha256:f39c2c19fe824b47484b96f9692932248a54c43799a84282cfe58d05a6449efd", size = 247633, upload-time = "2025-11-02T12:26:33.887Z" }, - { url = "https://files.pythonhosted.org/packages/c9/ad/33b2ccec09bf96c2b2ef3f9a6f66baac8253d7565d8839e024a6b905d45d/psutil-7.1.3-cp37-abi3-win_arm64.whl", hash = "sha256:bd0d69cee829226a761e92f28140bec9a5ee9d5b4fb4b0cc589068dbfff559b1", size = 244608, upload-time = "2025-11-02T12:26:36.136Z" }, +version = "7.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/73/cb/09e5184fb5fc0358d110fc3ca7f6b1d033800734d34cac10f4136cfac10e/psutil-7.2.1.tar.gz", hash = "sha256:f7583aec590485b43ca601dd9cea0dcd65bd7bb21d30ef4ddbf4ea6b5ed1bdd3", size = 490253, upload-time = "2025-12-29T08:26:00.169Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/77/8e/f0c242053a368c2aa89584ecd1b054a18683f13d6e5a318fc9ec36582c94/psutil-7.2.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:ba9f33bb525b14c3ea563b2fd521a84d2fa214ec59e3e6a2858f78d0844dd60d", size = 129624, upload-time = "2025-12-29T08:26:04.255Z" }, + { url = "https://files.pythonhosted.org/packages/26/97/a58a4968f8990617decee234258a2b4fc7cd9e35668387646c1963e69f26/psutil-7.2.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:81442dac7abfc2f4f4385ea9e12ddf5a796721c0f6133260687fec5c3780fa49", size = 130132, upload-time = "2025-12-29T08:26:06.228Z" }, + { url = "https://files.pythonhosted.org/packages/db/6d/ed44901e830739af5f72a85fa7ec5ff1edea7f81bfbf4875e409007149bd/psutil-7.2.1-cp313-cp313t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ea46c0d060491051d39f0d2cff4f98d5c72b288289f57a21556cc7d504db37fc", size = 180612, upload-time = "2025-12-29T08:26:08.276Z" }, + { url = "https://files.pythonhosted.org/packages/c7/65/b628f8459bca4efbfae50d4bf3feaab803de9a160b9d5f3bd9295a33f0c2/psutil-7.2.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:35630d5af80d5d0d49cfc4d64c1c13838baf6717a13effb35869a5919b854cdf", size = 183201, upload-time = "2025-12-29T08:26:10.622Z" }, + { url = "https://files.pythonhosted.org/packages/fb/23/851cadc9764edcc18f0effe7d0bf69f727d4cf2442deb4a9f78d4e4f30f2/psutil-7.2.1-cp313-cp313t-win_amd64.whl", hash = "sha256:923f8653416604e356073e6e0bccbe7c09990acef442def2f5640dd0faa9689f", size = 139081, upload-time = "2025-12-29T08:26:12.483Z" }, + { url = "https://files.pythonhosted.org/packages/59/82/d63e8494ec5758029f31c6cb06d7d161175d8281e91d011a4a441c8a43b5/psutil-7.2.1-cp313-cp313t-win_arm64.whl", hash = "sha256:cfbe6b40ca48019a51827f20d830887b3107a74a79b01ceb8cc8de4ccb17b672", size = 134767, upload-time = "2025-12-29T08:26:14.528Z" }, + { url = "https://files.pythonhosted.org/packages/05/c2/5fb764bd61e40e1fe756a44bd4c21827228394c17414ade348e28f83cd79/psutil-7.2.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:494c513ccc53225ae23eec7fe6e1482f1b8a44674241b54561f755a898650679", size = 129716, upload-time = "2025-12-29T08:26:16.017Z" }, + { url = "https://files.pythonhosted.org/packages/c9/d2/935039c20e06f615d9ca6ca0ab756cf8408a19d298ffaa08666bc18dc805/psutil-7.2.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3fce5f92c22b00cdefd1645aa58ab4877a01679e901555067b1bd77039aa589f", size = 130133, upload-time = "2025-12-29T08:26:18.009Z" }, + { url = "https://files.pythonhosted.org/packages/77/69/19f1eb0e01d24c2b3eacbc2f78d3b5add8a89bf0bb69465bc8d563cc33de/psutil-7.2.1-cp314-cp314t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:93f3f7b0bb07711b49626e7940d6fe52aa9940ad86e8f7e74842e73189712129", size = 181518, upload-time = "2025-12-29T08:26:20.241Z" }, + { url = "https://files.pythonhosted.org/packages/e1/6d/7e18b1b4fa13ad370787626c95887b027656ad4829c156bb6569d02f3262/psutil-7.2.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d34d2ca888208eea2b5c68186841336a7f5e0b990edec929be909353a202768a", size = 184348, upload-time = "2025-12-29T08:26:22.215Z" }, + { url = "https://files.pythonhosted.org/packages/98/60/1672114392dd879586d60dd97896325df47d9a130ac7401318005aab28ec/psutil-7.2.1-cp314-cp314t-win_amd64.whl", hash = "sha256:2ceae842a78d1603753561132d5ad1b2f8a7979cb0c283f5b52fb4e6e14b1a79", size = 140400, upload-time = "2025-12-29T08:26:23.993Z" }, + { url = "https://files.pythonhosted.org/packages/fb/7b/d0e9d4513c46e46897b46bcfc410d51fc65735837ea57a25170f298326e6/psutil-7.2.1-cp314-cp314t-win_arm64.whl", hash = "sha256:08a2f175e48a898c8eb8eace45ce01777f4785bc744c90aa2cc7f2fa5462a266", size = 135430, upload-time = "2025-12-29T08:26:25.999Z" }, + { url = "https://files.pythonhosted.org/packages/c5/cf/5180eb8c8bdf6a503c6919f1da28328bd1e6b3b1b5b9d5b01ae64f019616/psutil-7.2.1-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:b2e953fcfaedcfbc952b44744f22d16575d3aa78eb4f51ae74165b4e96e55f42", size = 128137, upload-time = "2025-12-29T08:26:27.759Z" }, + { url = "https://files.pythonhosted.org/packages/c5/2c/78e4a789306a92ade5000da4f5de3255202c534acdadc3aac7b5458fadef/psutil-7.2.1-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:05cc68dbb8c174828624062e73078e7e35406f4ca2d0866c272c2410d8ef06d1", size = 128947, upload-time = "2025-12-29T08:26:29.548Z" }, + { url = "https://files.pythonhosted.org/packages/29/f8/40e01c350ad9a2b3cb4e6adbcc8a83b17ee50dd5792102b6142385937db5/psutil-7.2.1-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5e38404ca2bb30ed7267a46c02f06ff842e92da3bb8c5bfdadbd35a5722314d8", size = 154694, upload-time = "2025-12-29T08:26:32.147Z" }, + { url = "https://files.pythonhosted.org/packages/06/e4/b751cdf839c011a9714a783f120e6a86b7494eb70044d7d81a25a5cd295f/psutil-7.2.1-cp36-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ab2b98c9fc19f13f59628d94df5cc4cc4844bc572467d113a8b517d634e362c6", size = 156136, upload-time = "2025-12-29T08:26:34.079Z" }, + { url = "https://files.pythonhosted.org/packages/44/ad/bbf6595a8134ee1e94a4487af3f132cef7fce43aef4a93b49912a48c3af7/psutil-7.2.1-cp36-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:f78baafb38436d5a128f837fab2d92c276dfb48af01a240b861ae02b2413ada8", size = 148108, upload-time = "2025-12-29T08:26:36.225Z" }, + { url = "https://files.pythonhosted.org/packages/1c/15/dd6fd869753ce82ff64dcbc18356093471a5a5adf4f77ed1f805d473d859/psutil-7.2.1-cp36-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:99a4cd17a5fdd1f3d014396502daa70b5ec21bf4ffe38393e152f8e449757d67", size = 147402, upload-time = "2025-12-29T08:26:39.21Z" }, + { url = "https://files.pythonhosted.org/packages/34/68/d9317542e3f2b180c4306e3f45d3c922d7e86d8ce39f941bb9e2e9d8599e/psutil-7.2.1-cp37-abi3-win_amd64.whl", hash = "sha256:b1b0671619343aa71c20ff9767eced0483e4fc9e1f489d50923738caf6a03c17", size = 136938, upload-time = "2025-12-29T08:26:41.036Z" }, + { url = "https://files.pythonhosted.org/packages/3e/73/2ce007f4198c80fcf2cb24c169884f833fe93fbc03d55d302627b094ee91/psutil-7.2.1-cp37-abi3-win_arm64.whl", hash = "sha256:0d67c1822c355aa6f7314d92018fb4268a76668a536f133599b91edd48759442", size = 133836, upload-time = "2025-12-29T08:26:43.086Z" }, ] [[package]] @@ -5357,200 +5474,218 @@ wheels = [ [[package]] name = "pybase64" -version = "1.4.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/04/14/43297a7b7f0c1bf0c00b596f754ee3ac946128c64d21047ccf9c9bbc5165/pybase64-1.4.2.tar.gz", hash = "sha256:46cdefd283ed9643315d952fe44de80dc9b9a811ce6e3ec97fd1827af97692d0", size = 137246, upload-time = "2025-07-27T13:08:57.808Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f3/6d/0a7159c24ed35c8b9190b148376ad9b96598354f94ede29df74861da9ec6/pybase64-1.4.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:82b4593b480773b17698fef33c68bae0e1c474ba07663fad74249370c46b46c9", size = 38240, upload-time = "2025-07-27T13:02:17.876Z" }, - { url = "https://files.pythonhosted.org/packages/86/2e/dad4cd832a90a49d98867e824180585e7c928504987d37304bccae11a314/pybase64-1.4.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a126f29d29cb4a498db179135dbf955442a0de5b00f374523f5dcceb9074ff58", size = 31658, upload-time = "2025-07-27T13:02:20.823Z" }, - { url = "https://files.pythonhosted.org/packages/1d/d8/30ea35dc2c8c568be93e1379efcaa35092e37efa2ce7f1985ccc63babee7/pybase64-1.4.2-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:1eef93c29cc5567480d168f9cc1ebd3fc3107c65787aed2019a8ea68575a33e0", size = 65963, upload-time = "2025-07-27T13:02:22.376Z" }, - { url = "https://files.pythonhosted.org/packages/f6/da/1c22f2a21d6bb9ec2a214d15ae02d5b20a95335de218a0ecbf769c535a5c/pybase64-1.4.2-cp310-cp310-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:17b871a34aaeb0644145cb6bf28feb163f593abea11aec3dbcc34a006edfc828", size = 68887, upload-time = "2025-07-27T13:02:23.606Z" }, - { url = "https://files.pythonhosted.org/packages/ac/8d/e04d489ba99b444ce94b4d5b232365d00b0f0e8564275d7ba7434dcabe72/pybase64-1.4.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1f734e16293637a35d282ce594eb05a7a90ea3ae2bc84a3496a5df9e6b890725", size = 57503, upload-time = "2025-07-27T13:02:24.83Z" }, - { url = "https://files.pythonhosted.org/packages/7e/b8/5ec9c334f30cf898709a084d596bf4b47aec2e07870f07bac5cf39754eca/pybase64-1.4.2-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:22bd38db2d990d5545dde83511edeec366630d00679dbd945472315c09041dc6", size = 54517, upload-time = "2025-07-27T13:02:26.006Z" }, - { url = "https://files.pythonhosted.org/packages/b9/5a/6e4424ecca041e53aa7c14525f99edd43d0117c23c5d9cb14e931458a536/pybase64-1.4.2-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:dc65cee686dda72007b7541b2014f33ee282459c781b9b61305bd8b9cfadc8e1", size = 57167, upload-time = "2025-07-27T13:02:27.47Z" }, - { url = "https://files.pythonhosted.org/packages/5f/d0/13f1a9467cf565eecc21dce89fb0723458d8c563d2ccfb99b96e8318dfd5/pybase64-1.4.2-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:1e79641c420a22e49c67c046895efad05bf5f8b1dbe0dd78b4af3ab3f2923fe2", size = 57718, upload-time = "2025-07-27T13:02:28.631Z" }, - { url = "https://files.pythonhosted.org/packages/3e/34/d80335c36ad9400b18b4f92e9f680cf7646102fe4919f7bce5786a2ccb7b/pybase64-1.4.2-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:12f5e7db522ef780a8b333dab5f7d750d270b23a1684bc2235ba50756c7ba428", size = 53021, upload-time = "2025-07-27T13:02:29.823Z" }, - { url = "https://files.pythonhosted.org/packages/68/57/504ff75f7c78df28be126fe6634083d28d7f84c17e04a74a7dcb50ab2377/pybase64-1.4.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a618b1e1a63e75dd40c2a397d875935ed0835464dc55cb1b91e8f880113d0444", size = 56306, upload-time = "2025-07-27T13:02:31.314Z" }, - { url = "https://files.pythonhosted.org/packages/bf/bc/2d21cda8b73c8c9f5cd3d7e6e26dd6dfc96491052112f282332a3d5bf1d9/pybase64-1.4.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:89b0a51702c7746fa914e75e680ad697b979cdead6b418603f56a6fc9de2f50f", size = 50101, upload-time = "2025-07-27T13:02:32.662Z" }, - { url = "https://files.pythonhosted.org/packages/88/6d/51942e7737bb0711ca3e55db53924fd7f07166d79da5508ab8f5fd5972a8/pybase64-1.4.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c5161b8b82f8ba5dbbc3f76e0270622a2c2fdb9ffaf092d8f774ad7ec468c027", size = 66555, upload-time = "2025-07-27T13:02:34.122Z" }, - { url = "https://files.pythonhosted.org/packages/b6/c8/c46024d196402e7be4d3fad85336863a34816c3436c51fcf9c7c0781bf11/pybase64-1.4.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:2168de920c9b1e57850e9ff681852923a953601f73cc96a0742a42236695c316", size = 55684, upload-time = "2025-07-27T13:02:35.427Z" }, - { url = "https://files.pythonhosted.org/packages/6a/c5/953782c9d599ff5217ee87f19e317c494cd4840afcab4c48f99cb78ca201/pybase64-1.4.2-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:7a1e3dc977562abe40ab43483223013be71b215a5d5f3c78a666e70a5076eeec", size = 52475, upload-time = "2025-07-27T13:02:36.634Z" }, - { url = "https://files.pythonhosted.org/packages/05/fb/57d36173631aab67ca4558cdbde1047fc67a09b77f9c53addd57c7e9fdd4/pybase64-1.4.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:4cf1e8a57449e48137ef4de00a005e24c3f1cffc0aafc488e36ceb5bb2cbb1da", size = 53943, upload-time = "2025-07-27T13:02:37.777Z" }, - { url = "https://files.pythonhosted.org/packages/75/73/23e5bb0bffac0cabe2d11d1c618f6ef73da9f430da03c5249931e3c49b63/pybase64-1.4.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d8e1a381ba124f26a93d5925efbf6e6c36287fc2c93d74958e8b677c30a53fc0", size = 68411, upload-time = "2025-07-27T13:02:39.302Z" }, - { url = "https://files.pythonhosted.org/packages/ce/e7/0d5c99e5e61ff5e46949a0128b49fc2c47afc0d2b815333459b17aa9d467/pybase64-1.4.2-cp310-cp310-win32.whl", hash = "sha256:8fdd9c5b60ec9a1db854f5f96bba46b80a9520069282dc1d37ff433eb8248b1f", size = 33614, upload-time = "2025-07-27T13:02:40.478Z" }, - { url = "https://files.pythonhosted.org/packages/23/40/879b6de61d7c07a2cbf76b75e9739c4938c3a1f66ac03243f2ff7ec9fb6b/pybase64-1.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:37a6c73f14c6539c0ad1aebf0cce92138af25c99a6e7aee637d9f9fc634c8a40", size = 35790, upload-time = "2025-07-27T13:02:41.864Z" }, - { url = "https://files.pythonhosted.org/packages/d2/e2/75cec12880ce3f47a79a2b9a0cdc766dc0429a7ce967bb3ab3a4b55a7f6b/pybase64-1.4.2-cp310-cp310-win_arm64.whl", hash = "sha256:b3280d03b7b361622c469d005cc270d763d9e29d0a490c26addb4f82dfe71a79", size = 30900, upload-time = "2025-07-27T13:02:43.022Z" }, - { url = "https://files.pythonhosted.org/packages/da/fb/edaa56bbf04715efc3c36966cc0150e01d7a8336c3da182f850b7fd43d32/pybase64-1.4.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:26284ef64f142067293347bcc9d501d2b5d44b92eab9d941cb10a085fb01c666", size = 38238, upload-time = "2025-07-27T13:02:44.224Z" }, - { url = "https://files.pythonhosted.org/packages/28/a4/ca1538e9adf08f5016b3543b0060c18aea9a6e805dd20712a197c509d90d/pybase64-1.4.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:52dd32fe5cbfd8af8f3f034a4a65ee61948c72e5c358bf69d59543fc0dbcf950", size = 31659, upload-time = "2025-07-27T13:02:45.445Z" }, - { url = "https://files.pythonhosted.org/packages/0b/8f/f9b49926a60848ba98350dd648227ec524fb78340b47a450c4dbaf24b1bb/pybase64-1.4.2-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:37f133e8c96427995480bb6d396d9d49e949a3e829591845bb6a5a7f215ca177", size = 68318, upload-time = "2025-07-27T13:02:46.644Z" }, - { url = "https://files.pythonhosted.org/packages/29/9b/6ed2dd2bc8007f33b8316d6366b0901acbdd5665b419c2893b3dd48708de/pybase64-1.4.2-cp311-cp311-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a6ee3874b0abbdd4c903d3989682a3f016fd84188622879f6f95a5dc5718d7e5", size = 71357, upload-time = "2025-07-27T13:02:47.937Z" }, - { url = "https://files.pythonhosted.org/packages/fb/69/be9ac8127da8d8339db7129683bd2975cecb0bf40a82731e1a492577a177/pybase64-1.4.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5c69f177b1e404b22b05802127d6979acf4cb57f953c7de9472410f9c3fdece7", size = 59817, upload-time = "2025-07-27T13:02:49.163Z" }, - { url = "https://files.pythonhosted.org/packages/f4/a2/e3e09e000b509609276ee28b71beb0b61462d4a43b3e0db0a44c8652880c/pybase64-1.4.2-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:80c817e88ef2ca3cc9a285fde267690a1cb821ce0da4848c921c16f0fec56fda", size = 56639, upload-time = "2025-07-27T13:02:50.384Z" }, - { url = "https://files.pythonhosted.org/packages/01/70/ad7eff88aa4f1be06db705812e1f01749606933bf8fe9df553bb04b703e6/pybase64-1.4.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:7a4bb6e7e45bfdaea0f2aaf022fc9a013abe6e46ccea31914a77e10f44098688", size = 59368, upload-time = "2025-07-27T13:02:51.883Z" }, - { url = "https://files.pythonhosted.org/packages/9d/82/0cd1b4bcd2a4da7805cfa04587be783bf9583b34ac16cadc29cf119a4fa2/pybase64-1.4.2-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:2710a80d41a2b41293cb0e5b84b5464f54aa3f28f7c43de88784d2d9702b8a1c", size = 59981, upload-time = "2025-07-27T13:02:53.16Z" }, - { url = "https://files.pythonhosted.org/packages/3c/4c/8029a03468307dfaf0f9694d31830487ee43af5f8a73407004907724e8ac/pybase64-1.4.2-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:aa6122c8a81f6597e1c1116511f03ed42cf377c2100fe7debaae7ca62521095a", size = 54908, upload-time = "2025-07-27T13:02:54.363Z" }, - { url = "https://files.pythonhosted.org/packages/a1/8b/70bd0fe659e242efd0f60895a8ce1fe88e3a4084fd1be368974c561138c9/pybase64-1.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b7e22b02505d64db308e9feeb6cb52f1d554ede5983de0befa59ac2d2ffb6a5f", size = 58650, upload-time = "2025-07-27T13:02:55.905Z" }, - { url = "https://files.pythonhosted.org/packages/64/ca/9c1d23cbc4b9beac43386a32ad53903c816063cef3f14c10d7c3d6d49a23/pybase64-1.4.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:edfe4a3c8c4007f09591f49b46a89d287ef5e8cd6630339536fe98ff077263c2", size = 52323, upload-time = "2025-07-27T13:02:57.192Z" }, - { url = "https://files.pythonhosted.org/packages/aa/29/a6292e9047248c8616dc53131a49da6c97a61616f80e1e36c73d7ef895fe/pybase64-1.4.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:b79b4a53dd117ffbd03e96953f2e6bd2827bfe11afeb717ea16d9b0893603077", size = 68979, upload-time = "2025-07-27T13:02:58.594Z" }, - { url = "https://files.pythonhosted.org/packages/c2/e0/cfec7b948e170395d8e88066e01f50e71195db9837151db10c14965d6222/pybase64-1.4.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:fd9afa7a61d89d170607faf22287290045757e782089f0357b8f801d228d52c3", size = 58037, upload-time = "2025-07-27T13:02:59.753Z" }, - { url = "https://files.pythonhosted.org/packages/74/7e/0ac1850198c9c35ef631174009cee576f4d8afff3bf493ce310582976ab4/pybase64-1.4.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:5c17b092e4da677a595178d2db17a5d2fafe5c8e418d46c0c4e4cde5adb8cff3", size = 54416, upload-time = "2025-07-27T13:03:00.978Z" }, - { url = "https://files.pythonhosted.org/packages/1b/45/b0b037f27e86c50e62d927f0bc1bde8b798dd55ab39197b116702e508d05/pybase64-1.4.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:120799274cf55f3f5bb8489eaa85142f26170564baafa7cf3e85541c46b6ab13", size = 56257, upload-time = "2025-07-27T13:03:02.201Z" }, - { url = "https://files.pythonhosted.org/packages/d2/0d/5034598aac56336d88fd5aaf6f34630330643b51d399336b8c788d798fc5/pybase64-1.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:522e4e712686acec2d25de9759dda0b0618cb9f6588523528bc74715c0245c7b", size = 70889, upload-time = "2025-07-27T13:03:03.437Z" }, - { url = "https://files.pythonhosted.org/packages/8a/3b/0645f21bb08ecf45635b624958b5f9e569069d31ecbf125dc7e0e5b83f60/pybase64-1.4.2-cp311-cp311-win32.whl", hash = "sha256:bfd828792982db8d787515535948c1e340f1819407c8832f94384c0ebeaf9d74", size = 33631, upload-time = "2025-07-27T13:03:05.194Z" }, - { url = "https://files.pythonhosted.org/packages/8f/08/24f8103c1f19e78761026cdd9f3b3be73239bc19cf5ab6fef0e8042d0bc6/pybase64-1.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:7a9e89d40dbf833af481d1d5f1a44d173c9c4b56a7c8dba98e39a78ee87cfc52", size = 35781, upload-time = "2025-07-27T13:03:06.779Z" }, - { url = "https://files.pythonhosted.org/packages/66/cd/832fb035a0ea7eb53d776a5cfa961849e22828f6dfdfcdb9eb43ba3c0166/pybase64-1.4.2-cp311-cp311-win_arm64.whl", hash = "sha256:ce5809fa90619b03eab1cd63fec142e6cf1d361731a9b9feacf27df76c833343", size = 30903, upload-time = "2025-07-27T13:03:07.903Z" }, - { url = "https://files.pythonhosted.org/packages/28/6d/11ede991e800797b9f5ebd528013b34eee5652df93de61ffb24503393fa5/pybase64-1.4.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:db2c75d1388855b5a1015b65096d7dbcc708e7de3245dcbedeb872ec05a09326", size = 38326, upload-time = "2025-07-27T13:03:09.065Z" }, - { url = "https://files.pythonhosted.org/packages/fe/84/87f1f565f42e2397e2aaa2477c86419f5173c3699881c42325c090982f0a/pybase64-1.4.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6b621a972a01841368fdb9dedc55fd3c6e0c7217d0505ba3b1ebe95e7ef1b493", size = 31661, upload-time = "2025-07-27T13:03:10.295Z" }, - { url = "https://files.pythonhosted.org/packages/cb/2a/a24c810e7a61d2cc6f73fe9ee4872a03030887fa8654150901b15f376f65/pybase64-1.4.2-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:f48c32ac6a16cbf57a5a96a073fef6ff7e3526f623cd49faa112b7f9980bafba", size = 68192, upload-time = "2025-07-27T13:03:11.467Z" }, - { url = "https://files.pythonhosted.org/packages/ee/87/d9baf98cbfc37b8657290ad4421f3a3c36aa0eafe4872c5859cfb52f3448/pybase64-1.4.2-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:ace8b23093a6bb862477080d9059b784096ab2f97541e8bfc40d42f062875149", size = 71587, upload-time = "2025-07-27T13:03:12.719Z" }, - { url = "https://files.pythonhosted.org/packages/0b/89/3df043cc56ef3b91b7aa0c26ae822a2d7ec8da0b0fd7c309c879b0eb5988/pybase64-1.4.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1772c7532a7fb6301baea3dd3e010148dbf70cd1136a83c2f5f91bdc94822145", size = 59910, upload-time = "2025-07-27T13:03:14.266Z" }, - { url = "https://files.pythonhosted.org/packages/75/4f/6641e9edf37aeb4d4524dc7ba2168eff8d96c90e77f6283c2be3400ab380/pybase64-1.4.2-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:f86f7faddcba5cbfea475f8ab96567834c28bf09ca6c7c3d66ee445adac80d8f", size = 56701, upload-time = "2025-07-27T13:03:15.6Z" }, - { url = "https://files.pythonhosted.org/packages/2d/7f/20d8ac1046f12420a0954a45a13033e75f98aade36eecd00c64e3549b071/pybase64-1.4.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:0b8c8e275b5294089f314814b4a50174ab90af79d6a4850f6ae11261ff6a7372", size = 59288, upload-time = "2025-07-27T13:03:16.823Z" }, - { url = "https://files.pythonhosted.org/packages/17/ea/9c0ca570e3e50b3c6c3442e280c83b321a0464c86a9db1f982a4ff531550/pybase64-1.4.2-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:864d85a0470c615807ae8b97d724d068b940a2d10ac13a5f1b9e75a3ce441758", size = 60267, upload-time = "2025-07-27T13:03:18.132Z" }, - { url = "https://files.pythonhosted.org/packages/f9/ac/46894929d71ccedebbfb0284173b0fea96bc029cd262654ba8451a7035d6/pybase64-1.4.2-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:47254d97ed2d8351e30ecfdb9e2414547f66ba73f8a09f932c9378ff75cd10c5", size = 54801, upload-time = "2025-07-27T13:03:19.669Z" }, - { url = "https://files.pythonhosted.org/packages/6a/1e/02c95218ea964f0b2469717c2c69b48e63f4ca9f18af01a5b2a29e4c1216/pybase64-1.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:264b65ecc4f0ee73f3298ab83bbd8008f7f9578361b8df5b448f985d8c63e02a", size = 58599, upload-time = "2025-07-27T13:03:20.951Z" }, - { url = "https://files.pythonhosted.org/packages/15/45/ccc21004930789b8fb439d43e3212a6c260ccddb2bf450c39a20db093f33/pybase64-1.4.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:fbcc2b30cd740c16c9699f596f22c7a9e643591311ae72b1e776f2d539e9dd9d", size = 52388, upload-time = "2025-07-27T13:03:23.064Z" }, - { url = "https://files.pythonhosted.org/packages/c4/45/22e46e549710c4c237d77785b6fb1bc4c44c288a5c44237ba9daf5c34b82/pybase64-1.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:cda9f79c22d51ee4508f5a43b673565f1d26af4330c99f114e37e3186fdd3607", size = 68802, upload-time = "2025-07-27T13:03:24.673Z" }, - { url = "https://files.pythonhosted.org/packages/55/0c/232c6261b81296e5593549b36e6e7884a5da008776d12665923446322c36/pybase64-1.4.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:0c91c6d2a7232e2a1cd10b3b75a8bb657defacd4295a1e5e80455df2dfc84d4f", size = 57841, upload-time = "2025-07-27T13:03:25.948Z" }, - { url = "https://files.pythonhosted.org/packages/20/8a/b35a615ae6f04550d696bb179c414538b3b477999435fdd4ad75b76139e4/pybase64-1.4.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:a370dea7b1cee2a36a4d5445d4e09cc243816c5bc8def61f602db5a6f5438e52", size = 54320, upload-time = "2025-07-27T13:03:27.495Z" }, - { url = "https://files.pythonhosted.org/packages/d3/a9/8bd4f9bcc53689f1b457ecefed1eaa080e4949d65a62c31a38b7253d5226/pybase64-1.4.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9aa4de83f02e462a6f4e066811c71d6af31b52d7484de635582d0e3ec3d6cc3e", size = 56482, upload-time = "2025-07-27T13:03:28.942Z" }, - { url = "https://files.pythonhosted.org/packages/75/e5/4a7735b54a1191f61c3f5c2952212c85c2d6b06eb5fb3671c7603395f70c/pybase64-1.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:83a1c2f9ed00fee8f064d548c8654a480741131f280e5750bb32475b7ec8ee38", size = 70959, upload-time = "2025-07-27T13:03:30.171Z" }, - { url = "https://files.pythonhosted.org/packages/d3/67/e2b6cb32c782e12304d467418e70da0212567f42bd4d3b5eb1fdf64920ad/pybase64-1.4.2-cp312-cp312-win32.whl", hash = "sha256:a6e5688b18d558e8c6b8701cc8560836c4bbeba61d33c836b4dba56b19423716", size = 33683, upload-time = "2025-07-27T13:03:31.775Z" }, - { url = "https://files.pythonhosted.org/packages/4f/bc/d5c277496063a09707486180f17abbdbdebbf2f5c4441b20b11d3cb7dc7c/pybase64-1.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:c995d21b8bd08aa179cd7dd4db0695c185486ecc72da1e8f6c37ec86cadb8182", size = 35817, upload-time = "2025-07-27T13:03:32.99Z" }, - { url = "https://files.pythonhosted.org/packages/e6/69/e4be18ae685acff0ae77f75d4586590f29d2cd187bf603290cf1d635cad4/pybase64-1.4.2-cp312-cp312-win_arm64.whl", hash = "sha256:e254b9258c40509c2ea063a7784f6994988f3f26099d6e08704e3c15dfed9a55", size = 30900, upload-time = "2025-07-27T13:03:34.499Z" }, - { url = "https://files.pythonhosted.org/packages/f4/56/5337f27a8b8d2d6693f46f7b36bae47895e5820bfa259b0072574a4e1057/pybase64-1.4.2-cp313-cp313-android_21_arm64_v8a.whl", hash = "sha256:0f331aa59549de21f690b6ccc79360ffed1155c3cfbc852eb5c097c0b8565a2b", size = 33888, upload-time = "2025-07-27T13:03:35.698Z" }, - { url = "https://files.pythonhosted.org/packages/4c/09/f3f4b11fc9beda7e8625e29fb0f549958fcbb34fea3914e1c1d95116e344/pybase64-1.4.2-cp313-cp313-android_21_x86_64.whl", hash = "sha256:9dad20bf1f3ed9e6fe566c4c9d07d9a6c04f5a280daebd2082ffb8620b0a880d", size = 40796, upload-time = "2025-07-27T13:03:36.927Z" }, - { url = "https://files.pythonhosted.org/packages/e3/ff/470768f0fe6de0aa302a8cb1bdf2f9f5cffc3f69e60466153be68bc953aa/pybase64-1.4.2-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:69d3f0445b0faeef7bb7f93bf8c18d850785e2a77f12835f49e524cc54af04e7", size = 30914, upload-time = "2025-07-27T13:03:38.475Z" }, - { url = "https://files.pythonhosted.org/packages/75/6b/d328736662665e0892409dc410353ebef175b1be5eb6bab1dad579efa6df/pybase64-1.4.2-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:2372b257b1f4dd512f317fb27e77d313afd137334de64c87de8374027aacd88a", size = 31380, upload-time = "2025-07-27T13:03:39.7Z" }, - { url = "https://files.pythonhosted.org/packages/ca/96/7ff718f87c67f4147c181b73d0928897cefa17dc75d7abc6e37730d5908f/pybase64-1.4.2-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:fb794502b4b1ec91c4ca5d283ae71aef65e3de7721057bd9e2b3ec79f7a62d7d", size = 38230, upload-time = "2025-07-27T13:03:41.637Z" }, - { url = "https://files.pythonhosted.org/packages/4d/58/a3307b048d799ff596a3c7c574fcba66f9b6b8c899a3c00a698124ca7ad5/pybase64-1.4.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d5c532b03fd14a5040d6cf6571299a05616f925369c72ddf6fe2fb643eb36fed", size = 38319, upload-time = "2025-07-27T13:03:42.847Z" }, - { url = "https://files.pythonhosted.org/packages/08/a7/0bda06341b0a2c830d348c6e1c4d348caaae86c53dc9a046e943467a05e9/pybase64-1.4.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0f699514dc1d5689ca9cf378139e0214051922732f9adec9404bc680a8bef7c0", size = 31655, upload-time = "2025-07-27T13:03:44.426Z" }, - { url = "https://files.pythonhosted.org/packages/87/df/e1d6e8479e0c5113c2c63c7b44886935ce839c2d99884c7304ca9e86547c/pybase64-1.4.2-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:cd3e8713cbd32c8c6aa935feaf15c7670e2b7e8bfe51c24dc556811ebd293a29", size = 68232, upload-time = "2025-07-27T13:03:45.729Z" }, - { url = "https://files.pythonhosted.org/packages/71/ab/db4dbdfccb9ca874d6ce34a0784761471885d96730de85cee3d300381529/pybase64-1.4.2-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:d377d48acf53abf4b926c2a7a24a19deb092f366a04ffd856bf4b3aa330b025d", size = 71608, upload-time = "2025-07-27T13:03:47.01Z" }, - { url = "https://files.pythonhosted.org/packages/11/e9/508df958563951045d728bbfbd3be77465f9231cf805cb7ccaf6951fc9f1/pybase64-1.4.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d83c076e78d619b9e1dd674e2bf5fb9001aeb3e0b494b80a6c8f6d4120e38cd9", size = 59912, upload-time = "2025-07-27T13:03:48.277Z" }, - { url = "https://files.pythonhosted.org/packages/f2/58/7f2cef1ceccc682088958448d56727369de83fa6b29148478f4d2acd107a/pybase64-1.4.2-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:ab9cdb6a8176a5cb967f53e6ad60e40c83caaa1ae31c5e1b29e5c8f507f17538", size = 56413, upload-time = "2025-07-27T13:03:49.908Z" }, - { url = "https://files.pythonhosted.org/packages/08/7c/7e0af5c5728fa7e2eb082d88eca7c6bd17429be819d58518e74919d42e66/pybase64-1.4.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:adf0c103ad559dbfb9fe69edfd26a15c65d9c991a5ab0a25b04770f9eb0b9484", size = 59311, upload-time = "2025-07-27T13:03:51.238Z" }, - { url = "https://files.pythonhosted.org/packages/03/8b/09825d0f37e45b9a3f546e5f990b6cf2dd838e54ea74122c2464646e0c77/pybase64-1.4.2-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:0d03ef2f253d97ce0685d3624bf5e552d716b86cacb8a6c971333ba4b827e1fc", size = 60282, upload-time = "2025-07-27T13:03:52.56Z" }, - { url = "https://files.pythonhosted.org/packages/9c/3f/3711d2413f969bfd5b9cc19bc6b24abae361b7673ff37bcb90c43e199316/pybase64-1.4.2-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:e565abf906efee76ae4be1aef5df4aed0fda1639bc0d7732a3dafef76cb6fc35", size = 54845, upload-time = "2025-07-27T13:03:54.167Z" }, - { url = "https://files.pythonhosted.org/packages/c6/3c/4c7ce1ae4d828c2bb56d144322f81bffbaaac8597d35407c3d7cbb0ff98f/pybase64-1.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e3c6a5f15fd03f232fc6f295cce3684f7bb08da6c6d5b12cc771f81c9f125cc6", size = 58615, upload-time = "2025-07-27T13:03:55.494Z" }, - { url = "https://files.pythonhosted.org/packages/f5/8f/c2fc03bf4ed038358620065c75968a30184d5d3512d09d3ef9cc3bd48592/pybase64-1.4.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:bad9e3db16f448728138737bbd1af9dc2398efd593a8bdd73748cc02cd33f9c6", size = 52434, upload-time = "2025-07-27T13:03:56.808Z" }, - { url = "https://files.pythonhosted.org/packages/e2/0a/757d6df0a60327c893cfae903e15419914dd792092dc8cc5c9523d40bc9b/pybase64-1.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2683ef271328365c31afee0ed8fa29356fb8fb7c10606794656aa9ffb95e92be", size = 68824, upload-time = "2025-07-27T13:03:58.735Z" }, - { url = "https://files.pythonhosted.org/packages/a0/14/84abe2ed8c29014239be1cfab45dfebe5a5ca779b177b8b6f779bd8b69da/pybase64-1.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:265b20089cd470079114c09bb74b101b3bfc3c94ad6b4231706cf9eff877d570", size = 57898, upload-time = "2025-07-27T13:04:00.379Z" }, - { url = "https://files.pythonhosted.org/packages/7e/c6/d193031f90c864f7b59fa6d1d1b5af41f0f5db35439988a8b9f2d1b32a13/pybase64-1.4.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:e53173badead10ef8b839aa5506eecf0067c7b75ad16d9bf39bc7144631f8e67", size = 54319, upload-time = "2025-07-27T13:04:01.742Z" }, - { url = "https://files.pythonhosted.org/packages/cb/37/ec0c7a610ff8f994ee6e0c5d5d66b6b6310388b96ebb347b03ae39870fdf/pybase64-1.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:5823b8dcf74da7da0f761ed60c961e8928a6524e520411ad05fe7f9f47d55b40", size = 56472, upload-time = "2025-07-27T13:04:03.089Z" }, - { url = "https://files.pythonhosted.org/packages/c4/5a/e585b74f85cedd261d271e4c2ef333c5cfce7e80750771808f56fee66b98/pybase64-1.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1237f66c54357d325390da60aa5e21c6918fbcd1bf527acb9c1f4188c62cb7d5", size = 70966, upload-time = "2025-07-27T13:04:04.361Z" }, - { url = "https://files.pythonhosted.org/packages/ad/20/1b2fdd98b4ba36008419668c813025758214c543e362c66c49214ecd1127/pybase64-1.4.2-cp313-cp313-win32.whl", hash = "sha256:b0b851eb4f801d16040047f6889cca5e9dfa102b3e33f68934d12511245cef86", size = 33681, upload-time = "2025-07-27T13:04:06.126Z" }, - { url = "https://files.pythonhosted.org/packages/ff/64/3df4067d169c047054889f34b5a946cbe3785bca43404b93c962a5461a41/pybase64-1.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:19541c6e26d17d9522c02680fe242206ae05df659c82a657aabadf209cd4c6c7", size = 35822, upload-time = "2025-07-27T13:04:07.752Z" }, - { url = "https://files.pythonhosted.org/packages/d1/fd/db505188adf812e60ee923f196f9deddd8a1895b2b29b37f5db94afc3b1c/pybase64-1.4.2-cp313-cp313-win_arm64.whl", hash = "sha256:77a191863d576c0a5dd81f8a568a5ca15597cc980ae809dce62c717c8d42d8aa", size = 30899, upload-time = "2025-07-27T13:04:09.062Z" }, - { url = "https://files.pythonhosted.org/packages/d9/27/5f5fecd206ec1e06e1608a380af18dcb76a6ab08ade6597a3251502dcdb2/pybase64-1.4.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2e194bbabe3fdf9e47ba9f3e157394efe0849eb226df76432126239b3f44992c", size = 38677, upload-time = "2025-07-27T13:04:10.334Z" }, - { url = "https://files.pythonhosted.org/packages/bf/0f/abe4b5a28529ef5f74e8348fa6a9ef27d7d75fbd98103d7664cf485b7d8f/pybase64-1.4.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:39aef1dadf4a004f11dd09e703abaf6528a87c8dbd39c448bb8aebdc0a08c1be", size = 32066, upload-time = "2025-07-27T13:04:11.641Z" }, - { url = "https://files.pythonhosted.org/packages/ac/7e/ea0ce6a7155cada5526017ec588b6d6185adea4bf9331565272f4ef583c2/pybase64-1.4.2-cp313-cp313t-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:91cb920c7143e36ec8217031282c8651da3b2206d70343f068fac0e7f073b7f9", size = 72300, upload-time = "2025-07-27T13:04:12.969Z" }, - { url = "https://files.pythonhosted.org/packages/45/2d/e64c7a056c9ec48dfe130d1295e47a8c2b19c3984488fc08e5eaa1e86c88/pybase64-1.4.2-cp313-cp313t-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6958631143fb9e71f9842000da042ec2f6686506b6706e2dfda29e97925f6aa0", size = 75520, upload-time = "2025-07-27T13:04:14.374Z" }, - { url = "https://files.pythonhosted.org/packages/43/e0/e5f93b2e1cb0751a22713c4baa6c6eaf5f307385e369180486c8316ed21e/pybase64-1.4.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:dc35f14141ef3f1ac70d963950a278a2593af66fe5a1c7a208e185ca6278fa25", size = 65384, upload-time = "2025-07-27T13:04:16.204Z" }, - { url = "https://files.pythonhosted.org/packages/ff/23/8c645a1113ad88a1c6a3d0e825e93ef8b74ad3175148767853a0a4d7626e/pybase64-1.4.2-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:5d949d2d677859c3a8507e1b21432a039d2b995e0bd3fe307052b6ded80f207a", size = 60471, upload-time = "2025-07-27T13:04:17.947Z" }, - { url = "https://files.pythonhosted.org/packages/8b/81/edd0f7d8b0526b91730a0dd4ce6b4c8be2136cd69d424afe36235d2d2a06/pybase64-1.4.2-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:09caacdd3e15fe7253a67781edd10a6a918befab0052a2a3c215fe5d1f150269", size = 63945, upload-time = "2025-07-27T13:04:19.383Z" }, - { url = "https://files.pythonhosted.org/packages/a5/a5/edc224cd821fd65100b7af7c7e16b8f699916f8c0226c9c97bbae5a75e71/pybase64-1.4.2-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:e44b0e793b23f28ea0f15a9754bd0c960102a2ac4bccb8fafdedbd4cc4d235c0", size = 64858, upload-time = "2025-07-27T13:04:20.807Z" }, - { url = "https://files.pythonhosted.org/packages/11/3b/92853f968f1af7e42b7e54d21bdd319097b367e7dffa2ca20787361df74c/pybase64-1.4.2-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:849f274d0bcb90fc6f642c39274082724d108e41b15f3a17864282bd41fc71d5", size = 58557, upload-time = "2025-07-27T13:04:22.229Z" }, - { url = "https://files.pythonhosted.org/packages/76/09/0ec6bd2b2303b0ea5c6da7535edc9a608092075ef8c0cdd96e3e726cd687/pybase64-1.4.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:528dba7ef1357bd7ce1aea143084501f47f5dd0fff7937d3906a68565aa59cfe", size = 63624, upload-time = "2025-07-27T13:04:23.952Z" }, - { url = "https://files.pythonhosted.org/packages/73/6e/52cb1ced2a517a3118b2e739e9417432049013ac7afa15d790103059e8e4/pybase64-1.4.2-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:1da54be743d9a68671700cfe56c3ab8c26e8f2f5cc34eface905c55bc3a9af94", size = 56174, upload-time = "2025-07-27T13:04:25.419Z" }, - { url = "https://files.pythonhosted.org/packages/5b/9d/820fe79347467e48af985fe46180e1dd28e698ade7317bebd66de8a143f5/pybase64-1.4.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:9b07c0406c3eaa7014499b0aacafb21a6d1146cfaa85d56f0aa02e6d542ee8f3", size = 72640, upload-time = "2025-07-27T13:04:26.824Z" }, - { url = "https://files.pythonhosted.org/packages/53/58/e863e10d08361e694935c815b73faad7e1ab03f99ae154d86c4e2f331896/pybase64-1.4.2-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:312f2aa4cf5d199a97fbcaee75d2e59ebbaafcd091993eb373b43683498cdacb", size = 62453, upload-time = "2025-07-27T13:04:28.562Z" }, - { url = "https://files.pythonhosted.org/packages/95/f0/c392c4ac8ccb7a34b28377c21faa2395313e3c676d76c382642e19a20703/pybase64-1.4.2-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:ad59362fc267bf15498a318c9e076686e4beeb0dfe09b457fabbc2b32468b97a", size = 58103, upload-time = "2025-07-27T13:04:29.996Z" }, - { url = "https://files.pythonhosted.org/packages/32/30/00ab21316e7df8f526aa3e3dc06f74de6711d51c65b020575d0105a025b2/pybase64-1.4.2-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:01593bd064e7dcd6c86d04e94e44acfe364049500c20ac68ca1e708fbb2ca970", size = 60779, upload-time = "2025-07-27T13:04:31.549Z" }, - { url = "https://files.pythonhosted.org/packages/a6/65/114ca81839b1805ce4a2b7d58bc16e95634734a2059991f6382fc71caf3e/pybase64-1.4.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:5b81547ad8ea271c79fdf10da89a1e9313cb15edcba2a17adf8871735e9c02a0", size = 74684, upload-time = "2025-07-27T13:04:32.976Z" }, - { url = "https://files.pythonhosted.org/packages/54/8f/aa9d445b9bb693b8f6bb1456bd6d8576d79b7a63bf6c69af3a539235b15f/pybase64-1.4.2-cp313-cp313t-win32.whl", hash = "sha256:7edbe70b5654545a37e6e6b02de738303b1bbdfcde67f6cfec374cfb5cc4099e", size = 33961, upload-time = "2025-07-27T13:04:34.806Z" }, - { url = "https://files.pythonhosted.org/packages/0e/e5/da37cfb173c646fd4fc7c6aae2bc41d40de2ee49529854af8f4e6f498b45/pybase64-1.4.2-cp313-cp313t-win_amd64.whl", hash = "sha256:385690addf87c25d6366fab5d8ff512eed8a7ecb18da9e8152af1c789162f208", size = 36199, upload-time = "2025-07-27T13:04:36.223Z" }, - { url = "https://files.pythonhosted.org/packages/66/3e/1eb68fb7d00f2cec8bd9838e2a30d183d6724ae06e745fd6e65216f170ff/pybase64-1.4.2-cp313-cp313t-win_arm64.whl", hash = "sha256:c2070d0aa88580f57fe15ca88b09f162e604d19282915a95a3795b5d3c1c05b5", size = 31221, upload-time = "2025-07-27T13:04:37.704Z" }, - { url = "https://files.pythonhosted.org/packages/99/bf/00a87d951473ce96c8c08af22b6983e681bfabdb78dd2dcf7ee58eac0932/pybase64-1.4.2-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:4157ad277a32cf4f02a975dffc62a3c67d73dfa4609b2c1978ef47e722b18b8e", size = 30924, upload-time = "2025-07-27T13:04:39.189Z" }, - { url = "https://files.pythonhosted.org/packages/ae/43/dee58c9d60e60e6fb32dc6da722d84592e22f13c277297eb4ce6baf99a99/pybase64-1.4.2-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:e113267dc349cf624eb4f4fbf53fd77835e1aa048ac6877399af426aab435757", size = 31390, upload-time = "2025-07-27T13:04:40.995Z" }, - { url = "https://files.pythonhosted.org/packages/e1/11/b28906fc2e330b8b1ab4bc845a7bef808b8506734e90ed79c6062b095112/pybase64-1.4.2-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:cea5aaf218fd9c5c23afacfe86fd4464dfedc1a0316dd3b5b4075b068cc67df0", size = 38212, upload-time = "2025-07-27T13:04:42.729Z" }, - { url = "https://files.pythonhosted.org/packages/24/9e/868d1e104413d14b19feaf934fc7fad4ef5b18946385f8bb79684af40f24/pybase64-1.4.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:41213497abbd770435c7a9c8123fb02b93709ac4cf60155cd5aefc5f3042b600", size = 38303, upload-time = "2025-07-27T13:04:44.095Z" }, - { url = "https://files.pythonhosted.org/packages/a3/73/f7eac96ca505df0600280d6bfc671a9e2e2f947c2b04b12a70e36412f7eb/pybase64-1.4.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c8b522df7ee00f2ac1993ccd5e1f6608ae7482de3907668c2ff96a83ef213925", size = 31669, upload-time = "2025-07-27T13:04:45.845Z" }, - { url = "https://files.pythonhosted.org/packages/c6/43/8e18bea4fd455100112d6a73a83702843f067ef9b9272485b6bdfd9ed2f0/pybase64-1.4.2-cp314-cp314-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:06725022e540c5b098b978a0418ca979773e2cbdbb76f10bd97536f2ad1c5b49", size = 68452, upload-time = "2025-07-27T13:04:47.788Z" }, - { url = "https://files.pythonhosted.org/packages/e4/2e/851eb51284b97354ee5dfa1309624ab90920696e91a33cd85b13d20cc5c1/pybase64-1.4.2-cp314-cp314-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a3e54dcf0d0305ec88473c9d0009f698cabf86f88a8a10090efeff2879c421bb", size = 71674, upload-time = "2025-07-27T13:04:49.294Z" }, - { url = "https://files.pythonhosted.org/packages/57/0d/5cf1e5dc64aec8db43e8dee4e4046856d639a72bcb0fb3e716be42ced5f1/pybase64-1.4.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:67675cee727a60dc91173d2790206f01aa3c7b3fbccfa84fd5c1e3d883fe6caa", size = 60027, upload-time = "2025-07-27T13:04:50.769Z" }, - { url = "https://files.pythonhosted.org/packages/a4/8e/3479266bc0e65f6cc48b3938d4a83bff045330649869d950a378f2ddece0/pybase64-1.4.2-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:753da25d4fd20be7bda2746f545935773beea12d5cb5ec56ec2d2960796477b1", size = 56461, upload-time = "2025-07-27T13:04:52.37Z" }, - { url = "https://files.pythonhosted.org/packages/20/b6/f2b6cf59106dd78bae8717302be5b814cec33293504ad409a2eb752ad60c/pybase64-1.4.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:a78c768ce4ca550885246d14babdb8923e0f4a848dfaaeb63c38fc99e7ea4052", size = 59446, upload-time = "2025-07-27T13:04:53.967Z" }, - { url = "https://files.pythonhosted.org/packages/16/70/3417797dfccdfdd0a54e4ad17c15b0624f0fc2d6a362210f229f5c4e8fd0/pybase64-1.4.2-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:51b17f36d890c92f0618fb1c8db2ccc25e6ed07afa505bab616396fc9b0b0492", size = 60350, upload-time = "2025-07-27T13:04:55.881Z" }, - { url = "https://files.pythonhosted.org/packages/a0/c6/6e4269dd98d150ae95d321b311a345eae0f7fd459d97901b4a586d7513bb/pybase64-1.4.2-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:f92218d667049ab4f65d54fa043a88ffdb2f07fff1f868789ef705a5221de7ec", size = 54989, upload-time = "2025-07-27T13:04:57.436Z" }, - { url = "https://files.pythonhosted.org/packages/f9/e8/18c1b0c255f964fafd0412b0d5a163aad588aeccb8f84b9bf9c8611d80f6/pybase64-1.4.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:3547b3d1499919a06491b3f879a19fbe206af2bd1a424ecbb4e601eb2bd11fea", size = 58724, upload-time = "2025-07-27T13:04:59.406Z" }, - { url = "https://files.pythonhosted.org/packages/b1/ad/ddfbd2125fc20b94865fb232b2e9105376fa16eee492e4b7786d42a86cbf/pybase64-1.4.2-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:958af7b0e09ddeb13e8c2330767c47b556b1ade19c35370f6451d139cde9f2a9", size = 52285, upload-time = "2025-07-27T13:05:01.198Z" }, - { url = "https://files.pythonhosted.org/packages/b6/4c/b9d4ec9224add33c84b925a03d1a53cd4106efb449ea8e0ae7795fed7bf7/pybase64-1.4.2-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:4facc57f6671e2229a385a97a618273e7be36a9ea0a9d1c1b9347f14d19ceba8", size = 69036, upload-time = "2025-07-27T13:05:03.109Z" }, - { url = "https://files.pythonhosted.org/packages/92/38/7b96794da77bed3d9b4fea40f14ae563648fba83a696e7602fabe60c0eb7/pybase64-1.4.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:a32fc57d05d73a7c9b0ca95e9e265e21cf734195dc6873829a890058c35f5cfd", size = 57938, upload-time = "2025-07-27T13:05:04.744Z" }, - { url = "https://files.pythonhosted.org/packages/eb/c5/ae8bbce3c322d1b074e79f51f5df95961fe90cb8748df66c6bc97616e974/pybase64-1.4.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:3dc853243c81ce89cc7318e6946f860df28ddb7cd2a0648b981652d9ad09ee5a", size = 54474, upload-time = "2025-07-27T13:05:06.662Z" }, - { url = "https://files.pythonhosted.org/packages/15/9a/c09887c4bb1b43c03fc352e2671ef20c6686c6942a99106a45270ee5b840/pybase64-1.4.2-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:0e6d863a86b3e7bc6ac9bd659bebda4501b9da842521111b0b0e54eb51295df5", size = 56533, upload-time = "2025-07-27T13:05:08.368Z" }, - { url = "https://files.pythonhosted.org/packages/4f/0f/d5114d63d35d085639606a880cb06e2322841cd4b213adfc14d545c1186f/pybase64-1.4.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6579475140ff2067903725d8aca47f5747bcb211597a1edd60b58f6d90ada2bd", size = 71030, upload-time = "2025-07-27T13:05:10.3Z" }, - { url = "https://files.pythonhosted.org/packages/40/0e/fe6f1ed22ea52eb99f490a8441815ba21de288f4351aeef4968d71d20d2d/pybase64-1.4.2-cp314-cp314-win32.whl", hash = "sha256:373897f728d7b4f241a1f803ac732c27b6945d26d86b2741ad9b75c802e4e378", size = 34174, upload-time = "2025-07-27T13:05:12.254Z" }, - { url = "https://files.pythonhosted.org/packages/71/46/0e15bea52ffc63e8ae7935e945accbaf635e0aefa26d3e31fdf9bc9dcd01/pybase64-1.4.2-cp314-cp314-win_amd64.whl", hash = "sha256:1afe3361344617d298c1d08bc657ef56d0f702d6b72cb65d968b2771017935aa", size = 36308, upload-time = "2025-07-27T13:05:13.898Z" }, - { url = "https://files.pythonhosted.org/packages/4f/dc/55849fee2577bda77c1e078da04cc9237e8e474a8c8308deb702a26f2511/pybase64-1.4.2-cp314-cp314-win_arm64.whl", hash = "sha256:f131c9360babe522f3d90f34da3f827cba80318125cf18d66f2ee27e3730e8c4", size = 31341, upload-time = "2025-07-27T13:05:15.553Z" }, - { url = "https://files.pythonhosted.org/packages/39/44/c69d088e28b25e70ac742b6789cde038473815b2a69345c4bae82d5e244d/pybase64-1.4.2-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2583ac304131c1bd6e3120b0179333610f18816000db77c0a2dd6da1364722a8", size = 38678, upload-time = "2025-07-27T13:05:17.544Z" }, - { url = "https://files.pythonhosted.org/packages/00/93/2860ec067497b9cbb06242f96d44caebbd9eed32174e4eb8c1ffef760f94/pybase64-1.4.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:75a8116be4ea4cdd30a5c4f1a6f3b038e0d457eb03c8a2685d8ce2aa00ef8f92", size = 32066, upload-time = "2025-07-27T13:05:19.18Z" }, - { url = "https://files.pythonhosted.org/packages/d3/55/1e96249a38759332e8a01b31c370d88c60ceaf44692eb6ba4f0f451ee496/pybase64-1.4.2-cp314-cp314t-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:217ea776a098d7c08668e5526b9764f5048bbfd28cac86834217ddfe76a4e3c4", size = 72465, upload-time = "2025-07-27T13:05:20.866Z" }, - { url = "https://files.pythonhosted.org/packages/6d/ab/0f468605b899f3e35dbb7423fba3ff98aeed1ec16abb02428468494a58f4/pybase64-1.4.2-cp314-cp314t-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4ec14683e343c95b14248cdfdfa78c052582be7a3865fd570aa7cffa5ab5cf37", size = 75693, upload-time = "2025-07-27T13:05:22.896Z" }, - { url = "https://files.pythonhosted.org/packages/91/d1/9980a0159b699e2489baba05b71b7c953b29249118ba06fdbb3e9ea1b9b5/pybase64-1.4.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:480ecf21e1e956c5a10d3cf7b3b7e75bce3f9328cf08c101e4aab1925d879f34", size = 65577, upload-time = "2025-07-27T13:05:25Z" }, - { url = "https://files.pythonhosted.org/packages/16/86/b27e7b95f9863d245c0179a7245582eda3d262669d8f822777364d8fd7d5/pybase64-1.4.2-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:1fe1ebdc55e9447142e2f6658944aadfb5a4fbf03dbd509be34182585515ecc1", size = 60662, upload-time = "2025-07-27T13:05:27.138Z" }, - { url = "https://files.pythonhosted.org/packages/28/87/a7f0dde0abc26bfbee761f1d3558eb4b139f33ddd9fe1f6825ffa7daa22d/pybase64-1.4.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:c793a2b06753accdaf5e1a8bbe5d800aab2406919e5008174f989a1ca0081411", size = 64179, upload-time = "2025-07-27T13:05:28.996Z" }, - { url = "https://files.pythonhosted.org/packages/1e/88/5d6fa1c60e1363b4cac4c396978f39e9df4689e75225d7d9c0a5998e3a14/pybase64-1.4.2-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:6acae6e1d1f7ebe40165f08076c7a73692b2bf9046fefe673f350536e007f556", size = 64968, upload-time = "2025-07-27T13:05:30.818Z" }, - { url = "https://files.pythonhosted.org/packages/20/6e/2ed585af5b2211040445d9849326dd2445320c9316268794f5453cfbaf30/pybase64-1.4.2-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:88b91cd0949358aadcea75f8de5afbcf3c8c5fb9ec82325bd24285b7119cf56e", size = 58738, upload-time = "2025-07-27T13:05:32.629Z" }, - { url = "https://files.pythonhosted.org/packages/ce/94/e2960b56322eabb3fbf303fc5a72e6444594c1b90035f3975c6fe666db5c/pybase64-1.4.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:53316587e1b1f47a11a5ff068d3cbd4a3911c291f2aec14882734973684871b2", size = 63802, upload-time = "2025-07-27T13:05:34.687Z" }, - { url = "https://files.pythonhosted.org/packages/95/47/312139d764c223f534f751528ce3802887c279125eac64f71cd3b4e05abc/pybase64-1.4.2-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:caa7f20f43d00602cf9043b5ba758d54f5c41707d3709b2a5fac17361579c53c", size = 56341, upload-time = "2025-07-27T13:05:36.554Z" }, - { url = "https://files.pythonhosted.org/packages/3f/d7/aec9a6ed53b128dac32f8768b646ca5730c88eef80934054d7fa7d02f3ef/pybase64-1.4.2-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:2d93817e24fdd79c534ed97705df855af6f1d2535ceb8dfa80da9de75482a8d7", size = 72838, upload-time = "2025-07-27T13:05:38.459Z" }, - { url = "https://files.pythonhosted.org/packages/e3/a8/6ccc54c5f1f7c3450ad7c56da10c0f131d85ebe069ea6952b5b42f2e92d9/pybase64-1.4.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:63cd769b51474d8d08f7f2ce73b30380d9b4078ec92ea6b348ea20ed1e1af88a", size = 62633, upload-time = "2025-07-27T13:05:40.624Z" }, - { url = "https://files.pythonhosted.org/packages/34/22/2b9d89f8ff6f2a01d6d6a88664b20a4817049cfc3f2c62caca040706660c/pybase64-1.4.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:cd07e6a9993c392ec8eb03912a43c6a6b21b2deb79ee0d606700fe276e9a576f", size = 58282, upload-time = "2025-07-27T13:05:42.565Z" }, - { url = "https://files.pythonhosted.org/packages/b2/14/dbf6266177532a6a11804ac080ebffcee272f491b92820c39886ee20f201/pybase64-1.4.2-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:6a8944e8194adff4668350504bc6b7dbde2dab9244c88d99c491657d145b5af5", size = 60948, upload-time = "2025-07-27T13:05:44.48Z" }, - { url = "https://files.pythonhosted.org/packages/fd/7a/b2ae9046a66dd5746cd72836a41386517b1680bea5ce02f2b4f1c9ebc688/pybase64-1.4.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:04ab398ec4b6a212af57f6a21a6336d5a1d754ff4ccb215951366ab9080481b2", size = 74854, upload-time = "2025-07-27T13:05:46.416Z" }, - { url = "https://files.pythonhosted.org/packages/ef/7e/9856f6d6c38a7b730e001123d2d9fa816b8b1a45f0cdee1d509d5947b047/pybase64-1.4.2-cp314-cp314t-win32.whl", hash = "sha256:3b9201ecdcb1c3e23be4caebd6393a4e6615bd0722528f5413b58e22e3792dd3", size = 34490, upload-time = "2025-07-27T13:05:48.304Z" }, - { url = "https://files.pythonhosted.org/packages/c7/38/8523a9dc1ec8704dedbe5ccc95192ae9a7585f7eec85cc62946fe3cacd32/pybase64-1.4.2-cp314-cp314t-win_amd64.whl", hash = "sha256:36e9b0cad8197136d73904ef5a71d843381d063fd528c5ab203fc4990264f682", size = 36680, upload-time = "2025-07-27T13:05:50.264Z" }, - { url = "https://files.pythonhosted.org/packages/3c/52/5600104ef7b85f89fb8ec54f73504ead3f6f0294027e08d281f3cafb5c1a/pybase64-1.4.2-cp314-cp314t-win_arm64.whl", hash = "sha256:f25140496b02db0e7401567cd869fb13b4c8118bf5c2428592ec339987146d8b", size = 31600, upload-time = "2025-07-27T13:05:52.24Z" }, - { url = "https://files.pythonhosted.org/packages/32/34/b67371f4fcedd5e2def29b1cf92a4311a72f590c04850f370c75297b48ce/pybase64-1.4.2-graalpy311-graalpy242_311_native-macosx_10_9_x86_64.whl", hash = "sha256:b4eed40a5f1627ee65613a6ac834a33f8ba24066656f569c852f98eb16f6ab5d", size = 38667, upload-time = "2025-07-27T13:07:25.315Z" }, - { url = "https://files.pythonhosted.org/packages/aa/3e/e57fe09ed1c7e740d21c37023c5f7c8963b4c36380f41d10261cc76f93b4/pybase64-1.4.2-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:57885fa521e9add235af4db13e9e048d3a2934cd27d7c5efac1925e1b4d6538d", size = 32094, upload-time = "2025-07-27T13:07:28.235Z" }, - { url = "https://files.pythonhosted.org/packages/51/34/f40d3262c3953814b9bcdcf858436bd5bc1133a698be4bcc7ed2a8c0730d/pybase64-1.4.2-graalpy311-graalpy242_311_native-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:eef9255d926c64e2fca021d3aee98023bacb98e1518e5986d6aab04102411b04", size = 43212, upload-time = "2025-07-27T13:07:31.327Z" }, - { url = "https://files.pythonhosted.org/packages/8c/2a/5e05d25718cb8ffd68bd46553ddfd2b660893d937feda1716b8a3b21fb38/pybase64-1.4.2-graalpy311-graalpy242_311_native-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:89614ea2d2329b6708746c540e0f14d692125df99fb1203ff0de948d9e68dfc9", size = 35789, upload-time = "2025-07-27T13:07:34.026Z" }, - { url = "https://files.pythonhosted.org/packages/d5/9d/f56c3ee6e94faaae2896ecaf666428330cb24096abf7d2427371bb2b403a/pybase64-1.4.2-graalpy311-graalpy242_311_native-win_amd64.whl", hash = "sha256:e401cecd2d7ddcd558768b2140fd4430746be4d17fb14c99eec9e40789df136d", size = 35861, upload-time = "2025-07-27T13:07:37.099Z" }, - { url = "https://files.pythonhosted.org/packages/fb/04/bfe2bd0d76385750f3541724b4abfe4ea111b3cc01ff7e83f410054adc30/pybase64-1.4.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:4b29c93414ba965777643a9d98443f08f76ac04519ad717aa859113695372a07", size = 38226, upload-time = "2025-07-27T13:07:40.121Z" }, - { url = "https://files.pythonhosted.org/packages/22/13/c717855760b78ded1a9d308984c7e3e99fcf79c6cac5a231ed8c1238218f/pybase64-1.4.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5e0c3353c0bf099c5c3f8f750202c486abee8f23a566b49e9e7b1222fbf5f259", size = 31524, upload-time = "2025-07-27T13:07:43.946Z" }, - { url = "https://files.pythonhosted.org/packages/cf/da/2b7e69abfc62abe4d54b10d1e09ec78021a6b9b2d7e6e7b632243a19433e/pybase64-1.4.2-pp310-pypy310_pp73-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:4f98c5c6152d3c01d933fcde04322cd9ddcf65b5346034aac69a04c1a7cbb012", size = 40667, upload-time = "2025-07-27T13:07:46.715Z" }, - { url = "https://files.pythonhosted.org/packages/f1/11/ba738655fb3ba85c7a0605eddd2709fef606e654840c72ee5c5ff7ab29bf/pybase64-1.4.2-pp310-pypy310_pp73-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9096a4977b7aff7ef250f759fb6a4b6b7b6199d99c84070c7fc862dd3b208b34", size = 41290, upload-time = "2025-07-27T13:07:49.534Z" }, - { url = "https://files.pythonhosted.org/packages/5d/38/2d5502fcaf712297b95c1b6ca924656dd7d17501fd7f9c9e0b3bbf8892ef/pybase64-1.4.2-pp310-pypy310_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:49d8597e2872966399410502310b1e2a5b7e8d8ba96766ee1fe242e00bd80775", size = 35438, upload-time = "2025-07-27T13:07:52.327Z" }, - { url = "https://files.pythonhosted.org/packages/b6/db/e03b8b6daa60a3fbef21741403e0cf18b2aff3beebdf6e3596bb9bab16c7/pybase64-1.4.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:2ef16366565389a287df82659e055e88bdb6c36e46a3394950903e0a9cb2e5bf", size = 36121, upload-time = "2025-07-27T13:07:55.54Z" }, - { url = "https://files.pythonhosted.org/packages/0e/bf/5ebaa2d9ddb5fc506633bc8b820fc27e64da964937fb30929c0367c47d00/pybase64-1.4.2-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0a5393be20b0705870f5a8969749af84d734c077de80dd7e9f5424a247afa85e", size = 38162, upload-time = "2025-07-27T13:07:58.364Z" }, - { url = "https://files.pythonhosted.org/packages/25/41/795c5fd6e5571bb675bf9add8a048166dddf8951c2a903fea8557743886b/pybase64-1.4.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:448f0259a2f1a17eb086f70fe2ad9b556edba1fc5bc4e62ce6966179368ee9f8", size = 31452, upload-time = "2025-07-27T13:08:01.259Z" }, - { url = "https://files.pythonhosted.org/packages/aa/dd/c819003b59b2832256b72ad23cbeadbd95d083ef0318d07149a58b7a88af/pybase64-1.4.2-pp311-pypy311_pp73-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:1159e70cba8e76c3d8f334bd1f8fd52a1bb7384f4c3533831b23ab2df84a6ef3", size = 40668, upload-time = "2025-07-27T13:08:04.176Z" }, - { url = "https://files.pythonhosted.org/packages/0e/c5/38c6aba28678c4a4db49312a6b8171b93a0ffe9f21362cf4c0f325caa850/pybase64-1.4.2-pp311-pypy311_pp73-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7d943bc5dad8388971494554b97f22ae06a46cc7779ad0de3d4bfdf7d0bbea30", size = 41281, upload-time = "2025-07-27T13:08:07.395Z" }, - { url = "https://files.pythonhosted.org/packages/e5/23/5927bd9e59714e4e8cefd1d21ccd7216048bb1c6c3e7104b1b200afdc63d/pybase64-1.4.2-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:10b99182c561d86422c5de4265fd1f8f172fb38efaed9d72c71fb31e279a7f94", size = 35433, upload-time = "2025-07-27T13:08:10.551Z" }, - { url = "https://files.pythonhosted.org/packages/01/0f/fab7ed5bf4926523c3b39f7621cea3e0da43f539fbc2270e042f1afccb79/pybase64-1.4.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:bb082c1114f046e59fcbc4f2be13edc93b36d7b54b58605820605be948f8fdf6", size = 36131, upload-time = "2025-07-27T13:08:13.777Z" }, +version = "1.4.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/b8/4ed5c7ad5ec15b08d35cc79ace6145d5c1ae426e46435f4987379439dfea/pybase64-1.4.3.tar.gz", hash = "sha256:c2ed274c9e0ba9c8f9c4083cfe265e66dd679126cd9c2027965d807352f3f053", size = 137272, upload-time = "2025-12-06T13:27:04.013Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/39/47/16d7af6fae7803f4c691856bc0d8d433ccf30e106432e2ef7707ee19a38a/pybase64-1.4.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f63aa7f29139b8a05ce5f97cdb7fad63d29071e5bdc8a638a343311fe996112a", size = 38241, upload-time = "2025-12-06T13:22:27.396Z" }, + { url = "https://files.pythonhosted.org/packages/4d/3e/268beb8d2240ab55396af4d1b45d2494935982212549b92a5f5b57079bd3/pybase64-1.4.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f5943ec1ae87a8b4fe310905bb57205ea4330c75e2c628433a7d9dd52295b588", size = 31672, upload-time = "2025-12-06T13:22:28.854Z" }, + { url = "https://files.pythonhosted.org/packages/80/14/4365fa33222edcc46b6db4973f9e22bda82adfb6ab2a01afff591f1e41c8/pybase64-1.4.3-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:5f2b8aef86f35cd5894c13681faf433a1fffc5b2e76544dcb5416a514a1a8347", size = 65978, upload-time = "2025-12-06T13:22:30.191Z" }, + { url = "https://files.pythonhosted.org/packages/1c/22/e89739d8bc9b96c68ead44b4eec42fe555683d9997e4ba65216d384920fc/pybase64-1.4.3-cp310-cp310-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a6ec7e53dd09b0a8116ccf5c3265c7c7fce13c980747525be76902aef36a514a", size = 68903, upload-time = "2025-12-06T13:22:31.29Z" }, + { url = "https://files.pythonhosted.org/packages/77/e1/7e59a19f8999cdefe9eb0d56bfd701dd38263b0f6fb4a4d29fce165a1b36/pybase64-1.4.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7528604cd69c538e1dbaafded46e9e4915a2adcd6f2a60fcef6390d87ca922ea", size = 57516, upload-time = "2025-12-06T13:22:32.395Z" }, + { url = "https://files.pythonhosted.org/packages/42/ad/f47dc7e6fe32022b176868b88b671a32dab389718c8ca905cab79280aaaf/pybase64-1.4.3-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:4ec645f32b50593879031e09158f8681a1db9f5df0f72af86b3969a1c5d1fa2b", size = 54533, upload-time = "2025-12-06T13:22:33.457Z" }, + { url = "https://files.pythonhosted.org/packages/7c/9a/7ab312b5a324833953b00e47b23eb4f83d45bd5c5c854b4b4e51b2a0cf5b/pybase64-1.4.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:634a000c5b3485ccc18bb9b244e0124f74b6fbc7f43eade815170237a7b34c64", size = 57187, upload-time = "2025-12-06T13:22:34.566Z" }, + { url = "https://files.pythonhosted.org/packages/2c/84/80acab1fcbaaae103e6b862ef5019192c8f2cd8758433595a202179a0d1d/pybase64-1.4.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:309ea32ad07639a485580af1be0ad447a434deb1924e76adced63ac2319cfe15", size = 57730, upload-time = "2025-12-06T13:22:35.581Z" }, + { url = "https://files.pythonhosted.org/packages/1f/24/84256d472400ea3163d7d69c44bb7e2e1027f0f1d4d20c47629a7dc4578e/pybase64-1.4.3-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:d10d517566b748d3f25f6ac7162af779360c1c6426ad5f962927ee205990d27c", size = 53036, upload-time = "2025-12-06T13:22:36.621Z" }, + { url = "https://files.pythonhosted.org/packages/a3/0f/33aecbed312ee0431798a73fa25e00dedbffdd91389ee23121fed397c550/pybase64-1.4.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a74cc0f4d835400857cc5c6d27ec854f7949491e07a04e6d66e2137812831f4c", size = 56321, upload-time = "2025-12-06T13:22:37.7Z" }, + { url = "https://files.pythonhosted.org/packages/dc/1c/a341b050746658cbec8cab3c733aeb3ef52ce8f11e60d0d47adbdf729ebf/pybase64-1.4.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:1b591d774ac09d5eb73c156a03277cb271438fbd8042bae4109ff3a827cd218c", size = 50114, upload-time = "2025-12-06T13:22:38.752Z" }, + { url = "https://files.pythonhosted.org/packages/ba/d3/f7e6680ae6dc4ddff39112ad66e0fa6b2ec346e73881bafc08498c560bc0/pybase64-1.4.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5eb588d35a04302ef6157d17db62354a787ac6f8b1585dd0b90c33d63a97a550", size = 66570, upload-time = "2025-12-06T13:22:40.221Z" }, + { url = "https://files.pythonhosted.org/packages/4c/71/774748eecc7fe23869b7e5df028e3c4c2efa16b506b83ea3fa035ea95dc2/pybase64-1.4.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:df8b122d5be2c96962231cc4831d9c2e1eae6736fb12850cec4356d8b06fe6f8", size = 55700, upload-time = "2025-12-06T13:22:41.289Z" }, + { url = "https://files.pythonhosted.org/packages/b3/91/dd15075bb2fe0086193e1cd4bad80a43652c38d8a572f9218d46ba721802/pybase64-1.4.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:31b7a85c661fc591bbcce82fb8adaebe2941e6a83b08444b0957b77380452a4b", size = 52491, upload-time = "2025-12-06T13:22:42.628Z" }, + { url = "https://files.pythonhosted.org/packages/7b/27/f357d63ea3774c937fc47160e040419ed528827aa3d4306d5ec9826259c0/pybase64-1.4.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e6d7beaae65979fef250e25e66cf81c68a8f81910bcda1a2f43297ab486a7e4e", size = 53957, upload-time = "2025-12-06T13:22:44.615Z" }, + { url = "https://files.pythonhosted.org/packages/b3/c3/243693771701a54e67ff5ccbf4c038344f429613f5643169a7befc51f007/pybase64-1.4.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:4a6276bc3a3962d172a2b5aba544d89881c4037ea954517b86b00892c703d007", size = 68422, upload-time = "2025-12-06T13:22:45.641Z" }, + { url = "https://files.pythonhosted.org/packages/75/95/f987081bf6bc1d1eda3012dae1b06ad427732ef9933a632cb8b58f9917f8/pybase64-1.4.3-cp310-cp310-win32.whl", hash = "sha256:4bdd07ef017515204ee6eaab17e1ad05f83c0ccb5af8ae24a0fe6d9cb5bb0b7a", size = 33622, upload-time = "2025-12-06T13:22:47.348Z" }, + { url = "https://files.pythonhosted.org/packages/79/28/c169a769fe90128f16d394aad87b2096dd4bf2f035ae0927108a46b617df/pybase64-1.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:5db0b6bbda15110db2740c61970a8fda3bf9c93c3166a3f57f87c7865ed1125c", size = 35799, upload-time = "2025-12-06T13:22:48.731Z" }, + { url = "https://files.pythonhosted.org/packages/ab/f2/bdbe6af0bd4f3fe5bc70e77ead7f7d523bb9d3ca3ad50ac42b9adbb9ca14/pybase64-1.4.3-cp310-cp310-win_arm64.whl", hash = "sha256:f96367dfc82598569aa02b1103ebd419298293e59e1151abda2b41728703284b", size = 31158, upload-time = "2025-12-06T13:22:50.021Z" }, + { url = "https://files.pythonhosted.org/packages/2b/63/21e981e9d3f1f123e0b0ee2130112b1956cad9752309f574862c7ae77c08/pybase64-1.4.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:70b0d4a4d54e216ce42c2655315378b8903933ecfa32fced453989a92b4317b2", size = 38237, upload-time = "2025-12-06T13:22:52.159Z" }, + { url = "https://files.pythonhosted.org/packages/92/fb/3f448e139516404d2a3963915cc10dc9dde7d3a67de4edba2f827adfef17/pybase64-1.4.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8127f110cdee7a70e576c5c9c1d4e17e92e76c191869085efbc50419f4ae3c72", size = 31673, upload-time = "2025-12-06T13:22:53.241Z" }, + { url = "https://files.pythonhosted.org/packages/3c/fb/bb06a5b9885e7d853ac1e801c4d8abfdb4c8506deee33e53d55aa6690e67/pybase64-1.4.3-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:f9ef0388878bc15a084bd9bf73ec1b2b4ee513d11009b1506375e10a7aae5032", size = 68331, upload-time = "2025-12-06T13:22:54.197Z" }, + { url = "https://files.pythonhosted.org/packages/64/15/8d60b9ec5e658185fc2ee3333e01a6e30d717cf677b24f47cbb3a859d13c/pybase64-1.4.3-cp311-cp311-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:95a57cccf106352a72ed8bc8198f6820b16cc7d55aa3867a16dea7011ae7c218", size = 71370, upload-time = "2025-12-06T13:22:55.517Z" }, + { url = "https://files.pythonhosted.org/packages/ac/29/a3e5c1667cc8c38d025a4636855de0fc117fc62e2afeb033a3c6f12c6a22/pybase64-1.4.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cd1c47dfceb9c7bd3de210fb4e65904053ed2d7c9dce6d107f041ff6fbd7e21", size = 59834, upload-time = "2025-12-06T13:22:56.682Z" }, + { url = "https://files.pythonhosted.org/packages/a9/00/8ffcf9810bd23f3984698be161cf7edba656fd639b818039a7be1d6405d4/pybase64-1.4.3-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:9fe9922698f3e2f72874b26890d53a051c431d942701bb3a37aae94da0b12107", size = 56652, upload-time = "2025-12-06T13:22:57.724Z" }, + { url = "https://files.pythonhosted.org/packages/81/62/379e347797cdea4ab686375945bc77ad8d039c688c0d4d0cfb09d247beb9/pybase64-1.4.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:af5f4bd29c86b59bb4375e0491d16ec8a67548fa99c54763aaedaf0b4b5a6632", size = 59382, upload-time = "2025-12-06T13:22:58.758Z" }, + { url = "https://files.pythonhosted.org/packages/c6/f2/9338ffe2f487086f26a2c8ca175acb3baa86fce0a756ff5670a0822bb877/pybase64-1.4.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:c302f6ca7465262908131411226e02100f488f531bb5e64cb901aa3f439bccd9", size = 59990, upload-time = "2025-12-06T13:23:01.007Z" }, + { url = "https://files.pythonhosted.org/packages/f9/a4/85a6142b65b4df8625b337727aa81dc199642de3d09677804141df6ee312/pybase64-1.4.3-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:2f3f439fa4d7fde164ebbbb41968db7d66b064450ab6017c6c95cef0afa2b349", size = 54923, upload-time = "2025-12-06T13:23:02.369Z" }, + { url = "https://files.pythonhosted.org/packages/ac/00/e40215d25624012bf5b7416ca37f168cb75f6dd15acdb91ea1f2ea4dc4e7/pybase64-1.4.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7a23c6866551043f8b681a5e1e0d59469148b2920a3b4fc42b1275f25ea4217a", size = 58664, upload-time = "2025-12-06T13:23:03.378Z" }, + { url = "https://files.pythonhosted.org/packages/b0/73/d7e19a63e795c13837f2356268d95dc79d1180e756f57ced742a1e52fdeb/pybase64-1.4.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:56e6526f8565642abc5f84338cc131ce298a8ccab696b19bdf76fa6d7dc592ef", size = 52338, upload-time = "2025-12-06T13:23:04.458Z" }, + { url = "https://files.pythonhosted.org/packages/f2/32/3c746d7a310b69bdd9df77ffc85c41b80bce00a774717596f869b0d4a20e/pybase64-1.4.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6a792a8b9d866ffa413c9687d9b611553203753987a3a582d68cbc51cf23da45", size = 68993, upload-time = "2025-12-06T13:23:05.526Z" }, + { url = "https://files.pythonhosted.org/packages/5d/b3/63cec68f9d6f6e4c0b438d14e5f1ef536a5fe63ce14b70733ac5e31d7ab8/pybase64-1.4.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:62ad29a5026bb22cfcd1ca484ec34b0a5ced56ddba38ceecd9359b2818c9c4f9", size = 58055, upload-time = "2025-12-06T13:23:06.931Z" }, + { url = "https://files.pythonhosted.org/packages/d5/cb/7acf7c3c06f9692093c07f109668725dc37fb9a3df0fa912b50add645195/pybase64-1.4.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:11b9d1d2d32ec358c02214363b8fc3651f6be7dd84d880ecd597a6206a80e121", size = 54430, upload-time = "2025-12-06T13:23:07.936Z" }, + { url = "https://files.pythonhosted.org/packages/33/39/4eb33ff35d173bfff4002e184ce8907f5d0a42d958d61cd9058ef3570179/pybase64-1.4.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0aebaa7f238caa0a0d373616016e2040c6c879ebce3ba7ab3c59029920f13640", size = 56272, upload-time = "2025-12-06T13:23:09.253Z" }, + { url = "https://files.pythonhosted.org/packages/19/97/a76d65c375a254e65b730c6f56bf528feca91305da32eceab8bcc08591e6/pybase64-1.4.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e504682b20c63c2b0c000e5f98a80ea867f8d97642e042a5a39818e44ba4d599", size = 70904, upload-time = "2025-12-06T13:23:10.336Z" }, + { url = "https://files.pythonhosted.org/packages/5e/2c/8338b6d3da3c265002839e92af0a80d6db88385c313c73f103dfb800c857/pybase64-1.4.3-cp311-cp311-win32.whl", hash = "sha256:e9a8b81984e3c6fb1db9e1614341b0a2d98c0033d693d90c726677db1ffa3a4c", size = 33639, upload-time = "2025-12-06T13:23:11.9Z" }, + { url = "https://files.pythonhosted.org/packages/39/dc/32efdf2f5927e5449cc341c266a1bbc5fecd5319a8807d9c5405f76e6d02/pybase64-1.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:a90a8fa16a901fabf20de824d7acce07586e6127dc2333f1de05f73b1f848319", size = 35797, upload-time = "2025-12-06T13:23:13.174Z" }, + { url = "https://files.pythonhosted.org/packages/da/59/eda4f9cb0cbce5a45f0cd06131e710674f8123a4d570772c5b9694f88559/pybase64-1.4.3-cp311-cp311-win_arm64.whl", hash = "sha256:61d87de5bc94d143622e94390ec3e11b9c1d4644fe9be3a81068ab0f91056f59", size = 31160, upload-time = "2025-12-06T13:23:15.696Z" }, + { url = "https://files.pythonhosted.org/packages/86/a7/efcaa564f091a2af7f18a83c1c4875b1437db56ba39540451dc85d56f653/pybase64-1.4.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:18d85e5ab8b986bb32d8446aca6258ed80d1bafe3603c437690b352c648f5967", size = 38167, upload-time = "2025-12-06T13:23:16.821Z" }, + { url = "https://files.pythonhosted.org/packages/db/c7/c7ad35adff2d272bf2930132db2b3eea8c44bb1b1f64eb9b2b8e57cde7b4/pybase64-1.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3f5791a3491d116d0deaf4d83268f48792998519698f8751efb191eac84320e9", size = 31673, upload-time = "2025-12-06T13:23:17.835Z" }, + { url = "https://files.pythonhosted.org/packages/43/1b/9a8cab0042b464e9a876d5c65fe5127445a2436da36fda64899b119b1a1b/pybase64-1.4.3-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:f0b3f200c3e06316f6bebabd458b4e4bcd4c2ca26af7c0c766614d91968dee27", size = 68210, upload-time = "2025-12-06T13:23:18.813Z" }, + { url = "https://files.pythonhosted.org/packages/62/f7/965b79ff391ad208b50e412b5d3205ccce372a2d27b7218ae86d5295b105/pybase64-1.4.3-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bb632edfd132b3eaf90c39c89aa314beec4e946e210099b57d40311f704e11d4", size = 71599, upload-time = "2025-12-06T13:23:20.195Z" }, + { url = "https://files.pythonhosted.org/packages/03/4b/a3b5175130b3810bbb8ccfa1edaadbd3afddb9992d877c8a1e2f274b476e/pybase64-1.4.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:356ef1d74648ce997f5a777cf8f1aefecc1c0b4fe6201e0ef3ec8a08170e1b54", size = 59922, upload-time = "2025-12-06T13:23:21.487Z" }, + { url = "https://files.pythonhosted.org/packages/da/5d/c38d1572027fc601b62d7a407721688b04b4d065d60ca489912d6893e6cf/pybase64-1.4.3-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:c48361f90db32bacaa5518419d4eb9066ba558013aaf0c7781620279ecddaeb9", size = 56712, upload-time = "2025-12-06T13:23:22.77Z" }, + { url = "https://files.pythonhosted.org/packages/e7/d4/4e04472fef485caa8f561d904d4d69210a8f8fc1608ea15ebd9012b92655/pybase64-1.4.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:702bcaa16ae02139d881aeaef5b1c8ffb4a3fae062fe601d1e3835e10310a517", size = 59300, upload-time = "2025-12-06T13:23:24.543Z" }, + { url = "https://files.pythonhosted.org/packages/86/e7/16e29721b86734b881d09b7e23dfd7c8408ad01a4f4c7525f3b1088e25ec/pybase64-1.4.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:53d0ffe1847b16b647c6413d34d1de08942b7724273dd57e67dcbdb10c574045", size = 60278, upload-time = "2025-12-06T13:23:25.608Z" }, + { url = "https://files.pythonhosted.org/packages/b1/02/18515f211d7c046be32070709a8efeeef8a0203de4fd7521e6b56404731b/pybase64-1.4.3-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:9a1792e8b830a92736dae58f0c386062eb038dfe8004fb03ba33b6083d89cd43", size = 54817, upload-time = "2025-12-06T13:23:26.633Z" }, + { url = "https://files.pythonhosted.org/packages/e7/be/14e29d8e1a481dbff151324c96dd7b5d2688194bb65dc8a00ca0e1ad1e86/pybase64-1.4.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1d468b1b1ac5ad84875a46eaa458663c3721e8be5f155ade356406848d3701f6", size = 58611, upload-time = "2025-12-06T13:23:27.684Z" }, + { url = "https://files.pythonhosted.org/packages/b4/8a/a2588dfe24e1bbd742a554553778ab0d65fdf3d1c9a06d10b77047d142aa/pybase64-1.4.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:e97b7bdbd62e71898cd542a6a9e320d9da754ff3ebd02cb802d69087ee94d468", size = 52404, upload-time = "2025-12-06T13:23:28.714Z" }, + { url = "https://files.pythonhosted.org/packages/27/fc/afcda7445bebe0cbc38cafdd7813234cdd4fc5573ff067f1abf317bb0cec/pybase64-1.4.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b33aeaa780caaa08ffda87fc584d5eab61e3d3bbb5d86ead02161dc0c20d04bc", size = 68817, upload-time = "2025-12-06T13:23:30.079Z" }, + { url = "https://files.pythonhosted.org/packages/d3/3a/87c3201e555ed71f73e961a787241a2438c2bbb2ca8809c29ddf938a3157/pybase64-1.4.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1c0efcf78f11cf866bed49caa7b97552bc4855a892f9cc2372abcd3ed0056f0d", size = 57854, upload-time = "2025-12-06T13:23:31.17Z" }, + { url = "https://files.pythonhosted.org/packages/fd/7d/931c2539b31a7b375e7d595b88401eeb5bd6c5ce1059c9123f9b608aaa14/pybase64-1.4.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:66e3791f2ed725a46593f8bd2761ff37d01e2cdad065b1dceb89066f476e50c6", size = 54333, upload-time = "2025-12-06T13:23:32.422Z" }, + { url = "https://files.pythonhosted.org/packages/de/5e/537601e02cc01f27e9d75f440f1a6095b8df44fc28b1eef2cd739aea8cec/pybase64-1.4.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:72bb0b6bddadab26e1b069bb78e83092711a111a80a0d6b9edcb08199ad7299b", size = 56492, upload-time = "2025-12-06T13:23:33.515Z" }, + { url = "https://files.pythonhosted.org/packages/96/97/2a2e57acf8f5c9258d22aba52e71f8050e167b29ed2ee1113677c1b600c1/pybase64-1.4.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5b3365dbcbcdb0a294f0f50af0c0a16b27a232eddeeb0bceeefd844ef30d2a23", size = 70974, upload-time = "2025-12-06T13:23:36.27Z" }, + { url = "https://files.pythonhosted.org/packages/75/2e/a9e28941c6dab6f06e6d3f6783d3373044be9b0f9a9d3492c3d8d2260ac0/pybase64-1.4.3-cp312-cp312-win32.whl", hash = "sha256:7bca1ed3a5df53305c629ca94276966272eda33c0d71f862d2d3d043f1e1b91a", size = 33686, upload-time = "2025-12-06T13:23:37.848Z" }, + { url = "https://files.pythonhosted.org/packages/83/e3/507ab649d8c3512c258819c51d25c45d6e29d9ca33992593059e7b646a33/pybase64-1.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:9f2da8f56d9b891b18b4daf463a0640eae45a80af548ce435be86aa6eff3603b", size = 35833, upload-time = "2025-12-06T13:23:38.877Z" }, + { url = "https://files.pythonhosted.org/packages/bc/8a/6eba66cd549a2fc74bb4425fd61b839ba0ab3022d3c401b8a8dc2cc00c7a/pybase64-1.4.3-cp312-cp312-win_arm64.whl", hash = "sha256:0631d8a2d035de03aa9bded029b9513e1fee8ed80b7ddef6b8e9389ffc445da0", size = 31185, upload-time = "2025-12-06T13:23:39.908Z" }, + { url = "https://files.pythonhosted.org/packages/3a/50/b7170cb2c631944388fe2519507fe3835a4054a6a12a43f43781dae82be1/pybase64-1.4.3-cp313-cp313-android_21_arm64_v8a.whl", hash = "sha256:ea4b785b0607d11950b66ce7c328f452614aefc9c6d3c9c28bae795dc7f072e1", size = 33901, upload-time = "2025-12-06T13:23:40.951Z" }, + { url = "https://files.pythonhosted.org/packages/48/8b/69f50578e49c25e0a26e3ee72c39884ff56363344b79fc3967f5af420ed6/pybase64-1.4.3-cp313-cp313-android_21_x86_64.whl", hash = "sha256:6a10b6330188c3026a8b9c10e6b9b3f2e445779cf16a4c453d51a072241c65a2", size = 40807, upload-time = "2025-12-06T13:23:42.006Z" }, + { url = "https://files.pythonhosted.org/packages/5c/8d/20b68f11adfc4c22230e034b65c71392e3e338b413bf713c8945bd2ccfb3/pybase64-1.4.3-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:27fdff227a0c0e182e0ba37a99109645188978b920dfb20d8b9c17eeee370d0d", size = 30932, upload-time = "2025-12-06T13:23:43.348Z" }, + { url = "https://files.pythonhosted.org/packages/f7/79/b1b550ac6bff51a4880bf6e089008b2e1ca16f2c98db5e039a08ac3ad157/pybase64-1.4.3-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:2a8204f1fdfec5aa4184249b51296c0de95445869920c88123978304aad42df1", size = 31394, upload-time = "2025-12-06T13:23:44.317Z" }, + { url = "https://files.pythonhosted.org/packages/82/70/b5d7c5932bf64ee1ec5da859fbac981930b6a55d432a603986c7f509c838/pybase64-1.4.3-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:874fc2a3777de6baf6aa921a7aa73b3be98295794bea31bd80568a963be30767", size = 38078, upload-time = "2025-12-06T13:23:45.348Z" }, + { url = "https://files.pythonhosted.org/packages/56/fe/e66fe373bce717c6858427670736d54297938dad61c5907517ab4106bd90/pybase64-1.4.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2dc64a94a9d936b8e3449c66afabbaa521d3cc1a563d6bbaaa6ffa4535222e4b", size = 38158, upload-time = "2025-12-06T13:23:46.872Z" }, + { url = "https://files.pythonhosted.org/packages/80/a9/b806ed1dcc7aed2ea3dd4952286319e6f3a8b48615c8118f453948e01999/pybase64-1.4.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e48f86de1c145116ccf369a6e11720ce696c2ec02d285f440dfb57ceaa0a6cb4", size = 31672, upload-time = "2025-12-06T13:23:47.88Z" }, + { url = "https://files.pythonhosted.org/packages/1c/c9/24b3b905cf75e23a9a4deaf203b35ffcb9f473ac0e6d8257f91a05dfce62/pybase64-1.4.3-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:1d45c8fe8fe82b65c36b227bb4a2cf623d9ada16bed602ce2d3e18c35285b72a", size = 68244, upload-time = "2025-12-06T13:23:49.026Z" }, + { url = "https://files.pythonhosted.org/packages/f8/cd/d15b0c3e25e5859fab0416dc5b96d34d6bd2603c1c96a07bb2202b68ab92/pybase64-1.4.3-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:ad70c26ba091d8f5167e9d4e1e86a0483a5414805cdb598a813db635bd3be8b8", size = 71620, upload-time = "2025-12-06T13:23:50.081Z" }, + { url = "https://files.pythonhosted.org/packages/0d/31/4ca953cc3dcde2b3711d6bfd70a6f4ad2ca95a483c9698076ba605f1520f/pybase64-1.4.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e98310b7c43145221e7194ac9fa7fffc84763c87bfc5e2f59f9f92363475bdc1", size = 59930, upload-time = "2025-12-06T13:23:51.68Z" }, + { url = "https://files.pythonhosted.org/packages/60/55/e7f7bdcd0fd66e61dda08db158ffda5c89a306bbdaaf5a062fbe4e48f4a1/pybase64-1.4.3-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:398685a76034e91485a28aeebcb49e64cd663212fd697b2497ac6dfc1df5e671", size = 56425, upload-time = "2025-12-06T13:23:52.732Z" }, + { url = "https://files.pythonhosted.org/packages/cb/65/b592c7f921e51ca1aca3af5b0d201a98666d0a36b930ebb67e7c2ed27395/pybase64-1.4.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:7e46400a6461187ccb52ed75b0045d937529e801a53a9cd770b350509f9e4d50", size = 59327, upload-time = "2025-12-06T13:23:53.856Z" }, + { url = "https://files.pythonhosted.org/packages/23/95/1613d2fb82dbb1548595ad4179f04e9a8451bfa18635efce18b631eabe3f/pybase64-1.4.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:1b62b9f2f291d94f5e0b76ab499790b7dcc78a009d4ceea0b0428770267484b6", size = 60294, upload-time = "2025-12-06T13:23:54.937Z" }, + { url = "https://files.pythonhosted.org/packages/9d/73/40431f37f7d1b3eab4673e7946ff1e8f5d6bd425ec257e834dae8a6fc7b0/pybase64-1.4.3-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:f30ceb5fa4327809dede614be586efcbc55404406d71e1f902a6fdcf322b93b2", size = 54858, upload-time = "2025-12-06T13:23:56.031Z" }, + { url = "https://files.pythonhosted.org/packages/a7/84/f6368bcaf9f743732e002a9858646fd7a54f428490d427dd6847c5cfe89e/pybase64-1.4.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0d5f18ed53dfa1d4cf8b39ee542fdda8e66d365940e11f1710989b3cf4a2ed66", size = 58629, upload-time = "2025-12-06T13:23:57.12Z" }, + { url = "https://files.pythonhosted.org/packages/43/75/359532f9adb49c6b546cafc65c46ed75e2ccc220d514ba81c686fbd83965/pybase64-1.4.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:119d31aa4b58b85a8ebd12b63c07681a138c08dfc2fe5383459d42238665d3eb", size = 52448, upload-time = "2025-12-06T13:23:58.298Z" }, + { url = "https://files.pythonhosted.org/packages/92/6c/ade2ba244c3f33ed920a7ed572ad772eb0b5f14480b72d629d0c9e739a40/pybase64-1.4.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:3cf0218b0e2f7988cf7d738a73b6a1d14f3be6ce249d7c0f606e768366df2cce", size = 68841, upload-time = "2025-12-06T13:23:59.886Z" }, + { url = "https://files.pythonhosted.org/packages/a0/51/b345139cd236be382f2d4d4453c21ee6299e14d2f759b668e23080f8663f/pybase64-1.4.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:12f4ee5e988bc5c0c1106b0d8fc37fb0508f12dab76bac1b098cb500d148da9d", size = 57910, upload-time = "2025-12-06T13:24:00.994Z" }, + { url = "https://files.pythonhosted.org/packages/1a/b8/9f84bdc4f1c4f0052489396403c04be2f9266a66b70c776001eaf0d78c1f/pybase64-1.4.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:937826bc7b6b95b594a45180e81dd4d99bd4dd4814a443170e399163f7ff3fb6", size = 54335, upload-time = "2025-12-06T13:24:02.046Z" }, + { url = "https://files.pythonhosted.org/packages/d0/c7/be63b617d284de46578a366da77ede39c8f8e815ed0d82c7c2acca560fab/pybase64-1.4.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:88995d1460971ef80b13e3e007afbe4b27c62db0508bc7250a2ab0a0b4b91362", size = 56486, upload-time = "2025-12-06T13:24:03.141Z" }, + { url = "https://files.pythonhosted.org/packages/5e/96/f252c8f9abd6ded3ef1ccd3cdbb8393a33798007f761b23df8de1a2480e6/pybase64-1.4.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:72326fe163385ed3e1e806dd579d47fde5d8a59e51297a60fc4e6cbc1b4fc4ed", size = 70978, upload-time = "2025-12-06T13:24:04.221Z" }, + { url = "https://files.pythonhosted.org/packages/af/51/0f5714af7aeef96e30f968e4371d75ad60558aaed3579d7c6c8f1c43c18a/pybase64-1.4.3-cp313-cp313-win32.whl", hash = "sha256:b1623730c7892cf5ed0d6355e375416be6ef8d53ab9b284f50890443175c0ac3", size = 33684, upload-time = "2025-12-06T13:24:05.29Z" }, + { url = "https://files.pythonhosted.org/packages/b6/ad/0cea830a654eb08563fb8214150ef57546ece1cc421c09035f0e6b0b5ea9/pybase64-1.4.3-cp313-cp313-win_amd64.whl", hash = "sha256:8369887590f1646a5182ca2fb29252509da7ae31d4923dbb55d3e09da8cc4749", size = 35832, upload-time = "2025-12-06T13:24:06.35Z" }, + { url = "https://files.pythonhosted.org/packages/b4/0d/eec2a8214989c751bc7b4cad1860eb2c6abf466e76b77508c0f488c96a37/pybase64-1.4.3-cp313-cp313-win_arm64.whl", hash = "sha256:860b86bca71e5f0237e2ab8b2d9c4c56681f3513b1bf3e2117290c1963488390", size = 31175, upload-time = "2025-12-06T13:24:07.419Z" }, + { url = "https://files.pythonhosted.org/packages/db/c9/e23463c1a2913686803ef76b1a5ae7e6fac868249a66e48253d17ad7232c/pybase64-1.4.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:eb51db4a9c93215135dccd1895dca078e8785c357fabd983c9f9a769f08989a9", size = 38497, upload-time = "2025-12-06T13:24:08.873Z" }, + { url = "https://files.pythonhosted.org/packages/71/83/343f446b4b7a7579bf6937d2d013d82f1a63057cf05558e391ab6039d7db/pybase64-1.4.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a03ef3f529d85fd46b89971dfb00c634d53598d20ad8908fb7482955c710329d", size = 32076, upload-time = "2025-12-06T13:24:09.975Z" }, + { url = "https://files.pythonhosted.org/packages/46/fc/cb64964c3b29b432f54d1bce5e7691d693e33bbf780555151969ffd95178/pybase64-1.4.3-cp313-cp313t-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:2e745f2ce760c6cf04d8a72198ef892015ddb89f6ceba489e383518ecbdb13ab", size = 72317, upload-time = "2025-12-06T13:24:11.129Z" }, + { url = "https://files.pythonhosted.org/packages/0a/b7/fab2240da6f4e1ad46f71fa56ec577613cf5df9dce2d5b4cfaa4edd0e365/pybase64-1.4.3-cp313-cp313t-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6fac217cd9de8581a854b0ac734c50fd1fa4b8d912396c1fc2fce7c230efe3a7", size = 75534, upload-time = "2025-12-06T13:24:12.433Z" }, + { url = "https://files.pythonhosted.org/packages/91/3b/3e2f2b6e68e3d83ddb9fa799f3548fb7449765daec9bbd005a9fbe296d7f/pybase64-1.4.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:da1ee8fa04b283873de2d6e8fa5653e827f55b86bdf1a929c5367aaeb8d26f8a", size = 65399, upload-time = "2025-12-06T13:24:13.928Z" }, + { url = "https://files.pythonhosted.org/packages/6b/08/476ac5914c3b32e0274a2524fc74f01cbf4f4af4513d054e41574eb018f6/pybase64-1.4.3-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:b0bf8e884ee822ca7b1448eeb97fa131628fe0ff42f60cae9962789bd562727f", size = 60487, upload-time = "2025-12-06T13:24:15.177Z" }, + { url = "https://files.pythonhosted.org/packages/f1/b8/618a92915330cc9cba7880299b546a1d9dab1a21fd6c0292ee44a4fe608c/pybase64-1.4.3-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1bf749300382a6fd1f4f255b183146ef58f8e9cb2f44a077b3a9200dfb473a77", size = 63959, upload-time = "2025-12-06T13:24:16.854Z" }, + { url = "https://files.pythonhosted.org/packages/a5/52/af9d8d051652c3051862c442ec3861259c5cdb3fc69774bc701470bd2a59/pybase64-1.4.3-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:153a0e42329b92337664cfc356f2065248e6c9a1bd651bbcd6dcaf15145d3f06", size = 64874, upload-time = "2025-12-06T13:24:18.328Z" }, + { url = "https://files.pythonhosted.org/packages/e4/51/5381a7adf1f381bd184d33203692d3c57cf8ae9f250f380c3fecbdbe554b/pybase64-1.4.3-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:86ee56ac7f2184ca10217ed1c655c1a060273e233e692e9086da29d1ae1768db", size = 58572, upload-time = "2025-12-06T13:24:19.417Z" }, + { url = "https://files.pythonhosted.org/packages/e0/f0/578ee4ffce5818017de4fdf544e066c225bc435e73eb4793cde28a689d0b/pybase64-1.4.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:0e71a4db76726bf830b47477e7d830a75c01b2e9b01842e787a0836b0ba741e3", size = 63636, upload-time = "2025-12-06T13:24:20.497Z" }, + { url = "https://files.pythonhosted.org/packages/b9/ad/8ae94814bf20159ea06310b742433e53d5820aa564c9fdf65bf2d79f8799/pybase64-1.4.3-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:2ba7799ec88540acd9861b10551d24656ca3c2888ecf4dba2ee0a71544a8923f", size = 56193, upload-time = "2025-12-06T13:24:21.559Z" }, + { url = "https://files.pythonhosted.org/packages/d1/31/6438cfcc3d3f0fa84d229fa125c243d5094e72628e525dfefadf3bcc6761/pybase64-1.4.3-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2860299e4c74315f5951f0cf3e72ba0f201c3356c8a68f95a3ab4e620baf44e9", size = 72655, upload-time = "2025-12-06T13:24:22.673Z" }, + { url = "https://files.pythonhosted.org/packages/a3/0d/2bbc9e9c3fc12ba8a6e261482f03a544aca524f92eae0b4908c0a10ba481/pybase64-1.4.3-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:bb06015db9151f0c66c10aae8e3603adab6b6cd7d1f7335a858161d92fc29618", size = 62471, upload-time = "2025-12-06T13:24:23.8Z" }, + { url = "https://files.pythonhosted.org/packages/2c/0b/34d491e7f49c1dbdb322ea8da6adecda7c7cd70b6644557c6e4ca5c6f7c7/pybase64-1.4.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:242512a070817272865d37c8909059f43003b81da31f616bb0c391ceadffe067", size = 58119, upload-time = "2025-12-06T13:24:24.994Z" }, + { url = "https://files.pythonhosted.org/packages/ce/17/c21d0cde2a6c766923ae388fc1f78291e1564b0d38c814b5ea8a0e5e081c/pybase64-1.4.3-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:5d8277554a12d3e3eed6180ebda62786bf9fc8d7bb1ee00244258f4a87ca8d20", size = 60791, upload-time = "2025-12-06T13:24:26.046Z" }, + { url = "https://files.pythonhosted.org/packages/92/b2/eaa67038916a48de12b16f4c384bcc1b84b7ec731b23613cb05f27673294/pybase64-1.4.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f40b7ddd698fc1e13a4b64fbe405e4e0e1279e8197e37050e24154655f5f7c4e", size = 74701, upload-time = "2025-12-06T13:24:27.466Z" }, + { url = "https://files.pythonhosted.org/packages/42/10/abb7757c330bb869ebb95dab0c57edf5961ffbd6c095c8209cbbf75d117d/pybase64-1.4.3-cp313-cp313t-win32.whl", hash = "sha256:46d75c9387f354c5172582a9eaae153b53a53afeb9c19fcf764ea7038be3bd8b", size = 33965, upload-time = "2025-12-06T13:24:28.548Z" }, + { url = "https://files.pythonhosted.org/packages/63/a0/2d4e5a59188e9e6aed0903d580541aaea72dcbbab7bf50fb8b83b490b6c3/pybase64-1.4.3-cp313-cp313t-win_amd64.whl", hash = "sha256:d7344625591d281bec54e85cbfdab9e970f6219cac1570f2aa140b8c942ccb81", size = 36207, upload-time = "2025-12-06T13:24:29.646Z" }, + { url = "https://files.pythonhosted.org/packages/1f/05/95b902e8f567b4d4b41df768ccc438af618f8d111e54deaf57d2df46bd76/pybase64-1.4.3-cp313-cp313t-win_arm64.whl", hash = "sha256:28a3c60c55138e0028313f2eccd321fec3c4a0be75e57a8d3eb883730b1b0880", size = 31505, upload-time = "2025-12-06T13:24:30.687Z" }, + { url = "https://files.pythonhosted.org/packages/e4/80/4bd3dff423e5a91f667ca41982dc0b79495b90ec0c0f5d59aca513e50f8c/pybase64-1.4.3-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:015bb586a1ea1467f69d57427abe587469392215f59db14f1f5c39b52fdafaf5", size = 33835, upload-time = "2025-12-06T13:24:31.767Z" }, + { url = "https://files.pythonhosted.org/packages/45/60/a94d94cc1e3057f602e0b483c9ebdaef40911d84a232647a2fe593ab77bb/pybase64-1.4.3-cp314-cp314-android_24_x86_64.whl", hash = "sha256:d101e3a516f837c3dcc0e5a0b7db09582ebf99ed670865223123fb2e5839c6c0", size = 40673, upload-time = "2025-12-06T13:24:32.82Z" }, + { url = "https://files.pythonhosted.org/packages/e3/71/cf62b261d431857e8e054537a5c3c24caafa331de30daede7b2c6c558501/pybase64-1.4.3-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:8f183ac925a48046abe047360fe3a1b28327afb35309892132fe1915d62fb282", size = 30939, upload-time = "2025-12-06T13:24:34.001Z" }, + { url = "https://files.pythonhosted.org/packages/24/3e/d12f92a3c1f7c6ab5d53c155bff9f1084ba997a37a39a4f781ccba9455f3/pybase64-1.4.3-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:30bf3558e24dcce4da5248dcf6d73792adfcf4f504246967e9db155be4c439ad", size = 31401, upload-time = "2025-12-06T13:24:35.11Z" }, + { url = "https://files.pythonhosted.org/packages/9b/3d/9c27440031fea0d05146f8b70a460feb95d8b4e3d9ca8f45c972efb4c3d3/pybase64-1.4.3-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:a674b419de318d2ce54387dd62646731efa32b4b590907800f0bd40675c1771d", size = 38075, upload-time = "2025-12-06T13:24:36.53Z" }, + { url = "https://files.pythonhosted.org/packages/4b/d4/6c0e0cf0efd53c254173fbcd84a3d8fcbf5e0f66622473da425becec32a5/pybase64-1.4.3-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:720104fd7303d07bac302be0ff8f7f9f126f2f45c1edb4f48fdb0ff267e69fe1", size = 38257, upload-time = "2025-12-06T13:24:38.049Z" }, + { url = "https://files.pythonhosted.org/packages/50/eb/27cb0b610d5cd70f5ad0d66c14ad21c04b8db930f7139818e8fbdc14df4d/pybase64-1.4.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:83f1067f73fa5afbc3efc0565cecc6ed53260eccddef2ebe43a8ce2b99ea0e0a", size = 31685, upload-time = "2025-12-06T13:24:40.327Z" }, + { url = "https://files.pythonhosted.org/packages/db/26/b136a4b65e5c94ff06217f7726478df3f31ab1c777c2c02cf698e748183f/pybase64-1.4.3-cp314-cp314-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:b51204d349a4b208287a8aa5b5422be3baa88abf6cc8ff97ccbda34919bbc857", size = 68460, upload-time = "2025-12-06T13:24:41.735Z" }, + { url = "https://files.pythonhosted.org/packages/68/6d/84ce50e7ee1ae79984d689e05a9937b2460d4efa1e5b202b46762fb9036c/pybase64-1.4.3-cp314-cp314-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:30f2fd53efecbdde4bdca73a872a68dcb0d1bf8a4560c70a3e7746df973e1ef3", size = 71688, upload-time = "2025-12-06T13:24:42.908Z" }, + { url = "https://files.pythonhosted.org/packages/e3/57/6743e420416c3ff1b004041c85eb0ebd9c50e9cf05624664bfa1dc8b5625/pybase64-1.4.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0932b0c5cfa617091fd74f17d24549ce5de3628791998c94ba57be808078eeaf", size = 60040, upload-time = "2025-12-06T13:24:44.37Z" }, + { url = "https://files.pythonhosted.org/packages/3b/68/733324e28068a89119af2921ce548e1c607cc5c17d354690fc51c302e326/pybase64-1.4.3-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:acb61f5ab72bec808eb0d4ce8b87ec9f38d7d750cb89b1371c35eb8052a29f11", size = 56478, upload-time = "2025-12-06T13:24:45.815Z" }, + { url = "https://files.pythonhosted.org/packages/b5/9e/f3f4aa8cfe3357a3cdb0535b78eb032b671519d3ecc08c58c4c6b72b5a91/pybase64-1.4.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:2bc2d5bc15168f5c04c53bdfe5a1e543b2155f456ed1e16d7edce9ce73842021", size = 59463, upload-time = "2025-12-06T13:24:46.938Z" }, + { url = "https://files.pythonhosted.org/packages/aa/d1/53286038e1f0df1cf58abcf4a4a91b0f74ab44539c2547b6c31001ddd054/pybase64-1.4.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:8a7bc3cd23880bdca59758bcdd6f4ef0674f2393782763910a7466fab35ccb98", size = 60360, upload-time = "2025-12-06T13:24:48.039Z" }, + { url = "https://files.pythonhosted.org/packages/00/9a/5cc6ce95db2383d27ff4d790b8f8b46704d360d701ab77c4f655bcfaa6a7/pybase64-1.4.3-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:ad15acf618880d99792d71e3905b0e2508e6e331b76a1b34212fa0f11e01ad28", size = 54999, upload-time = "2025-12-06T13:24:49.547Z" }, + { url = "https://files.pythonhosted.org/packages/64/e7/c3c1d09c3d7ae79e3aa1358c6d912d6b85f29281e47aa94fc0122a415a2f/pybase64-1.4.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:448158d417139cb4851200e5fee62677ae51f56a865d50cda9e0d61bda91b116", size = 58736, upload-time = "2025-12-06T13:24:50.641Z" }, + { url = "https://files.pythonhosted.org/packages/db/d5/0baa08e3d8119b15b588c39f0d39fd10472f0372e3c54ca44649cbefa256/pybase64-1.4.3-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:9058c49b5a2f3e691b9db21d37eb349e62540f9f5fc4beabf8cbe3c732bead86", size = 52298, upload-time = "2025-12-06T13:24:51.791Z" }, + { url = "https://files.pythonhosted.org/packages/00/87/fc6f11474a1de7e27cd2acbb8d0d7508bda3efa73dfe91c63f968728b2a3/pybase64-1.4.3-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ce561724f6522907a66303aca27dce252d363fcd85884972d348f4403ba3011a", size = 69049, upload-time = "2025-12-06T13:24:53.253Z" }, + { url = "https://files.pythonhosted.org/packages/69/9d/7fb5566f669ac18b40aa5fc1c438e24df52b843c1bdc5da47d46d4c1c630/pybase64-1.4.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:63316560a94ac449fe86cb8b9e0a13714c659417e92e26a5cbf085cd0a0c838d", size = 57952, upload-time = "2025-12-06T13:24:54.342Z" }, + { url = "https://files.pythonhosted.org/packages/de/cc/ceb949232dbbd3ec4ee0190d1df4361296beceee9840390a63df8bc31784/pybase64-1.4.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:7ecd796f2ac0be7b73e7e4e232b8c16422014de3295d43e71d2b19fd4a4f5368", size = 54484, upload-time = "2025-12-06T13:24:55.774Z" }, + { url = "https://files.pythonhosted.org/packages/a7/69/659f3c8e6a5d7b753b9c42a4bd9c42892a0f10044e9c7351a4148d413a33/pybase64-1.4.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d01e102a12fb2e1ed3dc11611c2818448626637857ec3994a9cf4809dfd23477", size = 56542, upload-time = "2025-12-06T13:24:57Z" }, + { url = "https://files.pythonhosted.org/packages/85/2c/29c9e6c9c82b72025f9676f9e82eb1fd2339ad038cbcbf8b9e2ac02798fc/pybase64-1.4.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ebff797a93c2345f22183f454fd8607a34d75eca5a3a4a969c1c75b304cee39d", size = 71045, upload-time = "2025-12-06T13:24:58.179Z" }, + { url = "https://files.pythonhosted.org/packages/b9/84/5a3dce8d7a0040a5c0c14f0fe1311cd8db872913fa04438071b26b0dac04/pybase64-1.4.3-cp314-cp314-win32.whl", hash = "sha256:28b2a1bb0828c0595dc1ea3336305cd97ff85b01c00d81cfce4f92a95fb88f56", size = 34200, upload-time = "2025-12-06T13:24:59.956Z" }, + { url = "https://files.pythonhosted.org/packages/57/bc/ce7427c12384adee115b347b287f8f3cf65860b824d74fe2c43e37e81c1f/pybase64-1.4.3-cp314-cp314-win_amd64.whl", hash = "sha256:33338d3888700ff68c3dedfcd49f99bfc3b887570206130926791e26b316b029", size = 36323, upload-time = "2025-12-06T13:25:01.708Z" }, + { url = "https://files.pythonhosted.org/packages/9a/1b/2b8ffbe9a96eef7e3f6a5a7be75995eebfb6faaedc85b6da6b233e50c778/pybase64-1.4.3-cp314-cp314-win_arm64.whl", hash = "sha256:62725669feb5acb186458da2f9353e88ae28ef66bb9c4c8d1568b12a790dfa94", size = 31584, upload-time = "2025-12-06T13:25:02.801Z" }, + { url = "https://files.pythonhosted.org/packages/ac/d8/6824c2e6fb45b8fa4e7d92e3c6805432d5edc7b855e3e8e1eedaaf6efb7c/pybase64-1.4.3-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:153fe29be038948d9372c3e77ae7d1cab44e4ba7d9aaf6f064dbeea36e45b092", size = 38601, upload-time = "2025-12-06T13:25:04.222Z" }, + { url = "https://files.pythonhosted.org/packages/ea/e5/10d2b3a4ad3a4850be2704a2f70cd9c0cf55725c8885679872d3bc846c67/pybase64-1.4.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f7fe3decaa7c4a9e162327ec7bd81ce183d2b16f23c6d53b606649c6e0203e9e", size = 32078, upload-time = "2025-12-06T13:25:05.362Z" }, + { url = "https://files.pythonhosted.org/packages/43/04/8b15c34d3c2282f1c1b0850f1113a249401b618a382646a895170bc9b5e7/pybase64-1.4.3-cp314-cp314t-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:a5ae04ea114c86eb1da1f6e18d75f19e3b5ae39cb1d8d3cd87c29751a6a22780", size = 72474, upload-time = "2025-12-06T13:25:06.434Z" }, + { url = "https://files.pythonhosted.org/packages/42/00/f34b4d11278f8fdc68bc38f694a91492aa318f7c6f1bd7396197ac0f8b12/pybase64-1.4.3-cp314-cp314t-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1755b3dce3a2a5c7d17ff6d4115e8bee4a1d5aeae74469db02e47c8f477147da", size = 75706, upload-time = "2025-12-06T13:25:07.636Z" }, + { url = "https://files.pythonhosted.org/packages/bb/5d/71747d4ad7fe16df4c4c852bdbdeb1f2cf35677b48d7c34d3011a7a6ad3a/pybase64-1.4.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fb852f900e27ffc4ec1896817535a0fa19610ef8875a096b59f21d0aa42ff172", size = 65589, upload-time = "2025-12-06T13:25:08.809Z" }, + { url = "https://files.pythonhosted.org/packages/49/b1/d1e82bd58805bb5a3a662864800bab83a83a36ba56e7e3b1706c708002a5/pybase64-1.4.3-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:9cf21ea8c70c61eddab3421fbfce061fac4f2fb21f7031383005a1efdb13d0b9", size = 60670, upload-time = "2025-12-06T13:25:10.04Z" }, + { url = "https://files.pythonhosted.org/packages/15/67/16c609b7a13d1d9fc87eca12ba2dce5e67f949eeaab61a41bddff843cbb0/pybase64-1.4.3-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:afff11b331fdc27692fc75e85ae083340a35105cea1a3c4552139e2f0e0d174f", size = 64194, upload-time = "2025-12-06T13:25:11.48Z" }, + { url = "https://files.pythonhosted.org/packages/3c/11/37bc724e42960f0106c2d33dc957dcec8f760c91a908cc6c0df7718bc1a8/pybase64-1.4.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9a5143df542c1ce5c1f423874b948c4d689b3f05ec571f8792286197a39ba02", size = 64984, upload-time = "2025-12-06T13:25:12.645Z" }, + { url = "https://files.pythonhosted.org/packages/6e/66/b2b962a6a480dd5dae3029becf03ea1a650d326e39bf1c44ea3db78bb010/pybase64-1.4.3-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:d62e9861019ad63624b4a7914dff155af1cc5d6d79df3be14edcaedb5fdad6f9", size = 58750, upload-time = "2025-12-06T13:25:13.848Z" }, + { url = "https://files.pythonhosted.org/packages/2b/15/9b6d711035e29b18b2e1c03d47f41396d803d06ef15b6c97f45b75f73f04/pybase64-1.4.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:84cfd4d92668ef5766cc42a9c9474b88960ac2b860767e6e7be255c6fddbd34a", size = 63816, upload-time = "2025-12-06T13:25:15.356Z" }, + { url = "https://files.pythonhosted.org/packages/b4/21/e2901381ed0df62e2308380f30d9c4d87d6b74e33a84faed3478d33a7197/pybase64-1.4.3-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:60fc025437f9a7c2cc45e0c19ed68ed08ba672be2c5575fd9d98bdd8f01dd61f", size = 56348, upload-time = "2025-12-06T13:25:16.559Z" }, + { url = "https://files.pythonhosted.org/packages/c4/16/3d788388a178a0407aa814b976fe61bfa4af6760d9aac566e59da6e4a8b4/pybase64-1.4.3-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:edc8446196f04b71d3af76c0bd1fe0a45066ac5bffecca88adb9626ee28c266f", size = 72842, upload-time = "2025-12-06T13:25:18.055Z" }, + { url = "https://files.pythonhosted.org/packages/a6/63/c15b1f8bd47ea48a5a2d52a4ec61f037062932ea6434ab916107b58e861e/pybase64-1.4.3-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:e99f6fa6509c037794da57f906ade271f52276c956d00f748e5b118462021d48", size = 62651, upload-time = "2025-12-06T13:25:19.191Z" }, + { url = "https://files.pythonhosted.org/packages/bd/b8/f544a2e37c778d59208966d4ef19742a0be37c12fc8149ff34483c176616/pybase64-1.4.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:d94020ef09f624d841aa9a3a6029df8cf65d60d7a6d5c8687579fa68bd679b65", size = 58295, upload-time = "2025-12-06T13:25:20.822Z" }, + { url = "https://files.pythonhosted.org/packages/03/99/1fae8a3b7ac181e36f6e7864a62d42d5b1f4fa7edf408c6711e28fba6b4d/pybase64-1.4.3-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:f64ce70d89942a23602dee910dec9b48e5edf94351e1b378186b74fcc00d7f66", size = 60960, upload-time = "2025-12-06T13:25:22.099Z" }, + { url = "https://files.pythonhosted.org/packages/9d/9e/cd4c727742345ad8384569a4466f1a1428f4e5cc94d9c2ab2f53d30be3fe/pybase64-1.4.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8ea99f56e45c469818b9781903be86ba4153769f007ba0655fa3b46dc332803d", size = 74863, upload-time = "2025-12-06T13:25:23.442Z" }, + { url = "https://files.pythonhosted.org/packages/28/86/a236ecfc5b494e1e922da149689f690abc84248c7c1358f5605b8c9fdd60/pybase64-1.4.3-cp314-cp314t-win32.whl", hash = "sha256:343b1901103cc72362fd1f842524e3bb24978e31aea7ff11e033af7f373f66ab", size = 34513, upload-time = "2025-12-06T13:25:24.592Z" }, + { url = "https://files.pythonhosted.org/packages/56/ce/ca8675f8d1352e245eb012bfc75429ee9cf1f21c3256b98d9a329d44bf0f/pybase64-1.4.3-cp314-cp314t-win_amd64.whl", hash = "sha256:57aff6f7f9dea6705afac9d706432049642de5b01080d3718acc23af87c5af76", size = 36702, upload-time = "2025-12-06T13:25:25.72Z" }, + { url = "https://files.pythonhosted.org/packages/3b/30/4a675864877397179b09b720ee5fcb1cf772cf7bebc831989aff0a5f79c1/pybase64-1.4.3-cp314-cp314t-win_arm64.whl", hash = "sha256:e906aa08d4331e799400829e0f5e4177e76a3281e8a4bc82ba114c6b30e405c9", size = 31904, upload-time = "2025-12-06T13:25:26.826Z" }, + { url = "https://files.pythonhosted.org/packages/b2/7c/545fd4935a0e1ddd7147f557bf8157c73eecec9cffd523382fa7af2557de/pybase64-1.4.3-graalpy311-graalpy242_311_native-macosx_10_9_x86_64.whl", hash = "sha256:d27c1dfdb0c59a5e758e7a98bd78eaca5983c22f4a811a36f4f980d245df4611", size = 38393, upload-time = "2025-12-06T13:26:19.535Z" }, + { url = "https://files.pythonhosted.org/packages/c3/ca/ae7a96be9ddc96030d4e9dffc43635d4e136b12058b387fd47eb8301b60f/pybase64-1.4.3-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:0f1a0c51d6f159511e3431b73c25db31095ee36c394e26a4349e067c62f434e5", size = 32109, upload-time = "2025-12-06T13:26:20.72Z" }, + { url = "https://files.pythonhosted.org/packages/bf/44/d4b7adc7bf4fd5b52d8d099121760c450a52c390223806b873f0b6a2d551/pybase64-1.4.3-graalpy311-graalpy242_311_native-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a492518f3078a4e3faaef310697d21df9c6bc71908cebc8c2f6fbfa16d7d6b1f", size = 43227, upload-time = "2025-12-06T13:26:21.845Z" }, + { url = "https://files.pythonhosted.org/packages/08/86/2ba2d8734ef7939debeb52cf9952e457ba7aa226cae5c0e6dd631f9b851f/pybase64-1.4.3-graalpy311-graalpy242_311_native-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cae1a0f47784fd16df90d8acc32011c8d5fcdd9ab392c9ec49543e5f6a9c43a4", size = 35804, upload-time = "2025-12-06T13:26:23.149Z" }, + { url = "https://files.pythonhosted.org/packages/4f/5b/19c725dc3aaa6281f2ce3ea4c1628d154a40dd99657d1381995f8096768b/pybase64-1.4.3-graalpy311-graalpy242_311_native-win_amd64.whl", hash = "sha256:03cea70676ffbd39a1ab7930a2d24c625b416cacc9d401599b1d29415a43ab6a", size = 35880, upload-time = "2025-12-06T13:26:24.663Z" }, + { url = "https://files.pythonhosted.org/packages/17/45/92322aec1b6979e789b5710f73c59f2172bc37c8ce835305434796824b7b/pybase64-1.4.3-graalpy312-graalpy250_312_native-macosx_10_13_x86_64.whl", hash = "sha256:2baaa092f3475f3a9c87ac5198023918ea8b6c125f4c930752ab2cbe3cd1d520", size = 38746, upload-time = "2025-12-06T13:26:25.869Z" }, + { url = "https://files.pythonhosted.org/packages/11/94/f1a07402870388fdfc2ecec0c718111189732f7d0f2d7fe1386e19e8fad0/pybase64-1.4.3-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:cde13c0764b1af07a631729f26df019070dad759981d6975527b7e8ecb465b6c", size = 32573, upload-time = "2025-12-06T13:26:27.792Z" }, + { url = "https://files.pythonhosted.org/packages/fa/8f/43c3bb11ca9bacf81cb0b7a71500bb65b2eda6d5fe07433c09b543de97f3/pybase64-1.4.3-graalpy312-graalpy250_312_native-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5c29a582b0ea3936d02bd6fe9bf674ab6059e6e45ab71c78404ab2c913224414", size = 43461, upload-time = "2025-12-06T13:26:28.906Z" }, + { url = "https://files.pythonhosted.org/packages/2d/4c/2a5258329200be57497d3972b5308558c6de42e3749c6cc2aa1cbe34b25a/pybase64-1.4.3-graalpy312-graalpy250_312_native-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b6b664758c804fa919b4f1257aa8cf68e95db76fc331de5f70bfc3a34655afe1", size = 36058, upload-time = "2025-12-06T13:26:30.092Z" }, + { url = "https://files.pythonhosted.org/packages/ea/6d/41faa414cde66ec023b0ca8402a8f11cb61731c3dc27c082909cbbd1f929/pybase64-1.4.3-graalpy312-graalpy250_312_native-win_amd64.whl", hash = "sha256:f7537fa22ae56a0bf51e4b0ffc075926ad91c618e1416330939f7ef366b58e3b", size = 36231, upload-time = "2025-12-06T13:26:31.656Z" }, + { url = "https://files.pythonhosted.org/packages/2a/cf/6e712491bd665ea8633efb0b484121893ea838d8e830e06f39f2aae37e58/pybase64-1.4.3-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:94cf50c36bb2f8618982ee5a978c4beed9db97d35944fa96e8586dd953c7994a", size = 38007, upload-time = "2025-12-06T13:26:32.804Z" }, + { url = "https://files.pythonhosted.org/packages/38/c0/9272cae1c49176337dcdbd97511e2843faae1aaf5a5fb48569093c6cd4ce/pybase64-1.4.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:01bc3ff5ca1341685c6d2d945b035f442f7b9c3b068a5c6ee8408a41fda5754e", size = 31538, upload-time = "2025-12-06T13:26:34.001Z" }, + { url = "https://files.pythonhosted.org/packages/20/f2/17546f97befe429c73f622bbd869ceebb518c40fdb0dec4c4f98312e80a5/pybase64-1.4.3-pp310-pypy310_pp73-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:03d0aa3761a99034960496280c02aa063f856a3cc9b33771bc4eab0e4e72b5c2", size = 40682, upload-time = "2025-12-06T13:26:35.168Z" }, + { url = "https://files.pythonhosted.org/packages/92/a0/464b36d5dfb61f3da17858afaeaa876a9342d58e9f17803ce7f28b5de9e8/pybase64-1.4.3-pp310-pypy310_pp73-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7ca5b1ce768520acd6440280cdab35235b27ad2faacfcec064bc9c3377066ef1", size = 41306, upload-time = "2025-12-06T13:26:36.351Z" }, + { url = "https://files.pythonhosted.org/packages/07/c9/a748dfc0969a8d960ecf1e82c8a2a16046ffec22f8e7ece582aa3b1c6cf9/pybase64-1.4.3-pp310-pypy310_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3caa1e2ddad1c50553ffaaa1c86b74b3f9fbd505bea9970326ab88fc68c4c184", size = 35452, upload-time = "2025-12-06T13:26:37.772Z" }, + { url = "https://files.pythonhosted.org/packages/95/b7/4d37bd3577d1aa6c732dc099087fe027c48873e223de3784b095e5653f8b/pybase64-1.4.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:bd47076f736b27a8b0f9b30d93b6bb4f5af01b0dc8971f883ed3b75934f39a99", size = 36125, upload-time = "2025-12-06T13:26:39.78Z" }, + { url = "https://files.pythonhosted.org/packages/b2/76/160dded493c00d3376d4ad0f38a2119c5345de4a6693419ad39c3565959b/pybase64-1.4.3-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:277de6e03cc9090fb359365c686a2a3036d23aee6cd20d45d22b8c89d1247f17", size = 37939, upload-time = "2025-12-06T13:26:41.014Z" }, + { url = "https://files.pythonhosted.org/packages/b7/b8/a0f10be8d648d6f8f26e560d6e6955efa7df0ff1e009155717454d76f601/pybase64-1.4.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:ab1dd8b1ed2d1d750260ed58ab40defaa5ba83f76a30e18b9ebd5646f6247ae5", size = 31466, upload-time = "2025-12-06T13:26:42.539Z" }, + { url = "https://files.pythonhosted.org/packages/d3/22/832a2f9e76cdf39b52e01e40d8feeb6a04cf105494f2c3e3126d0149717f/pybase64-1.4.3-pp311-pypy311_pp73-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:bd4d2293de9fd212e294c136cec85892460b17d24e8c18a6ba18750928037750", size = 40681, upload-time = "2025-12-06T13:26:43.782Z" }, + { url = "https://files.pythonhosted.org/packages/12/d7/6610f34a8972415fab3bb4704c174a1cc477bffbc3c36e526428d0f3957d/pybase64-1.4.3-pp311-pypy311_pp73-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2af6d0d3a691911cc4c9a625f3ddcd3af720738c21be3d5c72de05629139d393", size = 41294, upload-time = "2025-12-06T13:26:44.936Z" }, + { url = "https://files.pythonhosted.org/packages/64/25/ed24400948a6c974ab1374a233cb7e8af0a5373cea0dd8a944627d17c34a/pybase64-1.4.3-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5cfc8c49a28322d82242088378f8542ce97459866ba73150b062a7073e82629d", size = 35447, upload-time = "2025-12-06T13:26:46.098Z" }, + { url = "https://files.pythonhosted.org/packages/ee/2b/e18ee7c5ee508a82897f021c1981533eca2940b5f072fc6ed0906c03a7a7/pybase64-1.4.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:debf737e09b8bf832ba86f5ecc3d3dbd0e3021d6cd86ba4abe962d6a5a77adb3", size = 36134, upload-time = "2025-12-06T13:26:47.35Z" }, ] [[package]] name = "pyclipper" -version = "1.3.0.post6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4a/b2/550fe500e49c464d73fabcb8cb04d47e4885d6ca4cfc1f5b0a125a95b19a/pyclipper-1.3.0.post6.tar.gz", hash = "sha256:42bff0102fa7a7f2abdd795a2594654d62b786d0c6cd67b72d469114fdeb608c", size = 165909, upload-time = "2024-10-18T12:23:09.069Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b5/34/0dca299fe41e9a92e78735502fed5238a4ac734755e624488df9b2eeec46/pyclipper-1.3.0.post6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fa0f5e78cfa8262277bb3d0225537b3c2a90ef68fd90a229d5d24cf49955dcf4", size = 269504, upload-time = "2024-10-18T12:21:55.735Z" }, - { url = "https://files.pythonhosted.org/packages/8a/5b/81528b08134b3c2abdfae821e1eff975c0703802d41974b02dfb2e101c55/pyclipper-1.3.0.post6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a01f182d8938c1dc515e8508ed2442f7eebd2c25c7d5cb29281f583c1a8008a4", size = 142599, upload-time = "2024-10-18T12:21:57.401Z" }, - { url = "https://files.pythonhosted.org/packages/84/a4/3e304f6c0d000382cd54d4a1e5f0d8fc28e1ae97413a2ec1016a7b840319/pyclipper-1.3.0.post6-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:640f20975727994d4abacd07396f564e9e5665ba5cb66ceb36b300c281f84fa4", size = 912209, upload-time = "2024-10-18T12:21:59.408Z" }, - { url = "https://files.pythonhosted.org/packages/f5/6a/28ec55cc3f972368b211fca017e081cf5a71009d1b8ec3559767cda5b289/pyclipper-1.3.0.post6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a63002f6bb0f1efa87c0b81634cbb571066f237067e23707dabf746306c92ba5", size = 929511, upload-time = "2024-10-18T12:22:01.454Z" }, - { url = "https://files.pythonhosted.org/packages/c4/56/c326f3454c5f30a31f58a5c3154d891fce58ad73ccbf1d3f4aacfcbd344d/pyclipper-1.3.0.post6-cp310-cp310-win32.whl", hash = "sha256:106b8622cd9fb07d80cbf9b1d752334c55839203bae962376a8c59087788af26", size = 100126, upload-time = "2024-10-18T12:22:02.83Z" }, - { url = "https://files.pythonhosted.org/packages/f8/e6/f8239af6346848b20a3448c554782fe59298ab06c1d040490242dc7e3c26/pyclipper-1.3.0.post6-cp310-cp310-win_amd64.whl", hash = "sha256:9699e98862dadefd0bea2360c31fa61ca553c660cbf6fb44993acde1b959f58f", size = 110470, upload-time = "2024-10-18T12:22:04.411Z" }, - { url = "https://files.pythonhosted.org/packages/50/a9/66ca5f252dcac93ca076698591b838ba17f9729591edf4b74fef7fbe1414/pyclipper-1.3.0.post6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c4247e7c44b34c87acbf38f99d48fb1acaf5da4a2cf4dcd601a9b24d431be4ef", size = 270930, upload-time = "2024-10-18T12:22:06.066Z" }, - { url = "https://files.pythonhosted.org/packages/59/fe/2ab5818b3504e179086e54a37ecc245525d069267b8c31b18ec3d0830cbf/pyclipper-1.3.0.post6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:851b3e58106c62a5534a1201295fe20c21714dee2eda68081b37ddb0367e6caa", size = 143411, upload-time = "2024-10-18T12:22:07.598Z" }, - { url = "https://files.pythonhosted.org/packages/09/f7/b58794f643e033a6d14da7c70f517315c3072f3c5fccdf4232fa8c8090c1/pyclipper-1.3.0.post6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16cc1705a915896d2aff52131c427df02265631279eac849ebda766432714cc0", size = 951754, upload-time = "2024-10-18T12:22:08.966Z" }, - { url = "https://files.pythonhosted.org/packages/c1/77/846a21957cd4ed266c36705ee340beaa923eb57d2bba013cfd7a5c417cfd/pyclipper-1.3.0.post6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ace1f0753cf71c5c5f6488b8feef5dd0fa8b976ad86b24bb51f708f513df4aac", size = 969608, upload-time = "2024-10-18T12:22:10.321Z" }, - { url = "https://files.pythonhosted.org/packages/c9/2b/580703daa6606d160caf596522d4cfdf62ae619b062a7ce6f905821a57e8/pyclipper-1.3.0.post6-cp311-cp311-win32.whl", hash = "sha256:dbc828641667142751b1127fd5c4291663490cf05689c85be4c5bcc89aaa236a", size = 100227, upload-time = "2024-10-18T12:22:11.991Z" }, - { url = "https://files.pythonhosted.org/packages/17/4b/a4cda18e8556d913ff75052585eb0d658500596b5f97fe8401d05123d47b/pyclipper-1.3.0.post6-cp311-cp311-win_amd64.whl", hash = "sha256:1c03f1ae43b18ee07730c3c774cc3cf88a10c12a4b097239b33365ec24a0a14a", size = 110442, upload-time = "2024-10-18T12:22:13.121Z" }, - { url = "https://files.pythonhosted.org/packages/fc/c8/197d9a1d8354922d24d11d22fb2e0cc1ebc182f8a30496b7ddbe89467ce1/pyclipper-1.3.0.post6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:6363b9d79ba1b5d8f32d1623e797c1e9f994600943402e68d5266067bdde173e", size = 270487, upload-time = "2024-10-18T12:22:14.852Z" }, - { url = "https://files.pythonhosted.org/packages/8e/8e/eb14eadf054494ad81446e21c4ea163b941747610b0eb9051644395f567e/pyclipper-1.3.0.post6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:32cd7fb9c1c893eb87f82a072dbb5e26224ea7cebbad9dc306d67e1ac62dd229", size = 143469, upload-time = "2024-10-18T12:22:16.109Z" }, - { url = "https://files.pythonhosted.org/packages/cf/e5/6c4a8df6e904c133bb4c5309d211d31c751db60cbd36a7250c02b05494a1/pyclipper-1.3.0.post6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3aab10e3c10ed8fa60c608fb87c040089b83325c937f98f06450cf9fcfdaf1d", size = 944206, upload-time = "2024-10-18T12:22:17.216Z" }, - { url = "https://files.pythonhosted.org/packages/76/65/cb014acc41cd5bf6bbfa4671c7faffffb9cee01706642c2dec70c5209ac8/pyclipper-1.3.0.post6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58eae2ff92a8cae1331568df076c4c5775bf946afab0068b217f0cf8e188eb3c", size = 963797, upload-time = "2024-10-18T12:22:18.881Z" }, - { url = "https://files.pythonhosted.org/packages/80/ec/b40cd81ab7598984167508a5369a2fa31a09fe3b3e3d0b73aa50e06d4b3f/pyclipper-1.3.0.post6-cp312-cp312-win32.whl", hash = "sha256:793b0aa54b914257aa7dc76b793dd4dcfb3c84011d48df7e41ba02b571616eaf", size = 99456, upload-time = "2024-10-18T12:22:20.084Z" }, - { url = "https://files.pythonhosted.org/packages/24/3a/7d6292e3c94fb6b872d8d7e80d909dc527ee6b0af73b753c63fdde65a7da/pyclipper-1.3.0.post6-cp312-cp312-win_amd64.whl", hash = "sha256:d3f9da96f83b8892504923beb21a481cd4516c19be1d39eb57a92ef1c9a29548", size = 110278, upload-time = "2024-10-18T12:22:21.178Z" }, - { url = "https://files.pythonhosted.org/packages/8c/b3/75232906bd13f869600d23bdb8fe6903cc899fa7e96981ae4c9b7d9c409e/pyclipper-1.3.0.post6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f129284d2c7bcd213d11c0f35e1ae506a1144ce4954e9d1734d63b120b0a1b58", size = 268254, upload-time = "2024-10-18T12:22:22.272Z" }, - { url = "https://files.pythonhosted.org/packages/0b/db/35843050a3dd7586781497a21ca6c8d48111afb66061cb40c3d3c288596d/pyclipper-1.3.0.post6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:188fbfd1d30d02247f92c25ce856f5f3c75d841251f43367dbcf10935bc48f38", size = 142204, upload-time = "2024-10-18T12:22:24.315Z" }, - { url = "https://files.pythonhosted.org/packages/7c/d7/1faa0ff35caa02cb32cb0583688cded3f38788f33e02bfe6461fbcc1bee1/pyclipper-1.3.0.post6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d6d129d0c2587f2f5904d201a4021f859afbb45fada4261c9fdedb2205b09d23", size = 943835, upload-time = "2024-10-18T12:22:26.233Z" }, - { url = "https://files.pythonhosted.org/packages/31/10/c0bf140bee2844e2c0617fdcc8a4e8daf98e71710046b06034e6f1963404/pyclipper-1.3.0.post6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c9c80b5c46eef38ba3f12dd818dc87f5f2a0853ba914b6f91b133232315f526", size = 962510, upload-time = "2024-10-18T12:22:27.573Z" }, - { url = "https://files.pythonhosted.org/packages/85/6f/8c6afc49b51b1bf16d5903ecd5aee657cf88f52c83cb5fabf771deeba728/pyclipper-1.3.0.post6-cp313-cp313-win32.whl", hash = "sha256:b15113ec4fc423b58e9ae80aa95cf5a0802f02d8f02a98a46af3d7d66ff0cc0e", size = 98836, upload-time = "2024-10-18T12:22:29.157Z" }, - { url = "https://files.pythonhosted.org/packages/d5/19/9ff4551b42f2068686c50c0d199072fa67aee57fc5cf86770cacf71efda3/pyclipper-1.3.0.post6-cp313-cp313-win_amd64.whl", hash = "sha256:e5ff68fa770ac654c7974fc78792978796f068bd274e95930c0691c31e192889", size = 109672, upload-time = "2024-10-18T12:22:30.411Z" }, +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/21/3c06205bb407e1f79b73b7b4dfb3950bd9537c4f625a68ab5cc41177f5bc/pyclipper-1.4.0.tar.gz", hash = "sha256:9882bd889f27da78add4dd6f881d25697efc740bf840274e749988d25496c8e1", size = 54489, upload-time = "2025-12-01T13:15:35.015Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/9f/a10173d32ecc2ce19a04d018163f3ca22a04c0c6ad03b464dcd32f9152a8/pyclipper-1.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:bafad70d2679c187120e8c44e1f9a8b06150bad8c0aecf612ad7dfbfa9510f73", size = 264510, upload-time = "2025-12-01T13:14:46.551Z" }, + { url = "https://files.pythonhosted.org/packages/e0/c2/5490ddc4a1f7ceeaa0258f4266397e720c02db515b2ca5bc69b85676f697/pyclipper-1.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0b74a9dd44b22a7fd35d65fb1ceeba57f3817f34a97a28c3255556362e491447", size = 139498, upload-time = "2025-12-01T13:14:48.31Z" }, + { url = "https://files.pythonhosted.org/packages/3b/0a/bea9102d1d75634b1a5702b0e92982451a1eafca73c4845d3dbe27eba13d/pyclipper-1.4.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0a4d2736fb3c42e8eb1d38bf27a720d1015526c11e476bded55138a977c17d9d", size = 970974, upload-time = "2025-12-01T13:14:49.799Z" }, + { url = "https://files.pythonhosted.org/packages/8b/1b/097f8776d5b3a10eb7b443b632221f4ed825d892e79e05682f4b10a1a59c/pyclipper-1.4.0-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b3b3630051b53ad2564cb079e088b112dd576e3d91038338ad1cc7915e0f14dc", size = 943315, upload-time = "2025-12-01T13:14:51.266Z" }, + { url = "https://files.pythonhosted.org/packages/fd/4d/17d6a3f1abf0f368d58f2309e80ee3761afb1fd1342f7780ab32ba4f0b1d/pyclipper-1.4.0-cp310-cp310-win32.whl", hash = "sha256:8d42b07a2f6cfe2d9b87daf345443583f00a14e856927782fde52f3a255e305a", size = 95286, upload-time = "2025-12-01T13:14:52.922Z" }, + { url = "https://files.pythonhosted.org/packages/53/ca/b30138427ed122ec9b47980b943164974a2ec606fa3f71597033b9a9f9a6/pyclipper-1.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:6a97b961f182b92d899ca88c1bb3632faea2e00ce18d07c5f789666ebb021ca4", size = 104227, upload-time = "2025-12-01T13:14:54.013Z" }, + { url = "https://files.pythonhosted.org/packages/de/e3/64cf7794319b088c288706087141e53ac259c7959728303276d18adc665d/pyclipper-1.4.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:adcb7ca33c5bdc33cd775e8b3eadad54873c802a6d909067a57348bcb96e7a2d", size = 264281, upload-time = "2025-12-01T13:14:55.47Z" }, + { url = "https://files.pythonhosted.org/packages/34/cd/44ec0da0306fa4231e76f1c2cb1fa394d7bde8db490a2b24d55b39865f69/pyclipper-1.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fd24849d2b94ec749ceac7c34c9f01010d23b6e9d9216cf2238b8481160e703d", size = 139426, upload-time = "2025-12-01T13:14:56.683Z" }, + { url = "https://files.pythonhosted.org/packages/ad/88/d8f6c6763ea622fe35e19c75d8b39ed6c55191ddc82d65e06bc46b26cb8e/pyclipper-1.4.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1b6c8d75ba20c6433c9ea8f1a0feb7e4d3ac06a09ad1fd6d571afc1ddf89b869", size = 989649, upload-time = "2025-12-01T13:14:58.28Z" }, + { url = "https://files.pythonhosted.org/packages/ff/e9/ea7d68c8c4af3842d6515bedcf06418610ad75f111e64c92c1d4785a1513/pyclipper-1.4.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:58e29d7443d7cc0e83ee9daf43927730386629786d00c63b04fe3b53ac01462c", size = 962842, upload-time = "2025-12-01T13:15:00.044Z" }, + { url = "https://files.pythonhosted.org/packages/4e/b7/0b4a272d8726e51ab05e2b933d8cc47f29757fb8212e38b619e170e6015c/pyclipper-1.4.0-cp311-cp311-win32.whl", hash = "sha256:a8d2b5fb75ebe57e21ce61e79a9131edec2622ff23cc665e4d1d1f201bc1a801", size = 95098, upload-time = "2025-12-01T13:15:01.359Z" }, + { url = "https://files.pythonhosted.org/packages/3a/76/4901de2919198bb2bd3d989f86d4a1dff363962425bb2d63e24e6c990042/pyclipper-1.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:e9b973467d9c5fa9bc30bb6ac95f9f4d7c3d9fc25f6cf2d1cc972088e5955c01", size = 104362, upload-time = "2025-12-01T13:15:02.439Z" }, + { url = "https://files.pythonhosted.org/packages/90/1b/7a07b68e0842324d46c03e512d8eefa9cb92ba2a792b3b4ebf939dafcac3/pyclipper-1.4.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:222ac96c8b8281b53d695b9c4fedc674f56d6d4320ad23f1bdbd168f4e316140", size = 265676, upload-time = "2025-12-01T13:15:04.15Z" }, + { url = "https://files.pythonhosted.org/packages/6b/dd/8bd622521c05d04963420ae6664093f154343ed044c53ea260a310c8bb4d/pyclipper-1.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f3672dbafbb458f1b96e1ee3e610d174acb5ace5bd2ed5d1252603bb797f2fc6", size = 140458, upload-time = "2025-12-01T13:15:05.76Z" }, + { url = "https://files.pythonhosted.org/packages/7a/06/6e3e241882bf7d6ab23d9c69ba4e85f1ec47397cbbeee948a16cf75e21ed/pyclipper-1.4.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d1f807e2b4760a8e5c6d6b4e8c1d71ef52b7fe1946ff088f4fa41e16a881a5ca", size = 978235, upload-time = "2025-12-01T13:15:06.993Z" }, + { url = "https://files.pythonhosted.org/packages/cf/f4/3418c1cd5eea640a9fa2501d4bc0b3655fa8d40145d1a4f484b987990a75/pyclipper-1.4.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce1f83c9a4e10ea3de1959f0ae79e9a5bd41346dff648fee6228ba9eaf8b3872", size = 961388, upload-time = "2025-12-01T13:15:08.467Z" }, + { url = "https://files.pythonhosted.org/packages/ac/94/c85401d24be634af529c962dd5d781f3cb62a67cd769534df2cb3feee97a/pyclipper-1.4.0-cp312-cp312-win32.whl", hash = "sha256:3ef44b64666ebf1cb521a08a60c3e639d21b8c50bfbe846ba7c52a0415e936f4", size = 95169, upload-time = "2025-12-01T13:15:10.098Z" }, + { url = "https://files.pythonhosted.org/packages/97/77/dfea08e3b230b82ee22543c30c35d33d42f846a77f96caf7c504dd54fab1/pyclipper-1.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:d1e5498d883b706a4ce636247f0d830c6eb34a25b843a1b78e2c969754ca9037", size = 104619, upload-time = "2025-12-01T13:15:11.592Z" }, + { url = "https://files.pythonhosted.org/packages/67/d0/cbce7d47de1e6458f66a4d999b091640134deb8f2c7351eab993b70d2e10/pyclipper-1.4.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d49df13cbb2627ccb13a1046f3ea6ebf7177b5504ec61bdef87d6a704046fd6e", size = 264342, upload-time = "2025-12-01T13:15:12.697Z" }, + { url = "https://files.pythonhosted.org/packages/ce/cc/742b9d69d96c58ac156947e1b56d0f81cbacbccf869e2ac7229f2f86dc4e/pyclipper-1.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:37bfec361e174110cdddffd5ecd070a8064015c99383d95eb692c253951eee8a", size = 139839, upload-time = "2025-12-01T13:15:13.911Z" }, + { url = "https://files.pythonhosted.org/packages/db/48/dd301d62c1529efdd721b47b9e5fb52120fcdac5f4d3405cfc0d2f391414/pyclipper-1.4.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:14c8bdb5a72004b721c4e6f448d2c2262d74a7f0c9e3076aeff41e564a92389f", size = 972142, upload-time = "2025-12-01T13:15:15.477Z" }, + { url = "https://files.pythonhosted.org/packages/07/bf/d493fd1b33bb090fa64e28c1009374d5d72fa705f9331cd56517c35e381e/pyclipper-1.4.0-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f2a50c22c3a78cb4e48347ecf06930f61ce98cf9252f2e292aa025471e9d75b1", size = 952789, upload-time = "2025-12-01T13:15:17.042Z" }, + { url = "https://files.pythonhosted.org/packages/cf/88/b95ea8ea21ddca34aa14b123226a81526dd2faaa993f9aabd3ed21231604/pyclipper-1.4.0-cp313-cp313-win32.whl", hash = "sha256:c9a3faa416ff536cee93417a72bfb690d9dea136dc39a39dbbe1e5dadf108c9c", size = 94817, upload-time = "2025-12-01T13:15:18.724Z" }, + { url = "https://files.pythonhosted.org/packages/ba/42/0a1920d276a0e1ca21dc0d13ee9e3ba10a9a8aa3abac76cd5e5a9f503306/pyclipper-1.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:d4b2d7c41086f1927d14947c563dfc7beed2f6c0d9af13c42fe3dcdc20d35832", size = 104007, upload-time = "2025-12-01T13:15:19.763Z" }, + { url = "https://files.pythonhosted.org/packages/1a/20/04d58c70f3ccd404f179f8dd81d16722a05a3bf1ab61445ee64e8218c1f8/pyclipper-1.4.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:7c87480fc91a5af4c1ba310bdb7de2f089a3eeef5fe351a3cedc37da1fcced1c", size = 265167, upload-time = "2025-12-01T13:15:20.844Z" }, + { url = "https://files.pythonhosted.org/packages/bd/2e/a570c1abe69b7260ca0caab4236ce6ea3661193ebf8d1bd7f78ccce537a5/pyclipper-1.4.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:81d8bb2d1fb9d66dc7ea4373b176bb4b02443a7e328b3b603a73faec088b952e", size = 139966, upload-time = "2025-12-01T13:15:22.036Z" }, + { url = "https://files.pythonhosted.org/packages/e8/3b/e0859e54adabdde8a24a29d3f525ebb31c71ddf2e8d93edce83a3c212ffc/pyclipper-1.4.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:773c0e06b683214dcfc6711be230c83b03cddebe8a57eae053d4603dd63582f9", size = 968216, upload-time = "2025-12-01T13:15:23.18Z" }, + { url = "https://files.pythonhosted.org/packages/f6/6b/e3c4febf0a35ae643ee579b09988dd931602b5bf311020535fd9e5b7e715/pyclipper-1.4.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9bc45f2463d997848450dbed91c950ca37c6cf27f84a49a5cad4affc0b469e39", size = 954198, upload-time = "2025-12-01T13:15:24.522Z" }, + { url = "https://files.pythonhosted.org/packages/fc/74/728efcee02e12acb486ce9d56fa037120c9bf5b77c54bbdbaa441c14a9d9/pyclipper-1.4.0-cp314-cp314-win32.whl", hash = "sha256:0b8c2105b3b3c44dbe1a266f64309407fe30bf372cf39a94dc8aaa97df00da5b", size = 96951, upload-time = "2025-12-01T13:15:25.79Z" }, + { url = "https://files.pythonhosted.org/packages/e3/d7/7f4354e69f10a917e5c7d5d72a499ef2e10945312f5e72c414a0a08d2ae4/pyclipper-1.4.0-cp314-cp314-win_amd64.whl", hash = "sha256:6c317e182590c88ec0194149995e3d71a979cfef3b246383f4e035f9d4a11826", size = 106782, upload-time = "2025-12-01T13:15:26.945Z" }, + { url = "https://files.pythonhosted.org/packages/63/60/fc32c7a3d7f61a970511ec2857ecd09693d8ac80d560ee7b8e67a6d268c9/pyclipper-1.4.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:f160a2c6ba036f7eaf09f1f10f4fbfa734234af9112fb5187877efed78df9303", size = 269880, upload-time = "2025-12-01T13:15:28.117Z" }, + { url = "https://files.pythonhosted.org/packages/49/df/c4a72d3f62f0ba03ec440c4fff56cd2d674a4334d23c5064cbf41c9583f6/pyclipper-1.4.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:a9f11ad133257c52c40d50de7a0ca3370a0cdd8e3d11eec0604ad3c34ba549e9", size = 141706, upload-time = "2025-12-01T13:15:30.134Z" }, + { url = "https://files.pythonhosted.org/packages/c5/0b/cf55df03e2175e1e2da9db585241401e0bc98f76bee3791bed39d0313449/pyclipper-1.4.0-cp314-cp314t-win32.whl", hash = "sha256:bbc827b77442c99deaeee26e0e7f172355ddb097a5e126aea206d447d3b26286", size = 105308, upload-time = "2025-12-01T13:15:31.225Z" }, + { url = "https://files.pythonhosted.org/packages/8f/dc/53df8b6931d47080b4fe4ee8450d42e660ee1c5c1556c7ab73359182b769/pyclipper-1.4.0-cp314-cp314t-win_amd64.whl", hash = "sha256:29dae3e0296dff8502eeb7639fcfee794b0eec8590ba3563aee28db269da6b04", size = 117608, upload-time = "2025-12-01T13:15:32.69Z" }, + { url = "https://files.pythonhosted.org/packages/18/59/81050abdc9e5b90ffc2c765738c5e40e9abd8e44864aaa737b600f16c562/pyclipper-1.4.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:98b2a40f98e1fc1b29e8a6094072e7e0c7dfe901e573bf6cfc6eb7ce84a7ae87", size = 126495, upload-time = "2025-12-01T13:15:33.743Z" }, ] [[package]] @@ -5564,7 +5699,7 @@ wheels = [ [[package]] name = "pydantic" -version = "2.12.4" +version = "2.12.5" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-types" }, @@ -5572,9 +5707,9 @@ dependencies = [ { name = "typing-extensions" }, { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/96/ad/a17bc283d7d81837c061c49e3eaa27a45991759a1b7eae1031921c6bd924/pydantic-2.12.4.tar.gz", hash = "sha256:0f8cb9555000a4b5b617f66bfd2566264c4984b27589d3b845685983e8ea85ac", size = 821038, upload-time = "2025-11-05T10:50:08.59Z" } +sdist = { url = "https://files.pythonhosted.org/packages/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591, upload-time = "2025-11-26T15:11:46.471Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/82/2f/e68750da9b04856e2a7ec56fc6f034a5a79775e9b9a81882252789873798/pydantic-2.12.4-py3-none-any.whl", hash = "sha256:92d3d202a745d46f9be6df459ac5a064fdaa3c1c4cd8adcfa332ccf3c05f871e", size = 463400, upload-time = "2025-11-05T10:50:06.732Z" }, + { url = "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d", size = 463580, upload-time = "2025-11-26T15:11:44.605Z" }, ] [[package]] @@ -5738,20 +5873,20 @@ sdist = { url = "https://files.pythonhosted.org/packages/5d/ab/34ec41718af73c001 [[package]] name = "pymdown-extensions" -version = "10.17.1" +version = "10.20" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "markdown" }, { name = "pyyaml" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e7/d9/a987e4d549c6c82353fce5fa5f650229bb60ea4c0d1684a2714a509aef58/pymdown_extensions-10.17.1.tar.gz", hash = "sha256:60d05fe55e7fb5a1e4740fc575facad20dc6ee3a748e8d3d36ba44142e75ce03", size = 845207, upload-time = "2025-11-11T21:44:58.815Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3e/35/e3814a5b7df295df69d035cfb8aab78b2967cdf11fcfae7faed726b66664/pymdown_extensions-10.20.tar.gz", hash = "sha256:5c73566ab0cf38c6ba084cb7c5ea64a119ae0500cce754ccb682761dfea13a52", size = 852774, upload-time = "2025-12-31T19:59:42.211Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/81/40/b2d7b9fdccc63e48ae4dbd363b6b89eb7ac346ea49ed667bb71f92af3021/pymdown_extensions-10.17.1-py3-none-any.whl", hash = "sha256:1f160209c82eecbb5d8a0d8f89a4d9bd6bdcbde9a8537761844cfc57ad5cd8a6", size = 266310, upload-time = "2025-11-11T21:44:56.809Z" }, + { url = "https://files.pythonhosted.org/packages/ea/10/47caf89cbb52e5bb764696fd52a8c591a2f0e851a93270c05a17f36000b5/pymdown_extensions-10.20-py3-none-any.whl", hash = "sha256:ea9e62add865da80a271d00bfa1c0fa085b20d133fb3fc97afdc88e682f60b2f", size = 268733, upload-time = "2025-12-31T19:59:40.652Z" }, ] [[package]] name = "pymilvus" -version = "2.6.5" +version = "2.6.6" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "grpcio" }, @@ -5761,9 +5896,9 @@ dependencies = [ { name = "python-dotenv" }, { name = "setuptools" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/02/04/9ace30346a8fe2d8e9a047678bb563bc63e1e181d6a583a8a205806a211b/pymilvus-2.6.5.tar.gz", hash = "sha256:08f790acbbb4888f76394daa807c0227efdd744b6d39f3130f39afe77ba17ac6", size = 1365608, upload-time = "2025-12-05T08:59:49.777Z" } +sdist = { url = "https://files.pythonhosted.org/packages/64/a3/66781567d1067f1fbf7dc5dc12f6af1c4b05bf17f81fe6d6b644353b9b72/pymilvus-2.6.6.tar.gz", hash = "sha256:2522617ea34dcc9adaad0128d230f0306962a122f08366a24dd0e84bdada1f60", size = 1380350, upload-time = "2025-12-30T09:11:28.477Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/58/45/c5416f2d64dd8add626a90166d8389a97ebc39c107ea01c15ea57bf3a07f/pymilvus-2.6.5-py3-none-any.whl", hash = "sha256:9e1caddd96361cd41f4e0685b6bd3d99bbaea94c8284b1fef5575bcfd47d7a2f", size = 280832, upload-time = "2025-12-05T08:59:48.016Z" }, + { url = "https://files.pythonhosted.org/packages/e5/ab/890c3e258c09981a4df875fe762166b92111fc1f9fb1e646025ebe3acb1b/pymilvus-2.6.6-py3-none-any.whl", hash = "sha256:0e61daa573b0025650f072493cb978a9ada9cdb1d450594707592174b1f297c0", size = 285098, upload-time = "2025-12-30T09:11:27.099Z" }, ] [package.optional-dependencies] @@ -5784,7 +5919,7 @@ dependencies = [ { name = "onnxruntime" }, { name = "protobuf" }, { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "scipy", version = "1.16.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "scipy", version = "1.17.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "transformers" }, ] sdist = { url = "https://files.pythonhosted.org/packages/0c/8f/a8212f3d932d566fdec354b49befa09174b239c8b8ad25a33b798cee393b/pymilvus_model-0.3.2.tar.gz", hash = "sha256:10ab49a989396943e08555b971f85182ddb50da47076e699a157c9a5ff542872", size = 36268, upload-time = "2025-03-31T08:47:45.007Z" } @@ -5794,19 +5929,20 @@ wheels = [ [[package]] name = "pynndescent" -version = "0.5.13" +version = "0.6.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "joblib" }, { name = "llvmlite" }, { name = "numba" }, - { name = "scikit-learn" }, + { name = "scikit-learn", version = "1.7.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "scikit-learn", version = "1.8.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "scipy", version = "1.16.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "scipy", version = "1.17.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7e/58/560a4db5eb3794d922fe55804b10326534ded3d971e1933c1eef91193f5e/pynndescent-0.5.13.tar.gz", hash = "sha256:d74254c0ee0a1eeec84597d5fe89fedcf778593eeabe32c2f97412934a9800fb", size = 2975955, upload-time = "2024-06-17T15:48:32.914Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4a/fb/7f58c397fb31666756457ee2ac4c0289ef2daad57f4ae4be8dec12f80b03/pynndescent-0.6.0.tar.gz", hash = "sha256:7ffde0fb5b400741e055a9f7d377e3702e02250616834231f6c209e39aac24f5", size = 2992987, upload-time = "2026-01-08T21:29:58.943Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/53/d23a97e0a2c690d40b165d1062e2c4ccc796be458a1ce59f6ba030434663/pynndescent-0.5.13-py3-none-any.whl", hash = "sha256:69aabb8f394bc631b6ac475a1c7f3994c54adf3f51cd63b2730fefba5771b949", size = 56850, upload-time = "2024-06-17T15:48:31.184Z" }, + { url = "https://files.pythonhosted.org/packages/b2/e6/94145d714402fd5ade00b5661f2d0ab981219e07f7db9bfa16786cdb9c04/pynndescent-0.6.0-py3-none-any.whl", hash = "sha256:dc8c74844e4c7f5cbd1e0cd6909da86fdc789e6ff4997336e344779c3d5538ef", size = 73511, upload-time = "2026-01-08T21:29:57.306Z" }, ] [[package]] @@ -5829,7 +5965,7 @@ name = "pyobjc-framework-cocoa" version = "12.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, ] sdist = { url = "https://files.pythonhosted.org/packages/02/a3/16ca9a15e77c061a9250afbae2eae26f2e1579eb8ca9462ae2d2c71e1169/pyobjc_framework_cocoa-12.1.tar.gz", hash = "sha256:5556c87db95711b985d5efdaaf01c917ddd41d148b1e52a0c66b1a2e2c5c1640", size = 2772191, upload-time = "2025-11-14T10:13:02.069Z" } wheels = [ @@ -5847,8 +5983,8 @@ name = "pyobjc-framework-coreml" version = "12.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/30/2d/baa9ea02cbb1c200683cb7273b69b4bee5070e86f2060b77e6a27c2a9d7e/pyobjc_framework_coreml-12.1.tar.gz", hash = "sha256:0d1a4216891a18775c9e0170d908714c18e4f53f9dc79fb0f5263b2aa81609ba", size = 40465, upload-time = "2025-11-14T10:14:02.265Z" } wheels = [ @@ -5866,8 +6002,8 @@ name = "pyobjc-framework-quartz" version = "12.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, ] sdist = { url = "https://files.pythonhosted.org/packages/94/18/cc59f3d4355c9456fc945eae7fe8797003c4da99212dd531ad1b0de8a0c6/pyobjc_framework_quartz-12.1.tar.gz", hash = "sha256:27f782f3513ac88ec9b6c82d9767eef95a5cf4175ce88a1e5a65875fee799608", size = 3159099, upload-time = "2025-11-14T10:21:24.31Z" } wheels = [ @@ -5885,10 +6021,10 @@ name = "pyobjc-framework-vision" version = "12.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-coreml", marker = "sys_platform == 'darwin'" }, - { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-core" }, + { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-framework-coreml" }, + { name = "pyobjc-framework-quartz" }, ] sdist = { url = "https://files.pythonhosted.org/packages/c2/5a/08bb3e278f870443d226c141af14205ff41c0274da1e053b72b11dfc9fb2/pyobjc_framework_vision-12.1.tar.gz", hash = "sha256:a30959100e85dcede3a786c544e621ad6eb65ff6abf85721f805822b8c5fe9b0", size = 59538, upload-time = "2025-11-14T10:23:21.979Z" } wheels = [ @@ -5947,20 +6083,20 @@ wheels = [ [[package]] name = "pyright" -version = "1.1.407" +version = "1.1.408" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "nodeenv" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a6/1b/0aa08ee42948b61745ac5b5b5ccaec4669e8884b53d31c8ec20b2fcd6b6f/pyright-1.1.407.tar.gz", hash = "sha256:099674dba5c10489832d4a4b2d302636152a9a42d317986c38474c76fe562262", size = 4122872, upload-time = "2025-10-24T23:17:15.145Z" } +sdist = { url = "https://files.pythonhosted.org/packages/74/b2/5db700e52554b8f025faa9c3c624c59f1f6c8841ba81ab97641b54322f16/pyright-1.1.408.tar.gz", hash = "sha256:f28f2321f96852fa50b5829ea492f6adb0e6954568d1caa3f3af3a5f555eb684", size = 4400578, upload-time = "2026-01-08T08:07:38.795Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/93/b69052907d032b00c40cb656d21438ec00b3a471733de137a3f65a49a0a0/pyright-1.1.407-py3-none-any.whl", hash = "sha256:6dd419f54fcc13f03b52285796d65e639786373f433e243f8b94cf93a7444d21", size = 5997008, upload-time = "2025-10-24T23:17:13.159Z" }, + { url = "https://files.pythonhosted.org/packages/0c/82/a2c93e32800940d9573fb28c346772a14778b84ba7524e691b324620ab89/pyright-1.1.408-py3-none-any.whl", hash = "sha256:090b32865f4fdb1e0e6cd82bf5618480d48eecd2eb2e70f960982a3d9a4c17c1", size = 6399144, upload-time = "2026-01-08T08:07:37.082Z" }, ] [[package]] name = "pytest" -version = "9.0.1" +version = "9.0.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, @@ -5971,9 +6107,9 @@ dependencies = [ { name = "pygments" }, { name = "tomli", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/07/56/f013048ac4bc4c1d9be45afd4ab209ea62822fb1598f40687e6bf45dcea4/pytest-9.0.1.tar.gz", hash = "sha256:3e9c069ea73583e255c3b21cf46b8d3c56f6e3a1a8f6da94ccb0fcf57b9d73c8", size = 1564125, upload-time = "2025-11-12T13:05:09.333Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0b/8b/6300fb80f858cda1c51ffa17075df5d846757081d11ab4aa35cef9e6258b/pytest-9.0.1-py3-none-any.whl", hash = "sha256:67be0030d194df2dfa7b556f2e56fb3c3315bd5c8822c6951162b92b32ce7dad", size = 373668, upload-time = "2025-11-12T13:05:07.379Z" }, + { url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" }, ] [[package]] @@ -6318,7 +6454,7 @@ wheels = [ [[package]] name = "rapidocr" -version = "3.4.2" +version = "3.5.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorlog" }, @@ -6335,7 +6471,7 @@ dependencies = [ { name = "tqdm" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/3c/83/5b8c8075954c5b61d938b8954710d986134c4ca7c32a841ad7d8c844cf6c/rapidocr-3.4.2-py3-none-any.whl", hash = "sha256:17845fa8cc9a20a935111e59482f2214598bba1547000cfd960d8924dd4522a5", size = 15056674, upload-time = "2025-10-11T14:43:00.296Z" }, + { url = "https://files.pythonhosted.org/packages/d2/f5/d93dfbb4f96acefe3a978ab2d0eab74d96c0388870f8d275792317c4857c/rapidocr-3.5.0-py3-none-any.whl", hash = "sha256:c0e361ffd0a26d2f27827361b0c563a87ee311e62b8c56ad52472c0fc6f1d78a", size = 15063134, upload-time = "2026-01-06T14:37:28.117Z" }, ] [[package]] @@ -6512,126 +6648,139 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/25/7a/b0178788f8dc6cafce37a212c99565fa1fe7872c70c6c9c1e1a372d9d88f/rich-14.2.0-py3-none-any.whl", hash = "sha256:76bc51fe2e57d2b1be1f96c524b890b816e334ab4c1e45888799bfaab0021edd", size = 243393, upload-time = "2025-10-09T14:16:51.245Z" }, ] +[[package]] +name = "rouge-score" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "absl-py" }, + { name = "nltk" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e2/c5/9136736c37022a6ad27fea38f3111eb8f02fe75d067f9a985cc358653102/rouge_score-0.1.2.tar.gz", hash = "sha256:c7d4da2683e68c9abf0135ef915d63a46643666f848e558a1b9f7ead17ff0f04", size = 17400, upload-time = "2022-07-22T22:46:22.909Z" } + [[package]] name = "rpds-py" -version = "0.29.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/98/33/23b3b3419b6a3e0f559c7c0d2ca8fc1b9448382b25245033788785921332/rpds_py-0.29.0.tar.gz", hash = "sha256:fe55fe686908f50154d1dc599232016e50c243b438c3b7432f24e2895b0e5359", size = 69359, upload-time = "2025-11-16T14:50:39.532Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/7a/c5b2ff381b74bc742768e8d870f26babac4ef256ba160bdbf8d57af56461/rpds_py-0.29.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:4ae4b88c6617e1b9e5038ab3fccd7bac0842fdda2b703117b2aa99bc85379113", size = 372385, upload-time = "2025-11-16T14:47:36.287Z" }, - { url = "https://files.pythonhosted.org/packages/28/36/531f1eb4d5bed4a9c150f363a7ec4a98d2dc746151bba5473bc38ee85dec/rpds_py-0.29.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7d9128ec9d8cecda6f044001fde4fb71ea7c24325336612ef8179091eb9596b9", size = 362869, upload-time = "2025-11-16T14:47:38.196Z" }, - { url = "https://files.pythonhosted.org/packages/54/df/7e9c0493a2015d9c82807a2d5f023ea9774e27a4c15b33ef1cdb7456138d/rpds_py-0.29.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d37812c3da8e06f2bb35b3cf10e4a7b68e776a706c13058997238762b4e07f4f", size = 391582, upload-time = "2025-11-16T14:47:39.746Z" }, - { url = "https://files.pythonhosted.org/packages/15/38/42a981c3592ef46fbd7e17adbf8730cc5ec87e6aa1770c658c44bbb52960/rpds_py-0.29.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:66786c3fb1d8de416a7fa8e1cb1ec6ba0a745b2b0eee42f9b7daa26f1a495545", size = 405685, upload-time = "2025-11-16T14:47:41.472Z" }, - { url = "https://files.pythonhosted.org/packages/12/45/628b8c15856c3849c3f52ec6dac93c046ed5faeed4a435af03b70525fd29/rpds_py-0.29.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b58f5c77f1af888b5fd1876c9a0d9858f6f88a39c9dd7c073a88e57e577da66d", size = 527067, upload-time = "2025-11-16T14:47:43.036Z" }, - { url = "https://files.pythonhosted.org/packages/dc/ba/6b56d09badeabd95098016d72a437d4a0fd82d4672ce92a7607df5d70a42/rpds_py-0.29.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:799156ef1f3529ed82c36eb012b5d7a4cf4b6ef556dd7cc192148991d07206ae", size = 412532, upload-time = "2025-11-16T14:47:44.484Z" }, - { url = "https://files.pythonhosted.org/packages/f1/39/2f1f3db92888314b50b8f9641f679188bd24b3665a8cb9923b7201ae8011/rpds_py-0.29.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:453783477aa4f2d9104c4b59b08c871431647cb7af51b549bbf2d9eb9c827756", size = 392736, upload-time = "2025-11-16T14:47:46.053Z" }, - { url = "https://files.pythonhosted.org/packages/60/43/3c3b1dcd827e50f2ae28786d846b8a351080d8a69a3b49bc10ae44cc39b1/rpds_py-0.29.0-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:24a7231493e3c4a4b30138b50cca089a598e52c34cf60b2f35cebf62f274fdea", size = 406300, upload-time = "2025-11-16T14:47:47.268Z" }, - { url = "https://files.pythonhosted.org/packages/da/02/bc96021b67f8525e6bcdd68935c4543ada61e1f3dcb067ed037d68b8c6d2/rpds_py-0.29.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7033c1010b1f57bb44d8067e8c25aa6fa2e944dbf46ccc8c92b25043839c3fd2", size = 423641, upload-time = "2025-11-16T14:47:48.878Z" }, - { url = "https://files.pythonhosted.org/packages/38/e9/c435ddb602ced19a80b8277a41371734f33ad3f91cc4ceb4d82596800a3c/rpds_py-0.29.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0248b19405422573621172ab8e3a1f29141362d13d9f72bafa2e28ea0cdca5a2", size = 574153, upload-time = "2025-11-16T14:47:50.435Z" }, - { url = "https://files.pythonhosted.org/packages/84/82/dc3c32e1f89ecba8a59600d4cd65fe0ad81b6c636ccdbf6cd177fd6a7bac/rpds_py-0.29.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:f9f436aee28d13b9ad2c764fc273e0457e37c2e61529a07b928346b219fcde3b", size = 600304, upload-time = "2025-11-16T14:47:51.599Z" }, - { url = "https://files.pythonhosted.org/packages/35/98/785290e0b7142470735dc1b1f68fb33aae29e5296f062c88396eedf796c8/rpds_py-0.29.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:24a16cb7163933906c62c272de20ea3c228e4542c8c45c1d7dc2b9913e17369a", size = 562211, upload-time = "2025-11-16T14:47:53.094Z" }, - { url = "https://files.pythonhosted.org/packages/30/58/4eeddcb0737c6875f3e30c65dc9d7e7a10dfd5779646a990fa602c6d56c5/rpds_py-0.29.0-cp310-cp310-win32.whl", hash = "sha256:1a409b0310a566bfd1be82119891fefbdce615ccc8aa558aff7835c27988cbef", size = 221803, upload-time = "2025-11-16T14:47:54.404Z" }, - { url = "https://files.pythonhosted.org/packages/54/77/b35a8dbdcbeb32505500547cdafaa9f8863e85f8faac50ef34464ec5a256/rpds_py-0.29.0-cp310-cp310-win_amd64.whl", hash = "sha256:c5523b0009e7c3c1263471b69d8da1c7d41b3ecb4cb62ef72be206b92040a950", size = 235530, upload-time = "2025-11-16T14:47:56.061Z" }, - { url = "https://files.pythonhosted.org/packages/36/ab/7fb95163a53ab122c74a7c42d2d2f012819af2cf3deb43fb0d5acf45cc1a/rpds_py-0.29.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:9b9c764a11fd637e0322a488560533112837f5334ffeb48b1be20f6d98a7b437", size = 372344, upload-time = "2025-11-16T14:47:57.279Z" }, - { url = "https://files.pythonhosted.org/packages/b3/45/f3c30084c03b0d0f918cb4c5ae2c20b0a148b51ba2b3f6456765b629bedd/rpds_py-0.29.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3fd2164d73812026ce970d44c3ebd51e019d2a26a4425a5dcbdfa93a34abc383", size = 363041, upload-time = "2025-11-16T14:47:58.908Z" }, - { url = "https://files.pythonhosted.org/packages/e3/e9/4d044a1662608c47a87cbb37b999d4d5af54c6d6ebdda93a4d8bbf8b2a10/rpds_py-0.29.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a097b7f7f7274164566ae90a221fd725363c0e9d243e2e9ed43d195ccc5495c", size = 391775, upload-time = "2025-11-16T14:48:00.197Z" }, - { url = "https://files.pythonhosted.org/packages/50/c9/7616d3ace4e6731aeb6e3cd85123e03aec58e439044e214b9c5c60fd8eb1/rpds_py-0.29.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7cdc0490374e31cedefefaa1520d5fe38e82fde8748cbc926e7284574c714d6b", size = 405624, upload-time = "2025-11-16T14:48:01.496Z" }, - { url = "https://files.pythonhosted.org/packages/c2/e2/6d7d6941ca0843609fd2d72c966a438d6f22617baf22d46c3d2156c31350/rpds_py-0.29.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:89ca2e673ddd5bde9b386da9a0aac0cab0e76f40c8f0aaf0d6311b6bbf2aa311", size = 527894, upload-time = "2025-11-16T14:48:03.167Z" }, - { url = "https://files.pythonhosted.org/packages/8d/f7/aee14dc2db61bb2ae1e3068f134ca9da5f28c586120889a70ff504bb026f/rpds_py-0.29.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a5d9da3ff5af1ca1249b1adb8ef0573b94c76e6ae880ba1852f033bf429d4588", size = 412720, upload-time = "2025-11-16T14:48:04.413Z" }, - { url = "https://files.pythonhosted.org/packages/2f/e2/2293f236e887c0360c2723d90c00d48dee296406994d6271faf1712e94ec/rpds_py-0.29.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8238d1d310283e87376c12f658b61e1ee23a14c0e54c7c0ce953efdbdc72deed", size = 392945, upload-time = "2025-11-16T14:48:06.252Z" }, - { url = "https://files.pythonhosted.org/packages/14/cd/ceea6147acd3bd1fd028d1975228f08ff19d62098078d5ec3eed49703797/rpds_py-0.29.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:2d6fb2ad1c36f91c4646989811e84b1ea5e0c3cf9690b826b6e32b7965853a63", size = 406385, upload-time = "2025-11-16T14:48:07.575Z" }, - { url = "https://files.pythonhosted.org/packages/52/36/fe4dead19e45eb77a0524acfdbf51e6cda597b26fc5b6dddbff55fbbb1a5/rpds_py-0.29.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:534dc9df211387547267ccdb42253aa30527482acb38dd9b21c5c115d66a96d2", size = 423943, upload-time = "2025-11-16T14:48:10.175Z" }, - { url = "https://files.pythonhosted.org/packages/a1/7b/4551510803b582fa4abbc8645441a2d15aa0c962c3b21ebb380b7e74f6a1/rpds_py-0.29.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d456e64724a075441e4ed648d7f154dc62e9aabff29bcdf723d0c00e9e1d352f", size = 574204, upload-time = "2025-11-16T14:48:11.499Z" }, - { url = "https://files.pythonhosted.org/packages/64/ba/071ccdd7b171e727a6ae079f02c26f75790b41555f12ca8f1151336d2124/rpds_py-0.29.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:a738f2da2f565989401bd6fd0b15990a4d1523c6d7fe83f300b7e7d17212feca", size = 600587, upload-time = "2025-11-16T14:48:12.822Z" }, - { url = "https://files.pythonhosted.org/packages/03/09/96983d48c8cf5a1e03c7d9cc1f4b48266adfb858ae48c7c2ce978dbba349/rpds_py-0.29.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a110e14508fd26fd2e472bb541f37c209409876ba601cf57e739e87d8a53cf95", size = 562287, upload-time = "2025-11-16T14:48:14.108Z" }, - { url = "https://files.pythonhosted.org/packages/40/f0/8c01aaedc0fa92156f0391f39ea93b5952bc0ec56b897763858f95da8168/rpds_py-0.29.0-cp311-cp311-win32.whl", hash = "sha256:923248a56dd8d158389a28934f6f69ebf89f218ef96a6b216a9be6861804d3f4", size = 221394, upload-time = "2025-11-16T14:48:15.374Z" }, - { url = "https://files.pythonhosted.org/packages/7e/a5/a8b21c54c7d234efdc83dc034a4d7cd9668e3613b6316876a29b49dece71/rpds_py-0.29.0-cp311-cp311-win_amd64.whl", hash = "sha256:539eb77eb043afcc45314d1be09ea6d6cafb3addc73e0547c171c6d636957f60", size = 235713, upload-time = "2025-11-16T14:48:16.636Z" }, - { url = "https://files.pythonhosted.org/packages/a7/1f/df3c56219523947b1be402fa12e6323fe6d61d883cf35d6cb5d5bb6db9d9/rpds_py-0.29.0-cp311-cp311-win_arm64.whl", hash = "sha256:bdb67151ea81fcf02d8f494703fb728d4d34d24556cbff5f417d74f6f5792e7c", size = 229157, upload-time = "2025-11-16T14:48:17.891Z" }, - { url = "https://files.pythonhosted.org/packages/3c/50/bc0e6e736d94e420df79be4deb5c9476b63165c87bb8f19ef75d100d21b3/rpds_py-0.29.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a0891cfd8db43e085c0ab93ab7e9b0c8fee84780d436d3b266b113e51e79f954", size = 376000, upload-time = "2025-11-16T14:48:19.141Z" }, - { url = "https://files.pythonhosted.org/packages/3e/3a/46676277160f014ae95f24de53bed0e3b7ea66c235e7de0b9df7bd5d68ba/rpds_py-0.29.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3897924d3f9a0361472d884051f9a2460358f9a45b1d85a39a158d2f8f1ad71c", size = 360575, upload-time = "2025-11-16T14:48:20.443Z" }, - { url = "https://files.pythonhosted.org/packages/75/ba/411d414ed99ea1afdd185bbabeeaac00624bd1e4b22840b5e9967ade6337/rpds_py-0.29.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a21deb8e0d1571508c6491ce5ea5e25669b1dd4adf1c9d64b6314842f708b5d", size = 392159, upload-time = "2025-11-16T14:48:22.12Z" }, - { url = "https://files.pythonhosted.org/packages/8f/b1/e18aa3a331f705467a48d0296778dc1fea9d7f6cf675bd261f9a846c7e90/rpds_py-0.29.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9efe71687d6427737a0a2de9ca1c0a216510e6cd08925c44162be23ed7bed2d5", size = 410602, upload-time = "2025-11-16T14:48:23.563Z" }, - { url = "https://files.pythonhosted.org/packages/2f/6c/04f27f0c9f2299274c76612ac9d2c36c5048bb2c6c2e52c38c60bf3868d9/rpds_py-0.29.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:40f65470919dc189c833e86b2c4bd21bd355f98436a2cef9e0a9a92aebc8e57e", size = 515808, upload-time = "2025-11-16T14:48:24.949Z" }, - { url = "https://files.pythonhosted.org/packages/83/56/a8412aa464fb151f8bc0d91fb0bb888adc9039bd41c1c6ba8d94990d8cf8/rpds_py-0.29.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:def48ff59f181130f1a2cb7c517d16328efac3ec03951cca40c1dc2049747e83", size = 416015, upload-time = "2025-11-16T14:48:26.782Z" }, - { url = "https://files.pythonhosted.org/packages/04/4c/f9b8a05faca3d9e0a6397c90d13acb9307c9792b2bff621430c58b1d6e76/rpds_py-0.29.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad7bd570be92695d89285a4b373006930715b78d96449f686af422debb4d3949", size = 395325, upload-time = "2025-11-16T14:48:28.055Z" }, - { url = "https://files.pythonhosted.org/packages/34/60/869f3bfbf8ed7b54f1ad9a5543e0fdffdd40b5a8f587fe300ee7b4f19340/rpds_py-0.29.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:5a572911cd053137bbff8e3a52d31c5d2dba51d3a67ad902629c70185f3f2181", size = 410160, upload-time = "2025-11-16T14:48:29.338Z" }, - { url = "https://files.pythonhosted.org/packages/91/aa/e5b496334e3aba4fe4c8a80187b89f3c1294c5c36f2a926da74338fa5a73/rpds_py-0.29.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d583d4403bcbf10cffc3ab5cee23d7643fcc960dff85973fd3c2d6c86e8dbb0c", size = 425309, upload-time = "2025-11-16T14:48:30.691Z" }, - { url = "https://files.pythonhosted.org/packages/85/68/4e24a34189751ceb6d66b28f18159922828dd84155876551f7ca5b25f14f/rpds_py-0.29.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:070befbb868f257d24c3bb350dbd6e2f645e83731f31264b19d7231dd5c396c7", size = 574644, upload-time = "2025-11-16T14:48:31.964Z" }, - { url = "https://files.pythonhosted.org/packages/8c/cf/474a005ea4ea9c3b4f17b6108b6b13cebfc98ebaff11d6e1b193204b3a93/rpds_py-0.29.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:fc935f6b20b0c9f919a8ff024739174522abd331978f750a74bb68abd117bd19", size = 601605, upload-time = "2025-11-16T14:48:33.252Z" }, - { url = "https://files.pythonhosted.org/packages/f4/b1/c56f6a9ab8c5f6bb5c65c4b5f8229167a3a525245b0773f2c0896686b64e/rpds_py-0.29.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8c5a8ecaa44ce2d8d9d20a68a2483a74c07f05d72e94a4dff88906c8807e77b0", size = 564593, upload-time = "2025-11-16T14:48:34.643Z" }, - { url = "https://files.pythonhosted.org/packages/b3/13/0494cecce4848f68501e0a229432620b4b57022388b071eeff95f3e1e75b/rpds_py-0.29.0-cp312-cp312-win32.whl", hash = "sha256:ba5e1aeaf8dd6d8f6caba1f5539cddda87d511331714b7b5fc908b6cfc3636b7", size = 223853, upload-time = "2025-11-16T14:48:36.419Z" }, - { url = "https://files.pythonhosted.org/packages/1f/6a/51e9aeb444a00cdc520b032a28b07e5f8dc7bc328b57760c53e7f96997b4/rpds_py-0.29.0-cp312-cp312-win_amd64.whl", hash = "sha256:b5f6134faf54b3cb83375db0f113506f8b7770785be1f95a631e7e2892101977", size = 239895, upload-time = "2025-11-16T14:48:37.956Z" }, - { url = "https://files.pythonhosted.org/packages/d1/d4/8bce56cdad1ab873e3f27cb31c6a51d8f384d66b022b820525b879f8bed1/rpds_py-0.29.0-cp312-cp312-win_arm64.whl", hash = "sha256:b016eddf00dca7944721bf0cd85b6af7f6c4efaf83ee0b37c4133bd39757a8c7", size = 230321, upload-time = "2025-11-16T14:48:39.71Z" }, - { url = "https://files.pythonhosted.org/packages/fd/d9/c5de60d9d371bbb186c3e9bf75f4fc5665e11117a25a06a6b2e0afb7380e/rpds_py-0.29.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1585648d0760b88292eecab5181f5651111a69d90eff35d6b78aa32998886a61", size = 375710, upload-time = "2025-11-16T14:48:41.063Z" }, - { url = "https://files.pythonhosted.org/packages/b3/b3/0860cdd012291dc21272895ce107f1e98e335509ba986dd83d72658b82b9/rpds_py-0.29.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:521807963971a23996ddaf764c682b3e46459b3c58ccd79fefbe16718db43154", size = 360582, upload-time = "2025-11-16T14:48:42.423Z" }, - { url = "https://files.pythonhosted.org/packages/92/8a/a18c2f4a61b3407e56175f6aab6deacdf9d360191a3d6f38566e1eaf7266/rpds_py-0.29.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a8896986efaa243ab713c69e6491a4138410f0fe36f2f4c71e18bd5501e8014", size = 391172, upload-time = "2025-11-16T14:48:43.75Z" }, - { url = "https://files.pythonhosted.org/packages/fd/49/e93354258508c50abc15cdcd5fcf7ac4117f67bb6233ad7859f75e7372a0/rpds_py-0.29.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1d24564a700ef41480a984c5ebed62b74e6ce5860429b98b1fede76049e953e6", size = 409586, upload-time = "2025-11-16T14:48:45.498Z" }, - { url = "https://files.pythonhosted.org/packages/5a/8d/a27860dae1c19a6bdc901f90c81f0d581df1943355802961a57cdb5b6cd1/rpds_py-0.29.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e6596b93c010d386ae46c9fba9bfc9fc5965fa8228edeac51576299182c2e31c", size = 516339, upload-time = "2025-11-16T14:48:47.308Z" }, - { url = "https://files.pythonhosted.org/packages/fc/ad/a75e603161e79b7110c647163d130872b271c6b28712c803c65d492100f7/rpds_py-0.29.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5cc58aac218826d054c7da7f95821eba94125d88be673ff44267bb89d12a5866", size = 416201, upload-time = "2025-11-16T14:48:48.615Z" }, - { url = "https://files.pythonhosted.org/packages/b9/42/555b4ee17508beafac135c8b450816ace5a96194ce97fefc49d58e5652ea/rpds_py-0.29.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de73e40ebc04dd5d9556f50180395322193a78ec247e637e741c1b954810f295", size = 395095, upload-time = "2025-11-16T14:48:50.027Z" }, - { url = "https://files.pythonhosted.org/packages/cd/f0/c90b671b9031e800ec45112be42ea9f027f94f9ac25faaac8770596a16a1/rpds_py-0.29.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:295ce5ac7f0cf69a651ea75c8f76d02a31f98e5698e82a50a5f4d4982fbbae3b", size = 410077, upload-time = "2025-11-16T14:48:51.515Z" }, - { url = "https://files.pythonhosted.org/packages/3d/80/9af8b640b81fe21e6f718e9dec36c0b5f670332747243130a5490f292245/rpds_py-0.29.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1ea59b23ea931d494459c8338056fe7d93458c0bf3ecc061cd03916505369d55", size = 424548, upload-time = "2025-11-16T14:48:53.237Z" }, - { url = "https://files.pythonhosted.org/packages/e4/0b/b5647446e991736e6a495ef510e6710df91e880575a586e763baeb0aa770/rpds_py-0.29.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f49d41559cebd608042fdcf54ba597a4a7555b49ad5c1c0c03e0af82692661cd", size = 573661, upload-time = "2025-11-16T14:48:54.769Z" }, - { url = "https://files.pythonhosted.org/packages/f7/b3/1b1c9576839ff583d1428efbf59f9ee70498d8ce6c0b328ac02f1e470879/rpds_py-0.29.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:05a2bd42768ea988294ca328206efbcc66e220d2d9b7836ee5712c07ad6340ea", size = 600937, upload-time = "2025-11-16T14:48:56.247Z" }, - { url = "https://files.pythonhosted.org/packages/6c/7b/b6cfca2f9fee4c4494ce54f7fb1b9f578867495a9aa9fc0d44f5f735c8e0/rpds_py-0.29.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:33ca7bdfedd83339ca55da3a5e1527ee5870d4b8369456b5777b197756f3ca22", size = 564496, upload-time = "2025-11-16T14:48:57.691Z" }, - { url = "https://files.pythonhosted.org/packages/b9/fb/ba29ec7f0f06eb801bac5a23057a9ff7670623b5e8013bd59bec4aa09de8/rpds_py-0.29.0-cp313-cp313-win32.whl", hash = "sha256:20c51ae86a0bb9accc9ad4e6cdeec58d5ebb7f1b09dd4466331fc65e1766aae7", size = 223126, upload-time = "2025-11-16T14:48:59.058Z" }, - { url = "https://files.pythonhosted.org/packages/3c/6b/0229d3bed4ddaa409e6d90b0ae967ed4380e4bdd0dad6e59b92c17d42457/rpds_py-0.29.0-cp313-cp313-win_amd64.whl", hash = "sha256:6410e66f02803600edb0b1889541f4b5cc298a5ccda0ad789cc50ef23b54813e", size = 239771, upload-time = "2025-11-16T14:49:00.872Z" }, - { url = "https://files.pythonhosted.org/packages/e4/38/d2868f058b164f8efd89754d85d7b1c08b454f5c07ac2e6cc2e9bd4bd05b/rpds_py-0.29.0-cp313-cp313-win_arm64.whl", hash = "sha256:56838e1cd9174dc23c5691ee29f1d1be9eab357f27efef6bded1328b23e1ced2", size = 229994, upload-time = "2025-11-16T14:49:02.673Z" }, - { url = "https://files.pythonhosted.org/packages/52/91/5de91c5ec7d41759beec9b251630824dbb8e32d20c3756da1a9a9d309709/rpds_py-0.29.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:37d94eadf764d16b9a04307f2ab1d7af6dc28774bbe0535c9323101e14877b4c", size = 365886, upload-time = "2025-11-16T14:49:04.133Z" }, - { url = "https://files.pythonhosted.org/packages/85/7c/415d8c1b016d5f47ecec5145d9d6d21002d39dce8761b30f6c88810b455a/rpds_py-0.29.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d472cf73efe5726a067dce63eebe8215b14beabea7c12606fd9994267b3cfe2b", size = 355262, upload-time = "2025-11-16T14:49:05.543Z" }, - { url = "https://files.pythonhosted.org/packages/3d/14/bf83e2daa4f980e4dc848aed9299792a8b84af95e12541d9e7562f84a6ef/rpds_py-0.29.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:72fdfd5ff8992e4636621826371e3ac5f3e3b8323e9d0e48378e9c13c3dac9d0", size = 384826, upload-time = "2025-11-16T14:49:07.301Z" }, - { url = "https://files.pythonhosted.org/packages/33/b8/53330c50a810ae22b4fbba5e6cf961b68b9d72d9bd6780a7c0a79b070857/rpds_py-0.29.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2549d833abdf8275c901313b9e8ff8fba57e50f6a495035a2a4e30621a2f7cc4", size = 394234, upload-time = "2025-11-16T14:49:08.782Z" }, - { url = "https://files.pythonhosted.org/packages/cc/32/01e2e9645cef0e584f518cfde4567563e57db2257244632b603f61b40e50/rpds_py-0.29.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4448dad428f28a6a767c3e3b80cde3446a22a0efbddaa2360f4bb4dc836d0688", size = 520008, upload-time = "2025-11-16T14:49:10.253Z" }, - { url = "https://files.pythonhosted.org/packages/98/c3/0d1b95a81affae2b10f950782e33a1fd2edd6ce2a479966cac98c9a66f57/rpds_py-0.29.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:115f48170fd4296a33938d8c11f697f5f26e0472e43d28f35624764173a60e4d", size = 409569, upload-time = "2025-11-16T14:49:12.478Z" }, - { url = "https://files.pythonhosted.org/packages/fa/60/aa3b8678f3f009f675b99174fa2754302a7fbfe749162e8043d111de2d88/rpds_py-0.29.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e5bb73ffc029820f4348e9b66b3027493ae00bca6629129cd433fd7a76308ee", size = 385188, upload-time = "2025-11-16T14:49:13.88Z" }, - { url = "https://files.pythonhosted.org/packages/92/02/5546c1c8aa89c18d40c1fcffdcc957ba730dee53fb7c3ca3a46f114761d2/rpds_py-0.29.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:b1581fcde18fcdf42ea2403a16a6b646f8eb1e58d7f90a0ce693da441f76942e", size = 398587, upload-time = "2025-11-16T14:49:15.339Z" }, - { url = "https://files.pythonhosted.org/packages/6c/e0/ad6eeaf47e236eba052fa34c4073078b9e092bd44da6bbb35aaae9580669/rpds_py-0.29.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:16e9da2bda9eb17ea318b4c335ec9ac1818e88922cbe03a5743ea0da9ecf74fb", size = 416641, upload-time = "2025-11-16T14:49:16.832Z" }, - { url = "https://files.pythonhosted.org/packages/1a/93/0acedfd50ad9cdd3879c615a6dc8c5f1ce78d2fdf8b87727468bb5bb4077/rpds_py-0.29.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:28fd300326dd21198f311534bdb6d7e989dd09b3418b3a91d54a0f384c700967", size = 566683, upload-time = "2025-11-16T14:49:18.342Z" }, - { url = "https://files.pythonhosted.org/packages/62/53/8c64e0f340a9e801459fc6456821abc15b3582cb5dc3932d48705a9d9ac7/rpds_py-0.29.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2aba991e041d031c7939e1358f583ae405a7bf04804ca806b97a5c0e0af1ea5e", size = 592730, upload-time = "2025-11-16T14:49:19.767Z" }, - { url = "https://files.pythonhosted.org/packages/85/ef/3109b6584f8c4b0d2490747c916df833c127ecfa82be04d9a40a376f2090/rpds_py-0.29.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:7f437026dbbc3f08c99cc41a5b2570c6e1a1ddbe48ab19a9b814254128d4ea7a", size = 557361, upload-time = "2025-11-16T14:49:21.574Z" }, - { url = "https://files.pythonhosted.org/packages/ff/3b/61586475e82d57f01da2c16edb9115a618afe00ce86fe1b58936880b15af/rpds_py-0.29.0-cp313-cp313t-win32.whl", hash = "sha256:6e97846e9800a5d0fe7be4d008f0c93d0feeb2700da7b1f7528dabafb31dfadb", size = 211227, upload-time = "2025-11-16T14:49:23.03Z" }, - { url = "https://files.pythonhosted.org/packages/3b/3a/12dc43f13594a54ea0c9d7e9d43002116557330e3ad45bc56097ddf266e2/rpds_py-0.29.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f49196aec7c4b406495f60e6f947ad71f317a765f956d74bbd83996b9edc0352", size = 225248, upload-time = "2025-11-16T14:49:24.841Z" }, - { url = "https://files.pythonhosted.org/packages/89/b1/0b1474e7899371d9540d3bbb2a499a3427ae1fc39c998563fe9035a1073b/rpds_py-0.29.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:394d27e4453d3b4d82bb85665dc1fcf4b0badc30fc84282defed71643b50e1a1", size = 363731, upload-time = "2025-11-16T14:49:26.683Z" }, - { url = "https://files.pythonhosted.org/packages/28/12/3b7cf2068d0a334ed1d7b385a9c3c8509f4c2bcba3d4648ea71369de0881/rpds_py-0.29.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:55d827b2ae95425d3be9bc9a5838b6c29d664924f98146557f7715e331d06df8", size = 354343, upload-time = "2025-11-16T14:49:28.24Z" }, - { url = "https://files.pythonhosted.org/packages/eb/73/5afcf8924bc02a749416eda64e17ac9c9b28f825f4737385295a0e99b0c1/rpds_py-0.29.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc31a07ed352e5462d3ee1b22e89285f4ce97d5266f6d1169da1142e78045626", size = 385406, upload-time = "2025-11-16T14:49:29.943Z" }, - { url = "https://files.pythonhosted.org/packages/c8/37/5db736730662508535221737a21563591b6f43c77f2e388951c42f143242/rpds_py-0.29.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c4695dd224212f6105db7ea62197144230b808d6b2bba52238906a2762f1d1e7", size = 396162, upload-time = "2025-11-16T14:49:31.833Z" }, - { url = "https://files.pythonhosted.org/packages/70/0d/491c1017d14f62ce7bac07c32768d209a50ec567d76d9f383b4cfad19b80/rpds_py-0.29.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcae1770b401167f8b9e1e3f566562e6966ffa9ce63639916248a9e25fa8a244", size = 517719, upload-time = "2025-11-16T14:49:33.804Z" }, - { url = "https://files.pythonhosted.org/packages/d7/25/b11132afcb17cd5d82db173f0c8dab270ffdfaba43e5ce7a591837ae9649/rpds_py-0.29.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:90f30d15f45048448b8da21c41703b31c61119c06c216a1bf8c245812a0f0c17", size = 409498, upload-time = "2025-11-16T14:49:35.222Z" }, - { url = "https://files.pythonhosted.org/packages/0f/7d/e6543cedfb2e6403a1845710a5ab0e0ccf8fc288e0b5af9a70bfe2c12053/rpds_py-0.29.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:44a91e0ab77bdc0004b43261a4b8cd6d6b451e8d443754cfda830002b5745b32", size = 382743, upload-time = "2025-11-16T14:49:36.704Z" }, - { url = "https://files.pythonhosted.org/packages/75/11/a4ebc9f654293ae9fefb83b2b6be7f3253e85ea42a5db2f77d50ad19aaeb/rpds_py-0.29.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:4aa195e5804d32c682e453b34474f411ca108e4291c6a0f824ebdc30a91c973c", size = 400317, upload-time = "2025-11-16T14:49:39.132Z" }, - { url = "https://files.pythonhosted.org/packages/52/18/97677a60a81c7f0e5f64e51fb3f8271c5c8fcabf3a2df18e97af53d7c2bf/rpds_py-0.29.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7971bdb7bf4ee0f7e6f67fa4c7fbc6019d9850cc977d126904392d363f6f8318", size = 416979, upload-time = "2025-11-16T14:49:40.575Z" }, - { url = "https://files.pythonhosted.org/packages/f0/69/28ab391a9968f6c746b2a2db181eaa4d16afaa859fedc9c2f682d19f7e18/rpds_py-0.29.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:8ae33ad9ce580c7a47452c3b3f7d8a9095ef6208e0a0c7e4e2384f9fc5bf8212", size = 567288, upload-time = "2025-11-16T14:49:42.24Z" }, - { url = "https://files.pythonhosted.org/packages/3b/d3/0c7afdcdb830eee94f5611b64e71354ffe6ac8df82d00c2faf2bfffd1d4e/rpds_py-0.29.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:c661132ab2fb4eeede2ef69670fd60da5235209874d001a98f1542f31f2a8a94", size = 593157, upload-time = "2025-11-16T14:49:43.782Z" }, - { url = "https://files.pythonhosted.org/packages/e2/ac/a0fcbc2feed4241cf26d32268c195eb88ddd4bd862adfc9d4b25edfba535/rpds_py-0.29.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:bb78b3a0d31ac1bde132c67015a809948db751cb4e92cdb3f0b242e430b6ed0d", size = 554741, upload-time = "2025-11-16T14:49:45.557Z" }, - { url = "https://files.pythonhosted.org/packages/0f/f1/fcc24137c470df8588674a677f33719d5800ec053aaacd1de8a5d5d84d9e/rpds_py-0.29.0-cp314-cp314-win32.whl", hash = "sha256:f475f103488312e9bd4000bc890a95955a07b2d0b6e8884aef4be56132adbbf1", size = 215508, upload-time = "2025-11-16T14:49:47.562Z" }, - { url = "https://files.pythonhosted.org/packages/7b/c7/1d169b2045512eac019918fc1021ea07c30e84a4343f9f344e3e0aa8c788/rpds_py-0.29.0-cp314-cp314-win_amd64.whl", hash = "sha256:b9cf2359a4fca87cfb6801fae83a76aedf66ee1254a7a151f1341632acf67f1b", size = 228125, upload-time = "2025-11-16T14:49:49.064Z" }, - { url = "https://files.pythonhosted.org/packages/be/36/0cec88aaba70ec4a6e381c444b0d916738497d27f0c30406e3d9fcbd3bc2/rpds_py-0.29.0-cp314-cp314-win_arm64.whl", hash = "sha256:9ba8028597e824854f0f1733d8b964e914ae3003b22a10c2c664cb6927e0feb9", size = 221992, upload-time = "2025-11-16T14:49:50.777Z" }, - { url = "https://files.pythonhosted.org/packages/b1/fa/a2e524631717c9c0eb5d90d30f648cfba6b731047821c994acacb618406c/rpds_py-0.29.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:e71136fd0612556b35c575dc2726ae04a1669e6a6c378f2240312cf5d1a2ab10", size = 366425, upload-time = "2025-11-16T14:49:52.691Z" }, - { url = "https://files.pythonhosted.org/packages/a2/a4/6d43ebe0746ff694a30233f63f454aed1677bd50ab7a59ff6b2bb5ac61f2/rpds_py-0.29.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:76fe96632d53f3bf0ea31ede2f53bbe3540cc2736d4aec3b3801b0458499ef3a", size = 355282, upload-time = "2025-11-16T14:49:54.292Z" }, - { url = "https://files.pythonhosted.org/packages/fa/a7/52fd8270e0320b09eaf295766ae81dd175f65394687906709b3e75c71d06/rpds_py-0.29.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9459a33f077130dbb2c7c3cea72ee9932271fb3126404ba2a2661e4fe9eb7b79", size = 384968, upload-time = "2025-11-16T14:49:55.857Z" }, - { url = "https://files.pythonhosted.org/packages/f4/7d/e6bc526b7a14e1ef80579a52c1d4ad39260a058a51d66c6039035d14db9d/rpds_py-0.29.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5c9546cfdd5d45e562cc0444b6dddc191e625c62e866bf567a2c69487c7ad28a", size = 394714, upload-time = "2025-11-16T14:49:57.343Z" }, - { url = "https://files.pythonhosted.org/packages/c0/3f/f0ade3954e7db95c791e7eaf978aa7e08a756d2046e8bdd04d08146ed188/rpds_py-0.29.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12597d11d97b8f7e376c88929a6e17acb980e234547c92992f9f7c058f1a7310", size = 520136, upload-time = "2025-11-16T14:49:59.162Z" }, - { url = "https://files.pythonhosted.org/packages/87/b3/07122ead1b97009715ab9d4082be6d9bd9546099b2b03fae37c3116f72be/rpds_py-0.29.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28de03cf48b8a9e6ec10318f2197b83946ed91e2891f651a109611be4106ac4b", size = 409250, upload-time = "2025-11-16T14:50:00.698Z" }, - { url = "https://files.pythonhosted.org/packages/c9/c6/dcbee61fd1dc892aedcb1b489ba661313101aa82ec84b1a015d4c63ebfda/rpds_py-0.29.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd7951c964069039acc9d67a8ff1f0a7f34845ae180ca542b17dc1456b1f1808", size = 384940, upload-time = "2025-11-16T14:50:02.312Z" }, - { url = "https://files.pythonhosted.org/packages/47/11/914ecb6f3574cf9bf8b38aced4063e0f787d6e1eb30b181a7efbc6c1da9a/rpds_py-0.29.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:c07d107b7316088f1ac0177a7661ca0c6670d443f6fe72e836069025e6266761", size = 399392, upload-time = "2025-11-16T14:50:03.829Z" }, - { url = "https://files.pythonhosted.org/packages/f5/fd/2f4bd9433f58f816434bb934313584caa47dbc6f03ce5484df8ac8980561/rpds_py-0.29.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1de2345af363d25696969befc0c1688a6cb5e8b1d32b515ef84fc245c6cddba3", size = 416796, upload-time = "2025-11-16T14:50:05.558Z" }, - { url = "https://files.pythonhosted.org/packages/79/a5/449f0281af33efa29d5c71014399d74842342ae908d8cd38260320167692/rpds_py-0.29.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:00e56b12d2199ca96068057e1ae7f9998ab6e99cda82431afafd32f3ec98cca9", size = 566843, upload-time = "2025-11-16T14:50:07.243Z" }, - { url = "https://files.pythonhosted.org/packages/ab/32/0a6a1ccee2e37fcb1b7ba9afde762b77182dbb57937352a729c6cd3cf2bb/rpds_py-0.29.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3919a3bbecee589300ed25000b6944174e07cd20db70552159207b3f4bbb45b8", size = 593956, upload-time = "2025-11-16T14:50:09.029Z" }, - { url = "https://files.pythonhosted.org/packages/4a/3d/eb820f95dce4306f07a495ede02fb61bef36ea201d9137d4fcd5ab94ec1e/rpds_py-0.29.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e7fa2ccc312bbd91e43aa5e0869e46bc03278a3dddb8d58833150a18b0f0283a", size = 557288, upload-time = "2025-11-16T14:50:10.73Z" }, - { url = "https://files.pythonhosted.org/packages/e9/f8/b8ff786f40470462a252918e0836e0db903c28e88e3eec66bc4a7856ee5d/rpds_py-0.29.0-cp314-cp314t-win32.whl", hash = "sha256:97c817863ffc397f1e6a6e9d2d89fe5408c0a9922dac0329672fb0f35c867ea5", size = 211382, upload-time = "2025-11-16T14:50:12.827Z" }, - { url = "https://files.pythonhosted.org/packages/c9/7f/1a65ae870bc9d0576aebb0c501ea5dccf1ae2178fe2821042150ebd2e707/rpds_py-0.29.0-cp314-cp314t-win_amd64.whl", hash = "sha256:2023473f444752f0f82a58dfcbee040d0a1b3d1b3c2ec40e884bd25db6d117d2", size = 225919, upload-time = "2025-11-16T14:50:14.734Z" }, - { url = "https://files.pythonhosted.org/packages/f2/ac/b97e80bf107159e5b9ba9c91df1ab95f69e5e41b435f27bdd737f0d583ac/rpds_py-0.29.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:acd82a9e39082dc5f4492d15a6b6c8599aa21db5c35aaf7d6889aea16502c07d", size = 373963, upload-time = "2025-11-16T14:50:16.205Z" }, - { url = "https://files.pythonhosted.org/packages/40/5a/55e72962d5d29bd912f40c594e68880d3c7a52774b0f75542775f9250712/rpds_py-0.29.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:715b67eac317bf1c7657508170a3e011a1ea6ccb1c9d5f296e20ba14196be6b3", size = 364644, upload-time = "2025-11-16T14:50:18.22Z" }, - { url = "https://files.pythonhosted.org/packages/99/2a/6b6524d0191b7fc1351c3c0840baac42250515afb48ae40c7ed15499a6a2/rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3b1b87a237cb2dba4db18bcfaaa44ba4cd5936b91121b62292ff21df577fc43", size = 393847, upload-time = "2025-11-16T14:50:20.012Z" }, - { url = "https://files.pythonhosted.org/packages/1c/b8/c5692a7df577b3c0c7faed7ac01ee3c608b81750fc5d89f84529229b6873/rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1c3c3e8101bb06e337c88eb0c0ede3187131f19d97d43ea0e1c5407ea74c0cbf", size = 407281, upload-time = "2025-11-16T14:50:21.64Z" }, - { url = "https://files.pythonhosted.org/packages/f0/57/0546c6f84031b7ea08b76646a8e33e45607cc6bd879ff1917dc077bb881e/rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2b8e54d6e61f3ecd3abe032065ce83ea63417a24f437e4a3d73d2f85ce7b7cfe", size = 529213, upload-time = "2025-11-16T14:50:23.219Z" }, - { url = "https://files.pythonhosted.org/packages/fa/c1/01dd5f444233605555bc11fe5fed6a5c18f379f02013870c176c8e630a23/rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3fbd4e9aebf110473a420dea85a238b254cf8a15acb04b22a5a6b5ce8925b760", size = 413808, upload-time = "2025-11-16T14:50:25.262Z" }, - { url = "https://files.pythonhosted.org/packages/aa/0a/60f98b06156ea2a7af849fb148e00fbcfdb540909a5174a5ed10c93745c7/rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80fdf53d36e6c72819993e35d1ebeeb8e8fc688d0c6c2b391b55e335b3afba5a", size = 394600, upload-time = "2025-11-16T14:50:26.956Z" }, - { url = "https://files.pythonhosted.org/packages/37/f1/dc9312fc9bec040ece08396429f2bd9e0977924ba7a11c5ad7056428465e/rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:ea7173df5d86f625f8dde6d5929629ad811ed8decda3b60ae603903839ac9ac0", size = 408634, upload-time = "2025-11-16T14:50:28.989Z" }, - { url = "https://files.pythonhosted.org/packages/ed/41/65024c9fd40c89bb7d604cf73beda4cbdbcebe92d8765345dd65855b6449/rpds_py-0.29.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:76054d540061eda273274f3d13a21a4abdde90e13eaefdc205db37c05230efce", size = 426064, upload-time = "2025-11-16T14:50:30.674Z" }, - { url = "https://files.pythonhosted.org/packages/a2/e0/cf95478881fc88ca2fdbf56381d7df36567cccc39a05394beac72182cd62/rpds_py-0.29.0-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:9f84c549746a5be3bc7415830747a3a0312573afc9f95785eb35228bb17742ec", size = 575871, upload-time = "2025-11-16T14:50:33.428Z" }, - { url = "https://files.pythonhosted.org/packages/ea/c0/df88097e64339a0218b57bd5f9ca49898e4c394db756c67fccc64add850a/rpds_py-0.29.0-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:0ea962671af5cb9a260489e311fa22b2e97103e3f9f0caaea6f81390af96a9ed", size = 601702, upload-time = "2025-11-16T14:50:36.051Z" }, - { url = "https://files.pythonhosted.org/packages/87/f4/09ffb3ebd0cbb9e2c7c9b84d252557ecf434cd71584ee1e32f66013824df/rpds_py-0.29.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:f7728653900035fb7b8d06e1e5900545d8088efc9d5d4545782da7df03ec803f", size = 564054, upload-time = "2025-11-16T14:50:37.733Z" }, +version = "0.30.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/20/af/3f2f423103f1113b36230496629986e0ef7e199d2aa8392452b484b38ced/rpds_py-0.30.0.tar.gz", hash = "sha256:dd8ff7cf90014af0c0f787eea34794ebf6415242ee1d6fa91eaba725cc441e84", size = 69469, upload-time = "2025-11-30T20:24:38.837Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/06/0c/0c411a0ec64ccb6d104dcabe0e713e05e153a9a2c3c2bd2b32ce412166fe/rpds_py-0.30.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:679ae98e00c0e8d68a7fda324e16b90fd5260945b45d3b824c892cec9eea3288", size = 370490, upload-time = "2025-11-30T20:21:33.256Z" }, + { url = "https://files.pythonhosted.org/packages/19/6a/4ba3d0fb7297ebae71171822554abe48d7cab29c28b8f9f2c04b79988c05/rpds_py-0.30.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4cc2206b76b4f576934f0ed374b10d7ca5f457858b157ca52064bdfc26b9fc00", size = 359751, upload-time = "2025-11-30T20:21:34.591Z" }, + { url = "https://files.pythonhosted.org/packages/cd/7c/e4933565ef7f7a0818985d87c15d9d273f1a649afa6a52ea35ad011195ea/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:389a2d49eded1896c3d48b0136ead37c48e221b391c052fba3f4055c367f60a6", size = 389696, upload-time = "2025-11-30T20:21:36.122Z" }, + { url = "https://files.pythonhosted.org/packages/5e/01/6271a2511ad0815f00f7ed4390cf2567bec1d4b1da39e2c27a41e6e3b4de/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:32c8528634e1bf7121f3de08fa85b138f4e0dc47657866630611b03967f041d7", size = 403136, upload-time = "2025-11-30T20:21:37.728Z" }, + { url = "https://files.pythonhosted.org/packages/55/64/c857eb7cd7541e9b4eee9d49c196e833128a55b89a9850a9c9ac33ccf897/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f207f69853edd6f6700b86efb84999651baf3789e78a466431df1331608e5324", size = 524699, upload-time = "2025-11-30T20:21:38.92Z" }, + { url = "https://files.pythonhosted.org/packages/9c/ed/94816543404078af9ab26159c44f9e98e20fe47e2126d5d32c9d9948d10a/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:67b02ec25ba7a9e8fa74c63b6ca44cf5707f2fbfadae3ee8e7494297d56aa9df", size = 412022, upload-time = "2025-11-30T20:21:40.407Z" }, + { url = "https://files.pythonhosted.org/packages/61/b5/707f6cf0066a6412aacc11d17920ea2e19e5b2f04081c64526eb35b5c6e7/rpds_py-0.30.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0e95f6819a19965ff420f65578bacb0b00f251fefe2c8b23347c37174271f3", size = 390522, upload-time = "2025-11-30T20:21:42.17Z" }, + { url = "https://files.pythonhosted.org/packages/13/4e/57a85fda37a229ff4226f8cbcf09f2a455d1ed20e802ce5b2b4a7f5ed053/rpds_py-0.30.0-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:a452763cc5198f2f98898eb98f7569649fe5da666c2dc6b5ddb10fde5a574221", size = 404579, upload-time = "2025-11-30T20:21:43.769Z" }, + { url = "https://files.pythonhosted.org/packages/f9/da/c9339293513ec680a721e0e16bf2bac3db6e5d7e922488de471308349bba/rpds_py-0.30.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e0b65193a413ccc930671c55153a03ee57cecb49e6227204b04fae512eb657a7", size = 421305, upload-time = "2025-11-30T20:21:44.994Z" }, + { url = "https://files.pythonhosted.org/packages/f9/be/522cb84751114f4ad9d822ff5a1aa3c98006341895d5f084779b99596e5c/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:858738e9c32147f78b3ac24dc0edb6610000e56dc0f700fd5f651d0a0f0eb9ff", size = 572503, upload-time = "2025-11-30T20:21:46.91Z" }, + { url = "https://files.pythonhosted.org/packages/a2/9b/de879f7e7ceddc973ea6e4629e9b380213a6938a249e94b0cdbcc325bb66/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:da279aa314f00acbb803da1e76fa18666778e8a8f83484fba94526da5de2cba7", size = 598322, upload-time = "2025-11-30T20:21:48.709Z" }, + { url = "https://files.pythonhosted.org/packages/48/ac/f01fc22efec3f37d8a914fc1b2fb9bcafd56a299edbe96406f3053edea5a/rpds_py-0.30.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7c64d38fb49b6cdeda16ab49e35fe0da2e1e9b34bc38bd78386530f218b37139", size = 560792, upload-time = "2025-11-30T20:21:50.024Z" }, + { url = "https://files.pythonhosted.org/packages/e2/da/4e2b19d0f131f35b6146425f846563d0ce036763e38913d917187307a671/rpds_py-0.30.0-cp310-cp310-win32.whl", hash = "sha256:6de2a32a1665b93233cde140ff8b3467bdb9e2af2b91079f0333a0974d12d464", size = 221901, upload-time = "2025-11-30T20:21:51.32Z" }, + { url = "https://files.pythonhosted.org/packages/96/cb/156d7a5cf4f78a7cc571465d8aec7a3c447c94f6749c5123f08438bcf7bc/rpds_py-0.30.0-cp310-cp310-win_amd64.whl", hash = "sha256:1726859cd0de969f88dc8673bdd954185b9104e05806be64bcd87badbe313169", size = 235823, upload-time = "2025-11-30T20:21:52.505Z" }, + { url = "https://files.pythonhosted.org/packages/4d/6e/f964e88b3d2abee2a82c1ac8366da848fce1c6d834dc2132c3fda3970290/rpds_py-0.30.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a2bffea6a4ca9f01b3f8e548302470306689684e61602aa3d141e34da06cf425", size = 370157, upload-time = "2025-11-30T20:21:53.789Z" }, + { url = "https://files.pythonhosted.org/packages/94/ba/24e5ebb7c1c82e74c4e4f33b2112a5573ddc703915b13a073737b59b86e0/rpds_py-0.30.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dc4f992dfe1e2bc3ebc7444f6c7051b4bc13cd8e33e43511e8ffd13bf407010d", size = 359676, upload-time = "2025-11-30T20:21:55.475Z" }, + { url = "https://files.pythonhosted.org/packages/84/86/04dbba1b087227747d64d80c3b74df946b986c57af0a9f0c98726d4d7a3b/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:422c3cb9856d80b09d30d2eb255d0754b23e090034e1deb4083f8004bd0761e4", size = 389938, upload-time = "2025-11-30T20:21:57.079Z" }, + { url = "https://files.pythonhosted.org/packages/42/bb/1463f0b1722b7f45431bdd468301991d1328b16cffe0b1c2918eba2c4eee/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:07ae8a593e1c3c6b82ca3292efbe73c30b61332fd612e05abee07c79359f292f", size = 402932, upload-time = "2025-11-30T20:21:58.47Z" }, + { url = "https://files.pythonhosted.org/packages/99/ee/2520700a5c1f2d76631f948b0736cdf9b0acb25abd0ca8e889b5c62ac2e3/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12f90dd7557b6bd57f40abe7747e81e0c0b119bef015ea7726e69fe550e394a4", size = 525830, upload-time = "2025-11-30T20:21:59.699Z" }, + { url = "https://files.pythonhosted.org/packages/e0/ad/bd0331f740f5705cc555a5e17fdf334671262160270962e69a2bdef3bf76/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:99b47d6ad9a6da00bec6aabe5a6279ecd3c06a329d4aa4771034a21e335c3a97", size = 412033, upload-time = "2025-11-30T20:22:00.991Z" }, + { url = "https://files.pythonhosted.org/packages/f8/1e/372195d326549bb51f0ba0f2ecb9874579906b97e08880e7a65c3bef1a99/rpds_py-0.30.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33f559f3104504506a44bb666b93a33f5d33133765b0c216a5bf2f1e1503af89", size = 390828, upload-time = "2025-11-30T20:22:02.723Z" }, + { url = "https://files.pythonhosted.org/packages/ab/2b/d88bb33294e3e0c76bc8f351a3721212713629ffca1700fa94979cb3eae8/rpds_py-0.30.0-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:946fe926af6e44f3697abbc305ea168c2c31d3e3ef1058cf68f379bf0335a78d", size = 404683, upload-time = "2025-11-30T20:22:04.367Z" }, + { url = "https://files.pythonhosted.org/packages/50/32/c759a8d42bcb5289c1fac697cd92f6fe01a018dd937e62ae77e0e7f15702/rpds_py-0.30.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:495aeca4b93d465efde585977365187149e75383ad2684f81519f504f5c13038", size = 421583, upload-time = "2025-11-30T20:22:05.814Z" }, + { url = "https://files.pythonhosted.org/packages/2b/81/e729761dbd55ddf5d84ec4ff1f47857f4374b0f19bdabfcf929164da3e24/rpds_py-0.30.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9a0ca5da0386dee0655b4ccdf46119df60e0f10da268d04fe7cc87886872ba7", size = 572496, upload-time = "2025-11-30T20:22:07.713Z" }, + { url = "https://files.pythonhosted.org/packages/14/f6/69066a924c3557c9c30baa6ec3a0aa07526305684c6f86c696b08860726c/rpds_py-0.30.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8d6d1cc13664ec13c1b84241204ff3b12f9bb82464b8ad6e7a5d3486975c2eed", size = 598669, upload-time = "2025-11-30T20:22:09.312Z" }, + { url = "https://files.pythonhosted.org/packages/5f/48/905896b1eb8a05630d20333d1d8ffd162394127b74ce0b0784ae04498d32/rpds_py-0.30.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3896fa1be39912cf0757753826bc8bdc8ca331a28a7c4ae46b7a21280b06bb85", size = 561011, upload-time = "2025-11-30T20:22:11.309Z" }, + { url = "https://files.pythonhosted.org/packages/22/16/cd3027c7e279d22e5eb431dd3c0fbc677bed58797fe7581e148f3f68818b/rpds_py-0.30.0-cp311-cp311-win32.whl", hash = "sha256:55f66022632205940f1827effeff17c4fa7ae1953d2b74a8581baaefb7d16f8c", size = 221406, upload-time = "2025-11-30T20:22:13.101Z" }, + { url = "https://files.pythonhosted.org/packages/fa/5b/e7b7aa136f28462b344e652ee010d4de26ee9fd16f1bfd5811f5153ccf89/rpds_py-0.30.0-cp311-cp311-win_amd64.whl", hash = "sha256:a51033ff701fca756439d641c0ad09a41d9242fa69121c7d8769604a0a629825", size = 236024, upload-time = "2025-11-30T20:22:14.853Z" }, + { url = "https://files.pythonhosted.org/packages/14/a6/364bba985e4c13658edb156640608f2c9e1d3ea3c81b27aa9d889fff0e31/rpds_py-0.30.0-cp311-cp311-win_arm64.whl", hash = "sha256:47b0ef6231c58f506ef0b74d44e330405caa8428e770fec25329ed2cb971a229", size = 229069, upload-time = "2025-11-30T20:22:16.577Z" }, + { url = "https://files.pythonhosted.org/packages/03/e7/98a2f4ac921d82f33e03f3835f5bf3a4a40aa1bfdc57975e74a97b2b4bdd/rpds_py-0.30.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a161f20d9a43006833cd7068375a94d035714d73a172b681d8881820600abfad", size = 375086, upload-time = "2025-11-30T20:22:17.93Z" }, + { url = "https://files.pythonhosted.org/packages/4d/a1/bca7fd3d452b272e13335db8d6b0b3ecde0f90ad6f16f3328c6fb150c889/rpds_py-0.30.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6abc8880d9d036ecaafe709079969f56e876fcf107f7a8e9920ba6d5a3878d05", size = 359053, upload-time = "2025-11-30T20:22:19.297Z" }, + { url = "https://files.pythonhosted.org/packages/65/1c/ae157e83a6357eceff62ba7e52113e3ec4834a84cfe07fa4b0757a7d105f/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca28829ae5f5d569bb62a79512c842a03a12576375d5ece7d2cadf8abe96ec28", size = 390763, upload-time = "2025-11-30T20:22:21.661Z" }, + { url = "https://files.pythonhosted.org/packages/d4/36/eb2eb8515e2ad24c0bd43c3ee9cd74c33f7ca6430755ccdb240fd3144c44/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a1010ed9524c73b94d15919ca4d41d8780980e1765babf85f9a2f90d247153dd", size = 408951, upload-time = "2025-11-30T20:22:23.408Z" }, + { url = "https://files.pythonhosted.org/packages/d6/65/ad8dc1784a331fabbd740ef6f71ce2198c7ed0890dab595adb9ea2d775a1/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8d1736cfb49381ba528cd5baa46f82fdc65c06e843dab24dd70b63d09121b3f", size = 514622, upload-time = "2025-11-30T20:22:25.16Z" }, + { url = "https://files.pythonhosted.org/packages/63/8e/0cfa7ae158e15e143fe03993b5bcd743a59f541f5952e1546b1ac1b5fd45/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d948b135c4693daff7bc2dcfc4ec57237a29bd37e60c2fabf5aff2bbacf3e2f1", size = 414492, upload-time = "2025-11-30T20:22:26.505Z" }, + { url = "https://files.pythonhosted.org/packages/60/1b/6f8f29f3f995c7ffdde46a626ddccd7c63aefc0efae881dc13b6e5d5bb16/rpds_py-0.30.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47f236970bccb2233267d89173d3ad2703cd36a0e2a6e92d0560d333871a3d23", size = 394080, upload-time = "2025-11-30T20:22:27.934Z" }, + { url = "https://files.pythonhosted.org/packages/6d/d5/a266341051a7a3ca2f4b750a3aa4abc986378431fc2da508c5034d081b70/rpds_py-0.30.0-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:2e6ecb5a5bcacf59c3f912155044479af1d0b6681280048b338b28e364aca1f6", size = 408680, upload-time = "2025-11-30T20:22:29.341Z" }, + { url = "https://files.pythonhosted.org/packages/10/3b/71b725851df9ab7a7a4e33cf36d241933da66040d195a84781f49c50490c/rpds_py-0.30.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a8fa71a2e078c527c3e9dc9fc5a98c9db40bcc8a92b4e8858e36d329f8684b51", size = 423589, upload-time = "2025-11-30T20:22:31.469Z" }, + { url = "https://files.pythonhosted.org/packages/00/2b/e59e58c544dc9bd8bd8384ecdb8ea91f6727f0e37a7131baeff8d6f51661/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:73c67f2db7bc334e518d097c6d1e6fed021bbc9b7d678d6cc433478365d1d5f5", size = 573289, upload-time = "2025-11-30T20:22:32.997Z" }, + { url = "https://files.pythonhosted.org/packages/da/3e/a18e6f5b460893172a7d6a680e86d3b6bc87a54c1f0b03446a3c8c7b588f/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5ba103fb455be00f3b1c2076c9d4264bfcb037c976167a6047ed82f23153f02e", size = 599737, upload-time = "2025-11-30T20:22:34.419Z" }, + { url = "https://files.pythonhosted.org/packages/5c/e2/714694e4b87b85a18e2c243614974413c60aa107fd815b8cbc42b873d1d7/rpds_py-0.30.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7cee9c752c0364588353e627da8a7e808a66873672bcb5f52890c33fd965b394", size = 563120, upload-time = "2025-11-30T20:22:35.903Z" }, + { url = "https://files.pythonhosted.org/packages/6f/ab/d5d5e3bcedb0a77f4f613706b750e50a5a3ba1c15ccd3665ecc636c968fd/rpds_py-0.30.0-cp312-cp312-win32.whl", hash = "sha256:1ab5b83dbcf55acc8b08fc62b796ef672c457b17dbd7820a11d6c52c06839bdf", size = 223782, upload-time = "2025-11-30T20:22:37.271Z" }, + { url = "https://files.pythonhosted.org/packages/39/3b/f786af9957306fdc38a74cef405b7b93180f481fb48453a114bb6465744a/rpds_py-0.30.0-cp312-cp312-win_amd64.whl", hash = "sha256:a090322ca841abd453d43456ac34db46e8b05fd9b3b4ac0c78bcde8b089f959b", size = 240463, upload-time = "2025-11-30T20:22:39.021Z" }, + { url = "https://files.pythonhosted.org/packages/f3/d2/b91dc748126c1559042cfe41990deb92c4ee3e2b415f6b5234969ffaf0cc/rpds_py-0.30.0-cp312-cp312-win_arm64.whl", hash = "sha256:669b1805bd639dd2989b281be2cfd951c6121b65e729d9b843e9639ef1fd555e", size = 230868, upload-time = "2025-11-30T20:22:40.493Z" }, + { url = "https://files.pythonhosted.org/packages/ed/dc/d61221eb88ff410de3c49143407f6f3147acf2538c86f2ab7ce65ae7d5f9/rpds_py-0.30.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f83424d738204d9770830d35290ff3273fbb02b41f919870479fab14b9d303b2", size = 374887, upload-time = "2025-11-30T20:22:41.812Z" }, + { url = "https://files.pythonhosted.org/packages/fd/32/55fb50ae104061dbc564ef15cc43c013dc4a9f4527a1f4d99baddf56fe5f/rpds_py-0.30.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e7536cd91353c5273434b4e003cbda89034d67e7710eab8761fd918ec6c69cf8", size = 358904, upload-time = "2025-11-30T20:22:43.479Z" }, + { url = "https://files.pythonhosted.org/packages/58/70/faed8186300e3b9bdd138d0273109784eea2396c68458ed580f885dfe7ad/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2771c6c15973347f50fece41fc447c054b7ac2ae0502388ce3b6738cd366e3d4", size = 389945, upload-time = "2025-11-30T20:22:44.819Z" }, + { url = "https://files.pythonhosted.org/packages/bd/a8/073cac3ed2c6387df38f71296d002ab43496a96b92c823e76f46b8af0543/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0a59119fc6e3f460315fe9d08149f8102aa322299deaa5cab5b40092345c2136", size = 407783, upload-time = "2025-11-30T20:22:46.103Z" }, + { url = "https://files.pythonhosted.org/packages/77/57/5999eb8c58671f1c11eba084115e77a8899d6e694d2a18f69f0ba471ec8b/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76fec018282b4ead0364022e3c54b60bf368b9d926877957a8624b58419169b7", size = 515021, upload-time = "2025-11-30T20:22:47.458Z" }, + { url = "https://files.pythonhosted.org/packages/e0/af/5ab4833eadc36c0a8ed2bc5c0de0493c04f6c06de223170bd0798ff98ced/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:692bef75a5525db97318e8cd061542b5a79812d711ea03dbc1f6f8dbb0c5f0d2", size = 414589, upload-time = "2025-11-30T20:22:48.872Z" }, + { url = "https://files.pythonhosted.org/packages/b7/de/f7192e12b21b9e9a68a6d0f249b4af3fdcdff8418be0767a627564afa1f1/rpds_py-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9027da1ce107104c50c81383cae773ef5c24d296dd11c99e2629dbd7967a20c6", size = 394025, upload-time = "2025-11-30T20:22:50.196Z" }, + { url = "https://files.pythonhosted.org/packages/91/c4/fc70cd0249496493500e7cc2de87504f5aa6509de1e88623431fec76d4b6/rpds_py-0.30.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:9cf69cdda1f5968a30a359aba2f7f9aa648a9ce4b580d6826437f2b291cfc86e", size = 408895, upload-time = "2025-11-30T20:22:51.87Z" }, + { url = "https://files.pythonhosted.org/packages/58/95/d9275b05ab96556fefff73a385813eb66032e4c99f411d0795372d9abcea/rpds_py-0.30.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a4796a717bf12b9da9d3ad002519a86063dcac8988b030e405704ef7d74d2d9d", size = 422799, upload-time = "2025-11-30T20:22:53.341Z" }, + { url = "https://files.pythonhosted.org/packages/06/c1/3088fc04b6624eb12a57eb814f0d4997a44b0d208d6cace713033ff1a6ba/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5d4c2aa7c50ad4728a094ebd5eb46c452e9cb7edbfdb18f9e1221f597a73e1e7", size = 572731, upload-time = "2025-11-30T20:22:54.778Z" }, + { url = "https://files.pythonhosted.org/packages/d8/42/c612a833183b39774e8ac8fecae81263a68b9583ee343db33ab571a7ce55/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ba81a9203d07805435eb06f536d95a266c21e5b2dfbf6517748ca40c98d19e31", size = 599027, upload-time = "2025-11-30T20:22:56.212Z" }, + { url = "https://files.pythonhosted.org/packages/5f/60/525a50f45b01d70005403ae0e25f43c0384369ad24ffe46e8d9068b50086/rpds_py-0.30.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:945dccface01af02675628334f7cf49c2af4c1c904748efc5cf7bbdf0b579f95", size = 563020, upload-time = "2025-11-30T20:22:58.2Z" }, + { url = "https://files.pythonhosted.org/packages/0b/5d/47c4655e9bcd5ca907148535c10e7d489044243cc9941c16ed7cd53be91d/rpds_py-0.30.0-cp313-cp313-win32.whl", hash = "sha256:b40fb160a2db369a194cb27943582b38f79fc4887291417685f3ad693c5a1d5d", size = 223139, upload-time = "2025-11-30T20:23:00.209Z" }, + { url = "https://files.pythonhosted.org/packages/f2/e1/485132437d20aa4d3e1d8b3fb5a5e65aa8139f1e097080c2a8443201742c/rpds_py-0.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:806f36b1b605e2d6a72716f321f20036b9489d29c51c91f4dd29a3e3afb73b15", size = 240224, upload-time = "2025-11-30T20:23:02.008Z" }, + { url = "https://files.pythonhosted.org/packages/24/95/ffd128ed1146a153d928617b0ef673960130be0009c77d8fbf0abe306713/rpds_py-0.30.0-cp313-cp313-win_arm64.whl", hash = "sha256:d96c2086587c7c30d44f31f42eae4eac89b60dabbac18c7669be3700f13c3ce1", size = 230645, upload-time = "2025-11-30T20:23:03.43Z" }, + { url = "https://files.pythonhosted.org/packages/ff/1b/b10de890a0def2a319a2626334a7f0ae388215eb60914dbac8a3bae54435/rpds_py-0.30.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:eb0b93f2e5c2189ee831ee43f156ed34e2a89a78a66b98cadad955972548be5a", size = 364443, upload-time = "2025-11-30T20:23:04.878Z" }, + { url = "https://files.pythonhosted.org/packages/0d/bf/27e39f5971dc4f305a4fb9c672ca06f290f7c4e261c568f3dea16a410d47/rpds_py-0.30.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:922e10f31f303c7c920da8981051ff6d8c1a56207dbdf330d9047f6d30b70e5e", size = 353375, upload-time = "2025-11-30T20:23:06.342Z" }, + { url = "https://files.pythonhosted.org/packages/40/58/442ada3bba6e8e6615fc00483135c14a7538d2ffac30e2d933ccf6852232/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdc62c8286ba9bf7f47befdcea13ea0e26bf294bda99758fd90535cbaf408000", size = 383850, upload-time = "2025-11-30T20:23:07.825Z" }, + { url = "https://files.pythonhosted.org/packages/14/14/f59b0127409a33c6ef6f5c1ebd5ad8e32d7861c9c7adfa9a624fc3889f6c/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:47f9a91efc418b54fb8190a6b4aa7813a23fb79c51f4bb84e418f5476c38b8db", size = 392812, upload-time = "2025-11-30T20:23:09.228Z" }, + { url = "https://files.pythonhosted.org/packages/b3/66/e0be3e162ac299b3a22527e8913767d869e6cc75c46bd844aa43fb81ab62/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f3587eb9b17f3789ad50824084fa6f81921bbf9a795826570bda82cb3ed91f2", size = 517841, upload-time = "2025-11-30T20:23:11.186Z" }, + { url = "https://files.pythonhosted.org/packages/3d/55/fa3b9cf31d0c963ecf1ba777f7cf4b2a2c976795ac430d24a1f43d25a6ba/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39c02563fc592411c2c61d26b6c5fe1e51eaa44a75aa2c8735ca88b0d9599daa", size = 408149, upload-time = "2025-11-30T20:23:12.864Z" }, + { url = "https://files.pythonhosted.org/packages/60/ca/780cf3b1a32b18c0f05c441958d3758f02544f1d613abf9488cd78876378/rpds_py-0.30.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51a1234d8febafdfd33a42d97da7a43f5dcb120c1060e352a3fbc0c6d36e2083", size = 383843, upload-time = "2025-11-30T20:23:14.638Z" }, + { url = "https://files.pythonhosted.org/packages/82/86/d5f2e04f2aa6247c613da0c1dd87fcd08fa17107e858193566048a1e2f0a/rpds_py-0.30.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:eb2c4071ab598733724c08221091e8d80e89064cd472819285a9ab0f24bcedb9", size = 396507, upload-time = "2025-11-30T20:23:16.105Z" }, + { url = "https://files.pythonhosted.org/packages/4b/9a/453255d2f769fe44e07ea9785c8347edaf867f7026872e76c1ad9f7bed92/rpds_py-0.30.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6bdfdb946967d816e6adf9a3d8201bfad269c67efe6cefd7093ef959683c8de0", size = 414949, upload-time = "2025-11-30T20:23:17.539Z" }, + { url = "https://files.pythonhosted.org/packages/a3/31/622a86cdc0c45d6df0e9ccb6becdba5074735e7033c20e401a6d9d0e2ca0/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c77afbd5f5250bf27bf516c7c4a016813eb2d3e116139aed0096940c5982da94", size = 565790, upload-time = "2025-11-30T20:23:19.029Z" }, + { url = "https://files.pythonhosted.org/packages/1c/5d/15bbf0fb4a3f58a3b1c67855ec1efcc4ceaef4e86644665fff03e1b66d8d/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:61046904275472a76c8c90c9ccee9013d70a6d0f73eecefd38c1ae7c39045a08", size = 590217, upload-time = "2025-11-30T20:23:20.885Z" }, + { url = "https://files.pythonhosted.org/packages/6d/61/21b8c41f68e60c8cc3b2e25644f0e3681926020f11d06ab0b78e3c6bbff1/rpds_py-0.30.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c5f36a861bc4b7da6516dbdf302c55313afa09b81931e8280361a4f6c9a2d27", size = 555806, upload-time = "2025-11-30T20:23:22.488Z" }, + { url = "https://files.pythonhosted.org/packages/f9/39/7e067bb06c31de48de3eb200f9fc7c58982a4d3db44b07e73963e10d3be9/rpds_py-0.30.0-cp313-cp313t-win32.whl", hash = "sha256:3d4a69de7a3e50ffc214ae16d79d8fbb0922972da0356dcf4d0fdca2878559c6", size = 211341, upload-time = "2025-11-30T20:23:24.449Z" }, + { url = "https://files.pythonhosted.org/packages/0a/4d/222ef0b46443cf4cf46764d9c630f3fe4abaa7245be9417e56e9f52b8f65/rpds_py-0.30.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f14fc5df50a716f7ece6a80b6c78bb35ea2ca47c499e422aa4463455dd96d56d", size = 225768, upload-time = "2025-11-30T20:23:25.908Z" }, + { url = "https://files.pythonhosted.org/packages/86/81/dad16382ebbd3d0e0328776d8fd7ca94220e4fa0798d1dc5e7da48cb3201/rpds_py-0.30.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:68f19c879420aa08f61203801423f6cd5ac5f0ac4ac82a2368a9fcd6a9a075e0", size = 362099, upload-time = "2025-11-30T20:23:27.316Z" }, + { url = "https://files.pythonhosted.org/packages/2b/60/19f7884db5d5603edf3c6bce35408f45ad3e97e10007df0e17dd57af18f8/rpds_py-0.30.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ec7c4490c672c1a0389d319b3a9cfcd098dcdc4783991553c332a15acf7249be", size = 353192, upload-time = "2025-11-30T20:23:29.151Z" }, + { url = "https://files.pythonhosted.org/packages/bf/c4/76eb0e1e72d1a9c4703c69607cec123c29028bff28ce41588792417098ac/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f251c812357a3fed308d684a5079ddfb9d933860fc6de89f2b7ab00da481e65f", size = 384080, upload-time = "2025-11-30T20:23:30.785Z" }, + { url = "https://files.pythonhosted.org/packages/72/87/87ea665e92f3298d1b26d78814721dc39ed8d2c74b86e83348d6b48a6f31/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac98b175585ecf4c0348fd7b29c3864bda53b805c773cbf7bfdaffc8070c976f", size = 394841, upload-time = "2025-11-30T20:23:32.209Z" }, + { url = "https://files.pythonhosted.org/packages/77/ad/7783a89ca0587c15dcbf139b4a8364a872a25f861bdb88ed99f9b0dec985/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3e62880792319dbeb7eb866547f2e35973289e7d5696c6e295476448f5b63c87", size = 516670, upload-time = "2025-11-30T20:23:33.742Z" }, + { url = "https://files.pythonhosted.org/packages/5b/3c/2882bdac942bd2172f3da574eab16f309ae10a3925644e969536553cb4ee/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e7fc54e0900ab35d041b0601431b0a0eb495f0851a0639b6ef90f7741b39a18", size = 408005, upload-time = "2025-11-30T20:23:35.253Z" }, + { url = "https://files.pythonhosted.org/packages/ce/81/9a91c0111ce1758c92516a3e44776920b579d9a7c09b2b06b642d4de3f0f/rpds_py-0.30.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47e77dc9822d3ad616c3d5759ea5631a75e5809d5a28707744ef79d7a1bcfcad", size = 382112, upload-time = "2025-11-30T20:23:36.842Z" }, + { url = "https://files.pythonhosted.org/packages/cf/8e/1da49d4a107027e5fbc64daeab96a0706361a2918da10cb41769244b805d/rpds_py-0.30.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:b4dc1a6ff022ff85ecafef7979a2c6eb423430e05f1165d6688234e62ba99a07", size = 399049, upload-time = "2025-11-30T20:23:38.343Z" }, + { url = "https://files.pythonhosted.org/packages/df/5a/7ee239b1aa48a127570ec03becbb29c9d5a9eb092febbd1699d567cae859/rpds_py-0.30.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4559c972db3a360808309e06a74628b95eaccbf961c335c8fe0d590cf587456f", size = 415661, upload-time = "2025-11-30T20:23:40.263Z" }, + { url = "https://files.pythonhosted.org/packages/70/ea/caa143cf6b772f823bc7929a45da1fa83569ee49b11d18d0ada7f5ee6fd6/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0ed177ed9bded28f8deb6ab40c183cd1192aa0de40c12f38be4d59cd33cb5c65", size = 565606, upload-time = "2025-11-30T20:23:42.186Z" }, + { url = "https://files.pythonhosted.org/packages/64/91/ac20ba2d69303f961ad8cf55bf7dbdb4763f627291ba3d0d7d67333cced9/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ad1fa8db769b76ea911cb4e10f049d80bf518c104f15b3edb2371cc65375c46f", size = 591126, upload-time = "2025-11-30T20:23:44.086Z" }, + { url = "https://files.pythonhosted.org/packages/21/20/7ff5f3c8b00c8a95f75985128c26ba44503fb35b8e0259d812766ea966c7/rpds_py-0.30.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:46e83c697b1f1c72b50e5ee5adb4353eef7406fb3f2043d64c33f20ad1c2fc53", size = 553371, upload-time = "2025-11-30T20:23:46.004Z" }, + { url = "https://files.pythonhosted.org/packages/72/c7/81dadd7b27c8ee391c132a6b192111ca58d866577ce2d9b0ca157552cce0/rpds_py-0.30.0-cp314-cp314-win32.whl", hash = "sha256:ee454b2a007d57363c2dfd5b6ca4a5d7e2c518938f8ed3b706e37e5d470801ed", size = 215298, upload-time = "2025-11-30T20:23:47.696Z" }, + { url = "https://files.pythonhosted.org/packages/3e/d2/1aaac33287e8cfb07aab2e6b8ac1deca62f6f65411344f1433c55e6f3eb8/rpds_py-0.30.0-cp314-cp314-win_amd64.whl", hash = "sha256:95f0802447ac2d10bcc69f6dc28fe95fdf17940367b21d34e34c737870758950", size = 228604, upload-time = "2025-11-30T20:23:49.501Z" }, + { url = "https://files.pythonhosted.org/packages/e8/95/ab005315818cc519ad074cb7784dae60d939163108bd2b394e60dc7b5461/rpds_py-0.30.0-cp314-cp314-win_arm64.whl", hash = "sha256:613aa4771c99f03346e54c3f038e4cc574ac09a3ddfb0e8878487335e96dead6", size = 222391, upload-time = "2025-11-30T20:23:50.96Z" }, + { url = "https://files.pythonhosted.org/packages/9e/68/154fe0194d83b973cdedcdcc88947a2752411165930182ae41d983dcefa6/rpds_py-0.30.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:7e6ecfcb62edfd632e56983964e6884851786443739dbfe3582947e87274f7cb", size = 364868, upload-time = "2025-11-30T20:23:52.494Z" }, + { url = "https://files.pythonhosted.org/packages/83/69/8bbc8b07ec854d92a8b75668c24d2abcb1719ebf890f5604c61c9369a16f/rpds_py-0.30.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a1d0bc22a7cdc173fedebb73ef81e07faef93692b8c1ad3733b67e31e1b6e1b8", size = 353747, upload-time = "2025-11-30T20:23:54.036Z" }, + { url = "https://files.pythonhosted.org/packages/ab/00/ba2e50183dbd9abcce9497fa5149c62b4ff3e22d338a30d690f9af970561/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d08f00679177226c4cb8c5265012eea897c8ca3b93f429e546600c971bcbae7", size = 383795, upload-time = "2025-11-30T20:23:55.556Z" }, + { url = "https://files.pythonhosted.org/packages/05/6f/86f0272b84926bcb0e4c972262f54223e8ecc556b3224d281e6598fc9268/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5965af57d5848192c13534f90f9dd16464f3c37aaf166cc1da1cae1fd5a34898", size = 393330, upload-time = "2025-11-30T20:23:57.033Z" }, + { url = "https://files.pythonhosted.org/packages/cb/e9/0e02bb2e6dc63d212641da45df2b0bf29699d01715913e0d0f017ee29438/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a4e86e34e9ab6b667c27f3211ca48f73dba7cd3d90f8d5b11be56e5dbc3fb4e", size = 518194, upload-time = "2025-11-30T20:23:58.637Z" }, + { url = "https://files.pythonhosted.org/packages/ee/ca/be7bca14cf21513bdf9c0606aba17d1f389ea2b6987035eb4f62bd923f25/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5d3e6b26f2c785d65cc25ef1e5267ccbe1b069c5c21b8cc724efee290554419", size = 408340, upload-time = "2025-11-30T20:24:00.2Z" }, + { url = "https://files.pythonhosted.org/packages/c2/c7/736e00ebf39ed81d75544c0da6ef7b0998f8201b369acf842f9a90dc8fce/rpds_py-0.30.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:626a7433c34566535b6e56a1b39a7b17ba961e97ce3b80ec62e6f1312c025551", size = 383765, upload-time = "2025-11-30T20:24:01.759Z" }, + { url = "https://files.pythonhosted.org/packages/4a/3f/da50dfde9956aaf365c4adc9533b100008ed31aea635f2b8d7b627e25b49/rpds_py-0.30.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:acd7eb3f4471577b9b5a41baf02a978e8bdeb08b4b355273994f8b87032000a8", size = 396834, upload-time = "2025-11-30T20:24:03.687Z" }, + { url = "https://files.pythonhosted.org/packages/4e/00/34bcc2565b6020eab2623349efbdec810676ad571995911f1abdae62a3a0/rpds_py-0.30.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fe5fa731a1fa8a0a56b0977413f8cacac1768dad38d16b3a296712709476fbd5", size = 415470, upload-time = "2025-11-30T20:24:05.232Z" }, + { url = "https://files.pythonhosted.org/packages/8c/28/882e72b5b3e6f718d5453bd4d0d9cf8df36fddeb4ddbbab17869d5868616/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:74a3243a411126362712ee1524dfc90c650a503502f135d54d1b352bd01f2404", size = 565630, upload-time = "2025-11-30T20:24:06.878Z" }, + { url = "https://files.pythonhosted.org/packages/3b/97/04a65539c17692de5b85c6e293520fd01317fd878ea1995f0367d4532fb1/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:3e8eeb0544f2eb0d2581774be4c3410356eba189529a6b3e36bbbf9696175856", size = 591148, upload-time = "2025-11-30T20:24:08.445Z" }, + { url = "https://files.pythonhosted.org/packages/85/70/92482ccffb96f5441aab93e26c4d66489eb599efdcf96fad90c14bbfb976/rpds_py-0.30.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:dbd936cde57abfee19ab3213cf9c26be06d60750e60a8e4dd85d1ab12c8b1f40", size = 556030, upload-time = "2025-11-30T20:24:10.956Z" }, + { url = "https://files.pythonhosted.org/packages/20/53/7c7e784abfa500a2b6b583b147ee4bb5a2b3747a9166bab52fec4b5b5e7d/rpds_py-0.30.0-cp314-cp314t-win32.whl", hash = "sha256:dc824125c72246d924f7f796b4f63c1e9dc810c7d9e2355864b3c3a73d59ade0", size = 211570, upload-time = "2025-11-30T20:24:12.735Z" }, + { url = "https://files.pythonhosted.org/packages/d0/02/fa464cdfbe6b26e0600b62c528b72d8608f5cc49f96b8d6e38c95d60c676/rpds_py-0.30.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27f4b0e92de5bfbc6f86e43959e6edd1425c33b5e69aab0984a72047f2bcf1e3", size = 226532, upload-time = "2025-11-30T20:24:14.634Z" }, + { url = "https://files.pythonhosted.org/packages/69/71/3f34339ee70521864411f8b6992e7ab13ac30d8e4e3309e07c7361767d91/rpds_py-0.30.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c2262bdba0ad4fc6fb5545660673925c2d2a5d9e2e0fb603aad545427be0fc58", size = 372292, upload-time = "2025-11-30T20:24:16.537Z" }, + { url = "https://files.pythonhosted.org/packages/57/09/f183df9b8f2d66720d2ef71075c59f7e1b336bec7ee4c48f0a2b06857653/rpds_py-0.30.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:ee6af14263f25eedc3bb918a3c04245106a42dfd4f5c2285ea6f997b1fc3f89a", size = 362128, upload-time = "2025-11-30T20:24:18.086Z" }, + { url = "https://files.pythonhosted.org/packages/7a/68/5c2594e937253457342e078f0cc1ded3dd7b2ad59afdbf2d354869110a02/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3adbb8179ce342d235c31ab8ec511e66c73faa27a47e076ccc92421add53e2bb", size = 391542, upload-time = "2025-11-30T20:24:20.092Z" }, + { url = "https://files.pythonhosted.org/packages/49/5c/31ef1afd70b4b4fbdb2800249f34c57c64beb687495b10aec0365f53dfc4/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:250fa00e9543ac9b97ac258bd37367ff5256666122c2d0f2bc97577c60a1818c", size = 404004, upload-time = "2025-11-30T20:24:22.231Z" }, + { url = "https://files.pythonhosted.org/packages/e3/63/0cfbea38d05756f3440ce6534d51a491d26176ac045e2707adc99bb6e60a/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9854cf4f488b3d57b9aaeb105f06d78e5529d3145b1e4a41750167e8c213c6d3", size = 527063, upload-time = "2025-11-30T20:24:24.302Z" }, + { url = "https://files.pythonhosted.org/packages/42/e6/01e1f72a2456678b0f618fc9a1a13f882061690893c192fcad9f2926553a/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:993914b8e560023bc0a8bf742c5f303551992dcb85e247b1e5c7f4a7d145bda5", size = 413099, upload-time = "2025-11-30T20:24:25.916Z" }, + { url = "https://files.pythonhosted.org/packages/b8/25/8df56677f209003dcbb180765520c544525e3ef21ea72279c98b9aa7c7fb/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58edca431fb9b29950807e301826586e5bbf24163677732429770a697ffe6738", size = 392177, upload-time = "2025-11-30T20:24:27.834Z" }, + { url = "https://files.pythonhosted.org/packages/4a/b4/0a771378c5f16f8115f796d1f437950158679bcd2a7c68cf251cfb00ed5b/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:dea5b552272a944763b34394d04577cf0f9bd013207bc32323b5a89a53cf9c2f", size = 406015, upload-time = "2025-11-30T20:24:29.457Z" }, + { url = "https://files.pythonhosted.org/packages/36/d8/456dbba0af75049dc6f63ff295a2f92766b9d521fa00de67a2bd6427d57a/rpds_py-0.30.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ba3af48635eb83d03f6c9735dfb21785303e73d22ad03d489e88adae6eab8877", size = 423736, upload-time = "2025-11-30T20:24:31.22Z" }, + { url = "https://files.pythonhosted.org/packages/13/64/b4d76f227d5c45a7e0b796c674fd81b0a6c4fbd48dc29271857d8219571c/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:dff13836529b921e22f15cb099751209a60009731a68519630a24d61f0b1b30a", size = 573981, upload-time = "2025-11-30T20:24:32.934Z" }, + { url = "https://files.pythonhosted.org/packages/20/91/092bacadeda3edf92bf743cc96a7be133e13a39cdbfd7b5082e7ab638406/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:1b151685b23929ab7beec71080a8889d4d6d9fa9a983d213f07121205d48e2c4", size = 599782, upload-time = "2025-11-30T20:24:35.169Z" }, + { url = "https://files.pythonhosted.org/packages/d1/b7/b95708304cd49b7b6f82fdd039f1748b66ec2b21d6a45180910802f1abf1/rpds_py-0.30.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:ac37f9f516c51e5753f27dfdef11a88330f04de2d564be3991384b2f3535d02e", size = 562191, upload-time = "2025-11-30T20:24:36.853Z" }, ] [[package]] @@ -6664,28 +6813,28 @@ wheels = [ [[package]] name = "ruff" -version = "0.14.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/52/f0/62b5a1a723fe183650109407fa56abb433b00aa1c0b9ba555f9c4efec2c6/ruff-0.14.6.tar.gz", hash = "sha256:6f0c742ca6a7783a736b867a263b9a7a80a45ce9bee391eeda296895f1b4e1cc", size = 5669501, upload-time = "2025-11-21T14:26:17.903Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/67/d2/7dd544116d107fffb24a0064d41a5d2ed1c9d6372d142f9ba108c8e39207/ruff-0.14.6-py3-none-linux_armv6l.whl", hash = "sha256:d724ac2f1c240dbd01a2ae98db5d1d9a5e1d9e96eba999d1c48e30062df578a3", size = 13326119, upload-time = "2025-11-21T14:25:24.2Z" }, - { url = "https://files.pythonhosted.org/packages/36/6a/ad66d0a3315d6327ed6b01f759d83df3c4d5f86c30462121024361137b6a/ruff-0.14.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:9f7539ea257aa4d07b7ce87aed580e485c40143f2473ff2f2b75aee003186004", size = 13526007, upload-time = "2025-11-21T14:25:26.906Z" }, - { url = "https://files.pythonhosted.org/packages/a3/9d/dae6db96df28e0a15dea8e986ee393af70fc97fd57669808728080529c37/ruff-0.14.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7f6007e55b90a2a7e93083ba48a9f23c3158c433591c33ee2e99a49b889c6332", size = 12676572, upload-time = "2025-11-21T14:25:29.826Z" }, - { url = "https://files.pythonhosted.org/packages/76/a4/f319e87759949062cfee1b26245048e92e2acce900ad3a909285f9db1859/ruff-0.14.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a8e7b9d73d8728b68f632aa8e824ef041d068d231d8dbc7808532d3629a6bef", size = 13140745, upload-time = "2025-11-21T14:25:32.788Z" }, - { url = "https://files.pythonhosted.org/packages/95/d3/248c1efc71a0a8ed4e8e10b4b2266845d7dfc7a0ab64354afe049eaa1310/ruff-0.14.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d50d45d4553a3ebcbd33e7c5e0fe6ca4aafd9a9122492de357205c2c48f00775", size = 13076486, upload-time = "2025-11-21T14:25:35.601Z" }, - { url = "https://files.pythonhosted.org/packages/a5/19/b68d4563fe50eba4b8c92aa842149bb56dd24d198389c0ed12e7faff4f7d/ruff-0.14.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:118548dd121f8a21bfa8ab2c5b80e5b4aed67ead4b7567790962554f38e598ce", size = 13727563, upload-time = "2025-11-21T14:25:38.514Z" }, - { url = "https://files.pythonhosted.org/packages/47/ac/943169436832d4b0e867235abbdb57ce3a82367b47e0280fa7b4eabb7593/ruff-0.14.6-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:57256efafbfefcb8748df9d1d766062f62b20150691021f8ab79e2d919f7c11f", size = 15199755, upload-time = "2025-11-21T14:25:41.516Z" }, - { url = "https://files.pythonhosted.org/packages/c9/b9/288bb2399860a36d4bb0541cb66cce3c0f4156aaff009dc8499be0c24bf2/ruff-0.14.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ff18134841e5c68f8e5df1999a64429a02d5549036b394fafbe410f886e1989d", size = 14850608, upload-time = "2025-11-21T14:25:44.428Z" }, - { url = "https://files.pythonhosted.org/packages/ee/b1/a0d549dd4364e240f37e7d2907e97ee80587480d98c7799d2d8dc7a2f605/ruff-0.14.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:29c4b7ec1e66a105d5c27bd57fa93203637d66a26d10ca9809dc7fc18ec58440", size = 14118754, upload-time = "2025-11-21T14:25:47.214Z" }, - { url = "https://files.pythonhosted.org/packages/13/ac/9b9fe63716af8bdfddfacd0882bc1586f29985d3b988b3c62ddce2e202c3/ruff-0.14.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:167843a6f78680746d7e226f255d920aeed5e4ad9c03258094a2d49d3028b105", size = 13949214, upload-time = "2025-11-21T14:25:50.002Z" }, - { url = "https://files.pythonhosted.org/packages/12/27/4dad6c6a77fede9560b7df6802b1b697e97e49ceabe1f12baf3ea20862e9/ruff-0.14.6-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:16a33af621c9c523b1ae006b1b99b159bf5ac7e4b1f20b85b2572455018e0821", size = 14106112, upload-time = "2025-11-21T14:25:52.841Z" }, - { url = "https://files.pythonhosted.org/packages/6a/db/23e322d7177873eaedea59a7932ca5084ec5b7e20cb30f341ab594130a71/ruff-0.14.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:1432ab6e1ae2dc565a7eea707d3b03a0c234ef401482a6f1621bc1f427c2ff55", size = 13035010, upload-time = "2025-11-21T14:25:55.536Z" }, - { url = "https://files.pythonhosted.org/packages/a8/9c/20e21d4d69dbb35e6a1df7691e02f363423658a20a2afacf2a2c011800dc/ruff-0.14.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:4c55cfbbe7abb61eb914bfd20683d14cdfb38a6d56c6c66efa55ec6570ee4e71", size = 13054082, upload-time = "2025-11-21T14:25:58.625Z" }, - { url = "https://files.pythonhosted.org/packages/66/25/906ee6a0464c3125c8d673c589771a974965c2be1a1e28b5c3b96cb6ef88/ruff-0.14.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:efea3c0f21901a685fff4befda6d61a1bf4cb43de16da87e8226a281d614350b", size = 13303354, upload-time = "2025-11-21T14:26:01.816Z" }, - { url = "https://files.pythonhosted.org/packages/4c/58/60577569e198d56922b7ead07b465f559002b7b11d53f40937e95067ca1c/ruff-0.14.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:344d97172576d75dc6afc0e9243376dbe1668559c72de1864439c4fc95f78185", size = 14054487, upload-time = "2025-11-21T14:26:05.058Z" }, - { url = "https://files.pythonhosted.org/packages/67/0b/8e4e0639e4cc12547f41cb771b0b44ec8225b6b6a93393176d75fe6f7d40/ruff-0.14.6-py3-none-win32.whl", hash = "sha256:00169c0c8b85396516fdd9ce3446c7ca20c2a8f90a77aa945ba6b8f2bfe99e85", size = 13013361, upload-time = "2025-11-21T14:26:08.152Z" }, - { url = "https://files.pythonhosted.org/packages/fb/02/82240553b77fd1341f80ebb3eaae43ba011c7a91b4224a9f317d8e6591af/ruff-0.14.6-py3-none-win_amd64.whl", hash = "sha256:390e6480c5e3659f8a4c8d6a0373027820419ac14fa0d2713bd8e6c3e125b8b9", size = 14432087, upload-time = "2025-11-21T14:26:10.891Z" }, - { url = "https://files.pythonhosted.org/packages/a5/1f/93f9b0fad9470e4c829a5bb678da4012f0c710d09331b860ee555216f4ea/ruff-0.14.6-py3-none-win_arm64.whl", hash = "sha256:d43c81fbeae52cfa8728d8766bbf46ee4298c888072105815b392da70ca836b2", size = 13520930, upload-time = "2025-11-21T14:26:13.951Z" }, +version = "0.14.11" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d4/77/9a7fe084d268f8855d493e5031ea03fa0af8cc05887f638bf1c4e3363eb8/ruff-0.14.11.tar.gz", hash = "sha256:f6dc463bfa5c07a59b1ff2c3b9767373e541346ea105503b4c0369c520a66958", size = 5993417, upload-time = "2026-01-08T19:11:58.322Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f0/a6/a4c40a5aaa7e331f245d2dc1ac8ece306681f52b636b40ef87c88b9f7afd/ruff-0.14.11-py3-none-linux_armv6l.whl", hash = "sha256:f6ff2d95cbd335841a7217bdfd9c1d2e44eac2c584197ab1385579d55ff8830e", size = 12951208, upload-time = "2026-01-08T19:12:09.218Z" }, + { url = "https://files.pythonhosted.org/packages/5c/5c/360a35cb7204b328b685d3129c08aca24765ff92b5a7efedbdd6c150d555/ruff-0.14.11-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6f6eb5c1c8033680f4172ea9c8d3706c156223010b8b97b05e82c59bdc774ee6", size = 13330075, upload-time = "2026-01-08T19:12:02.549Z" }, + { url = "https://files.pythonhosted.org/packages/1b/9e/0cc2f1be7a7d33cae541824cf3f95b4ff40d03557b575912b5b70273c9ec/ruff-0.14.11-py3-none-macosx_11_0_arm64.whl", hash = "sha256:f2fc34cc896f90080fca01259f96c566f74069a04b25b6205d55379d12a6855e", size = 12257809, upload-time = "2026-01-08T19:12:00.366Z" }, + { url = "https://files.pythonhosted.org/packages/a7/e5/5faab97c15bb75228d9f74637e775d26ac703cc2b4898564c01ab3637c02/ruff-0.14.11-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:53386375001773ae812b43205d6064dae49ff0968774e6befe16a994fc233caa", size = 12678447, upload-time = "2026-01-08T19:12:13.899Z" }, + { url = "https://files.pythonhosted.org/packages/1b/33/e9767f60a2bef779fb5855cab0af76c488e0ce90f7bb7b8a45c8a2ba4178/ruff-0.14.11-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a697737dce1ca97a0a55b5ff0434ee7205943d4874d638fe3ae66166ff46edbe", size = 12758560, upload-time = "2026-01-08T19:11:42.55Z" }, + { url = "https://files.pythonhosted.org/packages/eb/84/4c6cf627a21462bb5102f7be2a320b084228ff26e105510cd2255ea868e5/ruff-0.14.11-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6845ca1da8ab81ab1dce755a32ad13f1db72e7fba27c486d5d90d65e04d17b8f", size = 13599296, upload-time = "2026-01-08T19:11:30.371Z" }, + { url = "https://files.pythonhosted.org/packages/88/e1/92b5ed7ea66d849f6157e695dc23d5d6d982bd6aa8d077895652c38a7cae/ruff-0.14.11-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:e36ce2fd31b54065ec6f76cb08d60159e1b32bdf08507862e32f47e6dde8bcbf", size = 15048981, upload-time = "2026-01-08T19:12:04.742Z" }, + { url = "https://files.pythonhosted.org/packages/61/df/c1bd30992615ac17c2fb64b8a7376ca22c04a70555b5d05b8f717163cf9f/ruff-0.14.11-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:590bcc0e2097ecf74e62a5c10a6b71f008ad82eb97b0a0079e85defe19fe74d9", size = 14633183, upload-time = "2026-01-08T19:11:40.069Z" }, + { url = "https://files.pythonhosted.org/packages/04/e9/fe552902f25013dd28a5428a42347d9ad20c4b534834a325a28305747d64/ruff-0.14.11-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:53fe71125fc158210d57fe4da26e622c9c294022988d08d9347ec1cf782adafe", size = 14050453, upload-time = "2026-01-08T19:11:37.555Z" }, + { url = "https://files.pythonhosted.org/packages/ae/93/f36d89fa021543187f98991609ce6e47e24f35f008dfe1af01379d248a41/ruff-0.14.11-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a35c9da08562f1598ded8470fcfef2afb5cf881996e6c0a502ceb61f4bc9c8a3", size = 13757889, upload-time = "2026-01-08T19:12:07.094Z" }, + { url = "https://files.pythonhosted.org/packages/b7/9f/c7fb6ecf554f28709a6a1f2a7f74750d400979e8cd47ed29feeaa1bd4db8/ruff-0.14.11-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:0f3727189a52179393ecf92ec7057c2210203e6af2676f08d92140d3e1ee72c1", size = 13955832, upload-time = "2026-01-08T19:11:55.064Z" }, + { url = "https://files.pythonhosted.org/packages/db/a0/153315310f250f76900a98278cf878c64dfb6d044e184491dd3289796734/ruff-0.14.11-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:eb09f849bd37147a789b85995ff734a6c4a095bed5fd1608c4f56afc3634cde2", size = 12586522, upload-time = "2026-01-08T19:11:35.356Z" }, + { url = "https://files.pythonhosted.org/packages/2f/2b/a73a2b6e6d2df1d74bf2b78098be1572191e54bec0e59e29382d13c3adc5/ruff-0.14.11-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:c61782543c1231bf71041461c1f28c64b961d457d0f238ac388e2ab173d7ecb7", size = 12724637, upload-time = "2026-01-08T19:11:47.796Z" }, + { url = "https://files.pythonhosted.org/packages/f0/41/09100590320394401cd3c48fc718a8ba71c7ddb1ffd07e0ad6576b3a3df2/ruff-0.14.11-py3-none-musllinux_1_2_i686.whl", hash = "sha256:82ff352ea68fb6766140381748e1f67f83c39860b6446966cff48a315c3e2491", size = 13145837, upload-time = "2026-01-08T19:11:32.87Z" }, + { url = "https://files.pythonhosted.org/packages/3b/d8/e035db859d1d3edf909381eb8ff3e89a672d6572e9454093538fe6f164b0/ruff-0.14.11-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:728e56879df4ca5b62a9dde2dd0eb0edda2a55160c0ea28c4025f18c03f86984", size = 13850469, upload-time = "2026-01-08T19:12:11.694Z" }, + { url = "https://files.pythonhosted.org/packages/4e/02/bb3ff8b6e6d02ce9e3740f4c17dfbbfb55f34c789c139e9cd91985f356c7/ruff-0.14.11-py3-none-win32.whl", hash = "sha256:337c5dd11f16ee52ae217757d9b82a26400be7efac883e9e852646f1557ed841", size = 12851094, upload-time = "2026-01-08T19:11:45.163Z" }, + { url = "https://files.pythonhosted.org/packages/58/f1/90ddc533918d3a2ad628bc3044cdfc094949e6d4b929220c3f0eb8a1c998/ruff-0.14.11-py3-none-win_amd64.whl", hash = "sha256:f981cea63d08456b2c070e64b79cb62f951aa1305282974d4d5216e6e0178ae6", size = 14001379, upload-time = "2026-01-08T19:11:52.591Z" }, + { url = "https://files.pythonhosted.org/packages/c4/1c/1dbe51782c0e1e9cfce1d1004752672d2d4629ea46945d19d731ad772b3b/ruff-0.14.11-py3-none-win_arm64.whl", hash = "sha256:649fb6c9edd7f751db276ef42df1f3df41c38d67d199570ae2a7bd6cbc3590f0", size = 12938644, upload-time = "2026-01-08T19:11:50.027Z" }, ] [[package]] @@ -6727,13 +6876,17 @@ torch = [ name = "scikit-learn" version = "1.7.2" source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'darwin'", + "python_full_version < '3.11' and platform_machine != 'x86_64' and sys_platform == 'darwin'", + "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "(python_full_version < '3.11' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')", +] dependencies = [ - { name = "joblib" }, + { name = "joblib", marker = "python_full_version < '3.11'" }, { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "scipy", version = "1.16.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "threadpoolctl" }, + { name = "threadpoolctl", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/98/c2/a7855e41c9d285dfe86dc50b250978105dce513d6e459ea66a6aeb0e1e0c/scikit_learn-1.7.2.tar.gz", hash = "sha256:20e9e49ecd130598f1ca38a1d85090e1a600147b9c02fa6f15d69cb53d968fda", size = 7193136, upload-time = "2025-09-09T08:21:29.075Z" } wheels = [ @@ -6769,19 +6922,83 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8e/87/24f541b6d62b1794939ae6422f8023703bbf6900378b2b34e0b4384dfefd/scikit_learn-1.7.2-cp314-cp314-win_amd64.whl", hash = "sha256:bb24510ed3f9f61476181e4db51ce801e2ba37541def12dc9333b946fc7a9cf8", size = 8820007, upload-time = "2025-09-09T08:21:26.713Z" }, ] +[[package]] +name = "scikit-learn" +version = "1.8.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and platform_machine == 'x86_64' and sys_platform == 'darwin'", + "python_full_version == '3.13.*' and platform_machine == 'x86_64' and sys_platform == 'darwin'", + "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'darwin'", + "python_full_version == '3.11.*' and platform_machine == 'x86_64' and sys_platform == 'darwin'", + "python_full_version >= '3.14' and platform_machine != 'x86_64' and sys_platform == 'darwin'", + "python_full_version >= '3.14' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "(python_full_version >= '3.14' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.14' and sys_platform != 'darwin' and sys_platform != 'linux')", + "python_full_version == '3.13.*' and platform_machine != 'x86_64' and sys_platform == 'darwin'", + "python_full_version == '3.13.*' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "(python_full_version == '3.13.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.13.*' and sys_platform != 'darwin' and sys_platform != 'linux')", + "python_full_version == '3.12.*' and platform_machine != 'x86_64' and sys_platform == 'darwin'", + "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')", + "python_full_version == '3.11.*' and platform_machine != 'x86_64' and sys_platform == 'darwin'", + "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux')", +] +dependencies = [ + { name = "joblib", marker = "python_full_version >= '3.11'" }, + { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "scipy", version = "1.17.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "threadpoolctl", marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0e/d4/40988bf3b8e34feec1d0e6a051446b1f66225f8529b9309becaeef62b6c4/scikit_learn-1.8.0.tar.gz", hash = "sha256:9bccbb3b40e3de10351f8f5068e105d0f4083b1a65fa07b6634fbc401a6287fd", size = 7335585, upload-time = "2025-12-10T07:08:53.618Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c9/92/53ea2181da8ac6bf27170191028aee7251f8f841f8d3edbfdcaf2008fde9/scikit_learn-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:146b4d36f800c013d267b29168813f7a03a43ecd2895d04861f1240b564421da", size = 8595835, upload-time = "2025-12-10T07:07:39.385Z" }, + { url = "https://files.pythonhosted.org/packages/01/18/d154dc1638803adf987910cdd07097d9c526663a55666a97c124d09fb96a/scikit_learn-1.8.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:f984ca4b14914e6b4094c5d52a32ea16b49832c03bd17a110f004db3c223e8e1", size = 8080381, upload-time = "2025-12-10T07:07:41.93Z" }, + { url = "https://files.pythonhosted.org/packages/8a/44/226142fcb7b7101e64fdee5f49dbe6288d4c7af8abf593237b70fca080a4/scikit_learn-1.8.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5e30adb87f0cc81c7690a84f7932dd66be5bac57cfe16b91cb9151683a4a2d3b", size = 8799632, upload-time = "2025-12-10T07:07:43.899Z" }, + { url = "https://files.pythonhosted.org/packages/36/4d/4a67f30778a45d542bbea5db2dbfa1e9e100bf9ba64aefe34215ba9f11f6/scikit_learn-1.8.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ada8121bcb4dac28d930febc791a69f7cb1673c8495e5eee274190b73a4559c1", size = 9103788, upload-time = "2025-12-10T07:07:45.982Z" }, + { url = "https://files.pythonhosted.org/packages/89/3c/45c352094cfa60050bcbb967b1faf246b22e93cb459f2f907b600f2ceda5/scikit_learn-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:c57b1b610bd1f40ba43970e11ce62821c2e6569e4d74023db19c6b26f246cb3b", size = 8081706, upload-time = "2025-12-10T07:07:48.111Z" }, + { url = "https://files.pythonhosted.org/packages/3d/46/5416595bb395757f754feb20c3d776553a386b661658fb21b7c814e89efe/scikit_learn-1.8.0-cp311-cp311-win_arm64.whl", hash = "sha256:2838551e011a64e3053ad7618dda9310175f7515f1742fa2d756f7c874c05961", size = 7688451, upload-time = "2025-12-10T07:07:49.873Z" }, + { url = "https://files.pythonhosted.org/packages/90/74/e6a7cc4b820e95cc38cf36cd74d5aa2b42e8ffc2d21fe5a9a9c45c1c7630/scikit_learn-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5fb63362b5a7ddab88e52b6dbb47dac3fd7dafeee740dc6c8d8a446ddedade8e", size = 8548242, upload-time = "2025-12-10T07:07:51.568Z" }, + { url = "https://files.pythonhosted.org/packages/49/d8/9be608c6024d021041c7f0b3928d4749a706f4e2c3832bbede4fb4f58c95/scikit_learn-1.8.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:5025ce924beccb28298246e589c691fe1b8c1c96507e6d27d12c5fadd85bfd76", size = 8079075, upload-time = "2025-12-10T07:07:53.697Z" }, + { url = "https://files.pythonhosted.org/packages/dd/47/f187b4636ff80cc63f21cd40b7b2d177134acaa10f6bb73746130ee8c2e5/scikit_learn-1.8.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4496bb2cf7a43ce1a2d7524a79e40bc5da45cf598dbf9545b7e8316ccba47bb4", size = 8660492, upload-time = "2025-12-10T07:07:55.574Z" }, + { url = "https://files.pythonhosted.org/packages/97/74/b7a304feb2b49df9fafa9382d4d09061a96ee9a9449a7cbea7988dda0828/scikit_learn-1.8.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a0bcfe4d0d14aec44921545fd2af2338c7471de9cb701f1da4c9d85906ab847a", size = 8931904, upload-time = "2025-12-10T07:07:57.666Z" }, + { url = "https://files.pythonhosted.org/packages/9f/c4/0ab22726a04ede56f689476b760f98f8f46607caecff993017ac1b64aa5d/scikit_learn-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:35c007dedb2ffe38fe3ee7d201ebac4a2deccd2408e8621d53067733e3c74809", size = 8019359, upload-time = "2025-12-10T07:07:59.838Z" }, + { url = "https://files.pythonhosted.org/packages/24/90/344a67811cfd561d7335c1b96ca21455e7e472d281c3c279c4d3f2300236/scikit_learn-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:8c497fff237d7b4e07e9ef1a640887fa4fb765647f86fbe00f969ff6280ce2bb", size = 7641898, upload-time = "2025-12-10T07:08:01.36Z" }, + { url = "https://files.pythonhosted.org/packages/03/aa/e22e0768512ce9255eba34775be2e85c2048da73da1193e841707f8f039c/scikit_learn-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0d6ae97234d5d7079dc0040990a6f7aeb97cb7fa7e8945f1999a429b23569e0a", size = 8513770, upload-time = "2025-12-10T07:08:03.251Z" }, + { url = "https://files.pythonhosted.org/packages/58/37/31b83b2594105f61a381fc74ca19e8780ee923be2d496fcd8d2e1147bd99/scikit_learn-1.8.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:edec98c5e7c128328124a029bceb09eda2d526997780fef8d65e9a69eead963e", size = 8044458, upload-time = "2025-12-10T07:08:05.336Z" }, + { url = "https://files.pythonhosted.org/packages/2d/5a/3f1caed8765f33eabb723596666da4ebbf43d11e96550fb18bdec42b467b/scikit_learn-1.8.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:74b66d8689d52ed04c271e1329f0c61635bcaf5b926db9b12d58914cdc01fe57", size = 8610341, upload-time = "2025-12-10T07:08:07.732Z" }, + { url = "https://files.pythonhosted.org/packages/38/cf/06896db3f71c75902a8e9943b444a56e727418f6b4b4a90c98c934f51ed4/scikit_learn-1.8.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8fdf95767f989b0cfedb85f7ed8ca215d4be728031f56ff5a519ee1e3276dc2e", size = 8900022, upload-time = "2025-12-10T07:08:09.862Z" }, + { url = "https://files.pythonhosted.org/packages/1c/f9/9b7563caf3ec8873e17a31401858efab6b39a882daf6c1bfa88879c0aa11/scikit_learn-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:2de443b9373b3b615aec1bb57f9baa6bb3a9bd093f1269ba95c17d870422b271", size = 7989409, upload-time = "2025-12-10T07:08:12.028Z" }, + { url = "https://files.pythonhosted.org/packages/49/bd/1f4001503650e72c4f6009ac0c4413cb17d2d601cef6f71c0453da2732fc/scikit_learn-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:eddde82a035681427cbedded4e6eff5e57fa59216c2e3e90b10b19ab1d0a65c3", size = 7619760, upload-time = "2025-12-10T07:08:13.688Z" }, + { url = "https://files.pythonhosted.org/packages/d2/7d/a630359fc9dcc95496588c8d8e3245cc8fd81980251079bc09c70d41d951/scikit_learn-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:7cc267b6108f0a1499a734167282c00c4ebf61328566b55ef262d48e9849c735", size = 8826045, upload-time = "2025-12-10T07:08:15.215Z" }, + { url = "https://files.pythonhosted.org/packages/cc/56/a0c86f6930cfcd1c7054a2bc417e26960bb88d32444fe7f71d5c2cfae891/scikit_learn-1.8.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:fe1c011a640a9f0791146011dfd3c7d9669785f9fed2b2a5f9e207536cf5c2fd", size = 8420324, upload-time = "2025-12-10T07:08:17.561Z" }, + { url = "https://files.pythonhosted.org/packages/46/1e/05962ea1cebc1cf3876667ecb14c283ef755bf409993c5946ade3b77e303/scikit_learn-1.8.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:72358cce49465d140cc4e7792015bb1f0296a9742d5622c67e31399b75468b9e", size = 8680651, upload-time = "2025-12-10T07:08:19.952Z" }, + { url = "https://files.pythonhosted.org/packages/fe/56/a85473cd75f200c9759e3a5f0bcab2d116c92a8a02ee08ccd73b870f8bb4/scikit_learn-1.8.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:80832434a6cc114f5219211eec13dcbc16c2bac0e31ef64c6d346cde3cf054cb", size = 8925045, upload-time = "2025-12-10T07:08:22.11Z" }, + { url = "https://files.pythonhosted.org/packages/cc/b7/64d8cfa896c64435ae57f4917a548d7ac7a44762ff9802f75a79b77cb633/scikit_learn-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ee787491dbfe082d9c3013f01f5991658b0f38aa8177e4cd4bf434c58f551702", size = 8507994, upload-time = "2025-12-10T07:08:23.943Z" }, + { url = "https://files.pythonhosted.org/packages/5e/37/e192ea709551799379958b4c4771ec507347027bb7c942662c7fbeba31cb/scikit_learn-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf97c10a3f5a7543f9b88cbf488d33d175e9146115a451ae34568597ba33dcde", size = 7869518, upload-time = "2025-12-10T07:08:25.71Z" }, + { url = "https://files.pythonhosted.org/packages/24/05/1af2c186174cc92dcab2233f327336058c077d38f6fe2aceb08e6ab4d509/scikit_learn-1.8.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:c22a2da7a198c28dd1a6e1136f19c830beab7fdca5b3e5c8bba8394f8a5c45b3", size = 8528667, upload-time = "2025-12-10T07:08:27.541Z" }, + { url = "https://files.pythonhosted.org/packages/a8/25/01c0af38fe969473fb292bba9dc2b8f9b451f3112ff242c647fee3d0dfe7/scikit_learn-1.8.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:6b595b07a03069a2b1740dc08c2299993850ea81cce4fe19b2421e0c970de6b7", size = 8066524, upload-time = "2025-12-10T07:08:29.822Z" }, + { url = "https://files.pythonhosted.org/packages/be/ce/a0623350aa0b68647333940ee46fe45086c6060ec604874e38e9ab7d8e6c/scikit_learn-1.8.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:29ffc74089f3d5e87dfca4c2c8450f88bdc61b0fc6ed5d267f3988f19a1309f6", size = 8657133, upload-time = "2025-12-10T07:08:31.865Z" }, + { url = "https://files.pythonhosted.org/packages/b8/cb/861b41341d6f1245e6ca80b1c1a8c4dfce43255b03df034429089ca2a2c5/scikit_learn-1.8.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fb65db5d7531bccf3a4f6bec3462223bea71384e2cda41da0f10b7c292b9e7c4", size = 8923223, upload-time = "2025-12-10T07:08:34.166Z" }, + { url = "https://files.pythonhosted.org/packages/76/18/a8def8f91b18cd1ba6e05dbe02540168cb24d47e8dcf69e8d00b7da42a08/scikit_learn-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:56079a99c20d230e873ea40753102102734c5953366972a71d5cb39a32bc40c6", size = 8096518, upload-time = "2025-12-10T07:08:36.339Z" }, + { url = "https://files.pythonhosted.org/packages/d1/77/482076a678458307f0deb44e29891d6022617b2a64c840c725495bee343f/scikit_learn-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:3bad7565bc9cf37ce19a7c0d107742b320c1285df7aab1a6e2d28780df167242", size = 7754546, upload-time = "2025-12-10T07:08:38.128Z" }, + { url = "https://files.pythonhosted.org/packages/2d/d1/ef294ca754826daa043b2a104e59960abfab4cf653891037d19dd5b6f3cf/scikit_learn-1.8.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:4511be56637e46c25721e83d1a9cea9614e7badc7040c4d573d75fbe257d6fd7", size = 8848305, upload-time = "2025-12-10T07:08:41.013Z" }, + { url = "https://files.pythonhosted.org/packages/5b/e2/b1f8b05138ee813b8e1a4149f2f0d289547e60851fd1bb268886915adbda/scikit_learn-1.8.0-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:a69525355a641bf8ef136a7fa447672fb54fe8d60cab5538d9eb7c6438543fb9", size = 8432257, upload-time = "2025-12-10T07:08:42.873Z" }, + { url = "https://files.pythonhosted.org/packages/26/11/c32b2138a85dcb0c99f6afd13a70a951bfdff8a6ab42d8160522542fb647/scikit_learn-1.8.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c2656924ec73e5939c76ac4c8b026fc203b83d8900362eb2599d8aee80e4880f", size = 8678673, upload-time = "2025-12-10T07:08:45.362Z" }, + { url = "https://files.pythonhosted.org/packages/c7/57/51f2384575bdec454f4fe4e7a919d696c9ebce914590abf3e52d47607ab8/scikit_learn-1.8.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15fc3b5d19cc2be65404786857f2e13c70c83dd4782676dd6814e3b89dc8f5b9", size = 8922467, upload-time = "2025-12-10T07:08:47.408Z" }, + { url = "https://files.pythonhosted.org/packages/35/4d/748c9e2872637a57981a04adc038dacaa16ba8ca887b23e34953f0b3f742/scikit_learn-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:00d6f1d66fbcf4eba6e356e1420d33cc06c70a45bb1363cd6f6a8e4ebbbdece2", size = 8774395, upload-time = "2025-12-10T07:08:49.337Z" }, + { url = "https://files.pythonhosted.org/packages/60/22/d7b2ebe4704a5e50790ba089d5c2ae308ab6bb852719e6c3bd4f04c3a363/scikit_learn-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:f28dd15c6bb0b66ba09728cf09fd8736c304be29409bd8445a080c1280619e8c", size = 8002647, upload-time = "2025-12-10T07:08:51.601Z" }, +] + [[package]] name = "scipy" version = "1.15.3" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.10.2' and python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'darwin'", - "python_full_version < '3.10.2' and platform_machine == 'x86_64' and sys_platform == 'darwin'", - "python_full_version >= '3.10.2' and python_full_version < '3.11' and platform_machine != 'x86_64' and sys_platform == 'darwin'", - "python_full_version >= '3.10.2' and python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'", - "(python_full_version >= '3.10.2' and python_full_version < '3.11' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.10.2' and python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')", - "python_full_version < '3.10.2' and platform_machine != 'x86_64' and sys_platform == 'darwin'", - "python_full_version < '3.10.2' and platform_machine == 'aarch64' and sys_platform == 'linux'", - "(python_full_version < '3.10.2' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10.2' and sys_platform != 'darwin' and sys_platform != 'linux')", + "python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'darwin'", + "python_full_version < '3.11' and platform_machine != 'x86_64' and sys_platform == 'darwin'", + "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "(python_full_version < '3.11' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')", ] dependencies = [ { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, @@ -6837,15 +7054,19 @@ wheels = [ [[package]] name = "scipy" -version = "1.16.3" +version = "1.17.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin'", + "python_full_version >= '3.14' and platform_machine == 'x86_64' and sys_platform == 'darwin'", + "python_full_version == '3.13.*' and platform_machine == 'x86_64' and sys_platform == 'darwin'", "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'darwin'", "python_full_version == '3.11.*' and platform_machine == 'x86_64' and sys_platform == 'darwin'", - "python_full_version >= '3.13' and platform_machine != 'x86_64' and sys_platform == 'darwin'", - "python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux'", - "(python_full_version >= '3.13' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and sys_platform != 'darwin' and sys_platform != 'linux')", + "python_full_version >= '3.14' and platform_machine != 'x86_64' and sys_platform == 'darwin'", + "python_full_version >= '3.14' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "(python_full_version >= '3.14' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.14' and sys_platform != 'darwin' and sys_platform != 'linux')", + "python_full_version == '3.13.*' and platform_machine != 'x86_64' and sys_platform == 'darwin'", + "python_full_version == '3.13.*' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "(python_full_version == '3.13.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.13.*' and sys_platform != 'darwin' and sys_platform != 'linux')", "python_full_version == '3.12.*' and platform_machine != 'x86_64' and sys_platform == 'darwin'", "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'", "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')", @@ -6856,68 +7077,68 @@ resolution-markers = [ dependencies = [ { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0a/ca/d8ace4f98322d01abcd52d381134344bf7b431eba7ed8b42bdea5a3c2ac9/scipy-1.16.3.tar.gz", hash = "sha256:01e87659402762f43bd2fee13370553a17ada367d42e7487800bf2916535aecb", size = 30597883, upload-time = "2025-10-28T17:38:54.068Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9b/5f/6f37d7439de1455ce9c5a556b8d1db0979f03a796c030bafdf08d35b7bf9/scipy-1.16.3-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:40be6cf99e68b6c4321e9f8782e7d5ff8265af28ef2cd56e9c9b2638fa08ad97", size = 36630881, upload-time = "2025-10-28T17:31:47.104Z" }, - { url = "https://files.pythonhosted.org/packages/7c/89/d70e9f628749b7e4db2aa4cd89735502ff3f08f7b9b27d2e799485987cd9/scipy-1.16.3-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:8be1ca9170fcb6223cc7c27f4305d680ded114a1567c0bd2bfcbf947d1b17511", size = 28941012, upload-time = "2025-10-28T17:31:53.411Z" }, - { url = "https://files.pythonhosted.org/packages/a8/a8/0e7a9a6872a923505dbdf6bb93451edcac120363131c19013044a1e7cb0c/scipy-1.16.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:bea0a62734d20d67608660f69dcda23e7f90fb4ca20974ab80b6ed40df87a005", size = 20931935, upload-time = "2025-10-28T17:31:57.361Z" }, - { url = "https://files.pythonhosted.org/packages/bd/c7/020fb72bd79ad798e4dbe53938543ecb96b3a9ac3fe274b7189e23e27353/scipy-1.16.3-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:2a207a6ce9c24f1951241f4693ede2d393f59c07abc159b2cb2be980820e01fb", size = 23534466, upload-time = "2025-10-28T17:32:01.875Z" }, - { url = "https://files.pythonhosted.org/packages/be/a0/668c4609ce6dbf2f948e167836ccaf897f95fb63fa231c87da7558a374cd/scipy-1.16.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:532fb5ad6a87e9e9cd9c959b106b73145a03f04c7d57ea3e6f6bb60b86ab0876", size = 33593618, upload-time = "2025-10-28T17:32:06.902Z" }, - { url = "https://files.pythonhosted.org/packages/ca/6e/8942461cf2636cdae083e3eb72622a7fbbfa5cf559c7d13ab250a5dbdc01/scipy-1.16.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0151a0749efeaaab78711c78422d413c583b8cdd2011a3c1d6c794938ee9fdb2", size = 35899798, upload-time = "2025-10-28T17:32:12.665Z" }, - { url = "https://files.pythonhosted.org/packages/79/e8/d0f33590364cdbd67f28ce79368b373889faa4ee959588beddf6daef9abe/scipy-1.16.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b7180967113560cca57418a7bc719e30366b47959dd845a93206fbed693c867e", size = 36226154, upload-time = "2025-10-28T17:32:17.961Z" }, - { url = "https://files.pythonhosted.org/packages/39/c1/1903de608c0c924a1749c590064e65810f8046e437aba6be365abc4f7557/scipy-1.16.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:deb3841c925eeddb6afc1e4e4a45e418d19ec7b87c5df177695224078e8ec733", size = 38878540, upload-time = "2025-10-28T17:32:23.907Z" }, - { url = "https://files.pythonhosted.org/packages/f1/d0/22ec7036ba0b0a35bccb7f25ab407382ed34af0b111475eb301c16f8a2e5/scipy-1.16.3-cp311-cp311-win_amd64.whl", hash = "sha256:53c3844d527213631e886621df5695d35e4f6a75f620dca412bcd292f6b87d78", size = 38722107, upload-time = "2025-10-28T17:32:29.921Z" }, - { url = "https://files.pythonhosted.org/packages/7b/60/8a00e5a524bb3bf8898db1650d350f50e6cffb9d7a491c561dc9826c7515/scipy-1.16.3-cp311-cp311-win_arm64.whl", hash = "sha256:9452781bd879b14b6f055b26643703551320aa8d79ae064a71df55c00286a184", size = 25506272, upload-time = "2025-10-28T17:32:34.577Z" }, - { url = "https://files.pythonhosted.org/packages/40/41/5bf55c3f386b1643812f3a5674edf74b26184378ef0f3e7c7a09a7e2ca7f/scipy-1.16.3-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:81fc5827606858cf71446a5e98715ba0e11f0dbc83d71c7409d05486592a45d6", size = 36659043, upload-time = "2025-10-28T17:32:40.285Z" }, - { url = "https://files.pythonhosted.org/packages/1e/0f/65582071948cfc45d43e9870bf7ca5f0e0684e165d7c9ef4e50d783073eb/scipy-1.16.3-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:c97176013d404c7346bf57874eaac5187d969293bf40497140b0a2b2b7482e07", size = 28898986, upload-time = "2025-10-28T17:32:45.325Z" }, - { url = "https://files.pythonhosted.org/packages/96/5e/36bf3f0ac298187d1ceadde9051177d6a4fe4d507e8f59067dc9dd39e650/scipy-1.16.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:2b71d93c8a9936046866acebc915e2af2e292b883ed6e2cbe5c34beb094b82d9", size = 20889814, upload-time = "2025-10-28T17:32:49.277Z" }, - { url = "https://files.pythonhosted.org/packages/80/35/178d9d0c35394d5d5211bbff7ac4f2986c5488b59506fef9e1de13ea28d3/scipy-1.16.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:3d4a07a8e785d80289dfe66b7c27d8634a773020742ec7187b85ccc4b0e7b686", size = 23565795, upload-time = "2025-10-28T17:32:53.337Z" }, - { url = "https://files.pythonhosted.org/packages/fa/46/d1146ff536d034d02f83c8afc3c4bab2eddb634624d6529a8512f3afc9da/scipy-1.16.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0553371015692a898e1aa858fed67a3576c34edefa6b7ebdb4e9dde49ce5c203", size = 33349476, upload-time = "2025-10-28T17:32:58.353Z" }, - { url = "https://files.pythonhosted.org/packages/79/2e/415119c9ab3e62249e18c2b082c07aff907a273741b3f8160414b0e9193c/scipy-1.16.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:72d1717fd3b5e6ec747327ce9bda32d5463f472c9dce9f54499e81fbd50245a1", size = 35676692, upload-time = "2025-10-28T17:33:03.88Z" }, - { url = "https://files.pythonhosted.org/packages/27/82/df26e44da78bf8d2aeaf7566082260cfa15955a5a6e96e6a29935b64132f/scipy-1.16.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1fb2472e72e24d1530debe6ae078db70fb1605350c88a3d14bc401d6306dbffe", size = 36019345, upload-time = "2025-10-28T17:33:09.773Z" }, - { url = "https://files.pythonhosted.org/packages/82/31/006cbb4b648ba379a95c87262c2855cd0d09453e500937f78b30f02fa1cd/scipy-1.16.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c5192722cffe15f9329a3948c4b1db789fbb1f05c97899187dcf009b283aea70", size = 38678975, upload-time = "2025-10-28T17:33:15.809Z" }, - { url = "https://files.pythonhosted.org/packages/c2/7f/acbd28c97e990b421af7d6d6cd416358c9c293fc958b8529e0bd5d2a2a19/scipy-1.16.3-cp312-cp312-win_amd64.whl", hash = "sha256:56edc65510d1331dae01ef9b658d428e33ed48b4f77b1d51caf479a0253f96dc", size = 38555926, upload-time = "2025-10-28T17:33:21.388Z" }, - { url = "https://files.pythonhosted.org/packages/ce/69/c5c7807fd007dad4f48e0a5f2153038dc96e8725d3345b9ee31b2b7bed46/scipy-1.16.3-cp312-cp312-win_arm64.whl", hash = "sha256:a8a26c78ef223d3e30920ef759e25625a0ecdd0d60e5a8818b7513c3e5384cf2", size = 25463014, upload-time = "2025-10-28T17:33:25.975Z" }, - { url = "https://files.pythonhosted.org/packages/72/f1/57e8327ab1508272029e27eeef34f2302ffc156b69e7e233e906c2a5c379/scipy-1.16.3-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:d2ec56337675e61b312179a1ad124f5f570c00f920cc75e1000025451b88241c", size = 36617856, upload-time = "2025-10-28T17:33:31.375Z" }, - { url = "https://files.pythonhosted.org/packages/44/13/7e63cfba8a7452eb756306aa2fd9b37a29a323b672b964b4fdeded9a3f21/scipy-1.16.3-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:16b8bc35a4cc24db80a0ec836a9286d0e31b2503cb2fd7ff7fb0e0374a97081d", size = 28874306, upload-time = "2025-10-28T17:33:36.516Z" }, - { url = "https://files.pythonhosted.org/packages/15/65/3a9400efd0228a176e6ec3454b1fa998fbbb5a8defa1672c3f65706987db/scipy-1.16.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:5803c5fadd29de0cf27fa08ccbfe7a9e5d741bf63e4ab1085437266f12460ff9", size = 20865371, upload-time = "2025-10-28T17:33:42.094Z" }, - { url = "https://files.pythonhosted.org/packages/33/d7/eda09adf009a9fb81827194d4dd02d2e4bc752cef16737cc4ef065234031/scipy-1.16.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:b81c27fc41954319a943d43b20e07c40bdcd3ff7cf013f4fb86286faefe546c4", size = 23524877, upload-time = "2025-10-28T17:33:48.483Z" }, - { url = "https://files.pythonhosted.org/packages/7d/6b/3f911e1ebc364cb81320223a3422aab7d26c9c7973109a9cd0f27c64c6c0/scipy-1.16.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0c3b4dd3d9b08dbce0f3440032c52e9e2ab9f96ade2d3943313dfe51a7056959", size = 33342103, upload-time = "2025-10-28T17:33:56.495Z" }, - { url = "https://files.pythonhosted.org/packages/21/f6/4bfb5695d8941e5c570a04d9fcd0d36bce7511b7d78e6e75c8f9791f82d0/scipy-1.16.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7dc1360c06535ea6116a2220f760ae572db9f661aba2d88074fe30ec2aa1ff88", size = 35697297, upload-time = "2025-10-28T17:34:04.722Z" }, - { url = "https://files.pythonhosted.org/packages/04/e1/6496dadbc80d8d896ff72511ecfe2316b50313bfc3ebf07a3f580f08bd8c/scipy-1.16.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:663b8d66a8748051c3ee9c96465fb417509315b99c71550fda2591d7dd634234", size = 36021756, upload-time = "2025-10-28T17:34:13.482Z" }, - { url = "https://files.pythonhosted.org/packages/fe/bd/a8c7799e0136b987bda3e1b23d155bcb31aec68a4a472554df5f0937eef7/scipy-1.16.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eab43fae33a0c39006a88096cd7b4f4ef545ea0447d250d5ac18202d40b6611d", size = 38696566, upload-time = "2025-10-28T17:34:22.384Z" }, - { url = "https://files.pythonhosted.org/packages/cd/01/1204382461fcbfeb05b6161b594f4007e78b6eba9b375382f79153172b4d/scipy-1.16.3-cp313-cp313-win_amd64.whl", hash = "sha256:062246acacbe9f8210de8e751b16fc37458213f124bef161a5a02c7a39284304", size = 38529877, upload-time = "2025-10-28T17:35:51.076Z" }, - { url = "https://files.pythonhosted.org/packages/7f/14/9d9fbcaa1260a94f4bb5b64ba9213ceb5d03cd88841fe9fd1ffd47a45b73/scipy-1.16.3-cp313-cp313-win_arm64.whl", hash = "sha256:50a3dbf286dbc7d84f176f9a1574c705f277cb6565069f88f60db9eafdbe3ee2", size = 25455366, upload-time = "2025-10-28T17:35:59.014Z" }, - { url = "https://files.pythonhosted.org/packages/e2/a3/9ec205bd49f42d45d77f1730dbad9ccf146244c1647605cf834b3a8c4f36/scipy-1.16.3-cp313-cp313t-macosx_10_14_x86_64.whl", hash = "sha256:fb4b29f4cf8cc5a8d628bc8d8e26d12d7278cd1f219f22698a378c3d67db5e4b", size = 37027931, upload-time = "2025-10-28T17:34:31.451Z" }, - { url = "https://files.pythonhosted.org/packages/25/06/ca9fd1f3a4589cbd825b1447e5db3a8ebb969c1eaf22c8579bd286f51b6d/scipy-1.16.3-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:8d09d72dc92742988b0e7750bddb8060b0c7079606c0d24a8cc8e9c9c11f9079", size = 29400081, upload-time = "2025-10-28T17:34:39.087Z" }, - { url = "https://files.pythonhosted.org/packages/6a/56/933e68210d92657d93fb0e381683bc0e53a965048d7358ff5fbf9e6a1b17/scipy-1.16.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:03192a35e661470197556de24e7cb1330d84b35b94ead65c46ad6f16f6b28f2a", size = 21391244, upload-time = "2025-10-28T17:34:45.234Z" }, - { url = "https://files.pythonhosted.org/packages/a8/7e/779845db03dc1418e215726329674b40576879b91814568757ff0014ad65/scipy-1.16.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:57d01cb6f85e34f0946b33caa66e892aae072b64b034183f3d87c4025802a119", size = 23929753, upload-time = "2025-10-28T17:34:51.793Z" }, - { url = "https://files.pythonhosted.org/packages/4c/4b/f756cf8161d5365dcdef9e5f460ab226c068211030a175d2fc7f3f41ca64/scipy-1.16.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:96491a6a54e995f00a28a3c3badfff58fd093bf26cd5fb34a2188c8c756a3a2c", size = 33496912, upload-time = "2025-10-28T17:34:59.8Z" }, - { url = "https://files.pythonhosted.org/packages/09/b5/222b1e49a58668f23839ca1542a6322bb095ab8d6590d4f71723869a6c2c/scipy-1.16.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cd13e354df9938598af2be05822c323e97132d5e6306b83a3b4ee6724c6e522e", size = 35802371, upload-time = "2025-10-28T17:35:08.173Z" }, - { url = "https://files.pythonhosted.org/packages/c1/8d/5964ef68bb31829bde27611f8c9deeac13764589fe74a75390242b64ca44/scipy-1.16.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:63d3cdacb8a824a295191a723ee5e4ea7768ca5ca5f2838532d9f2e2b3ce2135", size = 36190477, upload-time = "2025-10-28T17:35:16.7Z" }, - { url = "https://files.pythonhosted.org/packages/ab/f2/b31d75cb9b5fa4dd39a0a931ee9b33e7f6f36f23be5ef560bf72e0f92f32/scipy-1.16.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:e7efa2681ea410b10dde31a52b18b0154d66f2485328830e45fdf183af5aefc6", size = 38796678, upload-time = "2025-10-28T17:35:26.354Z" }, - { url = "https://files.pythonhosted.org/packages/b4/1e/b3723d8ff64ab548c38d87055483714fefe6ee20e0189b62352b5e015bb1/scipy-1.16.3-cp313-cp313t-win_amd64.whl", hash = "sha256:2d1ae2cf0c350e7705168ff2429962a89ad90c2d49d1dd300686d8b2a5af22fc", size = 38640178, upload-time = "2025-10-28T17:35:35.304Z" }, - { url = "https://files.pythonhosted.org/packages/8e/f3/d854ff38789aca9b0cc23008d607ced9de4f7ab14fa1ca4329f86b3758ca/scipy-1.16.3-cp313-cp313t-win_arm64.whl", hash = "sha256:0c623a54f7b79dd88ef56da19bc2873afec9673a48f3b85b18e4d402bdd29a5a", size = 25803246, upload-time = "2025-10-28T17:35:42.155Z" }, - { url = "https://files.pythonhosted.org/packages/99/f6/99b10fd70f2d864c1e29a28bbcaa0c6340f9d8518396542d9ea3b4aaae15/scipy-1.16.3-cp314-cp314-macosx_10_14_x86_64.whl", hash = "sha256:875555ce62743e1d54f06cdf22c1e0bc47b91130ac40fe5d783b6dfa114beeb6", size = 36606469, upload-time = "2025-10-28T17:36:08.741Z" }, - { url = "https://files.pythonhosted.org/packages/4d/74/043b54f2319f48ea940dd025779fa28ee360e6b95acb7cd188fad4391c6b/scipy-1.16.3-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:bb61878c18a470021fb515a843dc7a76961a8daceaaaa8bad1332f1bf4b54657", size = 28872043, upload-time = "2025-10-28T17:36:16.599Z" }, - { url = "https://files.pythonhosted.org/packages/4d/e1/24b7e50cc1c4ee6ffbcb1f27fe9f4c8b40e7911675f6d2d20955f41c6348/scipy-1.16.3-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:f2622206f5559784fa5c4b53a950c3c7c1cf3e84ca1b9c4b6c03f062f289ca26", size = 20862952, upload-time = "2025-10-28T17:36:22.966Z" }, - { url = "https://files.pythonhosted.org/packages/dd/3a/3e8c01a4d742b730df368e063787c6808597ccb38636ed821d10b39ca51b/scipy-1.16.3-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:7f68154688c515cdb541a31ef8eb66d8cd1050605be9dcd74199cbd22ac739bc", size = 23508512, upload-time = "2025-10-28T17:36:29.731Z" }, - { url = "https://files.pythonhosted.org/packages/1f/60/c45a12b98ad591536bfe5330cb3cfe1850d7570259303563b1721564d458/scipy-1.16.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8b3c820ddb80029fe9f43d61b81d8b488d3ef8ca010d15122b152db77dc94c22", size = 33413639, upload-time = "2025-10-28T17:36:37.982Z" }, - { url = "https://files.pythonhosted.org/packages/71/bc/35957d88645476307e4839712642896689df442f3e53b0fa016ecf8a3357/scipy-1.16.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d3837938ae715fc0fe3c39c0202de3a8853aff22ca66781ddc2ade7554b7e2cc", size = 35704729, upload-time = "2025-10-28T17:36:46.547Z" }, - { url = "https://files.pythonhosted.org/packages/3b/15/89105e659041b1ca11c386e9995aefacd513a78493656e57789f9d9eab61/scipy-1.16.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:aadd23f98f9cb069b3bd64ddc900c4d277778242e961751f77a8cb5c4b946fb0", size = 36086251, upload-time = "2025-10-28T17:36:55.161Z" }, - { url = "https://files.pythonhosted.org/packages/1a/87/c0ea673ac9c6cc50b3da2196d860273bc7389aa69b64efa8493bdd25b093/scipy-1.16.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b7c5f1bda1354d6a19bc6af73a649f8285ca63ac6b52e64e658a5a11d4d69800", size = 38716681, upload-time = "2025-10-28T17:37:04.1Z" }, - { url = "https://files.pythonhosted.org/packages/91/06/837893227b043fb9b0d13e4bd7586982d8136cb249ffb3492930dab905b8/scipy-1.16.3-cp314-cp314-win_amd64.whl", hash = "sha256:e5d42a9472e7579e473879a1990327830493a7047506d58d73fc429b84c1d49d", size = 39358423, upload-time = "2025-10-28T17:38:20.005Z" }, - { url = "https://files.pythonhosted.org/packages/95/03/28bce0355e4d34a7c034727505a02d19548549e190bedd13a721e35380b7/scipy-1.16.3-cp314-cp314-win_arm64.whl", hash = "sha256:6020470b9d00245926f2d5bb93b119ca0340f0d564eb6fbaad843eaebf9d690f", size = 26135027, upload-time = "2025-10-28T17:38:24.966Z" }, - { url = "https://files.pythonhosted.org/packages/b2/6f/69f1e2b682efe9de8fe9f91040f0cd32f13cfccba690512ba4c582b0bc29/scipy-1.16.3-cp314-cp314t-macosx_10_14_x86_64.whl", hash = "sha256:e1d27cbcb4602680a49d787d90664fa4974063ac9d4134813332a8c53dbe667c", size = 37028379, upload-time = "2025-10-28T17:37:14.061Z" }, - { url = "https://files.pythonhosted.org/packages/7c/2d/e826f31624a5ebbab1cd93d30fd74349914753076ed0593e1d56a98c4fb4/scipy-1.16.3-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:9b9c9c07b6d56a35777a1b4cc8966118fb16cfd8daf6743867d17d36cfad2d40", size = 29400052, upload-time = "2025-10-28T17:37:21.709Z" }, - { url = "https://files.pythonhosted.org/packages/69/27/d24feb80155f41fd1f156bf144e7e049b4e2b9dd06261a242905e3bc7a03/scipy-1.16.3-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:3a4c460301fb2cffb7f88528f30b3127742cff583603aa7dc964a52c463b385d", size = 21391183, upload-time = "2025-10-28T17:37:29.559Z" }, - { url = "https://files.pythonhosted.org/packages/f8/d3/1b229e433074c5738a24277eca520a2319aac7465eea7310ea6ae0e98ae2/scipy-1.16.3-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:f667a4542cc8917af1db06366d3f78a5c8e83badd56409f94d1eac8d8d9133fa", size = 23930174, upload-time = "2025-10-28T17:37:36.306Z" }, - { url = "https://files.pythonhosted.org/packages/16/9d/d9e148b0ec680c0f042581a2be79a28a7ab66c0c4946697f9e7553ead337/scipy-1.16.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f379b54b77a597aa7ee5e697df0d66903e41b9c85a6dd7946159e356319158e8", size = 33497852, upload-time = "2025-10-28T17:37:42.228Z" }, - { url = "https://files.pythonhosted.org/packages/2f/22/4e5f7561e4f98b7bea63cf3fd7934bff1e3182e9f1626b089a679914d5c8/scipy-1.16.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4aff59800a3b7f786b70bfd6ab551001cb553244988d7d6b8299cb1ea653b353", size = 35798595, upload-time = "2025-10-28T17:37:48.102Z" }, - { url = "https://files.pythonhosted.org/packages/83/42/6644d714c179429fc7196857866f219fef25238319b650bb32dde7bf7a48/scipy-1.16.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:da7763f55885045036fabcebd80144b757d3db06ab0861415d1c3b7c69042146", size = 36186269, upload-time = "2025-10-28T17:37:53.72Z" }, - { url = "https://files.pythonhosted.org/packages/ac/70/64b4d7ca92f9cf2e6fc6aaa2eecf80bb9b6b985043a9583f32f8177ea122/scipy-1.16.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ffa6eea95283b2b8079b821dc11f50a17d0571c92b43e2b5b12764dc5f9b285d", size = 38802779, upload-time = "2025-10-28T17:37:59.393Z" }, - { url = "https://files.pythonhosted.org/packages/61/82/8d0e39f62764cce5ffd5284131e109f07cf8955aef9ab8ed4e3aa5e30539/scipy-1.16.3-cp314-cp314t-win_amd64.whl", hash = "sha256:d9f48cafc7ce94cf9b15c6bffdc443a81a27bf7075cf2dcd5c8b40f85d10c4e7", size = 39471128, upload-time = "2025-10-28T17:38:05.259Z" }, - { url = "https://files.pythonhosted.org/packages/64/47/a494741db7280eae6dc033510c319e34d42dd41b7ac0c7ead39354d1a2b5/scipy-1.16.3-cp314-cp314t-win_arm64.whl", hash = "sha256:21d9d6b197227a12dcbf9633320a4e34c6b0e51c57268df255a0942983bac562", size = 26464127, upload-time = "2025-10-28T17:38:11.34Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/56/3e/9cca699f3486ce6bc12ff46dc2031f1ec8eb9ccc9a320fdaf925f1417426/scipy-1.17.0.tar.gz", hash = "sha256:2591060c8e648d8b96439e111ac41fd8342fdeff1876be2e19dea3fe8930454e", size = 30396830, upload-time = "2026-01-10T21:34:23.009Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/4b/c89c131aa87cad2b77a54eb0fb94d633a842420fa7e919dc2f922037c3d8/scipy-1.17.0-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:2abd71643797bd8a106dff97894ff7869eeeb0af0f7a5ce02e4227c6a2e9d6fd", size = 31381316, upload-time = "2026-01-10T21:24:33.42Z" }, + { url = "https://files.pythonhosted.org/packages/5e/5f/a6b38f79a07d74989224d5f11b55267714707582908a5f1ae854cf9a9b84/scipy-1.17.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:ef28d815f4d2686503e5f4f00edc387ae58dfd7a2f42e348bb53359538f01558", size = 27966760, upload-time = "2026-01-10T21:24:38.911Z" }, + { url = "https://files.pythonhosted.org/packages/c1/20/095ad24e031ee8ed3c5975954d816b8e7e2abd731e04f8be573de8740885/scipy-1.17.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:272a9f16d6bb4667e8b50d25d71eddcc2158a214df1b566319298de0939d2ab7", size = 20138701, upload-time = "2026-01-10T21:24:43.249Z" }, + { url = "https://files.pythonhosted.org/packages/89/11/4aad2b3858d0337756f3323f8960755704e530b27eb2a94386c970c32cbe/scipy-1.17.0-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:7204fddcbec2fe6598f1c5fdf027e9f259106d05202a959a9f1aecf036adc9f6", size = 22480574, upload-time = "2026-01-10T21:24:47.266Z" }, + { url = "https://files.pythonhosted.org/packages/85/bd/f5af70c28c6da2227e510875cadf64879855193a687fb19951f0f44cfd6b/scipy-1.17.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fc02c37a5639ee67d8fb646ffded6d793c06c5622d36b35cfa8fe5ececb8f042", size = 32862414, upload-time = "2026-01-10T21:24:52.566Z" }, + { url = "https://files.pythonhosted.org/packages/ef/df/df1457c4df3826e908879fe3d76bc5b6e60aae45f4ee42539512438cfd5d/scipy-1.17.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dac97a27520d66c12a34fd90a4fe65f43766c18c0d6e1c0a80f114d2260080e4", size = 35112380, upload-time = "2026-01-10T21:24:58.433Z" }, + { url = "https://files.pythonhosted.org/packages/5f/bb/88e2c16bd1dd4de19d80d7c5e238387182993c2fb13b4b8111e3927ad422/scipy-1.17.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ebb7446a39b3ae0fe8f416a9a3fdc6fba3f11c634f680f16a239c5187bc487c0", size = 34922676, upload-time = "2026-01-10T21:25:04.287Z" }, + { url = "https://files.pythonhosted.org/packages/02/ba/5120242cc735f71fc002cff0303d536af4405eb265f7c60742851e7ccfe9/scipy-1.17.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:474da16199f6af66601a01546144922ce402cb17362e07d82f5a6cf8f963e449", size = 37507599, upload-time = "2026-01-10T21:25:09.851Z" }, + { url = "https://files.pythonhosted.org/packages/52/c8/08629657ac6c0da198487ce8cd3de78e02cfde42b7f34117d56a3fe249dc/scipy-1.17.0-cp311-cp311-win_amd64.whl", hash = "sha256:255c0da161bd7b32a6c898e7891509e8a9289f0b1c6c7d96142ee0d2b114c2ea", size = 36380284, upload-time = "2026-01-10T21:25:15.632Z" }, + { url = "https://files.pythonhosted.org/packages/6c/4a/465f96d42c6f33ad324a40049dfd63269891db9324aa66c4a1c108c6f994/scipy-1.17.0-cp311-cp311-win_arm64.whl", hash = "sha256:85b0ac3ad17fa3be50abd7e69d583d98792d7edc08367e01445a1e2076005379", size = 24370427, upload-time = "2026-01-10T21:25:20.514Z" }, + { url = "https://files.pythonhosted.org/packages/0b/11/7241a63e73ba5a516f1930ac8d5b44cbbfabd35ac73a2d08ca206df007c4/scipy-1.17.0-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:0d5018a57c24cb1dd828bcf51d7b10e65986d549f52ef5adb6b4d1ded3e32a57", size = 31364580, upload-time = "2026-01-10T21:25:25.717Z" }, + { url = "https://files.pythonhosted.org/packages/ed/1d/5057f812d4f6adc91a20a2d6f2ebcdb517fdbc87ae3acc5633c9b97c8ba5/scipy-1.17.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:88c22af9e5d5a4f9e027e26772cc7b5922fab8bcc839edb3ae33de404feebd9e", size = 27969012, upload-time = "2026-01-10T21:25:30.921Z" }, + { url = "https://files.pythonhosted.org/packages/e3/21/f6ec556c1e3b6ec4e088da667d9987bb77cc3ab3026511f427dc8451187d/scipy-1.17.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:f3cd947f20fe17013d401b64e857c6b2da83cae567adbb75b9dcba865abc66d8", size = 20140691, upload-time = "2026-01-10T21:25:34.802Z" }, + { url = "https://files.pythonhosted.org/packages/7a/fe/5e5ad04784964ba964a96f16c8d4676aa1b51357199014dce58ab7ec5670/scipy-1.17.0-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:e8c0b331c2c1f531eb51f1b4fc9ba709521a712cce58f1aa627bc007421a5306", size = 22463015, upload-time = "2026-01-10T21:25:39.277Z" }, + { url = "https://files.pythonhosted.org/packages/4a/69/7c347e857224fcaf32a34a05183b9d8a7aca25f8f2d10b8a698b8388561a/scipy-1.17.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5194c445d0a1c7a6c1a4a4681b6b7c71baad98ff66d96b949097e7513c9d6742", size = 32724197, upload-time = "2026-01-10T21:25:44.084Z" }, + { url = "https://files.pythonhosted.org/packages/d1/fe/66d73b76d378ba8cc2fe605920c0c75092e3a65ae746e1e767d9d020a75a/scipy-1.17.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9eeb9b5f5997f75507814ed9d298ab23f62cf79f5a3ef90031b1ee2506abdb5b", size = 35009148, upload-time = "2026-01-10T21:25:50.591Z" }, + { url = "https://files.pythonhosted.org/packages/af/07/07dec27d9dc41c18d8c43c69e9e413431d20c53a0339c388bcf72f353c4b/scipy-1.17.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:40052543f7bbe921df4408f46003d6f01c6af109b9e2c8a66dd1cf6cf57f7d5d", size = 34798766, upload-time = "2026-01-10T21:25:59.41Z" }, + { url = "https://files.pythonhosted.org/packages/81/61/0470810c8a093cdacd4ba7504b8a218fd49ca070d79eca23a615f5d9a0b0/scipy-1.17.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0cf46c8013fec9d3694dc572f0b54100c28405d55d3e2cb15e2895b25057996e", size = 37405953, upload-time = "2026-01-10T21:26:07.75Z" }, + { url = "https://files.pythonhosted.org/packages/92/ce/672ed546f96d5d41ae78c4b9b02006cedd0b3d6f2bf5bb76ea455c320c28/scipy-1.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:0937a0b0d8d593a198cededd4c439a0ea216a3f36653901ea1f3e4be949056f8", size = 36328121, upload-time = "2026-01-10T21:26:16.509Z" }, + { url = "https://files.pythonhosted.org/packages/9d/21/38165845392cae67b61843a52c6455d47d0cc2a40dd495c89f4362944654/scipy-1.17.0-cp312-cp312-win_arm64.whl", hash = "sha256:f603d8a5518c7426414d1d8f82e253e454471de682ce5e39c29adb0df1efb86b", size = 24314368, upload-time = "2026-01-10T21:26:23.087Z" }, + { url = "https://files.pythonhosted.org/packages/0c/51/3468fdfd49387ddefee1636f5cf6d03ce603b75205bf439bbf0e62069bfd/scipy-1.17.0-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:65ec32f3d32dfc48c72df4291345dae4f048749bc8d5203ee0a3f347f96c5ce6", size = 31344101, upload-time = "2026-01-10T21:26:30.25Z" }, + { url = "https://files.pythonhosted.org/packages/b2/9a/9406aec58268d437636069419e6977af953d1e246df941d42d3720b7277b/scipy-1.17.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:1f9586a58039d7229ce77b52f8472c972448cded5736eaf102d5658bbac4c269", size = 27950385, upload-time = "2026-01-10T21:26:36.801Z" }, + { url = "https://files.pythonhosted.org/packages/4f/98/e7342709e17afdfd1b26b56ae499ef4939b45a23a00e471dfb5375eea205/scipy-1.17.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:9fad7d3578c877d606b1150135c2639e9de9cecd3705caa37b66862977cc3e72", size = 20122115, upload-time = "2026-01-10T21:26:42.107Z" }, + { url = "https://files.pythonhosted.org/packages/fd/0e/9eeeb5357a64fd157cbe0302c213517c541cc16b8486d82de251f3c68ede/scipy-1.17.0-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:423ca1f6584fc03936972b5f7c06961670dbba9f234e71676a7c7ccf938a0d61", size = 22442402, upload-time = "2026-01-10T21:26:48.029Z" }, + { url = "https://files.pythonhosted.org/packages/c9/10/be13397a0e434f98e0c79552b2b584ae5bb1c8b2be95db421533bbca5369/scipy-1.17.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fe508b5690e9eaaa9467fc047f833af58f1152ae51a0d0aed67aa5801f4dd7d6", size = 32696338, upload-time = "2026-01-10T21:26:55.521Z" }, + { url = "https://files.pythonhosted.org/packages/63/1e/12fbf2a3bb240161651c94bb5cdd0eae5d4e8cc6eaeceb74ab07b12a753d/scipy-1.17.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6680f2dfd4f6182e7d6db161344537da644d1cf85cf293f015c60a17ecf08752", size = 34977201, upload-time = "2026-01-10T21:27:03.501Z" }, + { url = "https://files.pythonhosted.org/packages/19/5b/1a63923e23ccd20bd32156d7dd708af5bbde410daa993aa2500c847ab2d2/scipy-1.17.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:eec3842ec9ac9de5917899b277428886042a93db0b227ebbe3a333b64ec7643d", size = 34777384, upload-time = "2026-01-10T21:27:11.423Z" }, + { url = "https://files.pythonhosted.org/packages/39/22/b5da95d74edcf81e540e467202a988c50fef41bd2011f46e05f72ba07df6/scipy-1.17.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d7425fcafbc09a03731e1bc05581f5fad988e48c6a861f441b7ab729a49a55ea", size = 37379586, upload-time = "2026-01-10T21:27:20.171Z" }, + { url = "https://files.pythonhosted.org/packages/b9/b6/8ac583d6da79e7b9e520579f03007cb006f063642afd6b2eeb16b890bf93/scipy-1.17.0-cp313-cp313-win_amd64.whl", hash = "sha256:87b411e42b425b84777718cc41516b8a7e0795abfa8e8e1d573bf0ef014f0812", size = 36287211, upload-time = "2026-01-10T21:28:43.122Z" }, + { url = "https://files.pythonhosted.org/packages/55/fb/7db19e0b3e52f882b420417644ec81dd57eeef1bd1705b6f689d8ff93541/scipy-1.17.0-cp313-cp313-win_arm64.whl", hash = "sha256:357ca001c6e37601066092e7c89cca2f1ce74e2a520ca78d063a6d2201101df2", size = 24312646, upload-time = "2026-01-10T21:28:49.893Z" }, + { url = "https://files.pythonhosted.org/packages/20/b6/7feaa252c21cc7aff335c6c55e1b90ab3e3306da3f048109b8b639b94648/scipy-1.17.0-cp313-cp313t-macosx_10_14_x86_64.whl", hash = "sha256:ec0827aa4d36cb79ff1b81de898e948a51ac0b9b1c43e4a372c0508c38c0f9a3", size = 31693194, upload-time = "2026-01-10T21:27:27.454Z" }, + { url = "https://files.pythonhosted.org/packages/76/bb/bbb392005abce039fb7e672cb78ac7d158700e826b0515cab6b5b60c26fb/scipy-1.17.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:819fc26862b4b3c73a60d486dbb919202f3d6d98c87cf20c223511429f2d1a97", size = 28365415, upload-time = "2026-01-10T21:27:34.26Z" }, + { url = "https://files.pythonhosted.org/packages/37/da/9d33196ecc99fba16a409c691ed464a3a283ac454a34a13a3a57c0d66f3a/scipy-1.17.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:363ad4ae2853d88ebcde3ae6ec46ccca903ea9835ee8ba543f12f575e7b07e4e", size = 20537232, upload-time = "2026-01-10T21:27:40.306Z" }, + { url = "https://files.pythonhosted.org/packages/56/9d/f4b184f6ddb28e9a5caea36a6f98e8ecd2a524f9127354087ce780885d83/scipy-1.17.0-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:979c3a0ff8e5ba254d45d59ebd38cde48fce4f10b5125c680c7a4bfe177aab07", size = 22791051, upload-time = "2026-01-10T21:27:46.539Z" }, + { url = "https://files.pythonhosted.org/packages/9b/9d/025cccdd738a72140efc582b1641d0dd4caf2e86c3fb127568dc80444e6e/scipy-1.17.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:130d12926ae34399d157de777472bf82e9061c60cc081372b3118edacafe1d00", size = 32815098, upload-time = "2026-01-10T21:27:54.389Z" }, + { url = "https://files.pythonhosted.org/packages/48/5f/09b879619f8bca15ce392bfc1894bd9c54377e01d1b3f2f3b595a1b4d945/scipy-1.17.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6e886000eb4919eae3a44f035e63f0fd8b651234117e8f6f29bad1cd26e7bc45", size = 35031342, upload-time = "2026-01-10T21:28:03.012Z" }, + { url = "https://files.pythonhosted.org/packages/f2/9a/f0f0a9f0aa079d2f106555b984ff0fbb11a837df280f04f71f056ea9c6e4/scipy-1.17.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:13c4096ac6bc31d706018f06a49abe0485f96499deb82066b94d19b02f664209", size = 34893199, upload-time = "2026-01-10T21:28:10.832Z" }, + { url = "https://files.pythonhosted.org/packages/90/b8/4f0f5cf0c5ea4d7548424e6533e6b17d164f34a6e2fb2e43ffebb6697b06/scipy-1.17.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:cacbaddd91fcffde703934897c5cd2c7cb0371fac195d383f4e1f1c5d3f3bd04", size = 37438061, upload-time = "2026-01-10T21:28:19.684Z" }, + { url = "https://files.pythonhosted.org/packages/f9/cc/2bd59140ed3b2fa2882fb15da0a9cb1b5a6443d67cfd0d98d4cec83a57ec/scipy-1.17.0-cp313-cp313t-win_amd64.whl", hash = "sha256:edce1a1cf66298cccdc48a1bdf8fb10a3bf58e8b58d6c3883dd1530e103f87c0", size = 36328593, upload-time = "2026-01-10T21:28:28.007Z" }, + { url = "https://files.pythonhosted.org/packages/13/1b/c87cc44a0d2c7aaf0f003aef2904c3d097b422a96c7e7c07f5efd9073c1b/scipy-1.17.0-cp313-cp313t-win_arm64.whl", hash = "sha256:30509da9dbec1c2ed8f168b8d8aa853bc6723fede1dbc23c7d43a56f5ab72a67", size = 24625083, upload-time = "2026-01-10T21:28:35.188Z" }, + { url = "https://files.pythonhosted.org/packages/1a/2d/51006cd369b8e7879e1c630999a19d1fbf6f8b5ed3e33374f29dc87e53b3/scipy-1.17.0-cp314-cp314-macosx_10_14_x86_64.whl", hash = "sha256:c17514d11b78be8f7e6331b983a65a7f5ca1fd037b95e27b280921fe5606286a", size = 31346803, upload-time = "2026-01-10T21:28:57.24Z" }, + { url = "https://files.pythonhosted.org/packages/d6/2e/2349458c3ce445f53a6c93d4386b1c4c5c0c540917304c01222ff95ff317/scipy-1.17.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:4e00562e519c09da34c31685f6acc3aa384d4d50604db0f245c14e1b4488bfa2", size = 27967182, upload-time = "2026-01-10T21:29:04.107Z" }, + { url = "https://files.pythonhosted.org/packages/5e/7c/df525fbfa77b878d1cfe625249529514dc02f4fd5f45f0f6295676a76528/scipy-1.17.0-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:f7df7941d71314e60a481e02d5ebcb3f0185b8d799c70d03d8258f6c80f3d467", size = 20139125, upload-time = "2026-01-10T21:29:10.179Z" }, + { url = "https://files.pythonhosted.org/packages/33/11/fcf9d43a7ed1234d31765ec643b0515a85a30b58eddccc5d5a4d12b5f194/scipy-1.17.0-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:aabf057c632798832f071a8dde013c2e26284043934f53b00489f1773b33527e", size = 22443554, upload-time = "2026-01-10T21:29:15.888Z" }, + { url = "https://files.pythonhosted.org/packages/80/5c/ea5d239cda2dd3d31399424967a24d556cf409fbea7b5b21412b0fd0a44f/scipy-1.17.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a38c3337e00be6fd8a95b4ed66b5d988bac4ec888fd922c2ea9fe5fb1603dd67", size = 32757834, upload-time = "2026-01-10T21:29:23.406Z" }, + { url = "https://files.pythonhosted.org/packages/b8/7e/8c917cc573310e5dc91cbeead76f1b600d3fb17cf0969db02c9cf92e3cfa/scipy-1.17.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00fb5f8ec8398ad90215008d8b6009c9db9fa924fd4c7d6be307c6f945f9cd73", size = 34995775, upload-time = "2026-01-10T21:29:31.915Z" }, + { url = "https://files.pythonhosted.org/packages/c5/43/176c0c3c07b3f7df324e7cdd933d3e2c4898ca202b090bd5ba122f9fe270/scipy-1.17.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f2a4942b0f5f7c23c7cd641a0ca1955e2ae83dedcff537e3a0259096635e186b", size = 34841240, upload-time = "2026-01-10T21:29:39.995Z" }, + { url = "https://files.pythonhosted.org/packages/44/8c/d1f5f4b491160592e7f084d997de53a8e896a3ac01cd07e59f43ca222744/scipy-1.17.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:dbf133ced83889583156566d2bdf7a07ff89228fe0c0cb727f777de92092ec6b", size = 37394463, upload-time = "2026-01-10T21:29:48.723Z" }, + { url = "https://files.pythonhosted.org/packages/9f/ec/42a6657f8d2d087e750e9a5dde0b481fd135657f09eaf1cf5688bb23c338/scipy-1.17.0-cp314-cp314-win_amd64.whl", hash = "sha256:3625c631a7acd7cfd929e4e31d2582cf00f42fcf06011f59281271746d77e061", size = 37053015, upload-time = "2026-01-10T21:30:51.418Z" }, + { url = "https://files.pythonhosted.org/packages/27/58/6b89a6afd132787d89a362d443a7bddd511b8f41336a1ae47f9e4f000dc4/scipy-1.17.0-cp314-cp314-win_arm64.whl", hash = "sha256:9244608d27eafe02b20558523ba57f15c689357c85bdcfe920b1828750aa26eb", size = 24951312, upload-time = "2026-01-10T21:30:56.771Z" }, + { url = "https://files.pythonhosted.org/packages/e9/01/f58916b9d9ae0112b86d7c3b10b9e685625ce6e8248df139d0fcb17f7397/scipy-1.17.0-cp314-cp314t-macosx_10_14_x86_64.whl", hash = "sha256:2b531f57e09c946f56ad0b4a3b2abee778789097871fc541e267d2eca081cff1", size = 31706502, upload-time = "2026-01-10T21:29:56.326Z" }, + { url = "https://files.pythonhosted.org/packages/59/8e/2912a87f94a7d1f8b38aabc0faf74b82d3b6c9e22be991c49979f0eceed8/scipy-1.17.0-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:13e861634a2c480bd237deb69333ac79ea1941b94568d4b0efa5db5e263d4fd1", size = 28380854, upload-time = "2026-01-10T21:30:01.554Z" }, + { url = "https://files.pythonhosted.org/packages/bd/1c/874137a52dddab7d5d595c1887089a2125d27d0601fce8c0026a24a92a0b/scipy-1.17.0-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:eb2651271135154aa24f6481cbae5cc8af1f0dd46e6533fb7b56aa9727b6a232", size = 20552752, upload-time = "2026-01-10T21:30:05.93Z" }, + { url = "https://files.pythonhosted.org/packages/3f/f0/7518d171cb735f6400f4576cf70f756d5b419a07fe1867da34e2c2c9c11b/scipy-1.17.0-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:c5e8647f60679790c2f5c76be17e2e9247dc6b98ad0d3b065861e082c56e078d", size = 22803972, upload-time = "2026-01-10T21:30:10.651Z" }, + { url = "https://files.pythonhosted.org/packages/7c/74/3498563a2c619e8a3ebb4d75457486c249b19b5b04a30600dfd9af06bea5/scipy-1.17.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5fb10d17e649e1446410895639f3385fd2bf4c3c7dfc9bea937bddcbc3d7b9ba", size = 32829770, upload-time = "2026-01-10T21:30:16.359Z" }, + { url = "https://files.pythonhosted.org/packages/48/d1/7b50cedd8c6c9d6f706b4b36fa8544d829c712a75e370f763b318e9638c1/scipy-1.17.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8547e7c57f932e7354a2319fab613981cde910631979f74c9b542bb167a8b9db", size = 35051093, upload-time = "2026-01-10T21:30:22.987Z" }, + { url = "https://files.pythonhosted.org/packages/e2/82/a2d684dfddb87ba1b3ea325df7c3293496ee9accb3a19abe9429bce94755/scipy-1.17.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:33af70d040e8af9d5e7a38b5ed3b772adddd281e3062ff23fec49e49681c38cf", size = 34909905, upload-time = "2026-01-10T21:30:28.704Z" }, + { url = "https://files.pythonhosted.org/packages/ef/5e/e565bd73991d42023eb82bb99e51c5b3d9e2c588ca9d4b3e2cc1d3ca62a6/scipy-1.17.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f9eb55bb97d00f8b7ab95cb64f873eb0bf54d9446264d9f3609130381233483f", size = 37457743, upload-time = "2026-01-10T21:30:34.819Z" }, + { url = "https://files.pythonhosted.org/packages/58/a8/a66a75c3d8f1fb2b83f66007d6455a06a6f6cf5618c3dc35bc9b69dd096e/scipy-1.17.0-cp314-cp314t-win_amd64.whl", hash = "sha256:1ff269abf702f6c7e67a4b7aad981d42871a11b9dd83c58d2d2ea624efbd1088", size = 37098574, upload-time = "2026-01-10T21:30:40.782Z" }, + { url = "https://files.pythonhosted.org/packages/56/a5/df8f46ef7da168f1bc52cd86e09a9de5c6f19cc1da04454d51b7d4f43408/scipy-1.17.0-cp314-cp314t-win_arm64.whl", hash = "sha256:031121914e295d9791319a1875444d55079885bbae5bdc9c5e0f2ee5f09d34ff", size = 25246266, upload-time = "2026-01-10T21:30:45.923Z" }, ] [[package]] @@ -6935,23 +7156,23 @@ wheels = [ [[package]] name = "sentence-transformers" -version = "5.1.2" +version = "5.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "huggingface-hub" }, - { name = "pillow" }, - { name = "scikit-learn" }, + { name = "scikit-learn", version = "1.7.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "scikit-learn", version = "1.8.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "scipy", version = "1.16.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "scipy", version = "1.17.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "torch", version = "2.2.2", source = { registry = "https://pypi.org/simple" }, marker = "platform_machine == 'x86_64' and sys_platform == 'darwin'" }, { name = "torch", version = "2.9.1", source = { registry = "https://pypi.org/simple" }, marker = "platform_machine != 'x86_64' or sys_platform != 'darwin'" }, { name = "tqdm" }, { name = "transformers" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0f/96/f3f3409179d14dbfdbea8622e2e9eaa3c8836ddcaecd2cd5ff0a11731d20/sentence_transformers-5.1.2.tar.gz", hash = "sha256:0f6c8bd916a78dc65b366feb8d22fd885efdb37432e7630020d113233af2b856", size = 375185, upload-time = "2025-10-22T12:47:55.019Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/a1/64e7b111e753307ffb7c5b6d039c52d4a91a47fa32a7f5bc377a49b22402/sentence_transformers-5.2.0.tar.gz", hash = "sha256:acaeb38717de689f3dab45d5e5a02ebe2f75960a4764ea35fea65f58a4d3019f", size = 381004, upload-time = "2025-12-11T14:12:31.038Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bb/a6/a607a737dc1a00b7afe267b9bfde101b8cee2529e197e57471d23137d4e5/sentence_transformers-5.1.2-py3-none-any.whl", hash = "sha256:724ce0ea62200f413f1a5059712aff66495bc4e815a1493f7f9bca242414c333", size = 488009, upload-time = "2025-10-22T12:47:53.433Z" }, + { url = "https://files.pythonhosted.org/packages/40/d0/3b2897ef6a0c0c801e9fecca26bcc77081648e38e8c772885ebdd8d7d252/sentence_transformers-5.2.0-py3-none-any.whl", hash = "sha256:aa57180f053687d29b08206766ae7db549be5074f61849def7b17bf0b8025ca2", size = 493748, upload-time = "2025-12-11T14:12:29.516Z" }, ] [[package]] @@ -7095,56 +7316,60 @@ wheels = [ [[package]] name = "soupsieve" -version = "2.8" +version = "2.8.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6d/e6/21ccce3262dd4889aa3332e5a119a3491a95e8f60939870a3a035aabac0d/soupsieve-2.8.tar.gz", hash = "sha256:e2dd4a40a628cb5f28f6d4b0db8800b8f581b65bb380b97de22ba5ca8d72572f", size = 103472, upload-time = "2025-08-27T15:39:51.78Z" } +sdist = { url = "https://files.pythonhosted.org/packages/89/23/adf3796d740536d63a6fbda113d07e60c734b6ed5d3058d1e47fc0495e47/soupsieve-2.8.1.tar.gz", hash = "sha256:4cf733bc50fa805f5df4b8ef4740fc0e0fa6218cf3006269afd3f9d6d80fd350", size = 117856, upload-time = "2025-12-18T13:50:34.655Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/14/a0/bb38d3b76b8cae341dad93a2dd83ab7462e6dbcdd84d43f54ee60a8dc167/soupsieve-2.8-py3-none-any.whl", hash = "sha256:0cc76456a30e20f5d7f2e14a98a4ae2ee4e5abdc7c5ea0aafe795f344bc7984c", size = 36679, upload-time = "2025-08-27T15:39:50.179Z" }, + { url = "https://files.pythonhosted.org/packages/48/f3/b67d6ea49ca9154453b6d70b34ea22f3996b9fa55da105a79d8732227adc/soupsieve-2.8.1-py3-none-any.whl", hash = "sha256:a11fe2a6f3d76ab3cf2de04eb339c1be5b506a8a47f2ceb6d139803177f85434", size = 36710, upload-time = "2025-12-18T13:50:33.267Z" }, ] [[package]] name = "sqlalchemy" -version = "2.0.44" +version = "2.0.45" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "greenlet", marker = "platform_machine == 'AMD64' or platform_machine == 'WIN32' or platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'ppc64le' or platform_machine == 'win32' or platform_machine == 'x86_64'" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f0/f2/840d7b9496825333f532d2e3976b8eadbf52034178aac53630d09fe6e1ef/sqlalchemy-2.0.44.tar.gz", hash = "sha256:0ae7454e1ab1d780aee69fd2aae7d6b8670a581d8847f2d1e0f7ddfbf47e5a22", size = 9819830, upload-time = "2025-10-10T14:39:12.935Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/a7/e9ccfa7eecaf34c6f57d8cb0bb7cbdeeff27017cc0f5d0ca90fdde7a7c0d/sqlalchemy-2.0.44-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7c77f3080674fc529b1bd99489378c7f63fcb4ba7f8322b79732e0258f0ea3ce", size = 2137282, upload-time = "2025-10-10T15:36:10.965Z" }, - { url = "https://files.pythonhosted.org/packages/b1/e1/50bc121885bdf10833a4f65ecbe9fe229a3215f4d65a58da8a181734cae3/sqlalchemy-2.0.44-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4c26ef74ba842d61635b0152763d057c8d48215d5be9bb8b7604116a059e9985", size = 2127322, upload-time = "2025-10-10T15:36:12.428Z" }, - { url = "https://files.pythonhosted.org/packages/46/f2/a8573b7230a3ce5ee4b961a2d510d71b43872513647398e595b744344664/sqlalchemy-2.0.44-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4a172b31785e2f00780eccab00bc240ccdbfdb8345f1e6063175b3ff12ad1b0", size = 3214772, upload-time = "2025-10-10T15:34:15.09Z" }, - { url = "https://files.pythonhosted.org/packages/4a/d8/c63d8adb6a7edaf8dcb6f75a2b1e9f8577960a1e489606859c4d73e7d32b/sqlalchemy-2.0.44-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9480c0740aabd8cb29c329b422fb65358049840b34aba0adf63162371d2a96e", size = 3214434, upload-time = "2025-10-10T15:47:00.473Z" }, - { url = "https://files.pythonhosted.org/packages/ee/a6/243d277a4b54fae74d4797957a7320a5c210c293487f931cbe036debb697/sqlalchemy-2.0.44-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:17835885016b9e4d0135720160db3095dc78c583e7b902b6be799fb21035e749", size = 3155365, upload-time = "2025-10-10T15:34:17.932Z" }, - { url = "https://files.pythonhosted.org/packages/5f/f8/6a39516ddd75429fd4ee5a0d72e4c80639fab329b2467c75f363c2ed9751/sqlalchemy-2.0.44-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cbe4f85f50c656d753890f39468fcd8190c5f08282caf19219f684225bfd5fd2", size = 3178910, upload-time = "2025-10-10T15:47:02.346Z" }, - { url = "https://files.pythonhosted.org/packages/43/f0/118355d4ad3c39d9a2f5ee4c7304a9665b3571482777357fa9920cd7a6b4/sqlalchemy-2.0.44-cp310-cp310-win32.whl", hash = "sha256:2fcc4901a86ed81dc76703f3b93ff881e08761c63263c46991081fd7f034b165", size = 2105624, upload-time = "2025-10-10T15:38:15.552Z" }, - { url = "https://files.pythonhosted.org/packages/61/83/6ae5f9466f8aa5d0dcebfff8c9c33b98b27ce23292df3b990454b3d434fd/sqlalchemy-2.0.44-cp310-cp310-win_amd64.whl", hash = "sha256:9919e77403a483ab81e3423151e8ffc9dd992c20d2603bf17e4a8161111e55f5", size = 2129240, upload-time = "2025-10-10T15:38:17.175Z" }, - { url = "https://files.pythonhosted.org/packages/e3/81/15d7c161c9ddf0900b076b55345872ed04ff1ed6a0666e5e94ab44b0163c/sqlalchemy-2.0.44-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0fe3917059c7ab2ee3f35e77757062b1bea10a0b6ca633c58391e3f3c6c488dd", size = 2140517, upload-time = "2025-10-10T15:36:15.64Z" }, - { url = "https://files.pythonhosted.org/packages/d4/d5/4abd13b245c7d91bdf131d4916fd9e96a584dac74215f8b5bc945206a974/sqlalchemy-2.0.44-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:de4387a354ff230bc979b46b2207af841dc8bf29847b6c7dbe60af186d97aefa", size = 2130738, upload-time = "2025-10-10T15:36:16.91Z" }, - { url = "https://files.pythonhosted.org/packages/cb/3c/8418969879c26522019c1025171cefbb2a8586b6789ea13254ac602986c0/sqlalchemy-2.0.44-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c3678a0fb72c8a6a29422b2732fe423db3ce119c34421b5f9955873eb9b62c1e", size = 3304145, upload-time = "2025-10-10T15:34:19.569Z" }, - { url = "https://files.pythonhosted.org/packages/94/2d/fdb9246d9d32518bda5d90f4b65030b9bf403a935cfe4c36a474846517cb/sqlalchemy-2.0.44-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3cf6872a23601672d61a68f390e44703442639a12ee9dd5a88bbce52a695e46e", size = 3304511, upload-time = "2025-10-10T15:47:05.088Z" }, - { url = "https://files.pythonhosted.org/packages/7d/fb/40f2ad1da97d5c83f6c1269664678293d3fe28e90ad17a1093b735420549/sqlalchemy-2.0.44-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:329aa42d1be9929603f406186630135be1e7a42569540577ba2c69952b7cf399", size = 3235161, upload-time = "2025-10-10T15:34:21.193Z" }, - { url = "https://files.pythonhosted.org/packages/95/cb/7cf4078b46752dca917d18cf31910d4eff6076e5b513c2d66100c4293d83/sqlalchemy-2.0.44-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:70e03833faca7166e6a9927fbee7c27e6ecde436774cd0b24bbcc96353bce06b", size = 3261426, upload-time = "2025-10-10T15:47:07.196Z" }, - { url = "https://files.pythonhosted.org/packages/f8/3b/55c09b285cb2d55bdfa711e778bdffdd0dc3ffa052b0af41f1c5d6e582fa/sqlalchemy-2.0.44-cp311-cp311-win32.whl", hash = "sha256:253e2f29843fb303eca6b2fc645aca91fa7aa0aa70b38b6950da92d44ff267f3", size = 2105392, upload-time = "2025-10-10T15:38:20.051Z" }, - { url = "https://files.pythonhosted.org/packages/c7/23/907193c2f4d680aedbfbdf7bf24c13925e3c7c292e813326c1b84a0b878e/sqlalchemy-2.0.44-cp311-cp311-win_amd64.whl", hash = "sha256:7a8694107eb4308a13b425ca8c0e67112f8134c846b6e1f722698708741215d5", size = 2130293, upload-time = "2025-10-10T15:38:21.601Z" }, - { url = "https://files.pythonhosted.org/packages/62/c4/59c7c9b068e6813c898b771204aad36683c96318ed12d4233e1b18762164/sqlalchemy-2.0.44-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:72fea91746b5890f9e5e0997f16cbf3d53550580d76355ba2d998311b17b2250", size = 2139675, upload-time = "2025-10-10T16:03:31.064Z" }, - { url = "https://files.pythonhosted.org/packages/d6/ae/eeb0920537a6f9c5a3708e4a5fc55af25900216bdb4847ec29cfddf3bf3a/sqlalchemy-2.0.44-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:585c0c852a891450edbb1eaca8648408a3cc125f18cf433941fa6babcc359e29", size = 2127726, upload-time = "2025-10-10T16:03:35.934Z" }, - { url = "https://files.pythonhosted.org/packages/d8/d5/2ebbabe0379418eda8041c06b0b551f213576bfe4c2f09d77c06c07c8cc5/sqlalchemy-2.0.44-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b94843a102efa9ac68a7a30cd46df3ff1ed9c658100d30a725d10d9c60a2f44", size = 3327603, upload-time = "2025-10-10T15:35:28.322Z" }, - { url = "https://files.pythonhosted.org/packages/45/e5/5aa65852dadc24b7d8ae75b7efb8d19303ed6ac93482e60c44a585930ea5/sqlalchemy-2.0.44-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:119dc41e7a7defcefc57189cfa0e61b1bf9c228211aba432b53fb71ef367fda1", size = 3337842, upload-time = "2025-10-10T15:43:45.431Z" }, - { url = "https://files.pythonhosted.org/packages/41/92/648f1afd3f20b71e880ca797a960f638d39d243e233a7082c93093c22378/sqlalchemy-2.0.44-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0765e318ee9179b3718c4fd7ba35c434f4dd20332fbc6857a5e8df17719c24d7", size = 3264558, upload-time = "2025-10-10T15:35:29.93Z" }, - { url = "https://files.pythonhosted.org/packages/40/cf/e27d7ee61a10f74b17740918e23cbc5bc62011b48282170dc4c66da8ec0f/sqlalchemy-2.0.44-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2e7b5b079055e02d06a4308d0481658e4f06bc7ef211567edc8f7d5dce52018d", size = 3301570, upload-time = "2025-10-10T15:43:48.407Z" }, - { url = "https://files.pythonhosted.org/packages/3b/3d/3116a9a7b63e780fb402799b6da227435be878b6846b192f076d2f838654/sqlalchemy-2.0.44-cp312-cp312-win32.whl", hash = "sha256:846541e58b9a81cce7dee8329f352c318de25aa2f2bbe1e31587eb1f057448b4", size = 2103447, upload-time = "2025-10-10T15:03:21.678Z" }, - { url = "https://files.pythonhosted.org/packages/25/83/24690e9dfc241e6ab062df82cc0df7f4231c79ba98b273fa496fb3dd78ed/sqlalchemy-2.0.44-cp312-cp312-win_amd64.whl", hash = "sha256:7cbcb47fd66ab294703e1644f78971f6f2f1126424d2b300678f419aa73c7b6e", size = 2130912, upload-time = "2025-10-10T15:03:24.656Z" }, - { url = "https://files.pythonhosted.org/packages/45/d3/c67077a2249fdb455246e6853166360054c331db4613cda3e31ab1cadbef/sqlalchemy-2.0.44-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ff486e183d151e51b1d694c7aa1695747599bb00b9f5f604092b54b74c64a8e1", size = 2135479, upload-time = "2025-10-10T16:03:37.671Z" }, - { url = "https://files.pythonhosted.org/packages/2b/91/eabd0688330d6fd114f5f12c4f89b0d02929f525e6bf7ff80aa17ca802af/sqlalchemy-2.0.44-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0b1af8392eb27b372ddb783b317dea0f650241cea5bd29199b22235299ca2e45", size = 2123212, upload-time = "2025-10-10T16:03:41.755Z" }, - { url = "https://files.pythonhosted.org/packages/b0/bb/43e246cfe0e81c018076a16036d9b548c4cc649de241fa27d8d9ca6f85ab/sqlalchemy-2.0.44-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2b61188657e3a2b9ac4e8f04d6cf8e51046e28175f79464c67f2fd35bceb0976", size = 3255353, upload-time = "2025-10-10T15:35:31.221Z" }, - { url = "https://files.pythonhosted.org/packages/b9/96/c6105ed9a880abe346b64d3b6ddef269ddfcab04f7f3d90a0bf3c5a88e82/sqlalchemy-2.0.44-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b87e7b91a5d5973dda5f00cd61ef72ad75a1db73a386b62877d4875a8840959c", size = 3260222, upload-time = "2025-10-10T15:43:50.124Z" }, - { url = "https://files.pythonhosted.org/packages/44/16/1857e35a47155b5ad927272fee81ae49d398959cb749edca6eaa399b582f/sqlalchemy-2.0.44-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:15f3326f7f0b2bfe406ee562e17f43f36e16167af99c4c0df61db668de20002d", size = 3189614, upload-time = "2025-10-10T15:35:32.578Z" }, - { url = "https://files.pythonhosted.org/packages/88/ee/4afb39a8ee4fc786e2d716c20ab87b5b1fb33d4ac4129a1aaa574ae8a585/sqlalchemy-2.0.44-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1e77faf6ff919aa8cd63f1c4e561cac1d9a454a191bb864d5dd5e545935e5a40", size = 3226248, upload-time = "2025-10-10T15:43:51.862Z" }, - { url = "https://files.pythonhosted.org/packages/32/d5/0e66097fc64fa266f29a7963296b40a80d6a997b7ac13806183700676f86/sqlalchemy-2.0.44-cp313-cp313-win32.whl", hash = "sha256:ee51625c2d51f8baadf2829fae817ad0b66b140573939dd69284d2ba3553ae73", size = 2101275, upload-time = "2025-10-10T15:03:26.096Z" }, - { url = "https://files.pythonhosted.org/packages/03/51/665617fe4f8c6450f42a6d8d69243f9420f5677395572c2fe9d21b493b7b/sqlalchemy-2.0.44-cp313-cp313-win_amd64.whl", hash = "sha256:c1c80faaee1a6c3428cecf40d16a2365bcf56c424c92c2b6f0f9ad204b899e9e", size = 2127901, upload-time = "2025-10-10T15:03:27.548Z" }, - { url = "https://files.pythonhosted.org/packages/9c/5e/6a29fa884d9fb7ddadf6b69490a9d45fded3b38541713010dad16b77d015/sqlalchemy-2.0.44-py3-none-any.whl", hash = "sha256:19de7ca1246fbef9f9d1bff8f1ab25641569df226364a0e40457dc5457c54b05", size = 1928718, upload-time = "2025-10-10T15:29:45.32Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/be/f9/5e4491e5ccf42f5d9cfc663741d261b3e6e1683ae7812114e7636409fcc6/sqlalchemy-2.0.45.tar.gz", hash = "sha256:1632a4bda8d2d25703fdad6363058d882541bdaaee0e5e3ddfa0cd3229efce88", size = 9869912, upload-time = "2025-12-09T21:05:16.737Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fe/70/75b1387d72e2847220441166c5eb4e9846dd753895208c13e6d66523b2d9/sqlalchemy-2.0.45-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c64772786d9eee72d4d3784c28f0a636af5b0a29f3fe26ff11f55efe90c0bd85", size = 2154148, upload-time = "2025-12-10T20:03:21.023Z" }, + { url = "https://files.pythonhosted.org/packages/d8/a4/7805e02323c49cb9d1ae5cd4913b28c97103079765f520043f914fca4cb3/sqlalchemy-2.0.45-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7ae64ebf7657395824a19bca98ab10eb9a3ecb026bf09524014f1bb81cb598d4", size = 3233051, upload-time = "2025-12-09T22:06:04.768Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ec/32ae09139f61bef3de3142e85c47abdee8db9a55af2bb438da54a4549263/sqlalchemy-2.0.45-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f02325709d1b1a1489f23a39b318e175a171497374149eae74d612634b234c0", size = 3232781, upload-time = "2025-12-09T22:09:54.435Z" }, + { url = "https://files.pythonhosted.org/packages/ad/bd/bf7b869b6f5585eac34222e1cf4405f4ba8c3b85dd6b1af5d4ce8bca695f/sqlalchemy-2.0.45-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d2c3684fca8a05f0ac1d9a21c1f4a266983a7ea9180efb80ffeb03861ecd01a0", size = 3182096, upload-time = "2025-12-09T22:06:06.169Z" }, + { url = "https://files.pythonhosted.org/packages/21/6a/c219720a241bb8f35c88815ccc27761f5af7fdef04b987b0e8a2c1a6dcaa/sqlalchemy-2.0.45-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:040f6f0545b3b7da6b9317fc3e922c9a98fc7243b2a1b39f78390fc0942f7826", size = 3205109, upload-time = "2025-12-09T22:09:55.969Z" }, + { url = "https://files.pythonhosted.org/packages/bd/c4/6ccf31b2bc925d5d95fab403ffd50d20d7c82b858cf1a4855664ca054dce/sqlalchemy-2.0.45-cp310-cp310-win32.whl", hash = "sha256:830d434d609fe7bfa47c425c445a8b37929f140a7a44cdaf77f6d34df3a7296a", size = 2114240, upload-time = "2025-12-09T21:29:54.007Z" }, + { url = "https://files.pythonhosted.org/packages/de/29/a27a31fca07316def418db6f7c70ab14010506616a2decef1906050a0587/sqlalchemy-2.0.45-cp310-cp310-win_amd64.whl", hash = "sha256:0209d9753671b0da74da2cfbb9ecf9c02f72a759e4b018b3ab35f244c91842c7", size = 2137615, upload-time = "2025-12-09T21:29:55.85Z" }, + { url = "https://files.pythonhosted.org/packages/a2/1c/769552a9d840065137272ebe86ffbb0bc92b0f1e0a68ee5266a225f8cd7b/sqlalchemy-2.0.45-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2e90a344c644a4fa871eb01809c32096487928bd2038bf10f3e4515cb688cc56", size = 2153860, upload-time = "2025-12-10T20:03:23.843Z" }, + { url = "https://files.pythonhosted.org/packages/f3/f8/9be54ff620e5b796ca7b44670ef58bc678095d51b0e89d6e3102ea468216/sqlalchemy-2.0.45-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b8c8b41b97fba5f62349aa285654230296829672fc9939cd7f35aab246d1c08b", size = 3309379, upload-time = "2025-12-09T22:06:07.461Z" }, + { url = "https://files.pythonhosted.org/packages/f6/2b/60ce3ee7a5ae172bfcd419ce23259bb874d2cddd44f67c5df3760a1e22f9/sqlalchemy-2.0.45-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:12c694ed6468333a090d2f60950e4250b928f457e4962389553d6ba5fe9951ac", size = 3309948, upload-time = "2025-12-09T22:09:57.643Z" }, + { url = "https://files.pythonhosted.org/packages/a3/42/bac8d393f5db550e4e466d03d16daaafd2bad1f74e48c12673fb499a7fc1/sqlalchemy-2.0.45-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f7d27a1d977a1cfef38a0e2e1ca86f09c4212666ce34e6ae542f3ed0a33bc606", size = 3261239, upload-time = "2025-12-09T22:06:08.879Z" }, + { url = "https://files.pythonhosted.org/packages/6f/12/43dc70a0528c59842b04ea1c1ed176f072a9b383190eb015384dd102fb19/sqlalchemy-2.0.45-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d62e47f5d8a50099b17e2bfc1b0c7d7ecd8ba6b46b1507b58cc4f05eefc3bb1c", size = 3284065, upload-time = "2025-12-09T22:09:59.454Z" }, + { url = "https://files.pythonhosted.org/packages/cf/9c/563049cf761d9a2ec7bc489f7879e9d94e7b590496bea5bbee9ed7b4cc32/sqlalchemy-2.0.45-cp311-cp311-win32.whl", hash = "sha256:3c5f76216e7b85770d5bb5130ddd11ee89f4d52b11783674a662c7dd57018177", size = 2113480, upload-time = "2025-12-09T21:29:57.03Z" }, + { url = "https://files.pythonhosted.org/packages/bc/fa/09d0a11fe9f15c7fa5c7f0dd26be3d235b0c0cbf2f9544f43bc42efc8a24/sqlalchemy-2.0.45-cp311-cp311-win_amd64.whl", hash = "sha256:a15b98adb7f277316f2c276c090259129ee4afca783495e212048daf846654b2", size = 2138407, upload-time = "2025-12-09T21:29:58.556Z" }, + { url = "https://files.pythonhosted.org/packages/2d/c7/1900b56ce19bff1c26f39a4ce427faec7716c81ac792bfac8b6a9f3dca93/sqlalchemy-2.0.45-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b3ee2aac15169fb0d45822983631466d60b762085bc4535cd39e66bea362df5f", size = 3333760, upload-time = "2025-12-09T22:11:02.66Z" }, + { url = "https://files.pythonhosted.org/packages/0a/93/3be94d96bb442d0d9a60e55a6bb6e0958dd3457751c6f8502e56ef95fed0/sqlalchemy-2.0.45-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba547ac0b361ab4f1608afbc8432db669bd0819b3e12e29fb5fa9529a8bba81d", size = 3348268, upload-time = "2025-12-09T22:13:49.054Z" }, + { url = "https://files.pythonhosted.org/packages/48/4b/f88ded696e61513595e4a9778f9d3f2bf7332cce4eb0c7cedaabddd6687b/sqlalchemy-2.0.45-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:215f0528b914e5c75ef2559f69dca86878a3beeb0c1be7279d77f18e8d180ed4", size = 3278144, upload-time = "2025-12-09T22:11:04.14Z" }, + { url = "https://files.pythonhosted.org/packages/ed/6a/310ecb5657221f3e1bd5288ed83aa554923fb5da48d760a9f7622afeb065/sqlalchemy-2.0.45-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:107029bf4f43d076d4011f1afb74f7c3e2ea029ec82eb23d8527d5e909e97aa6", size = 3313907, upload-time = "2025-12-09T22:13:50.598Z" }, + { url = "https://files.pythonhosted.org/packages/5c/39/69c0b4051079addd57c84a5bfb34920d87456dd4c90cf7ee0df6efafc8ff/sqlalchemy-2.0.45-cp312-cp312-win32.whl", hash = "sha256:0c9f6ada57b58420a2c0277ff853abe40b9e9449f8d7d231763c6bc30f5c4953", size = 2112182, upload-time = "2025-12-09T21:39:30.824Z" }, + { url = "https://files.pythonhosted.org/packages/f7/4e/510db49dd89fc3a6e994bee51848c94c48c4a00dc905e8d0133c251f41a7/sqlalchemy-2.0.45-cp312-cp312-win_amd64.whl", hash = "sha256:8defe5737c6d2179c7997242d6473587c3beb52e557f5ef0187277009f73e5e1", size = 2139200, upload-time = "2025-12-09T21:39:32.321Z" }, + { url = "https://files.pythonhosted.org/packages/6a/c8/7cc5221b47a54edc72a0140a1efa56e0a2730eefa4058d7ed0b4c4357ff8/sqlalchemy-2.0.45-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fe187fc31a54d7fd90352f34e8c008cf3ad5d064d08fedd3de2e8df83eb4a1cf", size = 3277082, upload-time = "2025-12-09T22:11:06.167Z" }, + { url = "https://files.pythonhosted.org/packages/0e/50/80a8d080ac7d3d321e5e5d420c9a522b0aa770ec7013ea91f9a8b7d36e4a/sqlalchemy-2.0.45-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:672c45cae53ba88e0dad74b9027dddd09ef6f441e927786b05bec75d949fbb2e", size = 3293131, upload-time = "2025-12-09T22:13:52.626Z" }, + { url = "https://files.pythonhosted.org/packages/da/4c/13dab31266fc9904f7609a5dc308a2432a066141d65b857760c3bef97e69/sqlalchemy-2.0.45-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:470daea2c1ce73910f08caf10575676a37159a6d16c4da33d0033546bddebc9b", size = 3225389, upload-time = "2025-12-09T22:11:08.093Z" }, + { url = "https://files.pythonhosted.org/packages/74/04/891b5c2e9f83589de202e7abaf24cd4e4fa59e1837d64d528829ad6cc107/sqlalchemy-2.0.45-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9c6378449e0940476577047150fd09e242529b761dc887c9808a9a937fe990c8", size = 3266054, upload-time = "2025-12-09T22:13:54.262Z" }, + { url = "https://files.pythonhosted.org/packages/f1/24/fc59e7f71b0948cdd4cff7a286210e86b0443ef1d18a23b0d83b87e4b1f7/sqlalchemy-2.0.45-cp313-cp313-win32.whl", hash = "sha256:4b6bec67ca45bc166c8729910bd2a87f1c0407ee955df110d78948f5b5827e8a", size = 2110299, upload-time = "2025-12-09T21:39:33.486Z" }, + { url = "https://files.pythonhosted.org/packages/c0/c5/d17113020b2d43073412aeca09b60d2009442420372123b8d49cc253f8b8/sqlalchemy-2.0.45-cp313-cp313-win_amd64.whl", hash = "sha256:afbf47dc4de31fa38fd491f3705cac5307d21d4bb828a4f020ee59af412744ee", size = 2136264, upload-time = "2025-12-09T21:39:36.801Z" }, + { url = "https://files.pythonhosted.org/packages/3d/8d/bb40a5d10e7a5f2195f235c0b2f2c79b0bf6e8f00c0c223130a4fbd2db09/sqlalchemy-2.0.45-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:83d7009f40ce619d483d26ac1b757dfe3167b39921379a8bd1b596cf02dab4a6", size = 3521998, upload-time = "2025-12-09T22:13:28.622Z" }, + { url = "https://files.pythonhosted.org/packages/75/a5/346128b0464886f036c039ea287b7332a410aa2d3fb0bb5d404cb8861635/sqlalchemy-2.0.45-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d8a2ca754e5415cde2b656c27900b19d50ba076aa05ce66e2207623d3fe41f5a", size = 3473434, upload-time = "2025-12-09T22:13:30.188Z" }, + { url = "https://files.pythonhosted.org/packages/cc/64/4e1913772646b060b025d3fc52ce91a58967fe58957df32b455de5a12b4f/sqlalchemy-2.0.45-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7f46ec744e7f51275582e6a24326e10c49fbdd3fc99103e01376841213028774", size = 3272404, upload-time = "2025-12-09T22:11:09.662Z" }, + { url = "https://files.pythonhosted.org/packages/b3/27/caf606ee924282fe4747ee4fd454b335a72a6e018f97eab5ff7f28199e16/sqlalchemy-2.0.45-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:883c600c345123c033c2f6caca18def08f1f7f4c3ebeb591a63b6fceffc95cce", size = 3277057, upload-time = "2025-12-09T22:13:56.213Z" }, + { url = "https://files.pythonhosted.org/packages/85/d0/3d64218c9724e91f3d1574d12eb7ff8f19f937643815d8daf792046d88ab/sqlalchemy-2.0.45-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2c0b74aa79e2deade948fe8593654c8ef4228c44ba862bb7c9585c8e0db90f33", size = 3222279, upload-time = "2025-12-09T22:11:11.1Z" }, + { url = "https://files.pythonhosted.org/packages/24/10/dd7688a81c5bc7690c2a3764d55a238c524cd1a5a19487928844cb247695/sqlalchemy-2.0.45-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:8a420169cef179d4c9064365f42d779f1e5895ad26ca0c8b4c0233920973db74", size = 3244508, upload-time = "2025-12-09T22:13:57.932Z" }, + { url = "https://files.pythonhosted.org/packages/aa/41/db75756ca49f777e029968d9c9fee338c7907c563267740c6d310a8e3f60/sqlalchemy-2.0.45-cp314-cp314-win32.whl", hash = "sha256:e50dcb81a5dfe4b7b4a4aa8f338116d127cb209559124f3694c70d6cd072b68f", size = 2113204, upload-time = "2025-12-09T21:39:38.365Z" }, + { url = "https://files.pythonhosted.org/packages/89/a2/0e1590e9adb292b1d576dbcf67ff7df8cf55e56e78d2c927686d01080f4b/sqlalchemy-2.0.45-cp314-cp314-win_amd64.whl", hash = "sha256:4748601c8ea959e37e03d13dcda4a44837afcd1b21338e637f7c935b8da06177", size = 2138785, upload-time = "2025-12-09T21:39:39.503Z" }, + { url = "https://files.pythonhosted.org/packages/42/39/f05f0ed54d451156bbed0e23eb0516bcad7cbb9f18b3bf219c786371b3f0/sqlalchemy-2.0.45-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cd337d3526ec5298f67d6a30bbbe4ed7e5e68862f0bf6dd21d289f8d37b7d60b", size = 3522029, upload-time = "2025-12-09T22:13:32.09Z" }, + { url = "https://files.pythonhosted.org/packages/54/0f/d15398b98b65c2bce288d5ee3f7d0a81f77ab89d9456994d5c7cc8b2a9db/sqlalchemy-2.0.45-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9a62b446b7d86a3909abbcd1cd3cc550a832f99c2bc37c5b22e1925438b9367b", size = 3475142, upload-time = "2025-12-09T22:13:33.739Z" }, + { url = "https://files.pythonhosted.org/packages/bf/e1/3ccb13c643399d22289c6a9786c1a91e3dcbb68bce4beb44926ac2c557bf/sqlalchemy-2.0.45-py3-none-any.whl", hash = "sha256:5225a288e4c8cc2308dbdd874edad6e7d0fd38eac1e9e5f23503425c8eee20d0", size = 1936672, upload-time = "2025-12-09T21:54:52.608Z" }, ] [[package]] @@ -7311,27 +7536,32 @@ wheels = [ [[package]] name = "tokenizers" -version = "0.22.1" +version = "0.22.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "huggingface-hub" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/1c/46/fb6854cec3278fbfa4a75b50232c77622bc517ac886156e6afbfa4d8fc6e/tokenizers-0.22.1.tar.gz", hash = "sha256:61de6522785310a309b3407bac22d99c4db5dba349935e99e4d15ea2226af2d9", size = 363123, upload-time = "2025-09-19T09:49:23.424Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bf/33/f4b2d94ada7ab297328fc671fed209368ddb82f965ec2224eb1892674c3a/tokenizers-0.22.1-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:59fdb013df17455e5f950b4b834a7b3ee2e0271e6378ccb33aa74d178b513c73", size = 3069318, upload-time = "2025-09-19T09:49:11.848Z" }, - { url = "https://files.pythonhosted.org/packages/1c/58/2aa8c874d02b974990e89ff95826a4852a8b2a273c7d1b4411cdd45a4565/tokenizers-0.22.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:8d4e484f7b0827021ac5f9f71d4794aaef62b979ab7608593da22b1d2e3c4edc", size = 2926478, upload-time = "2025-09-19T09:49:09.759Z" }, - { url = "https://files.pythonhosted.org/packages/1e/3b/55e64befa1e7bfea963cf4b787b2cea1011362c4193f5477047532ce127e/tokenizers-0.22.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19d2962dd28bc67c1f205ab180578a78eef89ac60ca7ef7cbe9635a46a56422a", size = 3256994, upload-time = "2025-09-19T09:48:56.701Z" }, - { url = "https://files.pythonhosted.org/packages/71/0b/fbfecf42f67d9b7b80fde4aabb2b3110a97fac6585c9470b5bff103a80cb/tokenizers-0.22.1-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:38201f15cdb1f8a6843e6563e6e79f4abd053394992b9bbdf5213ea3469b4ae7", size = 3153141, upload-time = "2025-09-19T09:48:59.749Z" }, - { url = "https://files.pythonhosted.org/packages/17/a9/b38f4e74e0817af8f8ef925507c63c6ae8171e3c4cb2d5d4624bf58fca69/tokenizers-0.22.1-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d1cbe5454c9a15df1b3443c726063d930c16f047a3cc724b9e6e1a91140e5a21", size = 3508049, upload-time = "2025-09-19T09:49:05.868Z" }, - { url = "https://files.pythonhosted.org/packages/d2/48/dd2b3dac46bb9134a88e35d72e1aa4869579eacc1a27238f1577270773ff/tokenizers-0.22.1-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e7d094ae6312d69cc2a872b54b91b309f4f6fbce871ef28eb27b52a98e4d0214", size = 3710730, upload-time = "2025-09-19T09:49:01.832Z" }, - { url = "https://files.pythonhosted.org/packages/93/0e/ccabc8d16ae4ba84a55d41345207c1e2ea88784651a5a487547d80851398/tokenizers-0.22.1-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:afd7594a56656ace95cdd6df4cca2e4059d294c5cfb1679c57824b605556cb2f", size = 3412560, upload-time = "2025-09-19T09:49:03.867Z" }, - { url = "https://files.pythonhosted.org/packages/d0/c6/dc3a0db5a6766416c32c034286d7c2d406da1f498e4de04ab1b8959edd00/tokenizers-0.22.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2ef6063d7a84994129732b47e7915e8710f27f99f3a3260b8a38fc7ccd083f4", size = 3250221, upload-time = "2025-09-19T09:49:07.664Z" }, - { url = "https://files.pythonhosted.org/packages/d7/a6/2c8486eef79671601ff57b093889a345dd3d576713ef047776015dc66de7/tokenizers-0.22.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ba0a64f450b9ef412c98f6bcd2a50c6df6e2443b560024a09fa6a03189726879", size = 9345569, upload-time = "2025-09-19T09:49:14.214Z" }, - { url = "https://files.pythonhosted.org/packages/6b/16/32ce667f14c35537f5f605fe9bea3e415ea1b0a646389d2295ec348d5657/tokenizers-0.22.1-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:331d6d149fa9c7d632cde4490fb8bbb12337fa3a0232e77892be656464f4b446", size = 9271599, upload-time = "2025-09-19T09:49:16.639Z" }, - { url = "https://files.pythonhosted.org/packages/51/7c/a5f7898a3f6baa3fc2685c705e04c98c1094c523051c805cdd9306b8f87e/tokenizers-0.22.1-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:607989f2ea68a46cb1dfbaf3e3aabdf3f21d8748312dbeb6263d1b3b66c5010a", size = 9533862, upload-time = "2025-09-19T09:49:19.146Z" }, - { url = "https://files.pythonhosted.org/packages/36/65/7e75caea90bc73c1dd8d40438adf1a7bc26af3b8d0a6705ea190462506e1/tokenizers-0.22.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a0f307d490295717726598ef6fa4f24af9d484809223bbc253b201c740a06390", size = 9681250, upload-time = "2025-09-19T09:49:21.501Z" }, - { url = "https://files.pythonhosted.org/packages/30/2c/959dddef581b46e6209da82df3b78471e96260e2bc463f89d23b1bf0e52a/tokenizers-0.22.1-cp39-abi3-win32.whl", hash = "sha256:b5120eed1442765cd90b903bb6cfef781fd8fe64e34ccaecbae4c619b7b12a82", size = 2472003, upload-time = "2025-09-19T09:49:27.089Z" }, - { url = "https://files.pythonhosted.org/packages/b3/46/e33a8c93907b631a99377ef4c5f817ab453d0b34f93529421f42ff559671/tokenizers-0.22.1-cp39-abi3-win_amd64.whl", hash = "sha256:65fd6e3fb11ca1e78a6a93602490f134d1fdeb13bcef99389d5102ea318ed138", size = 2674684, upload-time = "2025-09-19T09:49:24.953Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/73/6f/f80cfef4a312e1fb34baf7d85c72d4411afde10978d4657f8cdd811d3ccc/tokenizers-0.22.2.tar.gz", hash = "sha256:473b83b915e547aa366d1eee11806deaf419e17be16310ac0a14077f1e28f917", size = 372115, upload-time = "2026-01-05T10:45:15.988Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/92/97/5dbfabf04c7e348e655e907ed27913e03db0923abb5dfdd120d7b25630e1/tokenizers-0.22.2-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:544dd704ae7238755d790de45ba8da072e9af3eea688f698b137915ae959281c", size = 3100275, upload-time = "2026-01-05T10:41:02.158Z" }, + { url = "https://files.pythonhosted.org/packages/2e/47/174dca0502ef88b28f1c9e06b73ce33500eedfac7a7692108aec220464e7/tokenizers-0.22.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:1e418a55456beedca4621dbab65a318981467a2b188e982a23e117f115ce5001", size = 2981472, upload-time = "2026-01-05T10:41:00.276Z" }, + { url = "https://files.pythonhosted.org/packages/d6/84/7990e799f1309a8b87af6b948f31edaa12a3ed22d11b352eaf4f4b2e5753/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2249487018adec45d6e3554c71d46eb39fa8ea67156c640f7513eb26f318cec7", size = 3290736, upload-time = "2026-01-05T10:40:32.165Z" }, + { url = "https://files.pythonhosted.org/packages/78/59/09d0d9ba94dcd5f4f1368d4858d24546b4bdc0231c2354aa31d6199f0399/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25b85325d0815e86e0bac263506dd114578953b7b53d7de09a6485e4a160a7dd", size = 3168835, upload-time = "2026-01-05T10:40:38.847Z" }, + { url = "https://files.pythonhosted.org/packages/47/50/b3ebb4243e7160bda8d34b731e54dd8ab8b133e50775872e7a434e524c28/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bfb88f22a209ff7b40a576d5324bf8286b519d7358663db21d6246fb17eea2d5", size = 3521673, upload-time = "2026-01-05T10:40:56.614Z" }, + { url = "https://files.pythonhosted.org/packages/e0/fa/89f4cb9e08df770b57adb96f8cbb7e22695a4cb6c2bd5f0c4f0ebcf33b66/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1c774b1276f71e1ef716e5486f21e76333464f47bece56bbd554485982a9e03e", size = 3724818, upload-time = "2026-01-05T10:40:44.507Z" }, + { url = "https://files.pythonhosted.org/packages/64/04/ca2363f0bfbe3b3d36e95bf67e56a4c88c8e3362b658e616d1ac185d47f2/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:df6c4265b289083bf710dff49bc51ef252f9d5be33a45ee2bed151114a56207b", size = 3379195, upload-time = "2026-01-05T10:40:51.139Z" }, + { url = "https://files.pythonhosted.org/packages/2e/76/932be4b50ef6ccedf9d3c6639b056a967a86258c6d9200643f01269211ca/tokenizers-0.22.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:369cc9fc8cc10cb24143873a0d95438bb8ee257bb80c71989e3ee290e8d72c67", size = 3274982, upload-time = "2026-01-05T10:40:58.331Z" }, + { url = "https://files.pythonhosted.org/packages/1d/28/5f9f5a4cc211b69e89420980e483831bcc29dade307955cc9dc858a40f01/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:29c30b83d8dcd061078b05ae0cb94d3c710555fbb44861139f9f83dcca3dc3e4", size = 9478245, upload-time = "2026-01-05T10:41:04.053Z" }, + { url = "https://files.pythonhosted.org/packages/6c/fb/66e2da4704d6aadebf8cb39f1d6d1957df667ab24cff2326b77cda0dcb85/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:37ae80a28c1d3265bb1f22464c856bd23c02a05bb211e56d0c5301a435be6c1a", size = 9560069, upload-time = "2026-01-05T10:45:10.673Z" }, + { url = "https://files.pythonhosted.org/packages/16/04/fed398b05caa87ce9b1a1bb5166645e38196081b225059a6edaff6440fac/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:791135ee325f2336f498590eb2f11dc5c295232f288e75c99a36c5dbce63088a", size = 9899263, upload-time = "2026-01-05T10:45:12.559Z" }, + { url = "https://files.pythonhosted.org/packages/05/a1/d62dfe7376beaaf1394917e0f8e93ee5f67fea8fcf4107501db35996586b/tokenizers-0.22.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:38337540fbbddff8e999d59970f3c6f35a82de10053206a7562f1ea02d046fa5", size = 10033429, upload-time = "2026-01-05T10:45:14.333Z" }, + { url = "https://files.pythonhosted.org/packages/fd/18/a545c4ea42af3df6effd7d13d250ba77a0a86fb20393143bbb9a92e434d4/tokenizers-0.22.2-cp39-abi3-win32.whl", hash = "sha256:a6bf3f88c554a2b653af81f3204491c818ae2ac6fbc09e76ef4773351292bc92", size = 2502363, upload-time = "2026-01-05T10:45:20.593Z" }, + { url = "https://files.pythonhosted.org/packages/65/71/0670843133a43d43070abeb1949abfdef12a86d490bea9cd9e18e37c5ff7/tokenizers-0.22.2-cp39-abi3-win_amd64.whl", hash = "sha256:c9ea31edff2968b44a88f97d784c2f16dc0729b8b143ed004699ebca91f05c48", size = 2747786, upload-time = "2026-01-05T10:45:18.411Z" }, + { url = "https://files.pythonhosted.org/packages/72/f4/0de46cfa12cdcbcd464cc59fde36912af405696f687e53a091fb432f694c/tokenizers-0.22.2-cp39-abi3-win_arm64.whl", hash = "sha256:9ce725d22864a1e965217204946f830c37876eee3b2ba6fc6255e8e903d5fcbc", size = 2612133, upload-time = "2026-01-05T10:45:17.232Z" }, + { url = "https://files.pythonhosted.org/packages/84/04/655b79dbcc9b3ac5f1479f18e931a344af67e5b7d3b251d2dcdcd7558592/tokenizers-0.22.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:753d47ebd4542742ef9261d9da92cd545b2cacbb48349a1225466745bb866ec4", size = 3282301, upload-time = "2026-01-05T10:40:34.858Z" }, + { url = "https://files.pythonhosted.org/packages/46/cd/e4851401f3d8f6f45d8480262ab6a5c8cb9c4302a790a35aa14eeed6d2fd/tokenizers-0.22.2-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e10bf9113d209be7cd046d40fbabbaf3278ff6d18eb4da4c500443185dc1896c", size = 3161308, upload-time = "2026-01-05T10:40:40.737Z" }, + { url = "https://files.pythonhosted.org/packages/6f/6e/55553992a89982cd12d4a66dddb5e02126c58677ea3931efcbe601d419db/tokenizers-0.22.2-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:64d94e84f6660764e64e7e0b22baa72f6cd942279fdbb21d46abd70d179f0195", size = 3718964, upload-time = "2026-01-05T10:40:46.56Z" }, + { url = "https://files.pythonhosted.org/packages/59/8c/b1c87148aa15e099243ec9f0cf9d0e970cc2234c3257d558c25a2c5304e6/tokenizers-0.22.2-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f01a9c019878532f98927d2bacb79bbb404b43d3437455522a00a30718cdedb5", size = 3373542, upload-time = "2026-01-05T10:40:52.803Z" }, ] [[package]] @@ -7382,23 +7612,44 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/bd/75/8539d011f6be8e29f339c42e633aae3cb73bffa95dd0f9adec09b9c58e85/tomlkit-0.13.3-py3-none-any.whl", hash = "sha256:c89c649d79ee40629a9fda55f8ace8c6a1b42deb912b2a8fd8d942ddadb606b0", size = 38901, upload-time = "2025-06-05T07:13:43.546Z" }, ] +[[package]] +name = "toolguard" +version = "0.1.20" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "datamodel-code-generator" }, + { name = "langchain-core" }, + { name = "litellm" }, + { name = "markdown" }, + { name = "mellea" }, + { name = "pydantic" }, + { name = "pyright" }, + { name = "pytest" }, + { name = "pytest-json-report" }, + { name = "smolagents" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0c/56/595f6b14d7b2284700e36bad656e0312969ba0a63c5be5576fa5362c514f/toolguard-0.1.20.tar.gz", hash = "sha256:71c2a238d489c010a317b09634aeae42bd5a5c22dcbbac44a6d379621ee37347", size = 55466, upload-time = "2026-01-11T15:38:25.164Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6c/1d/ec3f7c34994881c02ed9865bda1564aeef66c86089dd357a5a6d156467f4/toolguard-0.1.20-py3-none-any.whl", hash = "sha256:04c57ad38bed3aa0021e65ca5f14f80cb7366c914df1cb744fa11ff931f53103", size = 86701, upload-time = "2026-01-11T15:38:23.695Z" }, +] + [[package]] name = "torch" version = "2.2.2" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin'", + "python_full_version >= '3.14' and platform_machine == 'x86_64' and sys_platform == 'darwin'", + "python_full_version == '3.13.*' and platform_machine == 'x86_64' and sys_platform == 'darwin'", "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'darwin'", "python_full_version == '3.11.*' and platform_machine == 'x86_64' and sys_platform == 'darwin'", - "python_full_version >= '3.10.2' and python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'darwin'", - "python_full_version < '3.10.2' and platform_machine == 'x86_64' and sys_platform == 'darwin'", + "python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'darwin'", ] dependencies = [ { name = "filelock", marker = "platform_machine == 'x86_64' and sys_platform == 'darwin'" }, { name = "fsspec", marker = "platform_machine == 'x86_64' and sys_platform == 'darwin'" }, { name = "jinja2", marker = "platform_machine == 'x86_64' and sys_platform == 'darwin'" }, { name = "networkx", version = "3.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'darwin'" }, - { name = "networkx", version = "3.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' and platform_machine == 'x86_64' and sys_platform == 'darwin'" }, + { name = "networkx", version = "3.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11' and platform_machine == 'x86_64' and sys_platform == 'darwin'" }, { name = "sympy", marker = "platform_machine == 'x86_64' and sys_platform == 'darwin'" }, { name = "typing-extensions", marker = "platform_machine == 'x86_64' and sys_platform == 'darwin'" }, ] @@ -7413,28 +7664,28 @@ name = "torch" version = "2.9.1" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.13' and platform_machine != 'x86_64' and sys_platform == 'darwin'", - "python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux'", - "(python_full_version >= '3.13' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and sys_platform != 'darwin' and sys_platform != 'linux')", + "python_full_version >= '3.14' and platform_machine != 'x86_64' and sys_platform == 'darwin'", + "python_full_version >= '3.14' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "(python_full_version >= '3.14' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.14' and sys_platform != 'darwin' and sys_platform != 'linux')", + "python_full_version == '3.13.*' and platform_machine != 'x86_64' and sys_platform == 'darwin'", + "python_full_version == '3.13.*' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "(python_full_version == '3.13.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.13.*' and sys_platform != 'darwin' and sys_platform != 'linux')", "python_full_version == '3.12.*' and platform_machine != 'x86_64' and sys_platform == 'darwin'", "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'", "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')", "python_full_version == '3.11.*' and platform_machine != 'x86_64' and sys_platform == 'darwin'", "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'", "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux')", - "python_full_version >= '3.10.2' and python_full_version < '3.11' and platform_machine != 'x86_64' and sys_platform == 'darwin'", - "python_full_version >= '3.10.2' and python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'", - "(python_full_version >= '3.10.2' and python_full_version < '3.11' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.10.2' and python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')", - "python_full_version < '3.10.2' and platform_machine != 'x86_64' and sys_platform == 'darwin'", - "python_full_version < '3.10.2' and platform_machine == 'aarch64' and sys_platform == 'linux'", - "(python_full_version < '3.10.2' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10.2' and sys_platform != 'darwin' and sys_platform != 'linux')", + "python_full_version < '3.11' and platform_machine != 'x86_64' and sys_platform == 'darwin'", + "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "(python_full_version < '3.11' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')", ] dependencies = [ { name = "filelock", marker = "platform_machine != 'x86_64' or sys_platform != 'darwin'" }, { name = "fsspec", marker = "platform_machine != 'x86_64' or sys_platform != 'darwin'" }, { name = "jinja2", marker = "platform_machine != 'x86_64' or sys_platform != 'darwin'" }, { name = "networkx", version = "3.4.2", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and platform_machine != 'x86_64') or (python_full_version < '3.11' and sys_platform != 'darwin')" }, - { name = "networkx", version = "3.6", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.11' and platform_machine != 'x86_64') or (python_full_version >= '3.11' and sys_platform != 'darwin')" }, + { name = "networkx", version = "3.6.1", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.11' and platform_machine != 'x86_64') or (python_full_version >= '3.11' and sys_platform != 'darwin')" }, { name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, { name = "nvidia-cuda-cupti-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, { name = "nvidia-cuda-nvrtc-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" }, @@ -7491,11 +7742,11 @@ name = "torchvision" version = "0.17.2" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.13' and platform_machine == 'x86_64' and sys_platform == 'darwin'", + "python_full_version >= '3.14' and platform_machine == 'x86_64' and sys_platform == 'darwin'", + "python_full_version == '3.13.*' and platform_machine == 'x86_64' and sys_platform == 'darwin'", "python_full_version == '3.12.*' and platform_machine == 'x86_64' and sys_platform == 'darwin'", "python_full_version == '3.11.*' and platform_machine == 'x86_64' and sys_platform == 'darwin'", - "python_full_version >= '3.10.2' and python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'darwin'", - "python_full_version < '3.10.2' and platform_machine == 'x86_64' and sys_platform == 'darwin'", + "python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'darwin'", ] dependencies = [ { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11' and platform_machine == 'x86_64' and sys_platform == 'darwin'" }, @@ -7514,21 +7765,21 @@ name = "torchvision" version = "0.24.1" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.13' and platform_machine != 'x86_64' and sys_platform == 'darwin'", - "python_full_version >= '3.13' and platform_machine == 'aarch64' and sys_platform == 'linux'", - "(python_full_version >= '3.13' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.13' and sys_platform != 'darwin' and sys_platform != 'linux')", + "python_full_version >= '3.14' and platform_machine != 'x86_64' and sys_platform == 'darwin'", + "python_full_version >= '3.14' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "(python_full_version >= '3.14' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.14' and sys_platform != 'darwin' and sys_platform != 'linux')", + "python_full_version == '3.13.*' and platform_machine != 'x86_64' and sys_platform == 'darwin'", + "python_full_version == '3.13.*' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "(python_full_version == '3.13.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.13.*' and sys_platform != 'darwin' and sys_platform != 'linux')", "python_full_version == '3.12.*' and platform_machine != 'x86_64' and sys_platform == 'darwin'", "python_full_version == '3.12.*' and platform_machine == 'aarch64' and sys_platform == 'linux'", "(python_full_version == '3.12.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.12.*' and sys_platform != 'darwin' and sys_platform != 'linux')", "python_full_version == '3.11.*' and platform_machine != 'x86_64' and sys_platform == 'darwin'", "python_full_version == '3.11.*' and platform_machine == 'aarch64' and sys_platform == 'linux'", "(python_full_version == '3.11.*' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'linux')", - "python_full_version >= '3.10.2' and python_full_version < '3.11' and platform_machine != 'x86_64' and sys_platform == 'darwin'", - "python_full_version >= '3.10.2' and python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'", - "(python_full_version >= '3.10.2' and python_full_version < '3.11' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.10.2' and python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')", - "python_full_version < '3.10.2' and platform_machine != 'x86_64' and sys_platform == 'darwin'", - "python_full_version < '3.10.2' and platform_machine == 'aarch64' and sys_platform == 'linux'", - "(python_full_version < '3.10.2' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.10.2' and sys_platform != 'darwin' and sys_platform != 'linux')", + "python_full_version < '3.11' and platform_machine != 'x86_64' and sys_platform == 'darwin'", + "python_full_version < '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux'", + "(python_full_version < '3.11' and platform_machine != 'aarch64' and sys_platform == 'linux') or (python_full_version < '3.11' and sys_platform != 'darwin' and sys_platform != 'linux')", ] dependencies = [ { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and platform_machine != 'x86_64') or (python_full_version < '3.11' and sys_platform != 'darwin')" }, @@ -7569,21 +7820,21 @@ wheels = [ [[package]] name = "tornado" -version = "6.5.2" +version = "6.5.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/09/ce/1eb500eae19f4648281bb2186927bb062d2438c2e5093d1360391afd2f90/tornado-6.5.2.tar.gz", hash = "sha256:ab53c8f9a0fa351e2c0741284e06c7a45da86afb544133201c5cc8578eb076a0", size = 510821, upload-time = "2025-08-08T18:27:00.78Z" } +sdist = { url = "https://files.pythonhosted.org/packages/37/1d/0a336abf618272d53f62ebe274f712e213f5a03c0b2339575430b8362ef2/tornado-6.5.4.tar.gz", hash = "sha256:a22fa9047405d03260b483980635f0b041989d8bcc9a313f8fe18b411d84b1d7", size = 513632, upload-time = "2025-12-15T19:21:03.836Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f6/48/6a7529df2c9cc12efd2e8f5dd219516184d703b34c06786809670df5b3bd/tornado-6.5.2-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:2436822940d37cde62771cff8774f4f00b3c8024fe482e16ca8387b8a2724db6", size = 442563, upload-time = "2025-08-08T18:26:42.945Z" }, - { url = "https://files.pythonhosted.org/packages/f2/b5/9b575a0ed3e50b00c40b08cbce82eb618229091d09f6d14bce80fc01cb0b/tornado-6.5.2-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:583a52c7aa94ee046854ba81d9ebb6c81ec0fd30386d96f7640c96dad45a03ef", size = 440729, upload-time = "2025-08-08T18:26:44.473Z" }, - { url = "https://files.pythonhosted.org/packages/1b/4e/619174f52b120efcf23633c817fd3fed867c30bff785e2cd5a53a70e483c/tornado-6.5.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b0fe179f28d597deab2842b86ed4060deec7388f1fd9c1b4a41adf8af058907e", size = 444295, upload-time = "2025-08-08T18:26:46.021Z" }, - { url = "https://files.pythonhosted.org/packages/95/fa/87b41709552bbd393c85dd18e4e3499dcd8983f66e7972926db8d96aa065/tornado-6.5.2-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b186e85d1e3536d69583d2298423744740986018e393d0321df7340e71898882", size = 443644, upload-time = "2025-08-08T18:26:47.625Z" }, - { url = "https://files.pythonhosted.org/packages/f9/41/fb15f06e33d7430ca89420283a8762a4e6b8025b800ea51796ab5e6d9559/tornado-6.5.2-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e792706668c87709709c18b353da1f7662317b563ff69f00bab83595940c7108", size = 443878, upload-time = "2025-08-08T18:26:50.599Z" }, - { url = "https://files.pythonhosted.org/packages/11/92/fe6d57da897776ad2e01e279170ea8ae726755b045fe5ac73b75357a5a3f/tornado-6.5.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:06ceb1300fd70cb20e43b1ad8aaee0266e69e7ced38fa910ad2e03285009ce7c", size = 444549, upload-time = "2025-08-08T18:26:51.864Z" }, - { url = "https://files.pythonhosted.org/packages/9b/02/c8f4f6c9204526daf3d760f4aa555a7a33ad0e60843eac025ccfd6ff4a93/tornado-6.5.2-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:74db443e0f5251be86cbf37929f84d8c20c27a355dd452a5cfa2aada0d001ec4", size = 443973, upload-time = "2025-08-08T18:26:53.625Z" }, - { url = "https://files.pythonhosted.org/packages/ae/2d/f5f5707b655ce2317190183868cd0f6822a1121b4baeae509ceb9590d0bd/tornado-6.5.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b5e735ab2889d7ed33b32a459cac490eda71a1ba6857b0118de476ab6c366c04", size = 443954, upload-time = "2025-08-08T18:26:55.072Z" }, - { url = "https://files.pythonhosted.org/packages/e8/59/593bd0f40f7355806bf6573b47b8c22f8e1374c9b6fd03114bd6b7a3dcfd/tornado-6.5.2-cp39-abi3-win32.whl", hash = "sha256:c6f29e94d9b37a95013bb669616352ddb82e3bfe8326fccee50583caebc8a5f0", size = 445023, upload-time = "2025-08-08T18:26:56.677Z" }, - { url = "https://files.pythonhosted.org/packages/c7/2a/f609b420c2f564a748a2d80ebfb2ee02a73ca80223af712fca591386cafb/tornado-6.5.2-cp39-abi3-win_amd64.whl", hash = "sha256:e56a5af51cc30dd2cae649429af65ca2f6571da29504a07995175df14c18f35f", size = 445427, upload-time = "2025-08-08T18:26:57.91Z" }, - { url = "https://files.pythonhosted.org/packages/5e/4f/e1f65e8f8c76d73658b33d33b81eed4322fb5085350e4328d5c956f0c8f9/tornado-6.5.2-cp39-abi3-win_arm64.whl", hash = "sha256:d6c33dc3672e3a1f3618eb63b7ef4683a7688e7b9e6e8f0d9aa5726360a004af", size = 444456, upload-time = "2025-08-08T18:26:59.207Z" }, + { url = "https://files.pythonhosted.org/packages/ab/a9/e94a9d5224107d7ce3cc1fab8d5dc97f5ea351ccc6322ee4fb661da94e35/tornado-6.5.4-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d6241c1a16b1c9e4cc28148b1cda97dd1c6cb4fb7068ac1bedc610768dff0ba9", size = 443909, upload-time = "2025-12-15T19:20:48.382Z" }, + { url = "https://files.pythonhosted.org/packages/db/7e/f7b8d8c4453f305a51f80dbb49014257bb7d28ccb4bbb8dd328ea995ecad/tornado-6.5.4-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2d50f63dda1d2cac3ae1fa23d254e16b5e38153758470e9956cbc3d813d40843", size = 442163, upload-time = "2025-12-15T19:20:49.791Z" }, + { url = "https://files.pythonhosted.org/packages/ba/b5/206f82d51e1bfa940ba366a8d2f83904b15942c45a78dd978b599870ab44/tornado-6.5.4-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1cf66105dc6acb5af613c054955b8137e34a03698aa53272dbda4afe252be17", size = 445746, upload-time = "2025-12-15T19:20:51.491Z" }, + { url = "https://files.pythonhosted.org/packages/8e/9d/1a3338e0bd30ada6ad4356c13a0a6c35fbc859063fa7eddb309183364ac1/tornado-6.5.4-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50ff0a58b0dc97939d29da29cd624da010e7f804746621c78d14b80238669335", size = 445083, upload-time = "2025-12-15T19:20:52.778Z" }, + { url = "https://files.pythonhosted.org/packages/50/d4/e51d52047e7eb9a582da59f32125d17c0482d065afd5d3bc435ff2120dc5/tornado-6.5.4-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5fb5e04efa54cf0baabdd10061eb4148e0be137166146fff835745f59ab9f7f", size = 445315, upload-time = "2025-12-15T19:20:53.996Z" }, + { url = "https://files.pythonhosted.org/packages/27/07/2273972f69ca63dbc139694a3fc4684edec3ea3f9efabf77ed32483b875c/tornado-6.5.4-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9c86b1643b33a4cd415f8d0fe53045f913bf07b4a3ef646b735a6a86047dda84", size = 446003, upload-time = "2025-12-15T19:20:56.101Z" }, + { url = "https://files.pythonhosted.org/packages/d1/83/41c52e47502bf7260044413b6770d1a48dda2f0246f95ee1384a3cd9c44a/tornado-6.5.4-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:6eb82872335a53dd063a4f10917b3efd28270b56a33db69009606a0312660a6f", size = 445412, upload-time = "2025-12-15T19:20:57.398Z" }, + { url = "https://files.pythonhosted.org/packages/10/c7/bc96917f06cbee182d44735d4ecde9c432e25b84f4c2086143013e7b9e52/tornado-6.5.4-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6076d5dda368c9328ff41ab5d9dd3608e695e8225d1cd0fd1e006f05da3635a8", size = 445392, upload-time = "2025-12-15T19:20:58.692Z" }, + { url = "https://files.pythonhosted.org/packages/0c/1a/d7592328d037d36f2d2462f4bc1fbb383eec9278bc786c1b111cbbd44cfa/tornado-6.5.4-cp39-abi3-win32.whl", hash = "sha256:1768110f2411d5cd281bac0a090f707223ce77fd110424361092859e089b38d1", size = 446481, upload-time = "2025-12-15T19:21:00.008Z" }, + { url = "https://files.pythonhosted.org/packages/d6/6d/c69be695a0a64fd37a97db12355a035a6d90f79067a3cf936ec2b1dc38cd/tornado-6.5.4-cp39-abi3-win_amd64.whl", hash = "sha256:fa07d31e0cd85c60713f2b995da613588aa03e1303d75705dca6af8babc18ddc", size = 446886, upload-time = "2025-12-15T19:21:01.287Z" }, + { url = "https://files.pythonhosted.org/packages/50/49/8dc3fd90902f70084bd2cd059d576ddb4f8bb44c2c7c0e33a11422acb17e/tornado-6.5.4-cp39-abi3-win_arm64.whl", hash = "sha256:053e6e16701eb6cbe641f308f4c1a9541f91b6261991160391bfc342e8a551a1", size = 445910, upload-time = "2025-12-15T19:21:02.571Z" }, ] [[package]] @@ -7609,7 +7860,7 @@ wheels = [ [[package]] name = "transformers" -version = "4.57.2" +version = "4.57.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "filelock" }, @@ -7624,9 +7875,9 @@ dependencies = [ { name = "tokenizers" }, { name = "tqdm" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/63/98/cf2515dba32791abe0540a252ccae7dc4f12fdeb03258182b6f014a78360/transformers-4.57.2.tar.gz", hash = "sha256:172a455ad5a570ecad89bea510a6c924c45fa90e46e859225fac07305d7946fc", size = 10141231, upload-time = "2025-11-24T17:54:14.293Z" } +sdist = { url = "https://files.pythonhosted.org/packages/dd/70/d42a739e8dfde3d92bb2fff5819cbf331fe9657323221e79415cd5eb65ee/transformers-4.57.3.tar.gz", hash = "sha256:df4945029aaddd7c09eec5cad851f30662f8bd1746721b34cc031d70c65afebc", size = 10139680, upload-time = "2025-11-25T15:51:30.139Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d3/21/15c69470cf94857d4664e74554fa01248eb57428fed831929405a0a63b0a/transformers-4.57.2-py3-none-any.whl", hash = "sha256:0918df354853c9931a637792cec519e137aceb150effd4c7924d6b8d36918fab", size = 11993097, upload-time = "2025-11-24T17:54:10.472Z" }, + { url = "https://files.pythonhosted.org/packages/6a/6b/2f416568b3c4c91c96e5a365d164f8a4a4a88030aa8ab4644181fdadce97/transformers-4.57.3-py3-none-any.whl", hash = "sha256:c77d353a4851b1880191603d36acb313411d3577f6e2897814f333841f7003f4", size = 11993463, upload-time = "2025-11-25T15:51:26.493Z" }, ] [[package]] @@ -7687,21 +7938,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/22/6a/210a302e8025ac492cbaea58d3720d66b7d8034c5d747ac5e4d2d235aa25/tree_sitter_c-0.24.1-cp310-abi3-win_arm64.whl", hash = "sha256:d46bbda06f838c2dcb91daf767813671fd366b49ad84ff37db702129267b46e1", size = 82715, upload-time = "2025-05-24T17:32:57.248Z" }, ] -[[package]] -name = "tree-sitter-java" -version = "0.23.5" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fa/dc/eb9c8f96304e5d8ae1663126d89967a622a80937ad2909903569ccb7ec8f/tree_sitter_java-0.23.5.tar.gz", hash = "sha256:f5cd57b8f1270a7f0438878750d02ccc79421d45cca65ff284f1527e9ef02e38", size = 138121, upload-time = "2024-12-21T18:24:26.936Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/67/21/b3399780b440e1567a11d384d0ebb1aea9b642d0d98becf30fa55c0e3a3b/tree_sitter_java-0.23.5-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:355ce0308672d6f7013ec913dee4a0613666f4cda9044a7824240d17f38209df", size = 58926, upload-time = "2024-12-21T18:24:12.53Z" }, - { url = "https://files.pythonhosted.org/packages/57/ef/6406b444e2a93bc72a04e802f4107e9ecf04b8de4a5528830726d210599c/tree_sitter_java-0.23.5-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:24acd59c4720dedad80d548fe4237e43ef2b7a4e94c8549b0ca6e4c4d7bf6e69", size = 62288, upload-time = "2024-12-21T18:24:14.634Z" }, - { url = "https://files.pythonhosted.org/packages/4e/6c/74b1c150d4f69c291ab0b78d5dd1b59712559bbe7e7daf6d8466d483463f/tree_sitter_java-0.23.5-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9401e7271f0b333df39fc8a8336a0caf1b891d9a2b89ddee99fae66b794fc5b7", size = 85533, upload-time = "2024-12-21T18:24:16.695Z" }, - { url = "https://files.pythonhosted.org/packages/29/09/e0d08f5c212062fd046db35c1015a2621c2631bc8b4aae5740d7adb276ad/tree_sitter_java-0.23.5-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:370b204b9500b847f6d0c5ad584045831cee69e9a3e4d878535d39e4a7e4c4f1", size = 84033, upload-time = "2024-12-21T18:24:18.758Z" }, - { url = "https://files.pythonhosted.org/packages/43/56/7d06b23ddd09bde816a131aa504ee11a1bbe87c6b62ab9b2ed23849a3382/tree_sitter_java-0.23.5-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:aae84449e330363b55b14a2af0585e4e0dae75eb64ea509b7e5b0e1de536846a", size = 82564, upload-time = "2024-12-21T18:24:20.493Z" }, - { url = "https://files.pythonhosted.org/packages/da/d6/0528c7e1e88a18221dbd8ccee3825bf274b1fa300f745fd74eb343878043/tree_sitter_java-0.23.5-cp39-abi3-win_amd64.whl", hash = "sha256:1ee45e790f8d31d416bc84a09dac2e2c6bc343e89b8a2e1d550513498eedfde7", size = 60650, upload-time = "2024-12-21T18:24:22.902Z" }, - { url = "https://files.pythonhosted.org/packages/72/57/5bab54d23179350356515526fff3cc0f3ac23bfbc1a1d518a15978d4880e/tree_sitter_java-0.23.5-cp39-abi3-win_arm64.whl", hash = "sha256:402efe136104c5603b429dc26c7e75ae14faaca54cfd319ecc41c8f2534750f4", size = 59059, upload-time = "2024-12-21T18:24:24.934Z" }, -] - [[package]] name = "tree-sitter-javascript" version = "0.25.0" @@ -7831,14 +8067,14 @@ wheels = [ [[package]] name = "types-jsonschema" -version = "4.25.1.20251009" +version = "4.26.0.20260109" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "referencing" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ef/da/5b901088da5f710690b422137e8ae74197fb1ca471e4aa84dd3ef0d6e295/types_jsonschema-4.25.1.20251009.tar.gz", hash = "sha256:75d0f5c5dd18dc23b664437a0c1a625743e8d2e665ceaf3aecb29841f3a5f97f", size = 15661, upload-time = "2025-10-09T02:54:36.963Z" } +sdist = { url = "https://files.pythonhosted.org/packages/04/03/a1509b0c13fc7a1fca1494c84bde8cce8a5c0582b6255b9640ebd3034017/types_jsonschema-4.26.0.20260109.tar.gz", hash = "sha256:340fe91e6ea517900d6ababb6262a86c176473b5bf8455b96e85a89e3cfb5daa", size = 15886, upload-time = "2026-01-09T03:21:45.464Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7f/6a/e5146754c0dfc272f176db9c245bc43cc19030262d891a5a85d472797e60/types_jsonschema-4.25.1.20251009-py3-none-any.whl", hash = "sha256:f30b329037b78e7a60146b1146feb0b6fb0b71628637584409bada83968dad3e", size = 15925, upload-time = "2025-10-09T02:54:35.847Z" }, + { url = "https://files.pythonhosted.org/packages/2f/d6/3db3134f35f1e4bf9a13517d759c1ae64086eddb8ad0047caee140021e64/types_jsonschema-4.26.0.20260109-py3-none-any.whl", hash = "sha256:e0276640d228732fb75d883905d607359b24a4ff745ba7f9a5f50e6fda891926", size = 15923, upload-time = "2026-01-09T03:21:43.828Z" }, ] [[package]] @@ -7852,20 +8088,20 @@ wheels = [ [[package]] name = "types-protobuf" -version = "6.32.1.20251105" +version = "6.32.1.20251210" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f3/ab/0dce6a9841b5ebf3e37401879bb8cc20724ad9c770a7649bee997696cc75/types_protobuf-6.32.1.20251105.tar.gz", hash = "sha256:641002611ff87dd9fedc38a39a29cacb9907ae5ce61489b53e99ca2074bef764", size = 63846, upload-time = "2025-11-05T03:04:43.456Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c2/59/c743a842911887cd96d56aa8936522b0cd5f7a7f228c96e81b59fced45be/types_protobuf-6.32.1.20251210.tar.gz", hash = "sha256:c698bb3f020274b1a2798ae09dc773728ce3f75209a35187bd11916ebfde6763", size = 63900, upload-time = "2025-12-10T03:14:25.451Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ed/57/3a0d89b33b7485b7ffd99ec7cf53b0c5c89194c481f0bd673fd67e5f273f/types_protobuf-6.32.1.20251105-py3-none-any.whl", hash = "sha256:a15109d38f7cfefd2539ef86d3f93a6a41c7cad53924f8aa1a51eaddbb72a660", size = 77890, upload-time = "2025-11-05T03:04:42.067Z" }, + { url = "https://files.pythonhosted.org/packages/aa/43/58e75bac4219cbafee83179505ff44cae3153ec279be0e30583a73b8f108/types_protobuf-6.32.1.20251210-py3-none-any.whl", hash = "sha256:2641f78f3696822a048cfb8d0ff42ccd85c25f12f871fbebe86da63793692140", size = 77921, upload-time = "2025-12-10T03:14:24.477Z" }, ] [[package]] name = "types-psutil" -version = "7.1.1.20251122" +version = "7.2.1.20251231" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6b/84/32d880ed39782da897d6c3c50e64aeb77352c7bec20fa3e2ce84a6ca9623/types_psutil-7.1.1.20251122.tar.gz", hash = "sha256:cdb39c30a81ce6e433aa672922d59b78c4c6a9c64cd9936f1f6894d26c82ae0f", size = 22275, upload-time = "2025-11-22T02:57:15.807Z" } +sdist = { url = "https://files.pythonhosted.org/packages/09/e0/f4881668da3fcc9473b3fb4b3dc028840cf57374d72b798c0912a183163a/types_psutil-7.2.1.20251231.tar.gz", hash = "sha256:dbf9df530b1130e131e4211ed8cea62c08007bfa69faf2883d296bd241d30e4a", size = 25620, upload-time = "2025-12-31T03:18:29.302Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/77/8d/3cab9f5c549b40c945eb022c981ca71ee4a65ced8790f2adaf2f531d3d13/types_psutil-7.1.1.20251122-py3-none-any.whl", hash = "sha256:9a5d074b472dee08c97891c986549f6ef4f0896fbdd6cdc0686c6663721995fe", size = 25503, upload-time = "2025-11-22T02:57:14.664Z" }, + { url = "https://files.pythonhosted.org/packages/12/61/81f180ffbcd0b3516fa3e0e95588dcd48200b6a08e3df53c6c0941a688fe/types_psutil-7.2.1.20251231-py3-none-any.whl", hash = "sha256:40735ca2fc818aed9dcbff7acb3317a774896615e3f4a7bd356afa224b9178e3", size = 32426, upload-time = "2025-12-31T03:18:28.14Z" }, ] [[package]] @@ -7927,23 +8163,23 @@ wheels = [ [[package]] name = "types-requests" -version = "2.32.4.20250913" +version = "2.32.4.20260107" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/36/27/489922f4505975b11de2b5ad07b4fe1dca0bca9be81a703f26c5f3acfce5/types_requests-2.32.4.20250913.tar.gz", hash = "sha256:abd6d4f9ce3a9383f269775a9835a4c24e5cd6b9f647d64f88aa4613c33def5d", size = 23113, upload-time = "2025-09-13T02:40:02.309Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0f/f3/a0663907082280664d745929205a89d41dffb29e89a50f753af7d57d0a96/types_requests-2.32.4.20260107.tar.gz", hash = "sha256:018a11ac158f801bfa84857ddec1650750e393df8a004a8a9ae2a9bec6fcb24f", size = 23165, upload-time = "2026-01-07T03:20:54.091Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2a/20/9a227ea57c1285986c4cf78400d0a91615d25b24e257fd9e2969606bdfae/types_requests-2.32.4.20250913-py3-none-any.whl", hash = "sha256:78c9c1fffebbe0fa487a418e0fa5252017e9c60d1a2da394077f1780f655d7e1", size = 20658, upload-time = "2025-09-13T02:40:01.115Z" }, + { url = "https://files.pythonhosted.org/packages/1c/12/709ea261f2bf91ef0a26a9eed20f2623227a8ed85610c1e54c5805692ecb/types_requests-2.32.4.20260107-py3-none-any.whl", hash = "sha256:b703fe72f8ce5b31ef031264fe9395cac8f46a04661a79f7ed31a80fb308730d", size = 20676, upload-time = "2026-01-07T03:20:52.929Z" }, ] [[package]] name = "types-setuptools" -version = "80.9.0.20250822" +version = "80.9.0.20251223" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/19/bd/1e5f949b7cb740c9f0feaac430e301b8f1c5f11a81e26324299ea671a237/types_setuptools-80.9.0.20250822.tar.gz", hash = "sha256:070ea7716968ec67a84c7f7768d9952ff24d28b65b6594797a464f1b3066f965", size = 41296, upload-time = "2025-08-22T03:02:08.771Z" } +sdist = { url = "https://files.pythonhosted.org/packages/00/07/d1b605230730990de20477150191d6dccf6aecc037da94c9960a5d563bc8/types_setuptools-80.9.0.20251223.tar.gz", hash = "sha256:d3411059ae2f5f03985217d86ac6084efea2c9e9cacd5f0869ef950f308169b2", size = 42420, upload-time = "2025-12-23T03:18:26.752Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b6/2d/475bf15c1cdc172e7a0d665b6e373ebfb1e9bf734d3f2f543d668b07a142/types_setuptools-80.9.0.20250822-py3-none-any.whl", hash = "sha256:53bf881cb9d7e46ed12c76ef76c0aaf28cfe6211d3fab12e0b83620b1a8642c3", size = 63179, upload-time = "2025-08-22T03:02:07.643Z" }, + { url = "https://files.pythonhosted.org/packages/78/5c/b8877da94012dbc6643e4eeca22bca9b99b295be05d161f8a403ae9387c0/types_setuptools-80.9.0.20251223-py3-none-any.whl", hash = "sha256:1b36db79d724c2287d83dc052cf887b47c0da6a2fff044378be0b019545f56e6", size = 64318, upload-time = "2025-12-23T03:18:25.868Z" }, ] [[package]] @@ -8003,11 +8239,11 @@ wheels = [ [[package]] name = "tzdata" -version = "2025.2" +version = "2025.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/95/32/1a225d6164441be760d75c2c42e2780dc0873fe382da3e98a2e1e48361e5/tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9", size = 196380, upload-time = "2025-03-23T13:54:43.652Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5e/a7/c202b344c5ca7daf398f3b8a477eeb205cf3b6f32e7ec3a6bac0629ca975/tzdata-2025.3.tar.gz", hash = "sha256:de39c2ca5dc7b0344f2eba86f49d614019d29f060fc4ebc8a417896a620b56a7", size = 196772, upload-time = "2025-12-13T17:45:35.667Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5c/23/c7abc0ca0a1526a0774eca151daeb8de62ec457e77262b66b359c3c7679e/tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8", size = 347839, upload-time = "2025-03-23T13:54:41.845Z" }, + { url = "https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl", hash = "sha256:06a47e5700f3081aab02b2e513160914ff0694bce9947d6b76ebd6bf57cfc5d1", size = 348521, upload-time = "2025-12-13T17:45:33.889Z" }, ] [[package]] @@ -8019,9 +8255,10 @@ dependencies = [ { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "pynndescent" }, - { name = "scikit-learn" }, + { name = "scikit-learn", version = "1.7.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "scikit-learn", version = "1.8.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "scipy", version = "1.16.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "scipy", version = "1.17.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "tqdm" }, ] sdist = { url = "https://files.pythonhosted.org/packages/5f/ee/6bc65bd375c812026a7af63fe9d09d409382120aff25f2152f1ba12af5ec/umap_learn-0.5.9.post2.tar.gz", hash = "sha256:bdf60462d779bd074ce177a0714ced17e6d161285590fa487f3f9548dd3c31c9", size = 95441, upload-time = "2025-07-03T00:18:02.479Z" } @@ -8031,11 +8268,40 @@ wheels = [ [[package]] name = "urllib3" -version = "2.5.0" +version = "2.6.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", size = 393185, upload-time = "2025-06-18T14:07:41.644Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795, upload-time = "2025-06-18T14:07:40.39Z" }, + { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" }, +] + +[[package]] +name = "uuid-utils" +version = "0.13.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fe/8a/17b11768dcb473d3a255c02ffdd94fbd1b345c906efea0a39124dcbaed52/uuid_utils-0.13.0.tar.gz", hash = "sha256:4c17df6427a9e23a4cd7fb9ee1efb53b8abb078660b9bdb2524ca8595022dfe1", size = 21921, upload-time = "2026-01-08T15:48:10.841Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/85/b8/d40848ca22781f206c60a1885fc737d2640392bd6b5792d455525accd89c/uuid_utils-0.13.0-cp39-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:83628283e977fb212e756bc055df8fdd2f9f589a2e539ba1abe755b8ce8df7a4", size = 602130, upload-time = "2026-01-08T15:47:34.877Z" }, + { url = "https://files.pythonhosted.org/packages/40/b9/00a944b8096632ea12638181f8e294abcde3e3b8b5e29b777f809896f6ae/uuid_utils-0.13.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:c47638ed6334ab19d80f73664f153b04bbb04ab8ce4298d10da6a292d4d21c47", size = 304213, upload-time = "2026-01-08T15:47:36.807Z" }, + { url = "https://files.pythonhosted.org/packages/da/d7/07b36c33aef683b81c9afff3ec178d5eb39d325447a68c3c68a62e4abb32/uuid_utils-0.13.0-cp39-abi3-manylinux_2_24_aarch64.whl", hash = "sha256:b276b538c57733ed406948584912da422a604313c71479654848b84b9e19c9b0", size = 340624, upload-time = "2026-01-08T15:47:38.821Z" }, + { url = "https://files.pythonhosted.org/packages/7d/55/fcff2fff02a27866cb1a6614c9df2b3ace721f0a0aab2b7b8f5a7d4e4221/uuid_utils-0.13.0-cp39-abi3-manylinux_2_24_armv7l.whl", hash = "sha256:bdaf2b77e34b199cf04cde28399495fd1ed951de214a4ece1f3919b2f945bb06", size = 346705, upload-time = "2026-01-08T15:47:40.397Z" }, + { url = "https://files.pythonhosted.org/packages/41/48/67438506c2bb8bee1b4b00d7c0b3ff866401b4790849bf591d654d4ea0bc/uuid_utils-0.13.0-cp39-abi3-manylinux_2_24_i686.whl", hash = "sha256:eb2f0baf81e82f9769a7684022dca8f3bf801ca1574a3e94df1876e9d6f9271e", size = 366023, upload-time = "2026-01-08T15:47:42.662Z" }, + { url = "https://files.pythonhosted.org/packages/8b/d7/2d91ce17f62fd764d593430de296b70843cc25229c772453f7261de9e6a8/uuid_utils-0.13.0-cp39-abi3-manylinux_2_24_ppc64le.whl", hash = "sha256:6be6c4d11275f5cc402a4fdba6c2b1ce45fd3d99bb78716cd1cc2cbf6802b2ce", size = 471149, upload-time = "2026-01-08T15:47:44.963Z" }, + { url = "https://files.pythonhosted.org/packages/2e/9a/aa0756186073ba84daf5704c150d41ede10eb3185d510e02532e2071550e/uuid_utils-0.13.0-cp39-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:77621cf6ceca7f42173a642a01c01c216f9eaec3b7b65d093d2d6a433ca0a83d", size = 342130, upload-time = "2026-01-08T15:47:46.331Z" }, + { url = "https://files.pythonhosted.org/packages/74/b4/3191789f4dc3bed59d79cec90559821756297a25d7dc34d1bf7781577a75/uuid_utils-0.13.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9a5a9eb06c2bb86dd876cd7b2fe927fc8543d14c90d971581db6ffda4a02526f", size = 524128, upload-time = "2026-01-08T15:47:47.628Z" }, + { url = "https://files.pythonhosted.org/packages/b2/30/29839210a8fff9fc219bfa7c8d8cd115324e92618cba0cda090d54d3d321/uuid_utils-0.13.0-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:775347c6110fb71360df17aac74132d8d47c1dbe71233ac98197fc872a791fd2", size = 615872, upload-time = "2026-01-08T15:47:50.61Z" }, + { url = "https://files.pythonhosted.org/packages/99/ed/15000c96a8bd8f5fd8efd622109bf52549ea0b366f8ce71c45580fa55878/uuid_utils-0.13.0-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:cf95f6370ad1a0910ee7b5ad5228fd19c4ae32fe3627389006adaf519408c41e", size = 581023, upload-time = "2026-01-08T15:47:52.776Z" }, + { url = "https://files.pythonhosted.org/packages/67/c8/3f809fa2dc2ca4bd331c792a3c7d3e45ae2b709d85847a12b8b27d1d5f19/uuid_utils-0.13.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5a88e23e0b2f4203fefe2ccbca5736ee06fcad10e61b5e7e39c8d7904bc13300", size = 546715, upload-time = "2026-01-08T15:47:54.415Z" }, + { url = "https://files.pythonhosted.org/packages/f5/80/4f7c7efd734d1494397c781bd3d421688e9c187ae836e3174625b1ddf8b0/uuid_utils-0.13.0-cp39-abi3-win32.whl", hash = "sha256:3e4f2cc54e6a99c0551158100ead528479ad2596847478cbad624977064ffce3", size = 177650, upload-time = "2026-01-08T15:47:55.679Z" }, + { url = "https://files.pythonhosted.org/packages/6c/94/d05ab68622e66ad787a241dfe5ccc649b3af09f30eae977b9ee8f7046aaa/uuid_utils-0.13.0-cp39-abi3-win_amd64.whl", hash = "sha256:046cb2756e1597b3de22d24851b769913e192135830486a0a70bf41327f0360c", size = 183211, upload-time = "2026-01-08T15:47:57.604Z" }, + { url = "https://files.pythonhosted.org/packages/69/37/674b3ce25cd715b831ea8ebbd828b74c40159f04c95d1bb963b2c876fe79/uuid_utils-0.13.0-cp39-abi3-win_arm64.whl", hash = "sha256:5447a680df6ef8a5a353976aaf4c97cc3a3a22b1ee13671c44227b921e3ae2a9", size = 183518, upload-time = "2026-01-08T15:47:59.148Z" }, + { url = "https://files.pythonhosted.org/packages/99/fa/1d92de9538463859228e68db679b766fd300770c9a2db849dcba0c0c5a57/uuid_utils-0.13.0-pp311-pypy311_pp73-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:e5182e2d95f38e65f2e5bce90648ef56987443da13e145afcd747e584f9bc69c", size = 587641, upload-time = "2026-01-08T15:48:00.433Z" }, + { url = "https://files.pythonhosted.org/packages/ca/07/6bd9e6f5367e38c2ee7178ad882d2bd1b0d17c5393974b09ab027a215eba/uuid_utils-0.13.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:e3909a8a1fbd79d7c8bdc874eeb83e23ccb7a7cb0aa821a49596cc96c0cce84b", size = 298273, upload-time = "2026-01-08T15:48:02.063Z" }, + { url = "https://files.pythonhosted.org/packages/dc/14/7061b868a8a6799c8df83768a23f313d4e22075069f01ee3c28fa82aa2c6/uuid_utils-0.13.0-pp311-pypy311_pp73-manylinux_2_24_aarch64.whl", hash = "sha256:5dc4c9f749bd2511b8dcbf0891e658d7d86880022963db050722ad7b502b5e22", size = 333618, upload-time = "2026-01-08T15:48:03.503Z" }, + { url = "https://files.pythonhosted.org/packages/bc/f1/f48c3c9c343c9071ade5f355403e344d817412d9cf379a2d04b181282e74/uuid_utils-0.13.0-pp311-pypy311_pp73-manylinux_2_24_armv7l.whl", hash = "sha256:516adf07f5b2cdb88d50f489c702b5f1a75ae8b2639bfd254f4192d5f7ee261f", size = 339104, upload-time = "2026-01-08T15:48:05.02Z" }, + { url = "https://files.pythonhosted.org/packages/47/22/8e3142b4baffee77ce533fe956446d3699ec42f1d5252911208cbef4501e/uuid_utils-0.13.0-pp311-pypy311_pp73-manylinux_2_24_i686.whl", hash = "sha256:aeee3bd89e8de6184a3ab778ce19f5ce9ad32849d1be549516e0ddb257562d8d", size = 359503, upload-time = "2026-01-08T15:48:06.347Z" }, + { url = "https://files.pythonhosted.org/packages/bd/1a/756f1f9e31b15019c87cd2becb1c596351c50967cd143443da38df8818d1/uuid_utils-0.13.0-pp311-pypy311_pp73-manylinux_2_24_ppc64le.whl", hash = "sha256:97985256c2e59b7caa51f5c8515f64d777328562a9c900ec65e9d627baf72737", size = 467480, upload-time = "2026-01-08T15:48:07.681Z" }, + { url = "https://files.pythonhosted.org/packages/0a/20/a6929e98d9a461ca49e96194a82a1cc3fd5420f3a2f53cbb34fca438549e/uuid_utils-0.13.0-pp311-pypy311_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:b7ccaa20e24c5f60f41a69ef571ed820737f9b0ade4cbeef56aaa8f80f5aa475", size = 333610, upload-time = "2026-01-08T15:48:09.375Z" }, ] [[package]] @@ -8066,16 +8332,16 @@ wheels = [ [[package]] name = "uvicorn" -version = "0.38.0" +version = "0.40.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, { name = "h11" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/cb/ce/f06b84e2697fef4688ca63bdb2fdf113ca0a3be33f94488f2cadb690b0cf/uvicorn-0.38.0.tar.gz", hash = "sha256:fd97093bdd120a2609fc0d3afe931d4d4ad688b6e75f0f929fde1bc36fe0e91d", size = 80605, upload-time = "2025-10-18T13:46:44.63Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/d1/8f3c683c9561a4e6689dd3b1d345c815f10f86acd044ee1fb9a4dcd0b8c5/uvicorn-0.40.0.tar.gz", hash = "sha256:839676675e87e73694518b5574fd0f24c9d97b46bea16df7b8c05ea1a51071ea", size = 81761, upload-time = "2025-12-21T14:16:22.45Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ee/d9/d88e73ca598f4f6ff671fb5fde8a32925c2e08a637303a1d12883c7305fa/uvicorn-0.38.0-py3-none-any.whl", hash = "sha256:48c0afd214ceb59340075b4a052ea1ee91c16fbc2a9b1469cca0e54566977b02", size = 68109, upload-time = "2025-10-18T13:46:42.958Z" }, + { url = "https://files.pythonhosted.org/packages/3d/d8/2083a1daa7439a66f3a48589a57d576aa117726762618f6bb09fe3798796/uvicorn-0.40.0-py3-none-any.whl", hash = "sha256:c6c8f55bc8bf13eb6fa9ff87ad62308bbbc33d0b67f84293151efe87e0d5f2ee", size = 68502, upload-time = "2025-12-21T14:16:21.041Z" }, ] [package.optional-dependencies] @@ -8135,7 +8401,7 @@ wheels = [ [[package]] name = "virtualenv" -version = "20.35.4" +version = "20.36.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "distlib" }, @@ -8143,9 +8409,9 @@ dependencies = [ { name = "platformdirs" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/20/28/e6f1a6f655d620846bd9df527390ecc26b3805a0c5989048c210e22c5ca9/virtualenv-20.35.4.tar.gz", hash = "sha256:643d3914d73d3eeb0c552cbb12d7e82adf0e504dbf86a3182f8771a153a1971c", size = 6028799, upload-time = "2025-10-29T06:57:40.511Z" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/a3/4d310fa5f00863544e1d0f4de93bddec248499ccf97d4791bc3122c9d4f3/virtualenv-20.36.1.tar.gz", hash = "sha256:8befb5c81842c641f8ee658481e42641c68b5eab3521d8e092d18320902466ba", size = 6032239, upload-time = "2026-01-09T18:21:01.296Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/79/0c/c05523fa3181fdf0c9c52a6ba91a23fbf3246cc095f26f6516f9c60e6771/virtualenv-20.35.4-py3-none-any.whl", hash = "sha256:c21c9cede36c9753eeade68ba7d523529f228a403463376cf821eaae2b650f1b", size = 6005095, upload-time = "2025-10-29T06:57:37.598Z" }, + { url = "https://files.pythonhosted.org/packages/6a/2a/dc2228b2888f51192c7dc766106cd475f1b768c10caaf9727659726f7391/virtualenv-20.36.1-py3-none-any.whl", hash = "sha256:575a8d6b124ef88f6f51d56d656132389f961062a9177016a50e4f507bbcc19f", size = 6008258, upload-time = "2026-01-09T18:20:59.425Z" }, ] [[package]] @@ -8324,61 +8590,70 @@ wheels = [ [[package]] name = "websockets" -version = "15.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/21/e6/26d09fab466b7ca9c7737474c52be4f76a40301b08362eb2dbc19dcc16c1/websockets-15.0.1.tar.gz", hash = "sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee", size = 177016, upload-time = "2025-03-05T20:03:41.606Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/da/6462a9f510c0c49837bbc9345aca92d767a56c1fb2939e1579df1e1cdcf7/websockets-15.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d63efaa0cd96cf0c5fe4d581521d9fa87744540d4bc999ae6e08595a1014b45b", size = 175423, upload-time = "2025-03-05T20:01:35.363Z" }, - { url = "https://files.pythonhosted.org/packages/1c/9f/9d11c1a4eb046a9e106483b9ff69bce7ac880443f00e5ce64261b47b07e7/websockets-15.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac60e3b188ec7574cb761b08d50fcedf9d77f1530352db4eef1707fe9dee7205", size = 173080, upload-time = "2025-03-05T20:01:37.304Z" }, - { url = "https://files.pythonhosted.org/packages/d5/4f/b462242432d93ea45f297b6179c7333dd0402b855a912a04e7fc61c0d71f/websockets-15.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5756779642579d902eed757b21b0164cd6fe338506a8083eb58af5c372e39d9a", size = 173329, upload-time = "2025-03-05T20:01:39.668Z" }, - { url = "https://files.pythonhosted.org/packages/6e/0c/6afa1f4644d7ed50284ac59cc70ef8abd44ccf7d45850d989ea7310538d0/websockets-15.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fdfe3e2a29e4db3659dbd5bbf04560cea53dd9610273917799f1cde46aa725e", size = 182312, upload-time = "2025-03-05T20:01:41.815Z" }, - { url = "https://files.pythonhosted.org/packages/dd/d4/ffc8bd1350b229ca7a4db2a3e1c482cf87cea1baccd0ef3e72bc720caeec/websockets-15.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c2529b320eb9e35af0fa3016c187dffb84a3ecc572bcee7c3ce302bfeba52bf", size = 181319, upload-time = "2025-03-05T20:01:43.967Z" }, - { url = "https://files.pythonhosted.org/packages/97/3a/5323a6bb94917af13bbb34009fac01e55c51dfde354f63692bf2533ffbc2/websockets-15.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac1e5c9054fe23226fb11e05a6e630837f074174c4c2f0fe442996112a6de4fb", size = 181631, upload-time = "2025-03-05T20:01:46.104Z" }, - { url = "https://files.pythonhosted.org/packages/a6/cc/1aeb0f7cee59ef065724041bb7ed667b6ab1eeffe5141696cccec2687b66/websockets-15.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5df592cd503496351d6dc14f7cdad49f268d8e618f80dce0cd5a36b93c3fc08d", size = 182016, upload-time = "2025-03-05T20:01:47.603Z" }, - { url = "https://files.pythonhosted.org/packages/79/f9/c86f8f7af208e4161a7f7e02774e9d0a81c632ae76db2ff22549e1718a51/websockets-15.0.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0a34631031a8f05657e8e90903e656959234f3a04552259458aac0b0f9ae6fd9", size = 181426, upload-time = "2025-03-05T20:01:48.949Z" }, - { url = "https://files.pythonhosted.org/packages/c7/b9/828b0bc6753db905b91df6ae477c0b14a141090df64fb17f8a9d7e3516cf/websockets-15.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3d00075aa65772e7ce9e990cab3ff1de702aa09be3940d1dc88d5abf1ab8a09c", size = 181360, upload-time = "2025-03-05T20:01:50.938Z" }, - { url = "https://files.pythonhosted.org/packages/89/fb/250f5533ec468ba6327055b7d98b9df056fb1ce623b8b6aaafb30b55d02e/websockets-15.0.1-cp310-cp310-win32.whl", hash = "sha256:1234d4ef35db82f5446dca8e35a7da7964d02c127b095e172e54397fb6a6c256", size = 176388, upload-time = "2025-03-05T20:01:52.213Z" }, - { url = "https://files.pythonhosted.org/packages/1c/46/aca7082012768bb98e5608f01658ff3ac8437e563eca41cf068bd5849a5e/websockets-15.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:39c1fec2c11dc8d89bba6b2bf1556af381611a173ac2b511cf7231622058af41", size = 176830, upload-time = "2025-03-05T20:01:53.922Z" }, - { url = "https://files.pythonhosted.org/packages/9f/32/18fcd5919c293a398db67443acd33fde142f283853076049824fc58e6f75/websockets-15.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:823c248b690b2fd9303ba00c4f66cd5e2d8c3ba4aa968b2779be9532a4dad431", size = 175423, upload-time = "2025-03-05T20:01:56.276Z" }, - { url = "https://files.pythonhosted.org/packages/76/70/ba1ad96b07869275ef42e2ce21f07a5b0148936688c2baf7e4a1f60d5058/websockets-15.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678999709e68425ae2593acf2e3ebcbcf2e69885a5ee78f9eb80e6e371f1bf57", size = 173082, upload-time = "2025-03-05T20:01:57.563Z" }, - { url = "https://files.pythonhosted.org/packages/86/f2/10b55821dd40eb696ce4704a87d57774696f9451108cff0d2824c97e0f97/websockets-15.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d50fd1ee42388dcfb2b3676132c78116490976f1300da28eb629272d5d93e905", size = 173330, upload-time = "2025-03-05T20:01:59.063Z" }, - { url = "https://files.pythonhosted.org/packages/a5/90/1c37ae8b8a113d3daf1065222b6af61cc44102da95388ac0018fcb7d93d9/websockets-15.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d99e5546bf73dbad5bf3547174cd6cb8ba7273062a23808ffea025ecb1cf8562", size = 182878, upload-time = "2025-03-05T20:02:00.305Z" }, - { url = "https://files.pythonhosted.org/packages/8e/8d/96e8e288b2a41dffafb78e8904ea7367ee4f891dafc2ab8d87e2124cb3d3/websockets-15.0.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:66dd88c918e3287efc22409d426c8f729688d89a0c587c88971a0faa2c2f3792", size = 181883, upload-time = "2025-03-05T20:02:03.148Z" }, - { url = "https://files.pythonhosted.org/packages/93/1f/5d6dbf551766308f6f50f8baf8e9860be6182911e8106da7a7f73785f4c4/websockets-15.0.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8dd8327c795b3e3f219760fa603dcae1dcc148172290a8ab15158cf85a953413", size = 182252, upload-time = "2025-03-05T20:02:05.29Z" }, - { url = "https://files.pythonhosted.org/packages/d4/78/2d4fed9123e6620cbf1706c0de8a1632e1a28e7774d94346d7de1bba2ca3/websockets-15.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8fdc51055e6ff4adeb88d58a11042ec9a5eae317a0a53d12c062c8a8865909e8", size = 182521, upload-time = "2025-03-05T20:02:07.458Z" }, - { url = "https://files.pythonhosted.org/packages/e7/3b/66d4c1b444dd1a9823c4a81f50231b921bab54eee2f69e70319b4e21f1ca/websockets-15.0.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:693f0192126df6c2327cce3baa7c06f2a117575e32ab2308f7f8216c29d9e2e3", size = 181958, upload-time = "2025-03-05T20:02:09.842Z" }, - { url = "https://files.pythonhosted.org/packages/08/ff/e9eed2ee5fed6f76fdd6032ca5cd38c57ca9661430bb3d5fb2872dc8703c/websockets-15.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:54479983bd5fb469c38f2f5c7e3a24f9a4e70594cd68cd1fa6b9340dadaff7cf", size = 181918, upload-time = "2025-03-05T20:02:11.968Z" }, - { url = "https://files.pythonhosted.org/packages/d8/75/994634a49b7e12532be6a42103597b71098fd25900f7437d6055ed39930a/websockets-15.0.1-cp311-cp311-win32.whl", hash = "sha256:16b6c1b3e57799b9d38427dda63edcbe4926352c47cf88588c0be4ace18dac85", size = 176388, upload-time = "2025-03-05T20:02:13.32Z" }, - { url = "https://files.pythonhosted.org/packages/98/93/e36c73f78400a65f5e236cd376713c34182e6663f6889cd45a4a04d8f203/websockets-15.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:27ccee0071a0e75d22cb35849b1db43f2ecd3e161041ac1ee9d2352ddf72f065", size = 176828, upload-time = "2025-03-05T20:02:14.585Z" }, - { url = "https://files.pythonhosted.org/packages/51/6b/4545a0d843594f5d0771e86463606a3988b5a09ca5123136f8a76580dd63/websockets-15.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3e90baa811a5d73f3ca0bcbf32064d663ed81318ab225ee4f427ad4e26e5aff3", size = 175437, upload-time = "2025-03-05T20:02:16.706Z" }, - { url = "https://files.pythonhosted.org/packages/f4/71/809a0f5f6a06522af902e0f2ea2757f71ead94610010cf570ab5c98e99ed/websockets-15.0.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:592f1a9fe869c778694f0aa806ba0374e97648ab57936f092fd9d87f8bc03665", size = 173096, upload-time = "2025-03-05T20:02:18.832Z" }, - { url = "https://files.pythonhosted.org/packages/3d/69/1a681dd6f02180916f116894181eab8b2e25b31e484c5d0eae637ec01f7c/websockets-15.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0701bc3cfcb9164d04a14b149fd74be7347a530ad3bbf15ab2c678a2cd3dd9a2", size = 173332, upload-time = "2025-03-05T20:02:20.187Z" }, - { url = "https://files.pythonhosted.org/packages/a6/02/0073b3952f5bce97eafbb35757f8d0d54812b6174ed8dd952aa08429bcc3/websockets-15.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8b56bdcdb4505c8078cb6c7157d9811a85790f2f2b3632c7d1462ab5783d215", size = 183152, upload-time = "2025-03-05T20:02:22.286Z" }, - { url = "https://files.pythonhosted.org/packages/74/45/c205c8480eafd114b428284840da0b1be9ffd0e4f87338dc95dc6ff961a1/websockets-15.0.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0af68c55afbd5f07986df82831c7bff04846928ea8d1fd7f30052638788bc9b5", size = 182096, upload-time = "2025-03-05T20:02:24.368Z" }, - { url = "https://files.pythonhosted.org/packages/14/8f/aa61f528fba38578ec553c145857a181384c72b98156f858ca5c8e82d9d3/websockets-15.0.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64dee438fed052b52e4f98f76c5790513235efaa1ef7f3f2192c392cd7c91b65", size = 182523, upload-time = "2025-03-05T20:02:25.669Z" }, - { url = "https://files.pythonhosted.org/packages/ec/6d/0267396610add5bc0d0d3e77f546d4cd287200804fe02323797de77dbce9/websockets-15.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d5f6b181bb38171a8ad1d6aa58a67a6aa9d4b38d0f8c5f496b9e42561dfc62fe", size = 182790, upload-time = "2025-03-05T20:02:26.99Z" }, - { url = "https://files.pythonhosted.org/packages/02/05/c68c5adbf679cf610ae2f74a9b871ae84564462955d991178f95a1ddb7dd/websockets-15.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5d54b09eba2bada6011aea5375542a157637b91029687eb4fdb2dab11059c1b4", size = 182165, upload-time = "2025-03-05T20:02:30.291Z" }, - { url = "https://files.pythonhosted.org/packages/29/93/bb672df7b2f5faac89761cb5fa34f5cec45a4026c383a4b5761c6cea5c16/websockets-15.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3be571a8b5afed347da347bfcf27ba12b069d9d7f42cb8c7028b5e98bbb12597", size = 182160, upload-time = "2025-03-05T20:02:31.634Z" }, - { url = "https://files.pythonhosted.org/packages/ff/83/de1f7709376dc3ca9b7eeb4b9a07b4526b14876b6d372a4dc62312bebee0/websockets-15.0.1-cp312-cp312-win32.whl", hash = "sha256:c338ffa0520bdb12fbc527265235639fb76e7bc7faafbb93f6ba80d9c06578a9", size = 176395, upload-time = "2025-03-05T20:02:33.017Z" }, - { url = "https://files.pythonhosted.org/packages/7d/71/abf2ebc3bbfa40f391ce1428c7168fb20582d0ff57019b69ea20fa698043/websockets-15.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcd5cf9e305d7b8338754470cf69cf81f420459dbae8a3b40cee57417f4614a7", size = 176841, upload-time = "2025-03-05T20:02:34.498Z" }, - { url = "https://files.pythonhosted.org/packages/cb/9f/51f0cf64471a9d2b4d0fc6c534f323b664e7095640c34562f5182e5a7195/websockets-15.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ee443ef070bb3b6ed74514f5efaa37a252af57c90eb33b956d35c8e9c10a1931", size = 175440, upload-time = "2025-03-05T20:02:36.695Z" }, - { url = "https://files.pythonhosted.org/packages/8a/05/aa116ec9943c718905997412c5989f7ed671bc0188ee2ba89520e8765d7b/websockets-15.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5a939de6b7b4e18ca683218320fc67ea886038265fd1ed30173f5ce3f8e85675", size = 173098, upload-time = "2025-03-05T20:02:37.985Z" }, - { url = "https://files.pythonhosted.org/packages/ff/0b/33cef55ff24f2d92924923c99926dcce78e7bd922d649467f0eda8368923/websockets-15.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:746ee8dba912cd6fc889a8147168991d50ed70447bf18bcda7039f7d2e3d9151", size = 173329, upload-time = "2025-03-05T20:02:39.298Z" }, - { url = "https://files.pythonhosted.org/packages/31/1d/063b25dcc01faa8fada1469bdf769de3768b7044eac9d41f734fd7b6ad6d/websockets-15.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:595b6c3969023ecf9041b2936ac3827e4623bfa3ccf007575f04c5a6aa318c22", size = 183111, upload-time = "2025-03-05T20:02:40.595Z" }, - { url = "https://files.pythonhosted.org/packages/93/53/9a87ee494a51bf63e4ec9241c1ccc4f7c2f45fff85d5bde2ff74fcb68b9e/websockets-15.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c714d2fc58b5ca3e285461a4cc0c9a66bd0e24c5da9911e30158286c9b5be7f", size = 182054, upload-time = "2025-03-05T20:02:41.926Z" }, - { url = "https://files.pythonhosted.org/packages/ff/b2/83a6ddf56cdcbad4e3d841fcc55d6ba7d19aeb89c50f24dd7e859ec0805f/websockets-15.0.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f3c1e2ab208db911594ae5b4f79addeb3501604a165019dd221c0bdcabe4db8", size = 182496, upload-time = "2025-03-05T20:02:43.304Z" }, - { url = "https://files.pythonhosted.org/packages/98/41/e7038944ed0abf34c45aa4635ba28136f06052e08fc2168520bb8b25149f/websockets-15.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:229cf1d3ca6c1804400b0a9790dc66528e08a6a1feec0d5040e8b9eb14422375", size = 182829, upload-time = "2025-03-05T20:02:48.812Z" }, - { url = "https://files.pythonhosted.org/packages/e0/17/de15b6158680c7623c6ef0db361da965ab25d813ae54fcfeae2e5b9ef910/websockets-15.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:756c56e867a90fb00177d530dca4b097dd753cde348448a1012ed6c5131f8b7d", size = 182217, upload-time = "2025-03-05T20:02:50.14Z" }, - { url = "https://files.pythonhosted.org/packages/33/2b/1f168cb6041853eef0362fb9554c3824367c5560cbdaad89ac40f8c2edfc/websockets-15.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:558d023b3df0bffe50a04e710bc87742de35060580a293c2a984299ed83bc4e4", size = 182195, upload-time = "2025-03-05T20:02:51.561Z" }, - { url = "https://files.pythonhosted.org/packages/86/eb/20b6cdf273913d0ad05a6a14aed4b9a85591c18a987a3d47f20fa13dcc47/websockets-15.0.1-cp313-cp313-win32.whl", hash = "sha256:ba9e56e8ceeeedb2e080147ba85ffcd5cd0711b89576b83784d8605a7df455fa", size = 176393, upload-time = "2025-03-05T20:02:53.814Z" }, - { url = "https://files.pythonhosted.org/packages/1b/6c/c65773d6cab416a64d191d6ee8a8b1c68a09970ea6909d16965d26bfed1e/websockets-15.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:e09473f095a819042ecb2ab9465aee615bd9c2028e4ef7d933600a8401c79561", size = 176837, upload-time = "2025-03-05T20:02:55.237Z" }, - { url = "https://files.pythonhosted.org/packages/02/9e/d40f779fa16f74d3468357197af8d6ad07e7c5a27ea1ca74ceb38986f77a/websockets-15.0.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0c9e74d766f2818bb95f84c25be4dea09841ac0f734d1966f415e4edfc4ef1c3", size = 173109, upload-time = "2025-03-05T20:03:17.769Z" }, - { url = "https://files.pythonhosted.org/packages/bc/cd/5b887b8585a593073fd92f7c23ecd3985cd2c3175025a91b0d69b0551372/websockets-15.0.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1009ee0c7739c08a0cd59de430d6de452a55e42d6b522de7aa15e6f67db0b8e1", size = 173343, upload-time = "2025-03-05T20:03:19.094Z" }, - { url = "https://files.pythonhosted.org/packages/fe/ae/d34f7556890341e900a95acf4886833646306269f899d58ad62f588bf410/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76d1f20b1c7a2fa82367e04982e708723ba0e7b8d43aa643d3dcd404d74f1475", size = 174599, upload-time = "2025-03-05T20:03:21.1Z" }, - { url = "https://files.pythonhosted.org/packages/71/e6/5fd43993a87db364ec60fc1d608273a1a465c0caba69176dd160e197ce42/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f29d80eb9a9263b8d109135351caf568cc3f80b9928bccde535c235de55c22d9", size = 174207, upload-time = "2025-03-05T20:03:23.221Z" }, - { url = "https://files.pythonhosted.org/packages/2b/fb/c492d6daa5ec067c2988ac80c61359ace5c4c674c532985ac5a123436cec/websockets-15.0.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b359ed09954d7c18bbc1680f380c7301f92c60bf924171629c5db97febb12f04", size = 174155, upload-time = "2025-03-05T20:03:25.321Z" }, - { url = "https://files.pythonhosted.org/packages/68/a1/dcb68430b1d00b698ae7a7e0194433bce4f07ded185f0ee5fb21e2a2e91e/websockets-15.0.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:cad21560da69f4ce7658ca2cb83138fb4cf695a2ba3e475e0559e05991aa8122", size = 176884, upload-time = "2025-03-05T20:03:27.934Z" }, - { url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743, upload-time = "2025-03-05T20:03:39.41Z" }, +version = "16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/04/24/4b2031d72e840ce4c1ccb255f693b15c334757fc50023e4db9537080b8c4/websockets-16.0.tar.gz", hash = "sha256:5f6261a5e56e8d5c42a4497b364ea24d94d9563e8fbd44e78ac40879c60179b5", size = 179346, upload-time = "2026-01-10T09:23:47.181Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/74/221f58decd852f4b59cc3354cccaf87e8ef695fede361d03dc9a7396573b/websockets-16.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:04cdd5d2d1dacbad0a7bf36ccbcd3ccd5a30ee188f2560b7a62a30d14107b31a", size = 177343, upload-time = "2026-01-10T09:22:21.28Z" }, + { url = "https://files.pythonhosted.org/packages/19/0f/22ef6107ee52ab7f0b710d55d36f5a5d3ef19e8a205541a6d7ffa7994e5a/websockets-16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8ff32bb86522a9e5e31439a58addbb0166f0204d64066fb955265c4e214160f0", size = 175021, upload-time = "2026-01-10T09:22:22.696Z" }, + { url = "https://files.pythonhosted.org/packages/10/40/904a4cb30d9b61c0e278899bf36342e9b0208eb3c470324a9ecbaac2a30f/websockets-16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:583b7c42688636f930688d712885cf1531326ee05effd982028212ccc13e5957", size = 175320, upload-time = "2026-01-10T09:22:23.94Z" }, + { url = "https://files.pythonhosted.org/packages/9d/2f/4b3ca7e106bc608744b1cdae041e005e446124bebb037b18799c2d356864/websockets-16.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7d837379b647c0c4c2355c2499723f82f1635fd2c26510e1f587d89bc2199e72", size = 183815, upload-time = "2026-01-10T09:22:25.469Z" }, + { url = "https://files.pythonhosted.org/packages/86/26/d40eaa2a46d4302becec8d15b0fc5e45bdde05191e7628405a19cf491ccd/websockets-16.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:df57afc692e517a85e65b72e165356ed1df12386ecb879ad5693be08fac65dde", size = 185054, upload-time = "2026-01-10T09:22:27.101Z" }, + { url = "https://files.pythonhosted.org/packages/b0/ba/6500a0efc94f7373ee8fefa8c271acdfd4dca8bd49a90d4be7ccabfc397e/websockets-16.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2b9f1e0d69bc60a4a87349d50c09a037a2607918746f07de04df9e43252c77a3", size = 184565, upload-time = "2026-01-10T09:22:28.293Z" }, + { url = "https://files.pythonhosted.org/packages/04/b4/96bf2cee7c8d8102389374a2616200574f5f01128d1082f44102140344cc/websockets-16.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:335c23addf3d5e6a8633f9f8eda77efad001671e80b95c491dd0924587ece0b3", size = 183848, upload-time = "2026-01-10T09:22:30.394Z" }, + { url = "https://files.pythonhosted.org/packages/02/8e/81f40fb00fd125357814e8c3025738fc4ffc3da4b6b4a4472a82ba304b41/websockets-16.0-cp310-cp310-win32.whl", hash = "sha256:37b31c1623c6605e4c00d466c9d633f9b812ea430c11c8a278774a1fde1acfa9", size = 178249, upload-time = "2026-01-10T09:22:32.083Z" }, + { url = "https://files.pythonhosted.org/packages/b4/5f/7e40efe8df57db9b91c88a43690ac66f7b7aa73a11aa6a66b927e44f26fa/websockets-16.0-cp310-cp310-win_amd64.whl", hash = "sha256:8e1dab317b6e77424356e11e99a432b7cb2f3ec8c5ab4dabbcee6add48f72b35", size = 178685, upload-time = "2026-01-10T09:22:33.345Z" }, + { url = "https://files.pythonhosted.org/packages/f2/db/de907251b4ff46ae804ad0409809504153b3f30984daf82a1d84a9875830/websockets-16.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:31a52addea25187bde0797a97d6fc3d2f92b6f72a9370792d65a6e84615ac8a8", size = 177340, upload-time = "2026-01-10T09:22:34.539Z" }, + { url = "https://files.pythonhosted.org/packages/f3/fa/abe89019d8d8815c8781e90d697dec52523fb8ebe308bf11664e8de1877e/websockets-16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:417b28978cdccab24f46400586d128366313e8a96312e4b9362a4af504f3bbad", size = 175022, upload-time = "2026-01-10T09:22:36.332Z" }, + { url = "https://files.pythonhosted.org/packages/58/5d/88ea17ed1ded2079358b40d31d48abe90a73c9e5819dbcde1606e991e2ad/websockets-16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:af80d74d4edfa3cb9ed973a0a5ba2b2a549371f8a741e0800cb07becdd20f23d", size = 175319, upload-time = "2026-01-10T09:22:37.602Z" }, + { url = "https://files.pythonhosted.org/packages/d2/ae/0ee92b33087a33632f37a635e11e1d99d429d3d323329675a6022312aac2/websockets-16.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:08d7af67b64d29823fed316505a89b86705f2b7981c07848fb5e3ea3020c1abe", size = 184631, upload-time = "2026-01-10T09:22:38.789Z" }, + { url = "https://files.pythonhosted.org/packages/c8/c5/27178df583b6c5b31b29f526ba2da5e2f864ecc79c99dae630a85d68c304/websockets-16.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7be95cfb0a4dae143eaed2bcba8ac23f4892d8971311f1b06f3c6b78952ee70b", size = 185870, upload-time = "2026-01-10T09:22:39.893Z" }, + { url = "https://files.pythonhosted.org/packages/87/05/536652aa84ddc1c018dbb7e2c4cbcd0db884580bf8e95aece7593fde526f/websockets-16.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d6297ce39ce5c2e6feb13c1a996a2ded3b6832155fcfc920265c76f24c7cceb5", size = 185361, upload-time = "2026-01-10T09:22:41.016Z" }, + { url = "https://files.pythonhosted.org/packages/6d/e2/d5332c90da12b1e01f06fb1b85c50cfc489783076547415bf9f0a659ec19/websockets-16.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1c1b30e4f497b0b354057f3467f56244c603a79c0d1dafce1d16c283c25f6e64", size = 184615, upload-time = "2026-01-10T09:22:42.442Z" }, + { url = "https://files.pythonhosted.org/packages/77/fb/d3f9576691cae9253b51555f841bc6600bf0a983a461c79500ace5a5b364/websockets-16.0-cp311-cp311-win32.whl", hash = "sha256:5f451484aeb5cafee1ccf789b1b66f535409d038c56966d6101740c1614b86c6", size = 178246, upload-time = "2026-01-10T09:22:43.654Z" }, + { url = "https://files.pythonhosted.org/packages/54/67/eaff76b3dbaf18dcddabc3b8c1dba50b483761cccff67793897945b37408/websockets-16.0-cp311-cp311-win_amd64.whl", hash = "sha256:8d7f0659570eefb578dacde98e24fb60af35350193e4f56e11190787bee77dac", size = 178684, upload-time = "2026-01-10T09:22:44.941Z" }, + { url = "https://files.pythonhosted.org/packages/84/7b/bac442e6b96c9d25092695578dda82403c77936104b5682307bd4deb1ad4/websockets-16.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:71c989cbf3254fbd5e84d3bff31e4da39c43f884e64f2551d14bb3c186230f00", size = 177365, upload-time = "2026-01-10T09:22:46.787Z" }, + { url = "https://files.pythonhosted.org/packages/b0/fe/136ccece61bd690d9c1f715baaeefd953bb2360134de73519d5df19d29ca/websockets-16.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8b6e209ffee39ff1b6d0fa7bfef6de950c60dfb91b8fcead17da4ee539121a79", size = 175038, upload-time = "2026-01-10T09:22:47.999Z" }, + { url = "https://files.pythonhosted.org/packages/40/1e/9771421ac2286eaab95b8575b0cb701ae3663abf8b5e1f64f1fd90d0a673/websockets-16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:86890e837d61574c92a97496d590968b23c2ef0aeb8a9bc9421d174cd378ae39", size = 175328, upload-time = "2026-01-10T09:22:49.809Z" }, + { url = "https://files.pythonhosted.org/packages/18/29/71729b4671f21e1eaa5d6573031ab810ad2936c8175f03f97f3ff164c802/websockets-16.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9b5aca38b67492ef518a8ab76851862488a478602229112c4b0d58d63a7a4d5c", size = 184915, upload-time = "2026-01-10T09:22:51.071Z" }, + { url = "https://files.pythonhosted.org/packages/97/bb/21c36b7dbbafc85d2d480cd65df02a1dc93bf76d97147605a8e27ff9409d/websockets-16.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e0334872c0a37b606418ac52f6ab9cfd17317ac26365f7f65e203e2d0d0d359f", size = 186152, upload-time = "2026-01-10T09:22:52.224Z" }, + { url = "https://files.pythonhosted.org/packages/4a/34/9bf8df0c0cf88fa7bfe36678dc7b02970c9a7d5e065a3099292db87b1be2/websockets-16.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a0b31e0b424cc6b5a04b8838bbaec1688834b2383256688cf47eb97412531da1", size = 185583, upload-time = "2026-01-10T09:22:53.443Z" }, + { url = "https://files.pythonhosted.org/packages/47/88/4dd516068e1a3d6ab3c7c183288404cd424a9a02d585efbac226cb61ff2d/websockets-16.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:485c49116d0af10ac698623c513c1cc01c9446c058a4e61e3bf6c19dff7335a2", size = 184880, upload-time = "2026-01-10T09:22:55.033Z" }, + { url = "https://files.pythonhosted.org/packages/91/d6/7d4553ad4bf1c0421e1ebd4b18de5d9098383b5caa1d937b63df8d04b565/websockets-16.0-cp312-cp312-win32.whl", hash = "sha256:eaded469f5e5b7294e2bdca0ab06becb6756ea86894a47806456089298813c89", size = 178261, upload-time = "2026-01-10T09:22:56.251Z" }, + { url = "https://files.pythonhosted.org/packages/c3/f0/f3a17365441ed1c27f850a80b2bc680a0fa9505d733fe152fdf5e98c1c0b/websockets-16.0-cp312-cp312-win_amd64.whl", hash = "sha256:5569417dc80977fc8c2d43a86f78e0a5a22fee17565d78621b6bb264a115d4ea", size = 178693, upload-time = "2026-01-10T09:22:57.478Z" }, + { url = "https://files.pythonhosted.org/packages/cc/9c/baa8456050d1c1b08dd0ec7346026668cbc6f145ab4e314d707bb845bf0d/websockets-16.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:878b336ac47938b474c8f982ac2f7266a540adc3fa4ad74ae96fea9823a02cc9", size = 177364, upload-time = "2026-01-10T09:22:59.333Z" }, + { url = "https://files.pythonhosted.org/packages/7e/0c/8811fc53e9bcff68fe7de2bcbe75116a8d959ac699a3200f4847a8925210/websockets-16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:52a0fec0e6c8d9a784c2c78276a48a2bdf099e4ccc2a4cad53b27718dbfd0230", size = 175039, upload-time = "2026-01-10T09:23:01.171Z" }, + { url = "https://files.pythonhosted.org/packages/aa/82/39a5f910cb99ec0b59e482971238c845af9220d3ab9fa76dd9162cda9d62/websockets-16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e6578ed5b6981005df1860a56e3617f14a6c307e6a71b4fff8c48fdc50f3ed2c", size = 175323, upload-time = "2026-01-10T09:23:02.341Z" }, + { url = "https://files.pythonhosted.org/packages/bd/28/0a25ee5342eb5d5f297d992a77e56892ecb65e7854c7898fb7d35e9b33bd/websockets-16.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:95724e638f0f9c350bb1c2b0a7ad0e83d9cc0c9259f3ea94e40d7b02a2179ae5", size = 184975, upload-time = "2026-01-10T09:23:03.756Z" }, + { url = "https://files.pythonhosted.org/packages/f9/66/27ea52741752f5107c2e41fda05e8395a682a1e11c4e592a809a90c6a506/websockets-16.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c0204dc62a89dc9d50d682412c10b3542d748260d743500a85c13cd1ee4bde82", size = 186203, upload-time = "2026-01-10T09:23:05.01Z" }, + { url = "https://files.pythonhosted.org/packages/37/e5/8e32857371406a757816a2b471939d51c463509be73fa538216ea52b792a/websockets-16.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:52ac480f44d32970d66763115edea932f1c5b1312de36df06d6b219f6741eed8", size = 185653, upload-time = "2026-01-10T09:23:06.301Z" }, + { url = "https://files.pythonhosted.org/packages/9b/67/f926bac29882894669368dc73f4da900fcdf47955d0a0185d60103df5737/websockets-16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6e5a82b677f8f6f59e8dfc34ec06ca6b5b48bc4fcda346acd093694cc2c24d8f", size = 184920, upload-time = "2026-01-10T09:23:07.492Z" }, + { url = "https://files.pythonhosted.org/packages/3c/a1/3d6ccdcd125b0a42a311bcd15a7f705d688f73b2a22d8cf1c0875d35d34a/websockets-16.0-cp313-cp313-win32.whl", hash = "sha256:abf050a199613f64c886ea10f38b47770a65154dc37181bfaff70c160f45315a", size = 178255, upload-time = "2026-01-10T09:23:09.245Z" }, + { url = "https://files.pythonhosted.org/packages/6b/ae/90366304d7c2ce80f9b826096a9e9048b4bb760e44d3b873bb272cba696b/websockets-16.0-cp313-cp313-win_amd64.whl", hash = "sha256:3425ac5cf448801335d6fdc7ae1eb22072055417a96cc6b31b3861f455fbc156", size = 178689, upload-time = "2026-01-10T09:23:10.483Z" }, + { url = "https://files.pythonhosted.org/packages/f3/1d/e88022630271f5bd349ed82417136281931e558d628dd52c4d8621b4a0b2/websockets-16.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8cc451a50f2aee53042ac52d2d053d08bf89bcb31ae799cb4487587661c038a0", size = 177406, upload-time = "2026-01-10T09:23:12.178Z" }, + { url = "https://files.pythonhosted.org/packages/f2/78/e63be1bf0724eeb4616efb1ae1c9044f7c3953b7957799abb5915bffd38e/websockets-16.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:daa3b6ff70a9241cf6c7fc9e949d41232d9d7d26fd3522b1ad2b4d62487e9904", size = 175085, upload-time = "2026-01-10T09:23:13.511Z" }, + { url = "https://files.pythonhosted.org/packages/bb/f4/d3c9220d818ee955ae390cf319a7c7a467beceb24f05ee7aaaa2414345ba/websockets-16.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:fd3cb4adb94a2a6e2b7c0d8d05cb94e6f1c81a0cf9dc2694fb65c7e8d94c42e4", size = 175328, upload-time = "2026-01-10T09:23:14.727Z" }, + { url = "https://files.pythonhosted.org/packages/63/bc/d3e208028de777087e6fb2b122051a6ff7bbcca0d6df9d9c2bf1dd869ae9/websockets-16.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:781caf5e8eee67f663126490c2f96f40906594cb86b408a703630f95550a8c3e", size = 185044, upload-time = "2026-01-10T09:23:15.939Z" }, + { url = "https://files.pythonhosted.org/packages/ad/6e/9a0927ac24bd33a0a9af834d89e0abc7cfd8e13bed17a86407a66773cc0e/websockets-16.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:caab51a72c51973ca21fa8a18bd8165e1a0183f1ac7066a182ff27107b71e1a4", size = 186279, upload-time = "2026-01-10T09:23:17.148Z" }, + { url = "https://files.pythonhosted.org/packages/b9/ca/bf1c68440d7a868180e11be653c85959502efd3a709323230314fda6e0b3/websockets-16.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:19c4dc84098e523fd63711e563077d39e90ec6702aff4b5d9e344a60cb3c0cb1", size = 185711, upload-time = "2026-01-10T09:23:18.372Z" }, + { url = "https://files.pythonhosted.org/packages/c4/f8/fdc34643a989561f217bb477cbc47a3a07212cbda91c0e4389c43c296ebf/websockets-16.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:a5e18a238a2b2249c9a9235466b90e96ae4795672598a58772dd806edc7ac6d3", size = 184982, upload-time = "2026-01-10T09:23:19.652Z" }, + { url = "https://files.pythonhosted.org/packages/dd/d1/574fa27e233764dbac9c52730d63fcf2823b16f0856b3329fc6268d6ae4f/websockets-16.0-cp314-cp314-win32.whl", hash = "sha256:a069d734c4a043182729edd3e9f247c3b2a4035415a9172fd0f1b71658a320a8", size = 177915, upload-time = "2026-01-10T09:23:21.458Z" }, + { url = "https://files.pythonhosted.org/packages/8a/f1/ae6b937bf3126b5134ce1f482365fde31a357c784ac51852978768b5eff4/websockets-16.0-cp314-cp314-win_amd64.whl", hash = "sha256:c0ee0e63f23914732c6d7e0cce24915c48f3f1512ec1d079ed01fc629dab269d", size = 178381, upload-time = "2026-01-10T09:23:22.715Z" }, + { url = "https://files.pythonhosted.org/packages/06/9b/f791d1db48403e1f0a27577a6beb37afae94254a8c6f08be4a23e4930bc0/websockets-16.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:a35539cacc3febb22b8f4d4a99cc79b104226a756aa7400adc722e83b0d03244", size = 177737, upload-time = "2026-01-10T09:23:24.523Z" }, + { url = "https://files.pythonhosted.org/packages/bd/40/53ad02341fa33b3ce489023f635367a4ac98b73570102ad2cdd770dacc9a/websockets-16.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:b784ca5de850f4ce93ec85d3269d24d4c82f22b7212023c974c401d4980ebc5e", size = 175268, upload-time = "2026-01-10T09:23:25.781Z" }, + { url = "https://files.pythonhosted.org/packages/74/9b/6158d4e459b984f949dcbbb0c5d270154c7618e11c01029b9bbd1bb4c4f9/websockets-16.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:569d01a4e7fba956c5ae4fc988f0d4e187900f5497ce46339c996dbf24f17641", size = 175486, upload-time = "2026-01-10T09:23:27.033Z" }, + { url = "https://files.pythonhosted.org/packages/e5/2d/7583b30208b639c8090206f95073646c2c9ffd66f44df967981a64f849ad/websockets-16.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:50f23cdd8343b984957e4077839841146f67a3d31ab0d00e6b824e74c5b2f6e8", size = 185331, upload-time = "2026-01-10T09:23:28.259Z" }, + { url = "https://files.pythonhosted.org/packages/45/b0/cce3784eb519b7b5ad680d14b9673a31ab8dcb7aad8b64d81709d2430aa8/websockets-16.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:152284a83a00c59b759697b7f9e9cddf4e3c7861dd0d964b472b70f78f89e80e", size = 186501, upload-time = "2026-01-10T09:23:29.449Z" }, + { url = "https://files.pythonhosted.org/packages/19/60/b8ebe4c7e89fb5f6cdf080623c9d92789a53636950f7abacfc33fe2b3135/websockets-16.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bc59589ab64b0022385f429b94697348a6a234e8ce22544e3681b2e9331b5944", size = 186062, upload-time = "2026-01-10T09:23:31.368Z" }, + { url = "https://files.pythonhosted.org/packages/88/a8/a080593f89b0138b6cba1b28f8df5673b5506f72879322288b031337c0b8/websockets-16.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:32da954ffa2814258030e5a57bc73a3635463238e797c7375dc8091327434206", size = 185356, upload-time = "2026-01-10T09:23:32.627Z" }, + { url = "https://files.pythonhosted.org/packages/c2/b6/b9afed2afadddaf5ebb2afa801abf4b0868f42f8539bfe4b071b5266c9fe/websockets-16.0-cp314-cp314t-win32.whl", hash = "sha256:5a4b4cc550cb665dd8a47f868c8d04c8230f857363ad3c9caf7a0c3bf8c61ca6", size = 178085, upload-time = "2026-01-10T09:23:33.816Z" }, + { url = "https://files.pythonhosted.org/packages/9f/3e/28135a24e384493fa804216b79a6a6759a38cc4ff59118787b9fb693df93/websockets-16.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b14dc141ed6d2dde437cddb216004bcac6a1df0935d79656387bd41632ba0bbd", size = 178531, upload-time = "2026-01-10T09:23:35.016Z" }, + { url = "https://files.pythonhosted.org/packages/72/07/c98a68571dcf256e74f1f816b8cc5eae6eb2d3d5cfa44d37f801619d9166/websockets-16.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:349f83cd6c9a415428ee1005cadb5c2c56f4389bc06a9af16103c3bc3dcc8b7d", size = 174947, upload-time = "2026-01-10T09:23:36.166Z" }, + { url = "https://files.pythonhosted.org/packages/7e/52/93e166a81e0305b33fe416338be92ae863563fe7bce446b0f687b9df5aea/websockets-16.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:4a1aba3340a8dca8db6eb5a7986157f52eb9e436b74813764241981ca4888f03", size = 175260, upload-time = "2026-01-10T09:23:37.409Z" }, + { url = "https://files.pythonhosted.org/packages/56/0c/2dbf513bafd24889d33de2ff0368190a0e69f37bcfa19009ef819fe4d507/websockets-16.0-pp311-pypy311_pp73-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f4a32d1bd841d4bcbffdcb3d2ce50c09c3909fbead375ab28d0181af89fd04da", size = 176071, upload-time = "2026-01-10T09:23:39.158Z" }, + { url = "https://files.pythonhosted.org/packages/a5/8f/aea9c71cc92bf9b6cc0f7f70df8f0b420636b6c96ef4feee1e16f80f75dd/websockets-16.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0298d07ee155e2e9fda5be8a9042200dd2e3bb0b8a38482156576f863a9d457c", size = 176968, upload-time = "2026-01-10T09:23:41.031Z" }, + { url = "https://files.pythonhosted.org/packages/9a/3f/f70e03f40ffc9a30d817eef7da1be72ee4956ba8d7255c399a01b135902a/websockets-16.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:a653aea902e0324b52f1613332ddf50b00c06fdaf7e92624fbf8c77c78fa5767", size = 178735, upload-time = "2026-01-10T09:23:42.259Z" }, + { url = "https://files.pythonhosted.org/packages/6f/28/258ebab549c2bf3e64d2b0217b973467394a9cea8c42f70418ca2c5d0d2e/websockets-16.0-py3-none-any.whl", hash = "sha256:1637db62fad1dc833276dded54215f2c7fa46912301a24bd94d45d46a011ceec", size = 171598, upload-time = "2026-01-10T09:23:45.395Z" }, ] [[package]] From 5e14de9d07f7a4d2c49b4b8f6b4ce4e23b344610 Mon Sep 17 00:00:00 2001 From: DAVID BOAZ Date: Wed, 14 Jan 2026 08:19:55 +0200 Subject: [PATCH 31/42] readme installation Signed-off-by: DAVID BOAZ --- altk/pre_tool/toolguard/README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/altk/pre_tool/toolguard/README.md b/altk/pre_tool/toolguard/README.md index 307aff2..44097ab 100644 --- a/altk/pre_tool/toolguard/README.md +++ b/altk/pre_tool/toolguard/README.md @@ -18,6 +18,11 @@ An agent lifecycle solution for enforcing business policy adherence in agentic w Business policies (or guidelines) are normally detailed in company documents, and have traditionally been hard-coded into automatic assistant platforms. Contemporary agentic approaches take the "best-effort" strategy, where the policies are appended to the agent's system prompt, an inherently non-deterministic approach, that does not scale effectively. Here we propose a deterministic, predictable and interpretable two-phase solution for agentic policy adherence at the tool-level: guards are executed prior to function invocation and raise alerts in case a tool-related policy deem violated. This component enforces **pre‑tool activation policy constraints**, ensuring that agent decisions comply with business rules **before** modifying system state. This prevents policy violations such as unauthorized tool calls or unsafe parameter values. +### Installation +``` +uv pip install "agent-lifecycle-toolkit[toolguard]" +``` + ## ToolGuardSpecComponent This component gets a set of tools and a policy document and generated multiple ToolGuard specifications, known as `ToolGuardSpec`s. Each specification is attached to a tool, and it declares a precondition that must apply before invoking the tool. The specification has a `name`, `description`, list of `refernces` to the original policy document, a set of declerative `compliance_examples`, describing test cases that the toolGuard should allow the tool invocation, and `violation_examples`, where the toolGuard should raise an exception. From 7201b893563c63e8a83ae5aede448d599bf68be6 Mon Sep 17 00:00:00 2001 From: DAVID BOAZ Date: Sun, 25 Jan 2026 10:10:59 +0200 Subject: [PATCH 32/42] fix async runtime Signed-off-by: DAVID BOAZ --- altk/pre_tool/toolguard/README.md | 2 +- .../toolguard/toolguard_code_component.py | 14 ++--- pyproject.toml | 2 +- .../pre_tool/toolguard/test_toolguard_code.py | 52 ++++++++++--------- .../toolguard/test_toolguard_specs.py | 2 +- 5 files changed, 38 insertions(+), 34 deletions(-) diff --git a/altk/pre_tool/toolguard/README.md b/altk/pre_tool/toolguard/README.md index 44097ab..c270489 100644 --- a/altk/pre_tool/toolguard/README.md +++ b/altk/pre_tool/toolguard/README.md @@ -100,7 +100,7 @@ The output of the build phase is a `ToolGuardsCodeGenerationResult` object with: * Each `ToolGuardCodeResult` details the name of guard Python file name and the guard function name. It also reference to the generated unit test files. #### Runtime phase -A running agent should use the runtime API to check if a tool call complies with the given policy. +A running agent should use the runtime async API to check if a tool call complies with the given policy. The input of the runtime phase is a `ToolGuardCodeRunInput` object: * `generated_guard_dir: str | Path`: Path in the local file system where the generated guard Python code (The code that was generated during the build time, described above) is located. * `tool_name: str`: The name of the tool that the agent is about to call diff --git a/altk/pre_tool/toolguard/toolguard_code_component.py b/altk/pre_tool/toolguard/toolguard_code_component.py index 98fb23e..0882450 100644 --- a/altk/pre_tool/toolguard/toolguard_code_component.py +++ b/altk/pre_tool/toolguard/toolguard_code_component.py @@ -8,7 +8,7 @@ from altk.core.toolkit import ComponentConfig, ComponentInput, AgentPhase, ComponentBase from toolguard.buildtime import ( - generate_guards_from_specs, + generate_guards_code, ToolGuardSpec, ToolGuardsCodeGenerationResult, ) @@ -90,7 +90,7 @@ async def _abuild( ) -> ToolGuardsCodeGenerationResult: config = cast(ToolGuardCodeComponentConfig, self.config) llm = TG_LLMClient(config.llm_client) - return await generate_guards_from_specs( + return await generate_guards_code( tools=data.tools, tool_specs=data.toolguard_specs, work_dir=data.out_dir, @@ -98,12 +98,17 @@ async def _abuild( ) def _run(self, data: ToolGuardCodeRunInput) -> ToolGuardCodeRunOutput: + raise NotImplementedError( + "Please use the _arun() function in an async context" + ) + + async def _arun(self, data: ToolGuardCodeRunInput) -> ToolGuardCodeRunOutput: code_root_dir = data.generated_guard_dir tool_name = data.tool_name tool_params = data.tool_args with load_toolguards(code_root_dir) as toolguards: try: - toolguards.check_toolcall(tool_name, tool_params, data.tool_invoker) + await toolguards.guard_toolcall(tool_name, tool_params, data.tool_invoker) return ToolGuardCodeRunOutput() except PolicyViolationException as e: return ToolGuardCodeRunOutput( @@ -111,6 +116,3 @@ def _run(self, data: ToolGuardCodeRunInput) -> ToolGuardCodeRunOutput: violation_level=ViolationLevel.ERROR, user_message=str(e) ) ) - - def _arun(self, data: ToolGuardCodeRunInput) -> ToolGuardCodeRunOutput: - return self._run(data) diff --git a/pyproject.toml b/pyproject.toml index 0e50093..1dc8f9f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -129,7 +129,7 @@ spotlight = [ ] toolguard = [ - "toolguard>=0.1.19" + "toolguard>=0.2.4" ] refraction = [ diff --git a/tests/pre_tool/toolguard/test_toolguard_code.py b/tests/pre_tool/toolguard/test_toolguard_code.py index 9d8a133..a6e011b 100644 --- a/tests/pre_tool/toolguard/test_toolguard_code.py +++ b/tests/pre_tool/toolguard/test_toolguard_code.py @@ -12,8 +12,9 @@ from datetime import datetime import os from pathlib import Path +import shutil from typing import Dict, cast - +import asyncio import dotenv import pytest @@ -53,15 +54,14 @@ def work_dir(): """Creates a temporary folder for test output and cleans it afterward.""" timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") - dir_path = str(Path(__file__).parent / "outputs" / f"work_{timestamp}") + dir_path = Path(__file__).parent / "outputs" / f"work_{timestamp}" print("Temporary work dir created:", dir_path) yield dir_path - # shutil.rmtree(dir_path) + shutil.rmtree(dir_path) # print("Temporary work dir removed:", dir_path) - def get_llm() -> BaseLLMClient: from altk.core.llm.providers.ibm_watsonx_ai.ibm_watsonx_ai import WatsonxLLMClient @@ -161,11 +161,11 @@ async def test_tool_guard_calculator_policy(work_dir: str): tool_invoker = ToolFunctionsInvoker(funcs) - def call(tool_name: str, args: Dict) -> ToolGuardCodeRunOutput: + async def call(tool_name: str, args: Dict) -> ToolGuardCodeRunOutput: """Executes a tool through its guard code.""" return cast( ToolGuardCodeRunOutput, - toolguard_code.process( + await toolguard_code.aprocess( ToolGuardCodeRunInput( generated_guard_dir=build_output.out_dir, tool_name=tool_name, @@ -176,32 +176,34 @@ def call(tool_name: str, args: Dict) -> ToolGuardCodeRunOutput: ), ) - def assert_complies(tool_name: str, args: Dict): + async def assert_complies(tool_name: str, args: Dict): """Asserts that no violation occurs.""" - assert call(tool_name, args).violation is None + assert (await call(tool_name, args)).violation is None - def assert_violates(tool_name: str, args: Dict): + async def assert_violates(tool_name: str, args: Dict): """Asserts that a violation occurs with level ERROR.""" - res = call(tool_name, args) + res = await call(tool_name, args) assert res.violation assert res.violation.violation_level == ViolationLevel.ERROR assert res.violation.user_message - # Valid input cases ----------------------------------------------------- - assert_complies("divide_tool", {"g": 5, "h": 4}) - assert_complies("add_tool", {"a": 5, "b": 4}) - assert_complies("subtract_tool", {"a": 5, "b": 4}) - assert_complies("multiply_tool", {"a": 5, "b": 4}) - assert_complies("map_kdi_number", {"i": 5}) - - # Violation cases ------------------------------------------------------- - assert_violates("divide_tool", {"g": 5, "h": 0}) - assert_violates("add_tool", {"a": 5, "b": 73}) - assert_violates("add_tool", {"a": 73, "b": 5}) - - # Violations for multiply_tool based on custom rules - assert_violates("multiply_tool", {"a": 2, "b": 73}) - assert_violates("multiply_tool", {"a": 22, "b": 2}) + await asyncio.gather(*[ + # Valid input cases ----------------------------------------------------- + assert_complies("divide_tool", {"g": 5, "h": 4}), + assert_complies("add_tool", {"a": 5, "b": 4}), + assert_complies("subtract_tool", {"a": 5, "b": 4}), + assert_complies("multiply_tool", {"a": 5, "b": 4}), + assert_complies("map_kdi_number", {"i": 5}), + + # Violation cases ------------------------------------------------------- + assert_violates("divide_tool", {"g": 5, "h": 0}), + assert_violates("add_tool", {"a": 5, "b": 73}), + assert_violates("add_tool", {"a": 73, "b": 5}), + + # Violations for multiply_tool based on custom rules + assert_violates("multiply_tool", {"a": 2, "b": 73}), + assert_violates("multiply_tool", {"a": 22, "b": 2}), + ]) # --------------------------------------------------------------------------- diff --git a/tests/pre_tool/toolguard/test_toolguard_specs.py b/tests/pre_tool/toolguard/test_toolguard_specs.py index ceae389..5630ed3 100644 --- a/tests/pre_tool/toolguard/test_toolguard_specs.py +++ b/tests/pre_tool/toolguard/test_toolguard_specs.py @@ -36,7 +36,7 @@ def out_dir(): Create a timestamped directory for test output, then delete it after the test. """ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") - dir_path = str(Path(__file__).parent / "outputs" / f"work_{timestamp}") + dir_path = Path(__file__).parent / "outputs" / f"work_{timestamp}" print("Temporary work dir created:", dir_path) yield dir_path From 228f8c3570e0d2c587c24851c42a62e843f084a4 Mon Sep 17 00:00:00 2001 From: DAVID BOAZ Date: Sun, 25 Jan 2026 10:25:44 +0200 Subject: [PATCH 33/42] fix reformat, and uv.lock --- .../toolguard/toolguard_code_component.py | 8 +-- .../pre_tool/toolguard/test_toolguard_code.py | 35 ++++++------- uv.lock | 50 +++++++++---------- 3 files changed, 47 insertions(+), 46 deletions(-) diff --git a/altk/pre_tool/toolguard/toolguard_code_component.py b/altk/pre_tool/toolguard/toolguard_code_component.py index 0882450..7053444 100644 --- a/altk/pre_tool/toolguard/toolguard_code_component.py +++ b/altk/pre_tool/toolguard/toolguard_code_component.py @@ -98,9 +98,7 @@ async def _abuild( ) def _run(self, data: ToolGuardCodeRunInput) -> ToolGuardCodeRunOutput: - raise NotImplementedError( - "Please use the _arun() function in an async context" - ) + raise NotImplementedError("Please use the _arun() function in an async context") async def _arun(self, data: ToolGuardCodeRunInput) -> ToolGuardCodeRunOutput: code_root_dir = data.generated_guard_dir @@ -108,7 +106,9 @@ async def _arun(self, data: ToolGuardCodeRunInput) -> ToolGuardCodeRunOutput: tool_params = data.tool_args with load_toolguards(code_root_dir) as toolguards: try: - await toolguards.guard_toolcall(tool_name, tool_params, data.tool_invoker) + await toolguards.guard_toolcall( + tool_name, tool_params, data.tool_invoker + ) return ToolGuardCodeRunOutput() except PolicyViolationException as e: return ToolGuardCodeRunOutput( diff --git a/tests/pre_tool/toolguard/test_toolguard_code.py b/tests/pre_tool/toolguard/test_toolguard_code.py index a6e011b..f559a3c 100644 --- a/tests/pre_tool/toolguard/test_toolguard_code.py +++ b/tests/pre_tool/toolguard/test_toolguard_code.py @@ -62,6 +62,7 @@ def work_dir(): shutil.rmtree(dir_path) # print("Temporary work dir removed:", dir_path) + def get_llm() -> BaseLLMClient: from altk.core.llm.providers.ibm_watsonx_ai.ibm_watsonx_ai import WatsonxLLMClient @@ -187,23 +188,23 @@ async def assert_violates(tool_name: str, args: Dict): assert res.violation.violation_level == ViolationLevel.ERROR assert res.violation.user_message - await asyncio.gather(*[ - # Valid input cases ----------------------------------------------------- - assert_complies("divide_tool", {"g": 5, "h": 4}), - assert_complies("add_tool", {"a": 5, "b": 4}), - assert_complies("subtract_tool", {"a": 5, "b": 4}), - assert_complies("multiply_tool", {"a": 5, "b": 4}), - assert_complies("map_kdi_number", {"i": 5}), - - # Violation cases ------------------------------------------------------- - assert_violates("divide_tool", {"g": 5, "h": 0}), - assert_violates("add_tool", {"a": 5, "b": 73}), - assert_violates("add_tool", {"a": 73, "b": 5}), - - # Violations for multiply_tool based on custom rules - assert_violates("multiply_tool", {"a": 2, "b": 73}), - assert_violates("multiply_tool", {"a": 22, "b": 2}), - ]) + await asyncio.gather( + *[ + # Valid input cases ----------------------------------------------------- + assert_complies("divide_tool", {"g": 5, "h": 4}), + assert_complies("add_tool", {"a": 5, "b": 4}), + assert_complies("subtract_tool", {"a": 5, "b": 4}), + assert_complies("multiply_tool", {"a": 5, "b": 4}), + assert_complies("map_kdi_number", {"i": 5}), + # Violation cases ------------------------------------------------------- + assert_violates("divide_tool", {"g": 5, "h": 0}), + assert_violates("add_tool", {"a": 5, "b": 73}), + assert_violates("add_tool", {"a": 73, "b": 5}), + # Violations for multiply_tool based on custom rules + assert_violates("multiply_tool", {"a": 2, "b": 73}), + assert_violates("multiply_tool", {"a": 22, "b": 2}), + ] + ) # --------------------------------------------------------------------------- diff --git a/uv.lock b/uv.lock index 02a4597..07beb6e 100644 --- a/uv.lock +++ b/uv.lock @@ -192,7 +192,7 @@ requires-dist = [ { name = "sentence-transformers", marker = "extra == 'routing'", specifier = ">=5.0.0" }, { name = "smolagents", specifier = ">=1.21.3" }, { name = "tomli", specifier = "==2.2.1" }, - { name = "toolguard", marker = "extra == 'toolguard'", specifier = ">=0.1.19" }, + { name = "toolguard", marker = "extra == 'toolguard'", specifier = ">=0.2.4" }, { name = "torch", marker = "platform_machine == 'x86_64' and sys_platform == 'darwin' and extra == 'spotlight'", specifier = "==2.2.2" }, { name = "torch", marker = "(platform_machine != 'x86_64' and extra == 'spotlight') or (sys_platform != 'darwin' and extra == 'spotlight')", specifier = ">=2.5.1" }, { name = "transformers", marker = "extra == 'spotlight'", specifier = ">=4.53.3" }, @@ -3428,7 +3428,7 @@ wheels = [ [[package]] name = "mellea" -version = "0.2.4" +version = "0.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "ansicolors" }, @@ -3452,9 +3452,9 @@ dependencies = [ { name = "types-tqdm" }, { name = "uvicorn" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/2f/8d/e9032cacc0b5f54236074a8e1a1333813db4f0613baea17e5d815305135b/mellea-0.2.4.tar.gz", hash = "sha256:973a20bf7a3f25a7e85a563d79a166b6bb8a12142d93a46f71df26632362db65", size = 208578, upload-time = "2026-01-08T17:25:40.282Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d5/e6/77f1de051b83e02ec923af3b8b5c7c6e9511a498d2b5b9658211c0985703/mellea-0.3.0.tar.gz", hash = "sha256:6b3e36e9e2a89a8ed4e9a2b3d59e6206865c5db81fa6ec565caec881444c6797", size = 217386, upload-time = "2026-01-21T19:25:25.167Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/96/b1482a8aec3419981e7f40eb9f7388a924c3023a77aaf032d39ddbe062ac/mellea-0.2.4-py3-none-any.whl", hash = "sha256:549b1b57f081a5837ea30692075ab29fd224a0600f4fa6da99b56be95b9c1759", size = 308071, upload-time = "2026-01-08T17:25:38.84Z" }, + { url = "https://files.pythonhosted.org/packages/38/2c/6105b27609ee16c092f54f220fc371f88af19fed5ec4fd78a713cf9e2081/mellea-0.3.0-py3-none-any.whl", hash = "sha256:bb70902a4efeafa5a0a9ae29cc39172d8979cfdf8646a2fc8ad592952cf495e4", size = 317487, upload-time = "2026-01-21T19:25:23.495Z" }, ] [[package]] @@ -4492,7 +4492,7 @@ name = "nvidia-cudnn-cu12" version = "9.10.2.21" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-cublas-cu12", marker = "platform_machine != 'x86_64' or sys_platform != 'darwin'" }, + { name = "nvidia-cublas-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/ba/51/e123d997aa098c61d029f76663dedbfb9bc8dcf8c60cbd6adbe42f76d049/nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:949452be657fa16687d0930933f032835951ef0892b37d2d53824d1a84dc97a8", size = 706758467, upload-time = "2025-06-06T21:54:08.597Z" }, @@ -4503,7 +4503,7 @@ name = "nvidia-cufft-cu12" version = "11.3.3.83" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-nvjitlink-cu12", marker = "platform_machine != 'x86_64' or sys_platform != 'darwin'" }, + { name = "nvidia-nvjitlink-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/1f/13/ee4e00f30e676b66ae65b4f08cb5bcbb8392c03f54f2d5413ea99a5d1c80/nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d2dd21ec0b88cf61b62e6b43564355e5222e4a3fb394cac0db101f2dd0d4f74", size = 193118695, upload-time = "2025-03-07T01:45:27.821Z" }, @@ -4530,9 +4530,9 @@ name = "nvidia-cusolver-cu12" version = "11.7.3.90" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-cublas-cu12", marker = "platform_machine != 'x86_64' or sys_platform != 'darwin'" }, - { name = "nvidia-cusparse-cu12", marker = "platform_machine != 'x86_64' or sys_platform != 'darwin'" }, - { name = "nvidia-nvjitlink-cu12", marker = "platform_machine != 'x86_64' or sys_platform != 'darwin'" }, + { name = "nvidia-cublas-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, + { name = "nvidia-cusparse-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, + { name = "nvidia-nvjitlink-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/85/48/9a13d2975803e8cf2777d5ed57b87a0b6ca2cc795f9a4f59796a910bfb80/nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:4376c11ad263152bd50ea295c05370360776f8c3427b30991df774f9fb26c450", size = 267506905, upload-time = "2025-03-07T01:47:16.273Z" }, @@ -4543,7 +4543,7 @@ name = "nvidia-cusparse-cu12" version = "12.5.8.93" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "nvidia-nvjitlink-cu12", marker = "platform_machine != 'x86_64' or sys_platform != 'darwin'" }, + { name = "nvidia-nvjitlink-cu12", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/c2/f5/e1854cb2f2bcd4280c44736c93550cc300ff4b8c95ebe370d0aa7d2b473d/nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ec05d76bbbd8b61b06a80e1eaf8cf4959c3d4ce8e711b65ebd0443bb0ebb13b", size = 288216466, upload-time = "2025-03-07T01:48:13.779Z" }, @@ -4603,9 +4603,9 @@ name = "ocrmac" version = "1.0.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "click" }, - { name = "pillow" }, - { name = "pyobjc-framework-vision" }, + { name = "click", marker = "sys_platform == 'darwin'" }, + { name = "pillow", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-vision", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/5e/07/3e15ab404f75875c5e48c47163300eb90b7409044d8711fc3aaf52503f2e/ocrmac-1.0.1.tar.gz", hash = "sha256:507fe5e4cbd67b2d03f6729a52bbc11f9d0b58241134eb958a5daafd4b9d93d9", size = 1454317, upload-time = "2026-01-08T16:44:26.412Z" } wheels = [ @@ -5965,7 +5965,7 @@ name = "pyobjc-framework-cocoa" version = "12.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/02/a3/16ca9a15e77c061a9250afbae2eae26f2e1579eb8ca9462ae2d2c71e1169/pyobjc_framework_cocoa-12.1.tar.gz", hash = "sha256:5556c87db95711b985d5efdaaf01c917ddd41d148b1e52a0c66b1a2e2c5c1640", size = 2772191, upload-time = "2025-11-14T10:13:02.069Z" } wheels = [ @@ -5983,8 +5983,8 @@ name = "pyobjc-framework-coreml" version = "12.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/30/2d/baa9ea02cbb1c200683cb7273b69b4bee5070e86f2060b77e6a27c2a9d7e/pyobjc_framework_coreml-12.1.tar.gz", hash = "sha256:0d1a4216891a18775c9e0170d908714c18e4f53f9dc79fb0f5263b2aa81609ba", size = 40465, upload-time = "2025-11-14T10:14:02.265Z" } wheels = [ @@ -6002,8 +6002,8 @@ name = "pyobjc-framework-quartz" version = "12.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/94/18/cc59f3d4355c9456fc945eae7fe8797003c4da99212dd531ad1b0de8a0c6/pyobjc_framework_quartz-12.1.tar.gz", hash = "sha256:27f782f3513ac88ec9b6c82d9767eef95a5cf4175ce88a1e5a65875fee799608", size = 3159099, upload-time = "2025-11-14T10:21:24.31Z" } wheels = [ @@ -6021,10 +6021,10 @@ name = "pyobjc-framework-vision" version = "12.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pyobjc-core" }, - { name = "pyobjc-framework-cocoa" }, - { name = "pyobjc-framework-coreml" }, - { name = "pyobjc-framework-quartz" }, + { name = "pyobjc-core", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-cocoa", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-coreml", marker = "sys_platform == 'darwin'" }, + { name = "pyobjc-framework-quartz", marker = "sys_platform == 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/c2/5a/08bb3e278f870443d226c141af14205ff41c0274da1e053b72b11dfc9fb2/pyobjc_framework_vision-12.1.tar.gz", hash = "sha256:a30959100e85dcede3a786c544e621ad6eb65ff6abf85721f805822b8c5fe9b0", size = 59538, upload-time = "2025-11-14T10:23:21.979Z" } wheels = [ @@ -7614,7 +7614,7 @@ wheels = [ [[package]] name = "toolguard" -version = "0.1.20" +version = "0.2.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "datamodel-code-generator" }, @@ -7628,9 +7628,9 @@ dependencies = [ { name = "pytest-json-report" }, { name = "smolagents" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0c/56/595f6b14d7b2284700e36bad656e0312969ba0a63c5be5576fa5362c514f/toolguard-0.1.20.tar.gz", hash = "sha256:71c2a238d489c010a317b09634aeae42bd5a5c22dcbbac44a6d379621ee37347", size = 55466, upload-time = "2026-01-11T15:38:25.164Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a7/52/46e5117bd01267aed8f6fea36f200b248c559b6cfc789aeff89fde3bd3d2/toolguard-0.2.4.tar.gz", hash = "sha256:d2677477b732825c4453fc69dcaffcf00320182ecbf474998030b8fde79d8d01", size = 56164, upload-time = "2026-01-22T16:36:07.134Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/6c/1d/ec3f7c34994881c02ed9865bda1564aeef66c86089dd357a5a6d156467f4/toolguard-0.1.20-py3-none-any.whl", hash = "sha256:04c57ad38bed3aa0021e65ca5f14f80cb7366c914df1cb744fa11ff931f53103", size = 86701, upload-time = "2026-01-11T15:38:23.695Z" }, + { url = "https://files.pythonhosted.org/packages/47/94/5ba7c1c587bd2b83ecf096aaee002f1c5246a4f7677fed5a06df54044cf6/toolguard-0.2.4-py3-none-any.whl", hash = "sha256:00fbe66da50c2859ca383139ad263a2af9b0bfa2900e507816f73c0f58462cdf", size = 86729, upload-time = "2026-01-22T16:36:05.772Z" }, ] [[package]] From 4b474234087c1a306ef78e351f4e26bb833762f4 Mon Sep 17 00:00:00 2001 From: DAVID BOAZ Date: Sun, 25 Jan 2026 10:30:55 +0200 Subject: [PATCH 34/42] DCO Remediation Commit for DAVID BOAZ I, DAVID BOAZ , hereby add my Signed-off-by to this commit: 228f8c3570e0d2c587c24851c42a62e843f084a4 Signed-off-by: DAVID BOAZ --- altk/pre_tool/toolguard/toolguard_spec_component.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/altk/pre_tool/toolguard/toolguard_spec_component.py b/altk/pre_tool/toolguard/toolguard_spec_component.py index 3c0bf6e..86dd214 100644 --- a/altk/pre_tool/toolguard/toolguard_spec_component.py +++ b/altk/pre_tool/toolguard/toolguard_spec_component.py @@ -7,7 +7,7 @@ from altk.pre_tool.toolguard.llm_client import TG_LLMClient -from ...core.toolkit import AgentPhase, ComponentBase, ComponentConfig, ComponentInput +from altk.core.toolkit import AgentPhase, ComponentBase, ComponentConfig, ComponentInput from toolguard.buildtime import ToolGuardSpec, generate_guard_specs logger = logging.getLogger(__name__) From e02f4bb8c885d587d21ecbd263ecf0a1af022f1e Mon Sep 17 00:00:00 2001 From: DAVID BOAZ Date: Thu, 29 Jan 2026 12:18:13 +0200 Subject: [PATCH 35/42] fix toolguard code component --- altk/pre_tool/toolguard/README.md | 51 +- .../toolguard/toolguard_code_component.py | 86 ++- pyproject.toml | 2 +- .../pre_tool/toolguard/test_toolguard_code.py | 8 +- uv.lock | 644 +++++++++++++++++- 5 files changed, 741 insertions(+), 50 deletions(-) diff --git a/altk/pre_tool/toolguard/README.md b/altk/pre_tool/toolguard/README.md index c270489..ffa4702 100644 --- a/altk/pre_tool/toolguard/README.md +++ b/altk/pre_tool/toolguard/README.md @@ -1,14 +1,14 @@ # ToolGuards for Enforcing Agentic Policy Adherence -An agent lifecycle solution for enforcing business policy adherence in agentic workflows. Enabling this component has demonstrated up to a **20‑point improvement** in end‑to‑end agent accuracy when invoking tools. This work is described in [EMNLP 2025 Towards Enforcing Company Policy Adherence in Agentic Workflows](https://arxiv.org/pdf/2507.16459), and is publiched in [this GitHub library](https://github.com/AgentToolkit/toolguard). +An agent lifecycle solution for enforcing business policy adherence in agentic workflows. Enabling this component has demonstrated up to a **20‑point improvement** in end‑to‑end agent accuracy when invoking tools. This work is described in [EMNLP 2025 Towards Enforcing Company Policy Adherence in Agentic Workflows](https://arxiv.org/pdf/2507.16459), and is published in [this GitHub library](https://github.com/AgentToolkit/toolguard). ## Table of Contents - [Overview](#overview) - [ToolGuardSpecComponent](#ToolGuardSpecComponent) - - [Configuarion](#component-configuarion) + - [Configuration](#component-configuration) - [Inputs and Outputs](#input-and-output) - [Usage example](#usage-example) - [ToolGuardCodeComponent](#ToolGuardCodeComponent) - - [Configuarion](#component-configuarion-1) + - [Configuration](#component-configuration-1) - [Inputs and Outputs](#input-and-output-1) - [Usage example](#usage-example-1) @@ -24,15 +24,15 @@ uv pip install "agent-lifecycle-toolkit[toolguard]" ``` ## ToolGuardSpecComponent -This component gets a set of tools and a policy document and generated multiple ToolGuard specifications, known as `ToolGuardSpec`s. Each specification is attached to a tool, and it declares a precondition that must apply before invoking the tool. The specification has a `name`, `description`, list of `refernces` to the original policy document, a set of declerative `compliance_examples`, describing test cases that the toolGuard should allow the tool invocation, and `violation_examples`, where the toolGuard should raise an exception. +This component gets a set of tools and a policy document and generates multiple ToolGuard specifications, known as `ToolGuardSpec`s. Each specification is attached to a tool, and it declares a precondition that must apply before invoking the tool. The specification has a `name`, `description`, list of `references` to the original policy document, a set of declarative `compliance_examples`, describing test cases that the toolGuard should allow the tool invocation, and `violation_examples`, where the toolGuard should raise an exception. -This componenet supports only a `build` phase. The generate specifications are returned as output, and are also saved to a specified file system directory. -The specifications are aimed to be used as input into our next component - the `ToolGuardCodeComponent` described below. +This component supports only a `build` phase. The generated specifications are returned as output, and are also saved to a specified file system directory. +The specifications are aimed to be used as input into our next component - the `ToolGuardCodeComponent` described below. -The two components are not concatenated by design. As the geneartion involves a non-deterministic language model, the results need to be reviewed by a human. Hence, the output specification files should be reviewed and optionaly edited. For example, removing a wrong compliance example. +The two components are not concatenated by design. As the generation involves a non-deterministic language model, the results need to be reviewed by a human. Hence, the output specification files should be reviewed and optionally edited. For example, removing a wrong compliance example. -### Component Configuarion -This component expects an LLM client configuarion. Here is a concerete example using WatsonX SDK: +### Component Configuration +This component expects an LLM client configuration. Here is a concrete example using WatsonX SDK: ```python from altk.core.llm.providers.ibm_watsonx_ai.ibm_watsonx_ai import WatsonxLLMClient llm_client = WatsonxLLMClient( @@ -48,7 +48,7 @@ llm_client = WatsonxLLMClient( The component build input is a `ToolGuardSpecBuildInput` object containing the following fields: * `policy_text: str`: Text of the policy document * `tools: List[Callable] | List[BaseTool] | str`: List of available tools. Either as Python functions, methods, Langgraph Tools, or a path to an Open API specification file. - * `out_dir: str`: A directory in the local file system where the specification objects will be saved. + * `out_dir: str`: A directory in the local file system where the specification objects will be saved. The component build output is a list of `ToolGuardSpec`, as described above. @@ -58,18 +58,18 @@ see [simple calculator test](../../../tests/pre_tool/toolguard/test_toolguard_sp ## ToolGuardCodeComponent -This components enfoorces policy adherence through a two-phase process: +This component enforces policy adherence through a two-phase process: (1) **Buildtime**: Given a set of `ToolGuardSpec`s, generates policy validation code - `ToolGuard`s. Similar to ToolGuard Specifications, generated `ToolGuards` are a good start, but they may contain errors. Hence, they should be also reviewed by a human. -(2) **Runtime**: ToolGuards are deployed within the agent's flow, and are triggered before agent's tool invocation. They can be deployed into the agent loop, or in an MCP Gateway. -The ToolGuards checks if a planned action complies with the policy. If it violates, the agent is prompted to self-reflect and revise its plan before proceeding. +(2) **Runtime**: ToolGuards are deployed within the agent's flow, and are triggered before agent's tool invocation. They can be deployed into the agent loop, or in an MCP Gateway. +The ToolGuards check if a planned action complies with the policy. If it violates, the agent is prompted to self-reflect and revise its plan before proceeding. -### Component Configuarion +### Component Configuration -This component expects an LLM client configuarion. +This component expects an LLM client configuration. Here is an example using a Watsonx LLM client: ``` from altk.core.llm.providers.ibm_watsonx_ai.ibm_watsonx_ai import WatsonxLLMClient @@ -87,17 +87,18 @@ toolguard_code_component = ToolGuardCodeComponent(config) ### Input and Output The Component has two phases: #### Build phase -An agent owner should use this API to generate ToolGuards - Python function that enforce the given business policy. +An agent owner should use this API to generate ToolGuards - Python functions that enforce the given business policy. The input of the build phase is a `ToolGuardCodeBuildInput` object, containing: * `tools: List[Callable] | List[BaseTool] | str`: List of available tools. Either as Python functions, methods, Langgraph Tools, or a path to an Open API specification file. - * `toolguard_specs: List[ToolGuardSpec]`: List of specifications, optionaly generated by `ToolGuardSpecComponent` component and reviewed. - * `out_dir: str | Path`: A directory in the local file system where the ToolGuard objects will be saved. + * `toolguard_specs: List[ToolGuardSpec]`: List of specifications, optionally generated by `ToolGuardSpecComponent` component and reviewed. + * `app_name: str`: Name of the application for which guards are being generated. This will be namespace of the guards generated code. + * `out_dir: str | Path`: A directory in the local file system where the ToolGuard objects will be saved. The output of the build phase is a `ToolGuardsCodeGenerationResult` object with: * `out_dir: Path`: Path to the file system where the results were saved. It is the same as the `input.out_dir`. - * `domain: RuntimeDomain`: A complex object descibing the generated APIs. For example, refernces to Python file names and class names. - * `tools: Dict[str, ToolGuardCodeResult]`: A Dictionary of the ToolGuardsResults, by the tool names. - * Each `ToolGuardCodeResult` details the name of guard Python file name and the guard function name. It also reference to the generated unit test files. + * `domain: RuntimeDomain`: A complex object describing the generated APIs. For example, references to Python file names and class names. + * `tools: Dict[str, ToolGuardCodeResult]`: A Dictionary of the ToolGuardsResults, by the tool names. + * Each `ToolGuardCodeResult` details the name of guard Python file name and the guard function name. It also references the generated unit test files. #### Runtime phase A running agent should use the runtime async API to check if a tool call complies with the given policy. @@ -106,7 +107,7 @@ The input of the runtime phase is a `ToolGuardCodeRunInput` object: * `tool_name: str`: The name of the tool that the agent is about to call * `tool_args: Dict[str, Any]`: A dictionary of the toolcall arguments, by the argument name. * `tool_invoker: IToolInvoker`: A proxy object that enables the guard to call other read-only tools. This is needed when the policy enforcement logic involves getting data from another tool. For example, before booking a flight, you need to check the flight status by calling the "get_flight_status" API. - The `IToolInvoker` interface contains a single method: + The `IToolInvoker` interface contains a single method: ``` def invoke(self, toolname: str, arguments: Dict[str, Any], return_type: Type[T]) -> T ``` @@ -115,10 +116,10 @@ The input of the runtime phase is a `ToolGuardCodeRunInput` object: * `toolguard.runtime.ToolFunctionsInvoker(funcs: List[Callable])` where the tools are defined as plain global Python functions. * `toolguard.runtime.ToolMethodsInvoker(obj: object)` where the tools are defined as methods in a given Python object. * `toolguard.runtime.LangchainToolInvoker(tools: List[BaseTool])` where the tools are a list of langchain tools. - -The outpput of the runtime phase is a `ToolGuardCodeRunOutput` object with an optional `violation` field. - * `violation: PolicyViolation | None`: Polpulated only if a violation was identified. If the toolcall complies with the policy, the violation is None. + +The output of the runtime phase is a `ToolGuardCodeRunOutput` object with an optional `violation` field. + * `violation: PolicyViolation | None`: Populated only if a violation was identified. If the toolcall complies with the policy, the violation is None. * `violation_level: "info" | "warn" | "error"`: Severity level of a safety violation. * `user_message: str | None`: A meaningful error message to the user (this message can be also passed to the agent reasoning phase to find an alternative next action). diff --git a/altk/pre_tool/toolguard/toolguard_code_component.py b/altk/pre_tool/toolguard/toolguard_code_component.py index 7053444..3f18e35 100644 --- a/altk/pre_tool/toolguard/toolguard_code_component.py +++ b/altk/pre_tool/toolguard/toolguard_code_component.py @@ -1,31 +1,50 @@ import logging -from pathlib import Path -from typing import Any, Callable, Dict, List, cast from enum import Enum -from pydantic import BaseModel, Field -from typing import Set -from langchain_core.tools import BaseTool +from pathlib import Path +from typing import Any, Callable, Dict, List, Set, cast -from altk.core.toolkit import ComponentConfig, ComponentInput, AgentPhase, ComponentBase +from langchain_core.tools import BaseTool +from pydantic import BaseModel, Field from toolguard.buildtime import ( - generate_guards_code, - ToolGuardSpec, ToolGuardsCodeGenerationResult, + ToolGuardSpec, + generate_guards_code, ) -from toolguard.runtime import IToolInvoker, load_toolguards, PolicyViolationException +from toolguard.buildtime.buildtime import OpenAPI +from toolguard.buildtime.data_types import TOOLS +from toolguard.extra.langchain_to_oas import langchain_tools_to_openapi +from toolguard.runtime import IToolInvoker, PolicyViolationException, load_toolguards +from altk.core.toolkit import AgentPhase, ComponentBase, ComponentConfig, ComponentInput from altk.pre_tool.toolguard.llm_client import TG_LLMClient logger = logging.getLogger(__name__) class ToolGuardCodeComponentConfig(ComponentConfig): + """Configuration for ToolGuardCodeComponent. + + This component enforces policy adherence through code generation and runtime validation. + It requires an LLM client configuration for generating ToolGuard code from specifications. + + Inherits all configuration from ComponentConfig, including llm_client settings. + """ + pass class ToolGuardCodeBuildInput(ComponentInput): + """Input configuration for building ToolGuard code generation. + + :param tools: List of callable functions, BaseTool instances, or string path to OpenAPI spec + :param toolguard_specs: List of ToolGuard specifications to generate guards from. results from `toolguard_spec_component` component. + :param app_name: Name of the application for which guards are being generated. This will be namespace of the guards generated code. + :param out_dir: Output directory path where generated guard code will be saved + """ + tools: List[Callable] | List[BaseTool] | str toolguard_specs: List[ToolGuardSpec] + app_name: str out_dir: str | Path @@ -33,6 +52,16 @@ class ToolGuardCodeBuildInput(ComponentInput): class ToolGuardCodeRunInput(ComponentInput): + """Input configuration for running ToolGuard code validation at runtime. + + A running agent uses this input to check if a tool call complies with the given policy. + + :param generated_guard_dir: Path in the local file system where the generated guard Python code (generated during build time) is located + :param tool_name: The name of the tool that the agent is about to call + :param tool_args: A dictionary of the tool call arguments, by the argument name + :param tool_invoker: A proxy object that enables the guard to call other read-only tools. This is needed when the policy enforcement logic involves getting data from another tool + """ + generated_guard_dir: str | Path tool_name: str = Field(description="Tool name") tool_args: Dict[str, Any] = Field(default={}, description="Tool arguments") @@ -68,10 +97,29 @@ class PolicyViolation(BaseModel): class ToolGuardCodeRunOutput(BaseModel): + """Output from ToolGuard code validation at runtime. + + Contains information about policy violations detected during tool call validation. + If the tool call complies with the policy, the violation field is None. + + :param violation: Populated only if a violation was identified. Contains violation level and user message + """ + violation: PolicyViolation | None = None class ToolGuardCodeComponent(ComponentBase): + """Component for enforcing policy adherence through a two-phase process. + + This component enforces policy adherence through code generation and runtime validation: + + (1) **Buildtime**: Given a set of ToolGuardSpecs, generates policy validation code - ToolGuards. + + (2) **Runtime**: ToolGuards are deployed within the agent's flow, and are triggered before + agent's tool invocation. They can be deployed into the agent loop, or in an MCP Gateway. + The ToolGuards check if a planned action complies with the policy. + """ + def __init__(self, config: ToolGuardCodeComponentConfig): super().__init__(config=config) @@ -91,12 +139,30 @@ async def _abuild( config = cast(ToolGuardCodeComponentConfig, self.config) llm = TG_LLMClient(config.llm_client) return await generate_guards_code( - tools=data.tools, + tools=self._to_tools(data.tools), tool_specs=data.toolguard_specs, work_dir=data.out_dir, llm=llm, + app_name=data.app_name, ) + def _to_tools(self, tools) -> TOOLS: + if isinstance(tools, str): + try: + return OpenAPI.load_from(tools).model_dump() + except Exception as e: + raise ValueError(f"Invalid OpenAPI spec file: {e}") from e + + elif isinstance(tools, list): + if all(isinstance(tool, Callable) for tool in tools): + return cast(List[Callable], tools) + elif all(isinstance(tool, BaseTool) for tool in tools): + return langchain_tools_to_openapi(tools) + else: + raise ValueError("Invalid tools list") + + raise ValueError("Invalid tools input") + def _run(self, data: ToolGuardCodeRunInput) -> ToolGuardCodeRunOutput: raise NotImplementedError("Please use the _arun() function in an async context") diff --git a/pyproject.toml b/pyproject.toml index 1dc8f9f..7b820cf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -129,7 +129,7 @@ spotlight = [ ] toolguard = [ - "toolguard>=0.2.4" + "toolguard>=0.2.6" ] refraction = [ diff --git a/tests/pre_tool/toolguard/test_toolguard_code.py b/tests/pre_tool/toolguard/test_toolguard_code.py index f559a3c..40934f5 100644 --- a/tests/pre_tool/toolguard/test_toolguard_code.py +++ b/tests/pre_tool/toolguard/test_toolguard_code.py @@ -123,16 +123,14 @@ async def test_tool_guard_calculator_policy(work_dir: str): specs = [ToolGuardSpec.load(policy_dir / f"{tool.__name__}.json") for tool in funcs] # Prepare build input for guard code generation - input = ToolGuardCodeBuildInput( - tools=funcs, - out_dir=work_dir, - toolguard_specs=specs, + build_input = ToolGuardCodeBuildInput( + tools=funcs, out_dir=work_dir, toolguard_specs=specs, app_name="calc" ) # Toolguarg code generation build_output = cast( ToolGuardsCodeGenerationResult, - await toolguard_code.aprocess(input, AgentPhase.BUILDTIME), + await toolguard_code.aprocess(build_input, AgentPhase.BUILDTIME), ) # build_output = load_toolguard_code_result("tests/pre_tool/toolguard/outputs/work_XXX") diff --git a/uv.lock b/uv.lock index 07beb6e..d619e60 100644 --- a/uv.lock +++ b/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 2 +revision = 3 requires-python = ">=3.10" resolution-markers = [ "python_full_version >= '3.14' and platform_machine == 'x86_64' and sys_platform == 'darwin'", @@ -192,7 +192,7 @@ requires-dist = [ { name = "sentence-transformers", marker = "extra == 'routing'", specifier = ">=5.0.0" }, { name = "smolagents", specifier = ">=1.21.3" }, { name = "tomli", specifier = "==2.2.1" }, - { name = "toolguard", marker = "extra == 'toolguard'", specifier = ">=0.2.4" }, + { name = "toolguard", marker = "extra == 'toolguard'", specifier = ">=0.2.6" }, { name = "torch", marker = "platform_machine == 'x86_64' and sys_platform == 'darwin' and extra == 'spotlight'", specifier = "==2.2.2" }, { name = "torch", marker = "(platform_machine != 'x86_64' and extra == 'spotlight') or (sys_platform != 'darwin' and extra == 'spotlight')", specifier = ">=2.5.1" }, { name = "transformers", marker = "extra == 'spotlight'", specifier = ">=4.53.3" }, @@ -481,6 +481,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373", size = 67615, upload-time = "2025-10-06T13:54:43.17Z" }, ] +[[package]] +name = "authlib" +version = "1.6.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cryptography" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bb/9b/b1661026ff24bc641b76b78c5222d614776b0c085bcfdac9bd15a1cb4b35/authlib-1.6.6.tar.gz", hash = "sha256:45770e8e056d0f283451d9996fbb59b70d45722b45d854d58f32878d0a40c38e", size = 164894, upload-time = "2025-12-12T08:01:41.464Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/51/321e821856452f7386c4e9df866f196720b1ad0c5ea1623ea7399969ae3b/authlib-1.6.6-py2.py3-none-any.whl", hash = "sha256:7d9e9bc535c13974313a87f53e8430eb6ea3d1cf6ae4f6efcd793f2e949143fd", size = 244005, upload-time = "2025-12-12T08:01:40.209Z" }, +] + [[package]] name = "babel" version = "2.17.0" @@ -517,6 +529,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d6/50/56cf20e2ee5127b603b81d5a69580a1a325083e2b921aa8f067da83927c0/backports_strenum-1.3.1-py3-none-any.whl", hash = "sha256:cdcfe36dc897e2615dc793b7d3097f54d359918fc448754a517e6f23044ccf83", size = 8304, upload-time = "2023-12-09T14:36:39.905Z" }, ] +[[package]] +name = "backports-tarfile" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/86/72/cd9b395f25e290e633655a100af28cb253e4393396264a98bd5f5951d50f/backports_tarfile-1.2.0.tar.gz", hash = "sha256:d75e02c268746e1b8144c278978b6e98e85de6ad16f8e4b0844a154557eca991", size = 86406, upload-time = "2024-05-28T17:01:54.731Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/fa/123043af240e49752f1c4bd24da5053b6bd00cad78c2be53c0d1e8b975bc/backports.tarfile-1.2.0-py3-none-any.whl", hash = "sha256:77e284d754527b01fb1e6fa8a1afe577858ebe4e9dad8919e34c862cb399bc34", size = 30181, upload-time = "2024-05-28T17:01:53.112Z" }, +] + [[package]] name = "backrefs" version = "6.1" @@ -601,6 +622,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e4/f8/972c96f5a2b6c4b3deca57009d93e946bbdbe2241dca9806d502f29dd3ee/bcrypt-5.0.0-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:6b8f520b61e8781efee73cba14e3e8c9556ccfb375623f4f97429544734545b4", size = 273375, upload-time = "2025-09-25T19:50:45.43Z" }, ] +[[package]] +name = "beartype" +version = "0.22.9" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/94/1009e248bbfbab11397abca7193bea6626806be9a327d399810d523a07cb/beartype-0.22.9.tar.gz", hash = "sha256:8f82b54aa723a2848a56008d18875f91c1db02c32ef6a62319a002e3e25a975f", size = 1608866, upload-time = "2025-12-13T06:50:30.72Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/71/cc/18245721fa7747065ab478316c7fea7c74777d07f37ae60db2e84f8172e8/beartype-0.22.9-py3-none-any.whl", hash = "sha256:d16c9bbc61ea14637596c5f6fbff2ee99cbe3573e46a716401734ef50c3060c2", size = 1333658, upload-time = "2025-12-13T06:50:28.266Z" }, +] + [[package]] name = "beautifulsoup4" version = "4.14.3" @@ -987,6 +1017,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/75/45/54bb2d8d4138964a94bef6e9afe48b0be4705ba66ac442ae7d8a8dc4ffef/click_option_group-0.5.9-py3-none-any.whl", hash = "sha256:ad2599248bd373e2e19bec5407967c3eec1d0d4fc4a5e77b08a0481e75991080", size = 11553, upload-time = "2025-10-09T09:38:00.066Z" }, ] +[[package]] +name = "cloudpickle" +version = "3.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/27/fb/576f067976d320f5f0114a8d9fa1215425441bb35627b1993e5afd8111e5/cloudpickle-3.1.2.tar.gz", hash = "sha256:7fda9eb655c9c230dab534f1983763de5835249750e85fbcef43aaa30a9a2414", size = 22330, upload-time = "2025-11-03T09:25:26.604Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl", hash = "sha256:9acb47f6afd73f60dc1df93bb801b472f05ff42fa6c84167d25cb206be1fbf4a", size = 22228, upload-time = "2025-11-03T09:25:25.534Z" }, +] + [[package]] name = "colorama" version = "0.4.6" @@ -1133,6 +1172,83 @@ toml = [ { name = "tomli", marker = "python_full_version <= '3.11'" }, ] +[[package]] +name = "cryptography" +version = "46.0.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/78/19/f748958276519adf6a0c1e79e7b8860b4830dda55ccdf29f2719b5fc499c/cryptography-46.0.4.tar.gz", hash = "sha256:bfd019f60f8abc2ed1b9be4ddc21cfef059c841d86d710bb69909a688cbb8f59", size = 749301, upload-time = "2026-01-28T00:24:37.379Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8d/99/157aae7949a5f30d51fcb1a9851e8ebd5c74bf99b5285d8bb4b8b9ee641e/cryptography-46.0.4-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:281526e865ed4166009e235afadf3a4c4cba6056f99336a99efba65336fd5485", size = 7173686, upload-time = "2026-01-28T00:23:07.515Z" }, + { url = "https://files.pythonhosted.org/packages/87/91/874b8910903159043b5c6a123b7e79c4559ddd1896e38967567942635778/cryptography-46.0.4-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5f14fba5bf6f4390d7ff8f086c566454bff0411f6d8aa7af79c88b6f9267aecc", size = 4275871, upload-time = "2026-01-28T00:23:09.439Z" }, + { url = "https://files.pythonhosted.org/packages/c0/35/690e809be77896111f5b195ede56e4b4ed0435b428c2f2b6d35046fbb5e8/cryptography-46.0.4-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:47bcd19517e6389132f76e2d5303ded6cf3f78903da2158a671be8de024f4cd0", size = 4423124, upload-time = "2026-01-28T00:23:11.529Z" }, + { url = "https://files.pythonhosted.org/packages/1a/5b/a26407d4f79d61ca4bebaa9213feafdd8806dc69d3d290ce24996d3cfe43/cryptography-46.0.4-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:01df4f50f314fbe7009f54046e908d1754f19d0c6d3070df1e6268c5a4af09fa", size = 4277090, upload-time = "2026-01-28T00:23:13.123Z" }, + { url = "https://files.pythonhosted.org/packages/0c/d8/4bb7aec442a9049827aa34cee1aa83803e528fa55da9a9d45d01d1bb933e/cryptography-46.0.4-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:5aa3e463596b0087b3da0dbe2b2487e9fc261d25da85754e30e3b40637d61f81", size = 4947652, upload-time = "2026-01-28T00:23:14.554Z" }, + { url = "https://files.pythonhosted.org/packages/2b/08/f83e2e0814248b844265802d081f2fac2f1cbe6cd258e72ba14ff006823a/cryptography-46.0.4-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:0a9ad24359fee86f131836a9ac3bffc9329e956624a2d379b613f8f8abaf5255", size = 4455157, upload-time = "2026-01-28T00:23:16.443Z" }, + { url = "https://files.pythonhosted.org/packages/0a/05/19d849cf4096448779d2dcc9bb27d097457dac36f7273ffa875a93b5884c/cryptography-46.0.4-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:dc1272e25ef673efe72f2096e92ae39dea1a1a450dd44918b15351f72c5a168e", size = 3981078, upload-time = "2026-01-28T00:23:17.838Z" }, + { url = "https://files.pythonhosted.org/packages/e6/89/f7bac81d66ba7cde867a743ea5b37537b32b5c633c473002b26a226f703f/cryptography-46.0.4-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:de0f5f4ec8711ebc555f54735d4c673fc34b65c44283895f1a08c2b49d2fd99c", size = 4276213, upload-time = "2026-01-28T00:23:19.257Z" }, + { url = "https://files.pythonhosted.org/packages/da/9f/7133e41f24edd827020ad21b068736e792bc68eecf66d93c924ad4719fb3/cryptography-46.0.4-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:eeeb2e33d8dbcccc34d64651f00a98cb41b2dc69cef866771a5717e6734dfa32", size = 4912190, upload-time = "2026-01-28T00:23:21.244Z" }, + { url = "https://files.pythonhosted.org/packages/a6/f7/6d43cbaddf6f65b24816e4af187d211f0bc536a29961f69faedc48501d8e/cryptography-46.0.4-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:3d425eacbc9aceafd2cb429e42f4e5d5633c6f873f5e567077043ef1b9bbf616", size = 4454641, upload-time = "2026-01-28T00:23:22.866Z" }, + { url = "https://files.pythonhosted.org/packages/9e/4f/ebd0473ad656a0ac912a16bd07db0f5d85184924e14fc88feecae2492834/cryptography-46.0.4-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:91627ebf691d1ea3976a031b61fb7bac1ccd745afa03602275dda443e11c8de0", size = 4405159, upload-time = "2026-01-28T00:23:25.278Z" }, + { url = "https://files.pythonhosted.org/packages/d1/f7/7923886f32dc47e27adeff8246e976d77258fd2aa3efdd1754e4e323bf49/cryptography-46.0.4-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2d08bc22efd73e8854b0b7caff402d735b354862f1145d7be3b9c0f740fef6a0", size = 4666059, upload-time = "2026-01-28T00:23:26.766Z" }, + { url = "https://files.pythonhosted.org/packages/eb/a7/0fca0fd3591dffc297278a61813d7f661a14243dd60f499a7a5b48acb52a/cryptography-46.0.4-cp311-abi3-win32.whl", hash = "sha256:82a62483daf20b8134f6e92898da70d04d0ef9a75829d732ea1018678185f4f5", size = 3026378, upload-time = "2026-01-28T00:23:28.317Z" }, + { url = "https://files.pythonhosted.org/packages/2d/12/652c84b6f9873f0909374864a57b003686c642ea48c84d6c7e2c515e6da5/cryptography-46.0.4-cp311-abi3-win_amd64.whl", hash = "sha256:6225d3ebe26a55dbc8ead5ad1265c0403552a63336499564675b29eb3184c09b", size = 3478614, upload-time = "2026-01-28T00:23:30.275Z" }, + { url = "https://files.pythonhosted.org/packages/b9/27/542b029f293a5cce59349d799d4d8484b3b1654a7b9a0585c266e974a488/cryptography-46.0.4-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:485e2b65d25ec0d901bca7bcae0f53b00133bf3173916d8e421f6fddde103908", size = 7116417, upload-time = "2026-01-28T00:23:31.958Z" }, + { url = "https://files.pythonhosted.org/packages/f8/f5/559c25b77f40b6bf828eabaf988efb8b0e17b573545edb503368ca0a2a03/cryptography-46.0.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:078e5f06bd2fa5aea5a324f2a09f914b1484f1d0c2a4d6a8a28c74e72f65f2da", size = 4264508, upload-time = "2026-01-28T00:23:34.264Z" }, + { url = "https://files.pythonhosted.org/packages/49/a1/551fa162d33074b660dc35c9bc3616fefa21a0e8c1edd27b92559902e408/cryptography-46.0.4-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:dce1e4f068f03008da7fa51cc7abc6ddc5e5de3e3d1550334eaf8393982a5829", size = 4409080, upload-time = "2026-01-28T00:23:35.793Z" }, + { url = "https://files.pythonhosted.org/packages/b0/6a/4d8d129a755f5d6df1bbee69ea2f35ebfa954fa1847690d1db2e8bca46a5/cryptography-46.0.4-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:2067461c80271f422ee7bdbe79b9b4be54a5162e90345f86a23445a0cf3fd8a2", size = 4270039, upload-time = "2026-01-28T00:23:37.263Z" }, + { url = "https://files.pythonhosted.org/packages/4c/f5/ed3fcddd0a5e39321e595e144615399e47e7c153a1fb8c4862aec3151ff9/cryptography-46.0.4-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:c92010b58a51196a5f41c3795190203ac52edfd5dc3ff99149b4659eba9d2085", size = 4926748, upload-time = "2026-01-28T00:23:38.884Z" }, + { url = "https://files.pythonhosted.org/packages/43/ae/9f03d5f0c0c00e85ecb34f06d3b79599f20630e4db91b8a6e56e8f83d410/cryptography-46.0.4-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:829c2b12bbc5428ab02d6b7f7e9bbfd53e33efd6672d21341f2177470171ad8b", size = 4442307, upload-time = "2026-01-28T00:23:40.56Z" }, + { url = "https://files.pythonhosted.org/packages/8b/22/e0f9f2dae8040695103369cf2283ef9ac8abe4d51f68710bec2afd232609/cryptography-46.0.4-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:62217ba44bf81b30abaeda1488686a04a702a261e26f87db51ff61d9d3510abd", size = 3959253, upload-time = "2026-01-28T00:23:42.827Z" }, + { url = "https://files.pythonhosted.org/packages/01/5b/6a43fcccc51dae4d101ac7d378a8724d1ba3de628a24e11bf2f4f43cba4d/cryptography-46.0.4-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:9c2da296c8d3415b93e6053f5a728649a87a48ce084a9aaf51d6e46c87c7f2d2", size = 4269372, upload-time = "2026-01-28T00:23:44.655Z" }, + { url = "https://files.pythonhosted.org/packages/17/b7/0f6b8c1dd0779df2b526e78978ff00462355e31c0a6f6cff8a3e99889c90/cryptography-46.0.4-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:9b34d8ba84454641a6bf4d6762d15847ecbd85c1316c0a7984e6e4e9f748ec2e", size = 4891908, upload-time = "2026-01-28T00:23:46.48Z" }, + { url = "https://files.pythonhosted.org/packages/83/17/259409b8349aa10535358807a472c6a695cf84f106022268d31cea2b6c97/cryptography-46.0.4-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:df4a817fa7138dd0c96c8c8c20f04b8aaa1fac3bbf610913dcad8ea82e1bfd3f", size = 4441254, upload-time = "2026-01-28T00:23:48.403Z" }, + { url = "https://files.pythonhosted.org/packages/9c/fe/e4a1b0c989b00cee5ffa0764401767e2d1cf59f45530963b894129fd5dce/cryptography-46.0.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:b1de0ebf7587f28f9190b9cb526e901bf448c9e6a99655d2b07fff60e8212a82", size = 4396520, upload-time = "2026-01-28T00:23:50.26Z" }, + { url = "https://files.pythonhosted.org/packages/b3/81/ba8fd9657d27076eb40d6a2f941b23429a3c3d2f56f5a921d6b936a27bc9/cryptography-46.0.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9b4d17bc7bd7cdd98e3af40b441feaea4c68225e2eb2341026c84511ad246c0c", size = 4651479, upload-time = "2026-01-28T00:23:51.674Z" }, + { url = "https://files.pythonhosted.org/packages/00/03/0de4ed43c71c31e4fe954edd50b9d28d658fef56555eba7641696370a8e2/cryptography-46.0.4-cp314-cp314t-win32.whl", hash = "sha256:c411f16275b0dea722d76544a61d6421e2cc829ad76eec79280dbdc9ddf50061", size = 3001986, upload-time = "2026-01-28T00:23:53.485Z" }, + { url = "https://files.pythonhosted.org/packages/5c/70/81830b59df7682917d7a10f833c4dab2a5574cd664e86d18139f2b421329/cryptography-46.0.4-cp314-cp314t-win_amd64.whl", hash = "sha256:728fedc529efc1439eb6107b677f7f7558adab4553ef8669f0d02d42d7b959a7", size = 3468288, upload-time = "2026-01-28T00:23:55.09Z" }, + { url = "https://files.pythonhosted.org/packages/56/f7/f648fdbb61d0d45902d3f374217451385edc7e7768d1b03ff1d0e5ffc17b/cryptography-46.0.4-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a9556ba711f7c23f77b151d5798f3ac44a13455cc68db7697a1096e6d0563cab", size = 7169583, upload-time = "2026-01-28T00:23:56.558Z" }, + { url = "https://files.pythonhosted.org/packages/d8/cc/8f3224cbb2a928de7298d6ed4790f5ebc48114e02bdc9559196bfb12435d/cryptography-46.0.4-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8bf75b0259e87fa70bddc0b8b4078b76e7fd512fd9afae6c1193bcf440a4dbef", size = 4275419, upload-time = "2026-01-28T00:23:58.364Z" }, + { url = "https://files.pythonhosted.org/packages/17/43/4a18faa7a872d00e4264855134ba82d23546c850a70ff209e04ee200e76f/cryptography-46.0.4-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3c268a3490df22270955966ba236d6bc4a8f9b6e4ffddb78aac535f1a5ea471d", size = 4419058, upload-time = "2026-01-28T00:23:59.867Z" }, + { url = "https://files.pythonhosted.org/packages/ee/64/6651969409821d791ba12346a124f55e1b76f66a819254ae840a965d4b9c/cryptography-46.0.4-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:812815182f6a0c1d49a37893a303b44eaac827d7f0d582cecfc81b6427f22973", size = 4278151, upload-time = "2026-01-28T00:24:01.731Z" }, + { url = "https://files.pythonhosted.org/packages/20/0b/a7fce65ee08c3c02f7a8310cc090a732344066b990ac63a9dfd0a655d321/cryptography-46.0.4-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:a90e43e3ef65e6dcf969dfe3bb40cbf5aef0d523dff95bfa24256be172a845f4", size = 4939441, upload-time = "2026-01-28T00:24:03.175Z" }, + { url = "https://files.pythonhosted.org/packages/db/a7/20c5701e2cd3e1dfd7a19d2290c522a5f435dd30957d431dcb531d0f1413/cryptography-46.0.4-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a05177ff6296644ef2876fce50518dffb5bcdf903c85250974fc8bc85d54c0af", size = 4451617, upload-time = "2026-01-28T00:24:05.403Z" }, + { url = "https://files.pythonhosted.org/packages/00/dc/3e16030ea9aa47b63af6524c354933b4fb0e352257c792c4deeb0edae367/cryptography-46.0.4-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:daa392191f626d50f1b136c9b4cf08af69ca8279d110ea24f5c2700054d2e263", size = 3977774, upload-time = "2026-01-28T00:24:06.851Z" }, + { url = "https://files.pythonhosted.org/packages/42/c8/ad93f14118252717b465880368721c963975ac4b941b7ef88f3c56bf2897/cryptography-46.0.4-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:e07ea39c5b048e085f15923511d8121e4a9dc45cee4e3b970ca4f0d338f23095", size = 4277008, upload-time = "2026-01-28T00:24:08.926Z" }, + { url = "https://files.pythonhosted.org/packages/00/cf/89c99698151c00a4631fbfcfcf459d308213ac29e321b0ff44ceeeac82f1/cryptography-46.0.4-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:d5a45ddc256f492ce42a4e35879c5e5528c09cd9ad12420828c972951d8e016b", size = 4903339, upload-time = "2026-01-28T00:24:12.009Z" }, + { url = "https://files.pythonhosted.org/packages/03/c3/c90a2cb358de4ac9309b26acf49b2a100957e1ff5cc1e98e6c4996576710/cryptography-46.0.4-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:6bb5157bf6a350e5b28aee23beb2d84ae6f5be390b2f8ee7ea179cda077e1019", size = 4451216, upload-time = "2026-01-28T00:24:13.975Z" }, + { url = "https://files.pythonhosted.org/packages/96/2c/8d7f4171388a10208671e181ca43cdc0e596d8259ebacbbcfbd16de593da/cryptography-46.0.4-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:dd5aba870a2c40f87a3af043e0dee7d9eb02d4aff88a797b48f2b43eff8c3ab4", size = 4404299, upload-time = "2026-01-28T00:24:16.169Z" }, + { url = "https://files.pythonhosted.org/packages/e9/23/cbb2036e450980f65c6e0a173b73a56ff3bccd8998965dea5cc9ddd424a5/cryptography-46.0.4-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:93d8291da8d71024379ab2cb0b5c57915300155ad42e07f76bea6ad838d7e59b", size = 4664837, upload-time = "2026-01-28T00:24:17.629Z" }, + { url = "https://files.pythonhosted.org/packages/0a/21/f7433d18fe6d5845329cbdc597e30caf983229c7a245bcf54afecc555938/cryptography-46.0.4-cp38-abi3-win32.whl", hash = "sha256:0563655cb3c6d05fb2afe693340bc050c30f9f34e15763361cf08e94749401fc", size = 3009779, upload-time = "2026-01-28T00:24:20.198Z" }, + { url = "https://files.pythonhosted.org/packages/3a/6a/bd2e7caa2facffedf172a45c1a02e551e6d7d4828658c9a245516a598d94/cryptography-46.0.4-cp38-abi3-win_amd64.whl", hash = "sha256:fa0900b9ef9c49728887d1576fd8d9e7e3ea872fa9b25ef9b64888adc434e976", size = 3466633, upload-time = "2026-01-28T00:24:21.851Z" }, + { url = "https://files.pythonhosted.org/packages/59/e0/f9c6c53e1f2a1c2507f00f2faba00f01d2f334b35b0fbfe5286715da2184/cryptography-46.0.4-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:766330cce7416c92b5e90c3bb71b1b79521760cdcfc3a6a1a182d4c9fab23d2b", size = 3476316, upload-time = "2026-01-28T00:24:24.144Z" }, + { url = "https://files.pythonhosted.org/packages/27/7a/f8d2d13227a9a1a9fe9c7442b057efecffa41f1e3c51d8622f26b9edbe8f/cryptography-46.0.4-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c236a44acfb610e70f6b3e1c3ca20ff24459659231ef2f8c48e879e2d32b73da", size = 4216693, upload-time = "2026-01-28T00:24:25.758Z" }, + { url = "https://files.pythonhosted.org/packages/c5/de/3787054e8f7972658370198753835d9d680f6cd4a39df9f877b57f0dd69c/cryptography-46.0.4-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:8a15fb869670efa8f83cbffbc8753c1abf236883225aed74cd179b720ac9ec80", size = 4382765, upload-time = "2026-01-28T00:24:27.577Z" }, + { url = "https://files.pythonhosted.org/packages/8a/5f/60e0afb019973ba6a0b322e86b3d61edf487a4f5597618a430a2a15f2d22/cryptography-46.0.4-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:fdc3daab53b212472f1524d070735b2f0c214239df131903bae1d598016fa822", size = 4216066, upload-time = "2026-01-28T00:24:29.056Z" }, + { url = "https://files.pythonhosted.org/packages/81/8e/bf4a0de294f147fee66f879d9bae6f8e8d61515558e3d12785dd90eca0be/cryptography-46.0.4-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:44cc0675b27cadb71bdbb96099cca1fa051cd11d2ade09e5cd3a2edb929ed947", size = 4382025, upload-time = "2026-01-28T00:24:30.681Z" }, + { url = "https://files.pythonhosted.org/packages/79/f4/9ceb90cfd6a3847069b0b0b353fd3075dc69b49defc70182d8af0c4ca390/cryptography-46.0.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:be8c01a7d5a55f9a47d1888162b76c8f49d62b234d88f0ff91a9fbebe32ffbc3", size = 3406043, upload-time = "2026-01-28T00:24:32.236Z" }, +] + +[[package]] +name = "cyclopts" +version = "4.5.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "docstring-parser" }, + { name = "rich" }, + { name = "rich-rst" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d4/93/6085aa89c3fff78a5180987354538d72e43b0db27e66a959302d0c07821a/cyclopts-4.5.1.tar.gz", hash = "sha256:fadc45304763fd9f5d6033727f176898d17a1778e194436964661a005078a3dd", size = 162075, upload-time = "2026-01-25T15:23:54.07Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1c/7c/996760c30f1302704af57c66ff2d723f7d656d0d0b93563b5528a51484bb/cyclopts-4.5.1-py3-none-any.whl", hash = "sha256:0642c93601e554ca6b7b9abd81093847ea4448b2616280f2a0952416574e8c7a", size = 199807, upload-time = "2026-01-25T15:23:55.219Z" }, +] + [[package]] name = "dataclasses-json" version = "0.6.7" @@ -1235,6 +1351,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/50/3d/9373ad9c56321fdab5b41197068e1d8c25883b3fea29dd361f9b55116869/dill-0.4.0-py3-none-any.whl", hash = "sha256:44f54bf6412c2c8464c14e8243eb163690a9800dbe2c367330883b19c7561049", size = 119668, upload-time = "2025-04-16T00:41:47.671Z" }, ] +[[package]] +name = "diskcache" +version = "5.6.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3f/21/1c1ffc1a039ddcc459db43cc108658f32c57d271d7289a2794e401d0fdb6/diskcache-5.6.3.tar.gz", hash = "sha256:2c3a3fa2743d8535d832ec61c2054a1641f41775aa7c556758a109941e33e4fc", size = 67916, upload-time = "2023-08-31T06:12:00.316Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/27/4570e78fc0bf5ea0ca45eb1de3818a23787af9b390c0b0a0033a1b8236f9/diskcache-5.6.3-py3-none-any.whl", hash = "sha256:5e31b2d5fbad117cc363ebaf6b689474db18a1f6438bc82358b024abd4c2ca19", size = 45550, upload-time = "2023-08-31T06:11:58.822Z" }, +] + [[package]] name = "distlib" version = "0.4.0" @@ -1253,6 +1378,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277, upload-time = "2023-12-24T09:54:30.421Z" }, ] +[[package]] +name = "dnspython" +version = "2.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/8b/57666417c0f90f08bcafa776861060426765fdb422eb10212086fb811d26/dnspython-2.8.0.tar.gz", hash = "sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f", size = 368251, upload-time = "2025-09-07T18:58:00.022Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af", size = 331094, upload-time = "2025-09-07T18:57:58.071Z" }, +] + [[package]] name = "docker" version = "7.1.0" @@ -1416,6 +1550,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/55/e2/2537ebcff11c1ee1ff17d8d0b6f4db75873e3b0fb32c2d4a2ee31ecb310a/docstring_parser-0.17.0-py3-none-any.whl", hash = "sha256:cf2569abd23dce8099b300f9b4fa8191e9582dda731fd533daf54c4551658708", size = 36896, upload-time = "2025-07-21T07:35:00.684Z" }, ] +[[package]] +name = "docutils" +version = "0.22.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ae/b6/03bb70946330e88ffec97aefd3ea75ba575cb2e762061e0e62a213befee8/docutils-0.22.4.tar.gz", hash = "sha256:4db53b1fde9abecbb74d91230d32ab626d94f6badfc575d6db9194a49df29968", size = 2291750, upload-time = "2025-12-18T19:00:26.443Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/02/10/5da547df7a391dcde17f59520a231527b8571e6f46fc8efb02ccb370ab12/docutils-0.22.4-py3-none-any.whl", hash = "sha256:d0013f540772d1420576855455d050a2180186c91c15779301ac2ccb3eeb68de", size = 633196, upload-time = "2025-12-18T19:00:18.077Z" }, +] + [[package]] name = "dotty-dict" version = "1.3.1" @@ -1434,6 +1577,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b0/0d/9feae160378a3553fa9a339b0e9c1a048e147a4127210e286ef18b730f03/durationpy-0.10-py3-none-any.whl", hash = "sha256:3b41e1b601234296b4fb368338fdcd3e13e0b4fb5b67345948f4f2bf9868b286", size = 3922, upload-time = "2025-05-17T13:52:36.463Z" }, ] +[[package]] +name = "email-validator" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "dnspython" }, + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f5/22/900cb125c76b7aaa450ce02fd727f452243f2e91a61af068b40adba60ea9/email_validator-2.3.0.tar.gz", hash = "sha256:9fc05c37f2f6cf439ff414f8fc46d917929974a82244c20eb10231ba60c54426", size = 51238, upload-time = "2025-08-26T13:09:06.831Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl", hash = "sha256:80f13f623413e6b197ae73bb10bf4eb0908faf509ad8362c5edeb0be7fd450b4", size = 35604, upload-time = "2025-08-26T13:09:05.858Z" }, +] + [[package]] name = "et-xmlfile" version = "2.0.0" @@ -1448,7 +1604,7 @@ name = "exceptiongroup" version = "1.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } wheels = [ @@ -1476,6 +1632,25 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fc/23/e22da510e1ec1488966330bf76d8ff4bd535cbfc93660eeb7657761a1bb2/faker-40.1.0-py3-none-any.whl", hash = "sha256:a616d35818e2a2387c297de80e2288083bc915e24b7e39d2fb5bc66cce3a929f", size = 1985317, upload-time = "2025-12-29T18:05:58.831Z" }, ] +[[package]] +name = "fakeredis" +version = "2.33.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "redis" }, + { name = "sortedcontainers" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5f/f9/57464119936414d60697fcbd32f38909bb5688b616ae13de6e98384433e0/fakeredis-2.33.0.tar.gz", hash = "sha256:d7bc9a69d21df108a6451bbffee23b3eba432c21a654afc7ff2d295428ec5770", size = 175187, upload-time = "2025-12-16T19:45:52.269Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6e/78/a850fed8aeef96d4a99043c90b818b2ed5419cd5b24a4049fd7cfb9f1471/fakeredis-2.33.0-py3-none-any.whl", hash = "sha256:de535f3f9ccde1c56672ab2fdd6a8efbc4f2619fc2f1acc87b8737177d71c965", size = 119605, upload-time = "2025-12-16T19:45:51.08Z" }, +] + +[package.optional-dependencies] +lua = [ + { name = "lupa" }, +] + [[package]] name = "fastapi" version = "0.128.0" @@ -1500,6 +1675,33 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/cb/a8/20d0723294217e47de6d9e2e40fd4a9d2f7c4b6ef974babd482a59743694/fastjsonschema-2.21.2-py3-none-any.whl", hash = "sha256:1c797122d0a86c5cace2e54bf4e819c36223b552017172f32c5c024a6b77e463", size = 24024, upload-time = "2025-08-14T18:49:34.776Z" }, ] +[[package]] +name = "fastmcp" +version = "2.14.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "authlib" }, + { name = "cyclopts" }, + { name = "exceptiongroup" }, + { name = "httpx" }, + { name = "jsonschema-path" }, + { name = "mcp" }, + { name = "openapi-pydantic" }, + { name = "platformdirs" }, + { name = "py-key-value-aio", extra = ["disk", "keyring", "memory"] }, + { name = "pydantic", extra = ["email"] }, + { name = "pydocket" }, + { name = "pyperclip" }, + { name = "python-dotenv" }, + { name = "rich" }, + { name = "uvicorn" }, + { name = "websockets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/72/b5/7c4744dc41390ed2c17fd462ef2d42f4448a1ec53dda8fe3a01ff2872313/fastmcp-2.14.3.tar.gz", hash = "sha256:abc9113d5fcf79dfb4c060a1e1c55fccb0d4bce4a2e3eab15ca352341eec8dd6", size = 8279206, upload-time = "2026-01-12T20:00:40.789Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fc/dc/f7dd14213bf511690dccaa5094d436947c253b418c86c86211d1c76e6e44/fastmcp-2.14.3-py3-none-any.whl", hash = "sha256:103c6b4c6e97a9acc251c81d303f110fe4f2bdba31353df515d66272bf1b9414", size = 416220, upload-time = "2026-01-12T20:00:42.543Z" }, +] + [[package]] name = "fastuuid" version = "0.14.0" @@ -2437,6 +2639,42 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7f/ed/e3705d6d02b4f7aea715a353c8ce193efd0b5db13e204df895d38734c244/isort-7.0.0-py3-none-any.whl", hash = "sha256:1bcabac8bc3c36c7fb7b98a76c8abb18e0f841a3ba81decac7691008592499c1", size = 94672, upload-time = "2025-10-11T13:30:57.665Z" }, ] +[[package]] +name = "jaraco-classes" +version = "3.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "more-itertools" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/c0/ed4a27bc5571b99e3cff68f8a9fa5b56ff7df1c2251cc715a652ddd26402/jaraco.classes-3.4.0.tar.gz", hash = "sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd", size = 11780, upload-time = "2024-03-31T07:27:36.643Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/66/b15ce62552d84bbfcec9a4873ab79d993a1dd4edb922cbfccae192bd5b5f/jaraco.classes-3.4.0-py3-none-any.whl", hash = "sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790", size = 6777, upload-time = "2024-03-31T07:27:34.792Z" }, +] + +[[package]] +name = "jaraco-context" +version = "6.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "backports-tarfile", marker = "python_full_version < '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cb/9c/a788f5bb29c61e456b8ee52ce76dbdd32fd72cd73dd67bc95f42c7a8d13c/jaraco_context-6.1.0.tar.gz", hash = "sha256:129a341b0a85a7db7879e22acd66902fda67882db771754574338898b2d5d86f", size = 15850, upload-time = "2026-01-13T02:53:53.847Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8d/48/aa685dbf1024c7bd82bede569e3a85f82c32fd3d79ba5fea578f0159571a/jaraco_context-6.1.0-py3-none-any.whl", hash = "sha256:a43b5ed85815223d0d3cfdb6d7ca0d2bc8946f28f30b6f3216bda070f68badda", size = 7065, upload-time = "2026-01-13T02:53:53.031Z" }, +] + +[[package]] +name = "jaraco-functools" +version = "4.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "more-itertools" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0f/27/056e0638a86749374d6f57d0b0db39f29509cce9313cf91bdc0ac4d91084/jaraco_functools-4.4.0.tar.gz", hash = "sha256:da21933b0417b89515562656547a77b4931f98176eb173644c0d35032a33d6bb", size = 19943, upload-time = "2025-12-21T09:29:43.6Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/c4/813bb09f0985cb21e959f21f2464169eca882656849adf727ac7bb7e1767/jaraco_functools-4.4.0-py3-none-any.whl", hash = "sha256:9eec1e36f45c818d9bf307c8948eb03b2b56cd44087b3cdc989abca1f20b9176", size = 10481, upload-time = "2025-12-21T09:29:42.27Z" }, +] + [[package]] name = "jedi" version = "0.19.2" @@ -2449,6 +2687,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9", size = 1572278, upload-time = "2024-11-11T01:41:40.175Z" }, ] +[[package]] +name = "jeepney" +version = "0.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7b/6f/357efd7602486741aa73ffc0617fb310a29b588ed0fd69c2399acbb85b0c/jeepney-0.9.0.tar.gz", hash = "sha256:cf0e9e845622b81e4a28df94c40345400256ec608d0e55bb8a3feaa9163f5732", size = 106758, upload-time = "2025-02-27T18:51:01.684Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b2/a3/e137168c9c44d18eff0376253da9f1e9234d0239e0ee230d2fee6cea8e55/jeepney-0.9.0-py3-none-any.whl", hash = "sha256:97e5714520c16fc0a45695e5365a2e11b81ea79bba796e26f9f1d178cb182683", size = 49010, upload-time = "2025-02-27T18:51:00.104Z" }, +] + [[package]] name = "jinja2" version = "3.1.6" @@ -2642,6 +2889,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", size = 90630, upload-time = "2026-01-07T13:41:05.306Z" }, ] +[[package]] +name = "jsonschema-path" +version = "0.3.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pathable" }, + { name = "pyyaml" }, + { name = "referencing" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6e/45/41ebc679c2a4fced6a722f624c18d658dee42612b83ea24c1caf7c0eb3a8/jsonschema_path-0.3.4.tar.gz", hash = "sha256:8365356039f16cc65fddffafda5f58766e34bebab7d6d105616ab52bc4297001", size = 11159, upload-time = "2025-01-24T14:33:16.547Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/58/3485da8cb93d2f393bce453adeef16896751f14ba3e2024bc21dc9597646/jsonschema_path-0.3.4-py3-none-any.whl", hash = "sha256:f502191fdc2b22050f9a81c9237be9d27145b9001c55842bece5e94e382e52f8", size = 14810, upload-time = "2025-01-24T14:33:14.652Z" }, +] + [[package]] name = "jsonschema-specifications" version = "2025.9.1" @@ -2709,6 +2971,24 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/bd/0d/2d240e7098e0cafba4d25e9530e7596b1bb1bd4476e41b10346bcaaa36d6/jupytext-1.18.1-py3-none-any.whl", hash = "sha256:24f999400726a1c658beae55e15fdd2a6255ab1a418697864cd779874e6011ab", size = 167143, upload-time = "2025-10-19T15:06:28.975Z" }, ] +[[package]] +name = "keyring" +version = "25.7.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "importlib-metadata", marker = "python_full_version < '3.12'" }, + { name = "jaraco-classes" }, + { name = "jaraco-context" }, + { name = "jaraco-functools" }, + { name = "jeepney", marker = "sys_platform == 'linux'" }, + { name = "pywin32-ctypes", marker = "sys_platform == 'win32'" }, + { name = "secretstorage", marker = "sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/43/4b/674af6ef2f97d56f0ab5153bf0bfa28ccb6c3ed4d1babf4305449668807b/keyring-25.7.0.tar.gz", hash = "sha256:fe01bd85eb3f8fb3dd0405defdeac9a5b4f6f0439edbb3149577f244a2e8245b", size = 63516, upload-time = "2025-11-16T16:26:09.482Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/db/e655086b7f3a705df045bf0933bdd9c2f79bb3c97bfef1384598bb79a217/keyring-25.7.0-py3-none-any.whl", hash = "sha256:be4a0b195f149690c166e850609a477c532ddbfbaed96a404d4e43f8d5e2689f", size = 39160, upload-time = "2025-11-16T16:26:08.402Z" }, +] + [[package]] name = "kstar-planner" version = "1.4.16" @@ -3118,6 +3398,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/30/a8/e61a8c2b3cc7a597073d9cde1fcbb567e9d827f1db30c93cf80422eac70d/llvmlite-0.46.0-cp314-cp314-win_amd64.whl", hash = "sha256:7821eda3ec1f18050f981819756631d60b6d7ab1a6cf806d9efefbe3f4082d61", size = 39153056, upload-time = "2025-12-08T18:15:33.938Z" }, ] +[[package]] +name = "loguru" +version = "0.7.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "win32-setctime", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3a/05/a1dae3dffd1116099471c643b8924f5aa6524411dc6c63fdae648c4f1aca/loguru-0.7.3.tar.gz", hash = "sha256:19480589e77d47b8d85b2c827ad95d49bf31b0dcde16593892eb51dd18706eb6", size = 63559, upload-time = "2024-12-06T11:20:56.608Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/29/0348de65b8cc732daa3e33e67806420b2ae89bdce2b04af740289c5c6c8c/loguru-0.7.3-py3-none-any.whl", hash = "sha256:31a33c10c8e1e10422bfd431aeb5d351c7cf7fa671e3c4df004162264b28220c", size = 61595, upload-time = "2024-12-06T11:20:54.538Z" }, +] + [[package]] name = "lomond" version = "0.3.3" @@ -3130,6 +3423,80 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0f/b1/02eebed49c754b01b17de7705caa8c4ceecfb4f926cdafc220c863584360/lomond-0.3.3-py2.py3-none-any.whl", hash = "sha256:df1dd4dd7b802a12b71907ab1abb08b8ce9950195311207579379eb3b1553de7", size = 35512, upload-time = "2018-09-21T15:17:38.686Z" }, ] +[[package]] +name = "lupa" +version = "2.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b8/1c/191c3e6ec6502e3dbe25a53e27f69a5daeac3e56de1f73c0138224171ead/lupa-2.6.tar.gz", hash = "sha256:9a770a6e89576be3447668d7ced312cd6fd41d3c13c2462c9dc2c2ab570e45d9", size = 7240282, upload-time = "2025-10-24T07:20:29.738Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a1/15/713cab5d0dfa4858f83b99b3e0329072df33dc14fc3ebbaa017e0f9755c4/lupa-2.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6b3dabda836317e63c5ad052826e156610f356a04b3003dfa0dbe66b5d54d671", size = 954828, upload-time = "2025-10-24T07:17:15.726Z" }, + { url = "https://files.pythonhosted.org/packages/2e/71/704740cbc6e587dd6cc8dabf2f04820ac6a671784e57cc3c29db795476db/lupa-2.6-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:8726d1c123bbe9fbb974ce29825e94121824e66003038ff4532c14cc2ed0c51c", size = 1919259, upload-time = "2025-10-24T07:17:18.586Z" }, + { url = "https://files.pythonhosted.org/packages/eb/18/f248341c423c5d48837e35584c6c3eb4acab7e722b6057d7b3e28e42dae8/lupa-2.6-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:f4e159e7d814171199b246f9235ca8961f6461ea8c1165ab428afa13c9289a94", size = 984998, upload-time = "2025-10-24T07:17:20.428Z" }, + { url = "https://files.pythonhosted.org/packages/44/1e/8a4bd471e018aad76bcb9455d298c2c96d82eced20f2ae8fcec8cd800948/lupa-2.6-cp310-cp310-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:202160e80dbfddfb79316692a563d843b767e0f6787bbd1c455f9d54052efa6c", size = 1174871, upload-time = "2025-10-24T07:17:22.755Z" }, + { url = "https://files.pythonhosted.org/packages/2a/5c/3a3f23fd6a91b0986eea1ceaf82ad3f9b958fe3515a9981fb9c4eb046c8b/lupa-2.6-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5deede7c5b36ab64f869dae4831720428b67955b0bb186c8349cf6ea121c852b", size = 1057471, upload-time = "2025-10-24T07:17:24.908Z" }, + { url = "https://files.pythonhosted.org/packages/45/ac/01be1fed778fb0c8f46ee8cbe344e4d782f6806fac12717f08af87aa4355/lupa-2.6-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:86f04901f920bbf7c0cac56807dc9597e42347123e6f1f3ca920f15f54188ce5", size = 2100592, upload-time = "2025-10-24T07:17:27.089Z" }, + { url = "https://files.pythonhosted.org/packages/3f/6c/1a05bb873e30830f8574e10cd0b4cdbc72e9dbad2a09e25810b5e3b1f75d/lupa-2.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6deef8f851d6afb965c84849aa5b8c38856942df54597a811ce0369ced678610", size = 1081396, upload-time = "2025-10-24T07:17:29.064Z" }, + { url = "https://files.pythonhosted.org/packages/a2/c2/a19dd80d6dc98b39bbf8135b8198e38aa7ca3360b720eac68d1d7e9286b5/lupa-2.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:21f2b5549681c2a13b1170a26159d30875d367d28f0247b81ca347222c755038", size = 1192007, upload-time = "2025-10-24T07:17:31.362Z" }, + { url = "https://files.pythonhosted.org/packages/4f/43/e1b297225c827f55752e46fdbfb021c8982081b0f24490e42776ea69ae3b/lupa-2.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:66eea57630eab5e6f49fdc5d7811c0a2a41f2011be4ea56a087ea76112011eb7", size = 2196661, upload-time = "2025-10-24T07:17:33.484Z" }, + { url = "https://files.pythonhosted.org/packages/2e/8f/2272d429a7fa9dc8dbd6e9c5c9073a03af6007eb22a4c78829fec6a34b80/lupa-2.6-cp310-cp310-win32.whl", hash = "sha256:60a403de8cab262a4fe813085dd77010effa6e2eb1886db2181df803140533b1", size = 1412738, upload-time = "2025-10-24T07:17:35.11Z" }, + { url = "https://files.pythonhosted.org/packages/35/2a/1708911271dd49ad87b4b373b5a4b0e0a0516d3d2af7b76355946c7ee171/lupa-2.6-cp310-cp310-win_amd64.whl", hash = "sha256:e4656a39d93dfa947cf3db56dc16c7916cb0cc8024acd3a952071263f675df64", size = 1656898, upload-time = "2025-10-24T07:17:36.949Z" }, + { url = "https://files.pythonhosted.org/packages/ca/29/1f66907c1ebf1881735afa695e646762c674f00738ebf66d795d59fc0665/lupa-2.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6d988c0f9331b9f2a5a55186701a25444ab10a1432a1021ee58011499ecbbdd5", size = 962875, upload-time = "2025-10-24T07:17:39.107Z" }, + { url = "https://files.pythonhosted.org/packages/e6/67/4a748604be360eb9c1c215f6a0da921cd1a2b44b2c5951aae6fb83019d3a/lupa-2.6-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:ebe1bbf48259382c72a6fe363dea61a0fd6fe19eab95e2ae881e20f3654587bf", size = 1935390, upload-time = "2025-10-24T07:17:41.427Z" }, + { url = "https://files.pythonhosted.org/packages/ac/0c/8ef9ee933a350428b7bdb8335a37ef170ab0bb008bbf9ca8f4f4310116b6/lupa-2.6-cp311-cp311-macosx_11_0_x86_64.whl", hash = "sha256:a8fcee258487cf77cdd41560046843bb38c2e18989cd19671dd1e2596f798306", size = 992193, upload-time = "2025-10-24T07:17:43.231Z" }, + { url = "https://files.pythonhosted.org/packages/65/46/e6c7facebdb438db8a65ed247e56908818389c1a5abbf6a36aab14f1057d/lupa-2.6-cp311-cp311-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:561a8e3be800827884e767a694727ed8482d066e0d6edfcbf423b05e63b05535", size = 1165844, upload-time = "2025-10-24T07:17:45.437Z" }, + { url = "https://files.pythonhosted.org/packages/1c/26/9f1154c6c95f175ccbf96aa96c8f569c87f64f463b32473e839137601a8b/lupa-2.6-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:af880a62d47991cae78b8e9905c008cbfdc4a3a9723a66310c2634fc7644578c", size = 1048069, upload-time = "2025-10-24T07:17:47.181Z" }, + { url = "https://files.pythonhosted.org/packages/68/67/2cc52ab73d6af81612b2ea24c870d3fa398443af8e2875e5befe142398b1/lupa-2.6-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:80b22923aa4023c86c0097b235615f89d469a0c4eee0489699c494d3367c4c85", size = 2079079, upload-time = "2025-10-24T07:17:49.755Z" }, + { url = "https://files.pythonhosted.org/packages/2e/dc/f843f09bbf325f6e5ee61730cf6c3409fc78c010d968c7c78acba3019ca7/lupa-2.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:153d2cc6b643f7efb9cfc0c6bb55ec784d5bac1a3660cfc5b958a7b8f38f4a75", size = 1071428, upload-time = "2025-10-24T07:17:51.991Z" }, + { url = "https://files.pythonhosted.org/packages/2e/60/37533a8d85bf004697449acb97ecdacea851acad28f2ad3803662487dd2a/lupa-2.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:3fa8777e16f3ded50b72967dc17e23f5a08e4f1e2c9456aff2ebdb57f5b2869f", size = 1181756, upload-time = "2025-10-24T07:17:53.752Z" }, + { url = "https://files.pythonhosted.org/packages/e4/f2/cf29b20dbb4927b6a3d27c339ac5d73e74306ecc28c8e2c900b2794142ba/lupa-2.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:8dbdcbe818c02a2f56f5ab5ce2de374dab03e84b25266cfbaef237829bc09b3f", size = 2175687, upload-time = "2025-10-24T07:17:56.228Z" }, + { url = "https://files.pythonhosted.org/packages/94/7c/050e02f80c7131b63db1474bff511e63c545b5a8636a24cbef3fc4da20b6/lupa-2.6-cp311-cp311-win32.whl", hash = "sha256:defaf188fde8f7a1e5ce3a5e6d945e533b8b8d547c11e43b96c9b7fe527f56dc", size = 1412592, upload-time = "2025-10-24T07:17:59.062Z" }, + { url = "https://files.pythonhosted.org/packages/6f/9a/6f2af98aa5d771cea661f66c8eb8f53772ec1ab1dfbce24126cfcd189436/lupa-2.6-cp311-cp311-win_amd64.whl", hash = "sha256:9505ae600b5c14f3e17e70f87f88d333717f60411faca1ddc6f3e61dce85fa9e", size = 1669194, upload-time = "2025-10-24T07:18:01.647Z" }, + { url = "https://files.pythonhosted.org/packages/94/86/ce243390535c39d53ea17ccf0240815e6e457e413e40428a658ea4ee4b8d/lupa-2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:47ce718817ef1cc0c40d87c3d5ae56a800d61af00fbc0fad1ca9be12df2f3b56", size = 951707, upload-time = "2025-10-24T07:18:03.884Z" }, + { url = "https://files.pythonhosted.org/packages/86/85/cedea5e6cbeb54396fdcc55f6b741696f3f036d23cfaf986d50d680446da/lupa-2.6-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:7aba985b15b101495aa4b07112cdc08baa0c545390d560ad5cfde2e9e34f4d58", size = 1916703, upload-time = "2025-10-24T07:18:05.6Z" }, + { url = "https://files.pythonhosted.org/packages/24/be/3d6b5f9a8588c01a4d88129284c726017b2089f3a3fd3ba8bd977292fea0/lupa-2.6-cp312-cp312-macosx_11_0_x86_64.whl", hash = "sha256:b766f62f95b2739f2248977d29b0722e589dcf4f0ccfa827ccbd29f0148bd2e5", size = 985152, upload-time = "2025-10-24T07:18:08.561Z" }, + { url = "https://files.pythonhosted.org/packages/eb/23/9f9a05beee5d5dce9deca4cb07c91c40a90541fc0a8e09db4ee670da550f/lupa-2.6-cp312-cp312-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:00a934c23331f94cb51760097ebfab14b005d55a6b30a2b480e3c53dd2fa290d", size = 1159599, upload-time = "2025-10-24T07:18:10.346Z" }, + { url = "https://files.pythonhosted.org/packages/40/4e/e7c0583083db9d7f1fd023800a9767d8e4391e8330d56c2373d890ac971b/lupa-2.6-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21de9f38bd475303e34a042b7081aabdf50bd9bafd36ce4faea2f90fd9f15c31", size = 1038686, upload-time = "2025-10-24T07:18:12.112Z" }, + { url = "https://files.pythonhosted.org/packages/1c/9f/5a4f7d959d4feba5e203ff0c31889e74d1ca3153122be4a46dca7d92bf7c/lupa-2.6-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cf3bda96d3fc41237e964a69c23647d50d4e28421111360274d4799832c560e9", size = 2071956, upload-time = "2025-10-24T07:18:14.572Z" }, + { url = "https://files.pythonhosted.org/packages/92/34/2f4f13ca65d01169b1720176aedc4af17bc19ee834598c7292db232cb6dc/lupa-2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5a76ead245da54801a81053794aa3975f213221f6542d14ec4b859ee2e7e0323", size = 1057199, upload-time = "2025-10-24T07:18:16.379Z" }, + { url = "https://files.pythonhosted.org/packages/35/2a/5f7d2eebec6993b0dcd428e0184ad71afb06a45ba13e717f6501bfed1da3/lupa-2.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:8dd0861741caa20886ddbda0a121d8e52fb9b5bb153d82fa9bba796962bf30e8", size = 1173693, upload-time = "2025-10-24T07:18:18.153Z" }, + { url = "https://files.pythonhosted.org/packages/e4/29/089b4d2f8e34417349af3904bb40bec40b65c8731f45e3fd8d497ca573e5/lupa-2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:239e63948b0b23023f81d9a19a395e768ed3da6a299f84e7963b8f813f6e3f9c", size = 2164394, upload-time = "2025-10-24T07:18:20.403Z" }, + { url = "https://files.pythonhosted.org/packages/f3/1b/79c17b23c921f81468a111cad843b076a17ef4b684c4a8dff32a7969c3f0/lupa-2.6-cp312-cp312-win32.whl", hash = "sha256:325894e1099499e7a6f9c351147661a2011887603c71086d36fe0f964d52d1ce", size = 1420647, upload-time = "2025-10-24T07:18:23.368Z" }, + { url = "https://files.pythonhosted.org/packages/b8/15/5121e68aad3584e26e1425a5c9a79cd898f8a152292059e128c206ee817c/lupa-2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c735a1ce8ee60edb0fe71d665f1e6b7c55c6021f1d340eb8c865952c602cd36f", size = 1688529, upload-time = "2025-10-24T07:18:25.523Z" }, + { url = "https://files.pythonhosted.org/packages/28/1d/21176b682ca5469001199d8b95fa1737e29957a3d185186e7a8b55345f2e/lupa-2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:663a6e58a0f60e7d212017d6678639ac8df0119bc13c2145029dcba084391310", size = 947232, upload-time = "2025-10-24T07:18:27.878Z" }, + { url = "https://files.pythonhosted.org/packages/ce/4c/d327befb684660ca13cf79cd1f1d604331808f9f1b6fb6bf57832f8edf80/lupa-2.6-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:d1f5afda5c20b1f3217a80e9bc1b77037f8a6eb11612fd3ada19065303c8f380", size = 1908625, upload-time = "2025-10-24T07:18:29.944Z" }, + { url = "https://files.pythonhosted.org/packages/66/8e/ad22b0a19454dfd08662237a84c792d6d420d36b061f239e084f29d1a4f3/lupa-2.6-cp313-cp313-macosx_11_0_x86_64.whl", hash = "sha256:26f2b3c085fe76e9119e48c1013c1cccdc1f51585d456858290475aa38e7089e", size = 981057, upload-time = "2025-10-24T07:18:31.553Z" }, + { url = "https://files.pythonhosted.org/packages/5c/48/74859073ab276bd0566c719f9ca0108b0cfc1956ca0d68678d117d47d155/lupa-2.6-cp313-cp313-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:60d2f902c7b96fb8ab98493dcff315e7bb4d0b44dc9dd76eb37de575025d5685", size = 1156227, upload-time = "2025-10-24T07:18:33.981Z" }, + { url = "https://files.pythonhosted.org/packages/09/6c/0e9ded061916877253c2266074060eb71ed99fb21d73c8c114a76725bce2/lupa-2.6-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a02d25dee3a3250967c36590128d9220ae02f2eda166a24279da0b481519cbff", size = 1035752, upload-time = "2025-10-24T07:18:36.32Z" }, + { url = "https://files.pythonhosted.org/packages/dd/ef/f8c32e454ef9f3fe909f6c7d57a39f950996c37a3deb7b391fec7903dab7/lupa-2.6-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6eae1ee16b886b8914ff292dbefbf2f48abfbdee94b33a88d1d5475e02423203", size = 2069009, upload-time = "2025-10-24T07:18:38.072Z" }, + { url = "https://files.pythonhosted.org/packages/53/dc/15b80c226a5225815a890ee1c11f07968e0aba7a852df41e8ae6fe285063/lupa-2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b0edd5073a4ee74ab36f74fe61450148e6044f3952b8d21248581f3c5d1a58be", size = 1056301, upload-time = "2025-10-24T07:18:40.165Z" }, + { url = "https://files.pythonhosted.org/packages/31/14/2086c1425c985acfb30997a67e90c39457122df41324d3c179d6ee2292c6/lupa-2.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:0c53ee9f22a8a17e7d4266ad48e86f43771951797042dd51d1494aaa4f5f3f0a", size = 1170673, upload-time = "2025-10-24T07:18:42.426Z" }, + { url = "https://files.pythonhosted.org/packages/10/e5/b216c054cf86576c0191bf9a9f05de6f7e8e07164897d95eea0078dca9b2/lupa-2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:de7c0f157a9064a400d828789191a96da7f4ce889969a588b87ec80de9b14772", size = 2162227, upload-time = "2025-10-24T07:18:46.112Z" }, + { url = "https://files.pythonhosted.org/packages/59/2f/33ecb5bedf4f3bc297ceacb7f016ff951331d352f58e7e791589609ea306/lupa-2.6-cp313-cp313-win32.whl", hash = "sha256:ee9523941ae0a87b5b703417720c5d78f72d2f5bc23883a2ea80a949a3ed9e75", size = 1419558, upload-time = "2025-10-24T07:18:48.371Z" }, + { url = "https://files.pythonhosted.org/packages/f9/b4/55e885834c847ea610e111d87b9ed4768f0afdaeebc00cd46810f25029f6/lupa-2.6-cp313-cp313-win_amd64.whl", hash = "sha256:b1335a5835b0a25ebdbc75cf0bda195e54d133e4d994877ef025e218c2e59db9", size = 1683424, upload-time = "2025-10-24T07:18:50.976Z" }, + { url = "https://files.pythonhosted.org/packages/66/9d/d9427394e54d22a35d1139ef12e845fd700d4872a67a34db32516170b746/lupa-2.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:dcb6d0a3264873e1653bc188499f48c1fb4b41a779e315eba45256cfe7bc33c1", size = 953818, upload-time = "2025-10-24T07:18:53.378Z" }, + { url = "https://files.pythonhosted.org/packages/10/41/27bbe81953fb2f9ecfced5d9c99f85b37964cfaf6aa8453bb11283983721/lupa-2.6-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:a37e01f2128f8c36106726cb9d360bac087d58c54b4522b033cc5691c584db18", size = 1915850, upload-time = "2025-10-24T07:18:55.259Z" }, + { url = "https://files.pythonhosted.org/packages/a3/98/f9ff60db84a75ba8725506bbf448fb085bc77868a021998ed2a66d920568/lupa-2.6-cp314-cp314-macosx_11_0_x86_64.whl", hash = "sha256:458bd7e9ff3c150b245b0fcfbb9bd2593d1152ea7f0a7b91c1d185846da033fe", size = 982344, upload-time = "2025-10-24T07:18:57.05Z" }, + { url = "https://files.pythonhosted.org/packages/41/f7/f39e0f1c055c3b887d86b404aaf0ca197b5edfd235a8b81b45b25bac7fc3/lupa-2.6-cp314-cp314-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:052ee82cac5206a02df77119c325339acbc09f5ce66967f66a2e12a0f3211cad", size = 1156543, upload-time = "2025-10-24T07:18:59.251Z" }, + { url = "https://files.pythonhosted.org/packages/9e/9c/59e6cffa0d672d662ae17bd7ac8ecd2c89c9449dee499e3eb13ca9cd10d9/lupa-2.6-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96594eca3c87dd07938009e95e591e43d554c1dbd0385be03c100367141db5a8", size = 1047974, upload-time = "2025-10-24T07:19:01.449Z" }, + { url = "https://files.pythonhosted.org/packages/23/c6/a04e9cef7c052717fcb28fb63b3824802488f688391895b618e39be0f684/lupa-2.6-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8faddd9d198688c8884091173a088a8e920ecc96cda2ffed576a23574c4b3f6", size = 2073458, upload-time = "2025-10-24T07:19:03.369Z" }, + { url = "https://files.pythonhosted.org/packages/e6/10/824173d10f38b51fc77785228f01411b6ca28826ce27404c7c912e0e442c/lupa-2.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:daebb3a6b58095c917e76ba727ab37b27477fb926957c825205fbda431552134", size = 1067683, upload-time = "2025-10-24T07:19:06.2Z" }, + { url = "https://files.pythonhosted.org/packages/b6/dc/9692fbcf3c924d9c4ece2d8d2f724451ac2e09af0bd2a782db1cef34e799/lupa-2.6-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:f3154e68972befe0f81564e37d8142b5d5d79931a18309226a04ec92487d4ea3", size = 1171892, upload-time = "2025-10-24T07:19:08.544Z" }, + { url = "https://files.pythonhosted.org/packages/84/ff/e318b628d4643c278c96ab3ddea07fc36b075a57383c837f5b11e537ba9d/lupa-2.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e4dadf77b9fedc0bfa53417cc28dc2278a26d4cbd95c29f8927ad4d8fe0a7ef9", size = 2166641, upload-time = "2025-10-24T07:19:10.485Z" }, + { url = "https://files.pythonhosted.org/packages/12/f7/a6f9ec2806cf2d50826980cdb4b3cffc7691dc6f95e13cc728846d5cb793/lupa-2.6-cp314-cp314-win32.whl", hash = "sha256:cb34169c6fa3bab3e8ac58ca21b8a7102f6a94b6a5d08d3636312f3f02fafd8f", size = 1456857, upload-time = "2025-10-24T07:19:37.989Z" }, + { url = "https://files.pythonhosted.org/packages/c5/de/df71896f25bdc18360fdfa3b802cd7d57d7fede41a0e9724a4625b412c85/lupa-2.6-cp314-cp314-win_amd64.whl", hash = "sha256:b74f944fe46c421e25d0f8692aef1e842192f6f7f68034201382ac440ef9ea67", size = 1731191, upload-time = "2025-10-24T07:19:40.281Z" }, + { url = "https://files.pythonhosted.org/packages/47/3c/a1f23b01c54669465f5f4c4083107d496fbe6fb45998771420e9aadcf145/lupa-2.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0e21b716408a21ab65723f8841cf7f2f37a844b7a965eeabb785e27fca4099cf", size = 999343, upload-time = "2025-10-24T07:19:12.519Z" }, + { url = "https://files.pythonhosted.org/packages/c5/6d/501994291cb640bfa2ccf7f554be4e6914afa21c4026bd01bff9ca8aac57/lupa-2.6-cp314-cp314t-macosx_11_0_universal2.whl", hash = "sha256:589db872a141bfff828340079bbdf3e9a31f2689f4ca0d88f97d9e8c2eae6142", size = 2000730, upload-time = "2025-10-24T07:19:14.869Z" }, + { url = "https://files.pythonhosted.org/packages/53/a5/457ffb4f3f20469956c2d4c4842a7675e884efc895b2f23d126d23e126cc/lupa-2.6-cp314-cp314t-macosx_11_0_x86_64.whl", hash = "sha256:cd852a91a4a9d4dcbb9a58100f820a75a425703ec3e3f049055f60b8533b7953", size = 1021553, upload-time = "2025-10-24T07:19:17.123Z" }, + { url = "https://files.pythonhosted.org/packages/51/6b/36bb5a5d0960f2a5c7c700e0819abb76fd9bf9c1d8a66e5106416d6e9b14/lupa-2.6-cp314-cp314t-manylinux2010_i686.manylinux_2_12_i686.manylinux_2_28_i686.whl", hash = "sha256:0334753be028358922415ca97a64a3048e4ed155413fc4eaf87dd0a7e2752983", size = 1133275, upload-time = "2025-10-24T07:19:20.51Z" }, + { url = "https://files.pythonhosted.org/packages/19/86/202ff4429f663013f37d2229f6176ca9f83678a50257d70f61a0a97281bf/lupa-2.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:661d895cd38c87658a34780fac54a690ec036ead743e41b74c3fb81a9e65a6aa", size = 1038441, upload-time = "2025-10-24T07:19:22.509Z" }, + { url = "https://files.pythonhosted.org/packages/a7/42/d8125f8e420714e5b52e9c08d88b5329dfb02dcca731b4f21faaee6cc5b5/lupa-2.6-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6aa58454ccc13878cc177c62529a2056be734da16369e451987ff92784994ca7", size = 2058324, upload-time = "2025-10-24T07:19:24.979Z" }, + { url = "https://files.pythonhosted.org/packages/2b/2c/47bf8b84059876e877a339717ddb595a4a7b0e8740bacae78ba527562e1c/lupa-2.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1425017264e470c98022bba8cff5bd46d054a827f5df6b80274f9cc71dafd24f", size = 1060250, upload-time = "2025-10-24T07:19:27.262Z" }, + { url = "https://files.pythonhosted.org/packages/c2/06/d88add2b6406ca1bdec99d11a429222837ca6d03bea42ca75afa169a78cb/lupa-2.6-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:224af0532d216e3105f0a127410f12320f7c5f1aa0300bdf9646b8d9afb0048c", size = 1151126, upload-time = "2025-10-24T07:19:29.522Z" }, + { url = "https://files.pythonhosted.org/packages/b4/a0/89e6a024c3b4485b89ef86881c9d55e097e7cb0bdb74efb746f2fa6a9a76/lupa-2.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9abb98d5a8fd27c8285302e82199f0e56e463066f88f619d6594a450bf269d80", size = 2153693, upload-time = "2025-10-24T07:19:31.379Z" }, + { url = "https://files.pythonhosted.org/packages/b6/36/a0f007dc58fc1bbf51fb85dcc82fcb1f21b8c4261361de7dab0e3d8521ef/lupa-2.6-cp314-cp314t-win32.whl", hash = "sha256:1849efeba7a8f6fb8aa2c13790bee988fd242ae404bd459509640eeea3d1e291", size = 1590104, upload-time = "2025-10-24T07:19:33.514Z" }, + { url = "https://files.pythonhosted.org/packages/7d/5e/db903ce9cf82c48d6b91bf6d63ae4c8d0d17958939a4e04ba6b9f38b8643/lupa-2.6-cp314-cp314t-win_amd64.whl", hash = "sha256:fc1498d1a4fc028bc521c26d0fad4ca00ed63b952e32fb95949bda76a04bad52", size = 1913818, upload-time = "2025-10-24T07:19:36.039Z" }, +] + [[package]] name = "lxml" version = "6.0.2" @@ -3405,6 +3772,31 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/af/33/ee4519fa02ed11a94aef9559552f3b17bb863f2ecfe1a35dc7f548cde231/matplotlib_inline-0.2.1-py3-none-any.whl", hash = "sha256:d56ce5156ba6085e00a9d54fead6ed29a9c47e215cd1bba2e976ef39f5710a76", size = 9516, upload-time = "2025-10-23T09:00:20.675Z" }, ] +[[package]] +name = "mcp" +version = "1.26.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "httpx" }, + { name = "httpx-sse" }, + { name = "jsonschema" }, + { name = "pydantic" }, + { name = "pydantic-settings" }, + { name = "pyjwt", extra = ["crypto"] }, + { name = "python-multipart" }, + { name = "pywin32", marker = "sys_platform == 'win32'" }, + { name = "sse-starlette" }, + { name = "starlette" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, + { name = "uvicorn", marker = "sys_platform != 'emscripten'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fc/6d/62e76bbb8144d6ed86e202b5edd8a4cb631e7c8130f3f4893c3f90262b10/mcp-1.26.0.tar.gz", hash = "sha256:db6e2ef491eecc1a0d93711a76f28dec2e05999f93afd48795da1c1137142c66", size = 608005, upload-time = "2026-01-24T19:40:32.468Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/d9/eaa1f80170d2b7c5ba23f3b59f766f3a0bb41155fbc32a69adfa1adaaef9/mcp-1.26.0-py3-none-any.whl", hash = "sha256:904a21c33c25aa98ddbeb47273033c435e595bbacfdb177f4bd87f6dceebe1ca", size = 233615, upload-time = "2026-01-24T19:40:30.652Z" }, +] + [[package]] name = "mdit-py-plugins" version = "0.5.0" @@ -4695,6 +5087,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b5/df/c306f7375d42bafb379934c2df4c2fa3964656c8c782bac75ee10c102818/openai-2.15.0-py3-none-any.whl", hash = "sha256:6ae23b932cd7230f7244e52954daa6602716d6b9bf235401a107af731baea6c3", size = 1067879, upload-time = "2026-01-09T22:10:06.446Z" }, ] +[[package]] +name = "openapi-pydantic" +version = "0.5.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/02/2e/58d83848dd1a79cb92ed8e63f6ba901ca282c5f09d04af9423ec26c56fd7/openapi_pydantic-0.5.1.tar.gz", hash = "sha256:ff6835af6bde7a459fb93eb93bb92b8749b754fc6e51b2f1590a19dc3005ee0d", size = 60892, upload-time = "2025-01-08T19:29:27.083Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/cf/03675d8bd8ecbf4445504d8071adab19f5f993676795708e36402ab38263/openapi_pydantic-0.5.1-py3-none-any.whl", hash = "sha256:a3a09ef4586f5bd760a8df7f43028b60cafb6d9f61de2acba9574766255ab146", size = 96381, upload-time = "2025-01-08T19:29:25.275Z" }, +] + [[package]] name = "opencv-python" version = "4.11.0.86" @@ -5051,6 +5455,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/16/32/f8e3c85d1d5250232a5d3477a2a28cc291968ff175caeadaf3cc19ce0e4a/parso-0.8.5-py2.py3-none-any.whl", hash = "sha256:646204b5ee239c396d040b90f9e272e9a8017c630092bf59980beb62fd033887", size = 106668, upload-time = "2025-08-23T15:15:25.663Z" }, ] +[[package]] +name = "pathable" +version = "0.4.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/67/93/8f2c2075b180c12c1e9f6a09d1a985bc2036906b13dff1d8917e395f2048/pathable-0.4.4.tar.gz", hash = "sha256:6905a3cd17804edfac7875b5f6c9142a218c7caef78693c2dbbbfbac186d88b2", size = 8124, upload-time = "2025-01-10T18:43:13.247Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7d/eb/b6260b31b1a96386c0a880edebe26f89669098acea8e0318bff6adb378fd/pathable-0.4.4-py3-none-any.whl", hash = "sha256:5ae9e94793b6ef5a4cbe0a7ce9dbbefc1eec38df253763fd0aeeacf2762dbbc2", size = 9592, upload-time = "2025-01-10T18:43:11.88Z" }, +] + [[package]] name = "pathspec" version = "1.0.3" @@ -5060,6 +5473,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/32/2b/121e912bd60eebd623f873fd090de0e84f322972ab25a7f9044c056804ed/pathspec-1.0.3-py3-none-any.whl", hash = "sha256:e80767021c1cc524aa3fb14bedda9c34406591343cc42797b386ce7b9354fb6c", size = 55021, upload-time = "2026-01-09T15:46:44.652Z" }, ] +[[package]] +name = "pathvalidate" +version = "3.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fa/2a/52a8da6fe965dea6192eb716b357558e103aea0a1e9a8352ad575a8406ca/pathvalidate-3.3.1.tar.gz", hash = "sha256:b18c07212bfead624345bb8e1d6141cdcf15a39736994ea0b94035ad2b1ba177", size = 63262, upload-time = "2025-06-15T09:07:20.736Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/70/875f4a23bfc4731703a5835487d0d2fb999031bd415e7d17c0ae615c18b7/pathvalidate-3.3.1-py3-none-any.whl", hash = "sha256:5263baab691f8e1af96092fa5137ee17df5bdfbd6cff1fcac4d6ef4bc2e1735f", size = 24305, upload-time = "2025-06-15T09:07:19.117Z" }, +] + [[package]] name = "pexpect" version = "4.9.0" @@ -5264,6 +5686,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5d/19/fd3ef348460c80af7bb4669ea7926651d1f95c23ff2df18b9d24bab4f3fa/pre_commit-4.5.1-py2.py3-none-any.whl", hash = "sha256:3b3afd891e97337708c1674210f8eba659b52a38ea5f822ff142d10786221f77", size = 226437, upload-time = "2025-12-16T21:14:32.409Z" }, ] +[[package]] +name = "prometheus-client" +version = "0.24.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f0/58/a794d23feb6b00fc0c72787d7e87d872a6730dd9ed7c7b3e954637d8f280/prometheus_client-0.24.1.tar.gz", hash = "sha256:7e0ced7fbbd40f7b84962d5d2ab6f17ef88a72504dcf7c0b40737b43b2a461f9", size = 85616, upload-time = "2026-01-14T15:26:26.965Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/74/c3/24a2f845e3917201628ecaba4f18bab4d18a337834c1df2a159ee9d22a42/prometheus_client-0.24.1-py3-none-any.whl", hash = "sha256:150db128af71a5c2482b36e588fc8a6b95e498750da4b17065947c16070f4055", size = 64057, upload-time = "2026-01-14T15:26:24.42Z" }, +] + [[package]] name = "prompt-toolkit" version = "3.0.52" @@ -5451,6 +5882,47 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842, upload-time = "2024-07-21T12:58:20.04Z" }, ] +[[package]] +name = "py-key-value-aio" +version = "0.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "beartype" }, + { name = "py-key-value-shared" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/93/ce/3136b771dddf5ac905cc193b461eb67967cf3979688c6696e1f2cdcde7ea/py_key_value_aio-0.3.0.tar.gz", hash = "sha256:858e852fcf6d696d231266da66042d3355a7f9871650415feef9fca7a6cd4155", size = 50801, upload-time = "2025-11-17T16:50:04.711Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/99/10/72f6f213b8f0bce36eff21fda0a13271834e9eeff7f9609b01afdc253c79/py_key_value_aio-0.3.0-py3-none-any.whl", hash = "sha256:1c781915766078bfd608daa769fefb97e65d1d73746a3dfb640460e322071b64", size = 96342, upload-time = "2025-11-17T16:50:03.801Z" }, +] + +[package.optional-dependencies] +disk = [ + { name = "diskcache" }, + { name = "pathvalidate" }, +] +keyring = [ + { name = "keyring" }, +] +memory = [ + { name = "cachetools" }, +] +redis = [ + { name = "redis" }, +] + +[[package]] +name = "py-key-value-shared" +version = "0.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "beartype" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7b/e4/1971dfc4620a3a15b4579fe99e024f5edd6e0967a71154771a059daff4db/py_key_value_shared-0.3.0.tar.gz", hash = "sha256:8fdd786cf96c3e900102945f92aa1473138ebe960ef49da1c833790160c28a4b", size = 11666, upload-time = "2025-11-17T16:50:06.849Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/51/e4/b8b0a03ece72f47dce2307d36e1c34725b7223d209fc679315ffe6a4e2c3/py_key_value_shared-0.3.0-py3-none-any.whl", hash = "sha256:5b0efba7ebca08bb158b1e93afc2f07d30b8f40c2fc12ce24a4c0d84f42f9298", size = 19560, upload-time = "2025-11-17T16:50:05.954Z" }, +] + [[package]] name = "pyasn1" version = "0.6.1" @@ -5712,6 +6184,11 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d", size = 463580, upload-time = "2025-11-26T15:11:44.605Z" }, ] +[package.optional-dependencies] +email = [ + { name = "email-validator" }, +] + [[package]] name = "pydantic-core" version = "2.41.5" @@ -5856,6 +6333,29 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2c/86/e74c978800131c657fc5145f2c1c63e0cea01a49b6216f729cf77a2e1edf/pydash-8.0.5-py3-none-any.whl", hash = "sha256:b2625f8981862e19911daa07f80ed47b315ce20d9b5eb57aaf97aaf570c3892f", size = 102077, upload-time = "2025-01-17T16:08:47.91Z" }, ] +[[package]] +name = "pydocket" +version = "0.17.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cloudpickle" }, + { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, + { name = "fakeredis", extra = ["lua"] }, + { name = "opentelemetry-api" }, + { name = "prometheus-client" }, + { name = "py-key-value-aio", extra = ["memory", "redis"] }, + { name = "python-json-logger" }, + { name = "redis" }, + { name = "rich" }, + { name = "taskgroup", marker = "python_full_version < '3.11'" }, + { name = "typer" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ec/65/eb3b4f7afac80308d74bab2a668b31f074524ff6fbc664a685c6ed7c8885/pydocket-0.17.3.tar.gz", hash = "sha256:8922b4ca5f3f428e69b7695b9b5a313bbedc3ce35c74045cadcd89f7c0e6ac2d", size = 329828, upload-time = "2026-01-27T01:08:06.514Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ea/3b/29c69e4f88f5e5ea5e90e3cf93493cafb68bf9a2f625b916cc26ab1def89/pydocket-0.17.3-py3-none-any.whl", hash = "sha256:9ef2c6e855f52a3210acff300bcbcc45773d79295e2deddcc7aef7f67b2a5ba7", size = 91626, upload-time = "2026-01-27T01:08:05.085Z" }, +] + [[package]] name = "pygments" version = "2.19.2" @@ -5865,6 +6365,20 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, ] +[[package]] +name = "pyjwt" +version = "2.10.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/46/bd74733ff231675599650d3e47f361794b22ef3e3770998dda30d3b63726/pyjwt-2.10.1.tar.gz", hash = "sha256:3cc5772eb20009233caf06e9d8a0577824723b44e6648ee0a2aedb6cf9381953", size = 87785, upload-time = "2024-11-28T03:43:29.933Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/61/ad/689f02752eeec26aed679477e80e632ef1b682313be70793d798c1d5fc8f/PyJWT-2.10.1-py3-none-any.whl", hash = "sha256:dcdd193e30abefd5debf142f9adfcdd2b58004e644f25406ffaebd50bd98dacb", size = 22997, upload-time = "2024-11-28T03:43:27.893Z" }, +] + +[package.optional-dependencies] +crypto = [ + { name = "cryptography" }, +] + [[package]] name = "pylatexenc" version = "2.10" @@ -6057,6 +6571,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/be/7a/097801205b991bc3115e8af1edb850d30aeaf0118520b016354cf5ccd3f6/pypdfium2-4.30.0-py3-none-win_arm64.whl", hash = "sha256:119b2969a6d6b1e8d55e99caaf05290294f2d0fe49c12a3f17102d01c441bd29", size = 2752118, upload-time = "2024-05-09T18:33:15.489Z" }, ] +[[package]] +name = "pyperclip" +version = "1.11.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e8/52/d87eba7cb129b81563019d1679026e7a112ef76855d6159d24754dbd2a51/pyperclip-1.11.0.tar.gz", hash = "sha256:244035963e4428530d9e3a6101a1ef97209c6825edab1567beac148ccc1db1b6", size = 12185, upload-time = "2025-09-26T14:40:37.245Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/80/fc9d01d5ed37ba4c42ca2b55b4339ae6e200b456be3a1aaddf4a9fa99b8c/pyperclip-1.11.0-py3-none-any.whl", hash = "sha256:299403e9ff44581cb9ba2ffeed69c7aa96a008622ad0c46cb575ca75b5b84273", size = 11063, upload-time = "2025-09-26T14:40:36.069Z" }, +] + [[package]] name = "pypika" version = "0.48.9" @@ -6224,6 +6747,24 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/34/bd/b0d440685fbcafee462bed793a74aea88541887c4c30556a55ac64914b8d/python_gitlab-6.5.0-py3-none-any.whl", hash = "sha256:494e1e8e5edd15286eaf7c286f3a06652688f1ee20a49e2a0218ddc5cc475e32", size = 144419, upload-time = "2025-10-17T21:40:01.233Z" }, ] +[[package]] +name = "python-json-logger" +version = "4.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/29/bf/eca6a3d43db1dae7070f70e160ab20b807627ba953663ba07928cdd3dc58/python_json_logger-4.0.0.tar.gz", hash = "sha256:f58e68eb46e1faed27e0f574a55a0455eecd7b8a5b88b85a784519ba3cff047f", size = 17683, upload-time = "2025-10-06T04:15:18.984Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/51/e5/fecf13f06e5e5f67e8837d777d1bc43fac0ed2b77a676804df5c34744727/python_json_logger-4.0.0-py3-none-any.whl", hash = "sha256:af09c9daf6a813aa4cc7180395f50f2a9e5fa056034c9953aec92e381c5ba1e2", size = 15548, upload-time = "2025-10-06T04:15:17.553Z" }, +] + +[[package]] +name = "python-multipart" +version = "0.0.22" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/01/979e98d542a70714b0cb2b6728ed0b7c46792b695e3eaec3e20711271ca3/python_multipart-0.0.22.tar.gz", hash = "sha256:7340bef99a7e0032613f56dc36027b959fd3b30a787ed62d310e951f7c3a3a58", size = 37612, upload-time = "2026-01-25T10:15:56.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1b/d0/397f9626e711ff749a95d96b7af99b9c566a9bb5129b8e4c10fc4d100304/python_multipart-0.0.22-py3-none-any.whl", hash = "sha256:2b2cd894c83d21bf49d702499531c7bafd057d730c201782048f7945d82de155", size = 24579, upload-time = "2026-01-25T10:15:54.811Z" }, +] + [[package]] name = "python-pptx" version = "1.0.2" @@ -6303,6 +6844,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c0/d2/21af5c535501a7233e734b8af901574572da66fcc254cb35d0609c9080dd/pywin32-311-cp314-cp314-win_arm64.whl", hash = "sha256:a508e2d9025764a8270f93111a970e1d0fbfc33f4153b388bb649b7eec4f9b42", size = 8932540, upload-time = "2025-07-14T20:13:36.379Z" }, ] +[[package]] +name = "pywin32-ctypes" +version = "0.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/85/9f/01a1a99704853cb63f253eea009390c88e7131c67e66a0a02099a8c917cb/pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755", size = 29471, upload-time = "2024-08-14T10:15:34.626Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/3d/8161f7711c017e01ac9f008dfddd9410dff3674334c233bde66e7ba65bbf/pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8", size = 30756, upload-time = "2024-08-14T10:15:33.187Z" }, +] + [[package]] name = "pyyaml" version = "6.0.3" @@ -6474,18 +7024,30 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d2/f5/d93dfbb4f96acefe3a978ab2d0eab74d96c0388870f8d275792317c4857c/rapidocr-3.5.0-py3-none-any.whl", hash = "sha256:c0e361ffd0a26d2f27827361b0c563a87ee311e62b8c56ad52472c0fc6f1d78a", size = 15063134, upload-time = "2026-01-06T14:37:28.117Z" }, ] +[[package]] +name = "redis" +version = "7.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "async-timeout", marker = "python_full_version < '3.11.3'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/43/c8/983d5c6579a411d8a99bc5823cc5712768859b5ce2c8afe1a65b37832c81/redis-7.1.0.tar.gz", hash = "sha256:b1cc3cfa5a2cb9c2ab3ba700864fb0ad75617b41f01352ce5779dabf6d5f9c3c", size = 4796669, upload-time = "2025-11-19T15:54:39.961Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/89/f0/8956f8a86b20d7bb9d6ac0187cf4cd54d8065bc9a1a09eb8011d4d326596/redis-7.1.0-py3-none-any.whl", hash = "sha256:23c52b208f92b56103e17c5d06bdc1a6c2c0b3106583985a76a18f83b265de2b", size = 354159, upload-time = "2025-11-19T15:54:38.064Z" }, +] + [[package]] name = "referencing" -version = "0.37.0" +version = "0.36.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "attrs" }, { name = "rpds-py" }, { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036, upload-time = "2025-10-13T15:30:48.871Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2f/db/98b5c277be99dd18bfd91dd04e1b759cad18d1a338188c936e92f921c7e2/referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa", size = 74744, upload-time = "2025-01-25T08:48:16.138Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766, upload-time = "2025-10-13T15:30:47.625Z" }, + { url = "https://files.pythonhosted.org/packages/c1/b1/3baf80dc6d2b7bc27a95a67752d0208e410351e3feb4eb78de5f77454d8d/referencing-0.36.2-py3-none-any.whl", hash = "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0", size = 26775, upload-time = "2025-01-25T08:48:14.241Z" }, ] [[package]] @@ -6648,6 +7210,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/25/7a/b0178788f8dc6cafce37a212c99565fa1fe7872c70c6c9c1e1a372d9d88f/rich-14.2.0-py3-none-any.whl", hash = "sha256:76bc51fe2e57d2b1be1f96c524b890b816e334ab4c1e45888799bfaab0021edd", size = 243393, upload-time = "2025-10-09T14:16:51.245Z" }, ] +[[package]] +name = "rich-rst" +version = "1.3.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "docutils" }, + { name = "rich" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bc/6d/a506aaa4a9eaa945ed8ab2b7347859f53593864289853c5d6d62b77246e0/rich_rst-1.3.2.tar.gz", hash = "sha256:a1196fdddf1e364b02ec68a05e8ff8f6914fee10fbca2e6b6735f166bb0da8d4", size = 14936, upload-time = "2025-10-14T16:49:45.332Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/13/2f/b4530fbf948867702d0a3f27de4a6aab1d156f406d72852ab902c4d04de9/rich_rst-1.3.2-py3-none-any.whl", hash = "sha256:a99b4907cbe118cf9d18b0b44de272efa61f15117c61e39ebdc431baf5df722a", size = 12567, upload-time = "2025-10-14T16:49:42.953Z" }, +] + [[package]] name = "rouge-score" version = "0.1.2" @@ -7141,6 +7716,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/56/a5/df8f46ef7da168f1bc52cd86e09a9de5c6f19cc1da04454d51b7d4f43408/scipy-1.17.0-cp314-cp314t-win_arm64.whl", hash = "sha256:031121914e295d9791319a1875444d55079885bbae5bdc9c5e0f2ee5f09d34ff", size = 25246266, upload-time = "2026-01-10T21:30:45.923Z" }, ] +[[package]] +name = "secretstorage" +version = "3.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cryptography", marker = "sys_platform != 'darwin'" }, + { name = "jeepney", marker = "sys_platform != 'darwin'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1c/03/e834bcd866f2f8a49a85eaff47340affa3bfa391ee9912a952a1faa68c7b/secretstorage-3.5.0.tar.gz", hash = "sha256:f04b8e4689cbce351744d5537bf6b1329c6fc68f91fa666f60a380edddcd11be", size = 19884, upload-time = "2025-11-23T19:02:53.191Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/46/f5af3402b579fd5e11573ce652019a67074317e18c1935cc0b4ba9b35552/secretstorage-3.5.0-py3-none-any.whl", hash = "sha256:0ce65888c0725fcb2c5bc0fdb8e5438eece02c523557ea40ce0703c266248137", size = 15554, upload-time = "2025-11-23T19:02:51.545Z" }, +] + [[package]] name = "semchunk" version = "2.2.2" @@ -7372,6 +7960,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/bf/e1/3ccb13c643399d22289c6a9786c1a91e3dcbb68bce4beb44926ac2c557bf/sqlalchemy-2.0.45-py3-none-any.whl", hash = "sha256:5225a288e4c8cc2308dbdd874edad6e7d0fd38eac1e9e5f23503425c8eee20d0", size = 1936672, upload-time = "2025-12-09T21:54:52.608Z" }, ] +[[package]] +name = "sse-starlette" +version = "3.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "starlette" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8b/8d/00d280c03ffd39aaee0e86ec81e2d3b9253036a0f93f51d10503adef0e65/sse_starlette-3.2.0.tar.gz", hash = "sha256:8127594edfb51abe44eac9c49e59b0b01f1039d0c7461c6fd91d4e03b70da422", size = 27253, upload-time = "2026-01-17T13:11:05.62Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/96/7f/832f015020844a8b8f7a9cbc103dd76ba8e3875004c41e08440ea3a2b41a/sse_starlette-3.2.0-py3-none-any.whl", hash = "sha256:5876954bd51920fc2cd51baee47a080eb88a37b5b784e615abb0b283f801cdbf", size = 12763, upload-time = "2026-01-17T13:11:03.775Z" }, +] + [[package]] name = "stack-data" version = "0.6.3" @@ -7443,6 +8044,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/9c/95/53b2b69ceae99b0705ffd68c5ed87cfda7f187ffbe7d88372bc7d2441caf/tarski_tmp-0.8.5-py3-none-any.whl", hash = "sha256:58d6af787cdfec8691cc24f21d84ed2078efc294924eed8e0d9828b584be0b47", size = 213291, upload-time = "2025-10-03T17:52:57.226Z" }, ] +[[package]] +name = "taskgroup" +version = "0.2.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f0/8d/e218e0160cc1b692e6e0e5ba34e8865dbb171efeb5fc9a704544b3020605/taskgroup-0.2.2.tar.gz", hash = "sha256:078483ac3e78f2e3f973e2edbf6941374fbea81b9c5d0a96f51d297717f4752d", size = 11504, upload-time = "2025-01-03T09:24:13.761Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/b1/74babcc824a57904e919f3af16d86c08b524c0691504baf038ef2d7f655c/taskgroup-0.2.2-py2.py3-none-any.whl", hash = "sha256:e2c53121609f4ae97303e9ea1524304b4de6faf9eb2c9280c7f87976479a52fb", size = 14237, upload-time = "2025-01-03T09:24:11.41Z" }, +] + [[package]] name = "tenacity" version = "9.1.2" @@ -7614,23 +8228,26 @@ wheels = [ [[package]] name = "toolguard" -version = "0.2.4" +version = "0.2.6" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "datamodel-code-generator" }, + { name = "fastmcp" }, { name = "langchain-core" }, { name = "litellm" }, + { name = "loguru" }, { name = "markdown" }, { name = "mellea" }, { name = "pydantic" }, { name = "pyright" }, { name = "pytest" }, + { name = "pytest-asyncio" }, { name = "pytest-json-report" }, { name = "smolagents" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a7/52/46e5117bd01267aed8f6fea36f200b248c559b6cfc789aeff89fde3bd3d2/toolguard-0.2.4.tar.gz", hash = "sha256:d2677477b732825c4453fc69dcaffcf00320182ecbf474998030b8fde79d8d01", size = 56164, upload-time = "2026-01-22T16:36:07.134Z" } +sdist = { url = "https://files.pythonhosted.org/packages/61/ec/a32b1f223ea0f1a9e3d354f627e184e1772249ab9713438584c26f8b4c7d/toolguard-0.2.6.tar.gz", hash = "sha256:962edc3d20c464e0fd27b4646b5bd6d25b07ebc7a068a2d08010576ce1483ee6", size = 58781, upload-time = "2026-01-29T09:29:05.167Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/47/94/5ba7c1c587bd2b83ecf096aaee002f1c5246a4f7677fed5a06df54044cf6/toolguard-0.2.4-py3-none-any.whl", hash = "sha256:00fbe66da50c2859ca383139ad263a2af9b0bfa2900e507816f73c0f58462cdf", size = 86729, upload-time = "2026-01-22T16:36:05.772Z" }, + { url = "https://files.pythonhosted.org/packages/77/aa/21f4732222429d2ee593baab0907135bcc776fa334ae38a63603d256eecc/toolguard-0.2.6-py3-none-any.whl", hash = "sha256:927cc22a4652885e1af032013108d20948ba48e2a8a142aaaec857883ffb8d21", size = 90388, upload-time = "2026-01-29T09:29:03.525Z" }, ] [[package]] @@ -8656,6 +9273,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6f/28/258ebab549c2bf3e64d2b0217b973467394a9cea8c42f70418ca2c5d0d2e/websockets-16.0-py3-none-any.whl", hash = "sha256:1637db62fad1dc833276dded54215f2c7fa46912301a24bd94d45d46a011ceec", size = 171598, upload-time = "2026-01-10T09:23:45.395Z" }, ] +[[package]] +name = "win32-setctime" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b3/8f/705086c9d734d3b663af0e9bb3d4de6578d08f46b1b101c2442fd9aecaa2/win32_setctime-1.2.0.tar.gz", hash = "sha256:ae1fdf948f5640aae05c511ade119313fb6a30d7eabe25fef9764dca5873c4c0", size = 4867, upload-time = "2024-12-07T15:28:28.314Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e1/07/c6fe3ad3e685340704d314d765b7912993bcb8dc198f0e7a89382d37974b/win32_setctime-1.2.0-py3-none-any.whl", hash = "sha256:95d644c4e708aba81dc3704a116d8cbc974d70b3bdb8be1d150e36be6e9d1390", size = 4083, upload-time = "2024-12-07T15:28:26.465Z" }, +] + [[package]] name = "wrapt" version = "2.0.1" From 56761e9d8efb86d7b0def199f435c39c9d7ee94d Mon Sep 17 00:00:00 2001 From: DAVID BOAZ Date: Thu, 29 Jan 2026 12:29:56 +0200 Subject: [PATCH 36/42] DCO Remediation Commit for DAVID BOAZ I, DAVID BOAZ , hereby add my Signed-off-by to this commit: e02f4bb8c885d587d21ecbd263ecf0a1af022f1e Signed-off-by: DAVID BOAZ --- altk/pre_tool/toolguard/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/altk/pre_tool/toolguard/README.md b/altk/pre_tool/toolguard/README.md index ffa4702..e24f558 100644 --- a/altk/pre_tool/toolguard/README.md +++ b/altk/pre_tool/toolguard/README.md @@ -1,5 +1,5 @@ # ToolGuards for Enforcing Agentic Policy Adherence -An agent lifecycle solution for enforcing business policy adherence in agentic workflows. Enabling this component has demonstrated up to a **20‑point improvement** in end‑to‑end agent accuracy when invoking tools. This work is described in [EMNLP 2025 Towards Enforcing Company Policy Adherence in Agentic Workflows](https://arxiv.org/pdf/2507.16459), and is published in [this GitHub library](https://github.com/AgentToolkit/toolguard). +Enforces business policy adherence in agentic workflows. Enabling this component has demonstrated up to a **20‑point improvement** in end‑to‑end agent accuracy when invoking tools. This work is described in [EMNLP 2025 Towards Enforcing Company Policy Adherence in Agentic Workflows](https://arxiv.org/pdf/2507.16459), and is published in [this GitHub library](https://github.com/AgentToolkit/toolguard). ## Table of Contents - [Overview](#overview) From 0f443faa973875b267db9d2f04b9976729c96e8f Mon Sep 17 00:00:00 2001 From: DAVID BOAZ Date: Thu, 29 Jan 2026 15:28:27 +0200 Subject: [PATCH 37/42] toolguard version 0.2.7 Signed-off-by: DAVID BOAZ --- pyproject.toml | 2 +- uv.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 7b820cf..f8f3562 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -129,7 +129,7 @@ spotlight = [ ] toolguard = [ - "toolguard>=0.2.6" + "toolguard>=0.2.7", ] refraction = [ diff --git a/uv.lock b/uv.lock index d619e60..c273822 100644 --- a/uv.lock +++ b/uv.lock @@ -192,7 +192,7 @@ requires-dist = [ { name = "sentence-transformers", marker = "extra == 'routing'", specifier = ">=5.0.0" }, { name = "smolagents", specifier = ">=1.21.3" }, { name = "tomli", specifier = "==2.2.1" }, - { name = "toolguard", marker = "extra == 'toolguard'", specifier = ">=0.2.6" }, + { name = "toolguard", marker = "extra == 'toolguard'", specifier = ">=0.2.7" }, { name = "torch", marker = "platform_machine == 'x86_64' and sys_platform == 'darwin' and extra == 'spotlight'", specifier = "==2.2.2" }, { name = "torch", marker = "(platform_machine != 'x86_64' and extra == 'spotlight') or (sys_platform != 'darwin' and extra == 'spotlight')", specifier = ">=2.5.1" }, { name = "transformers", marker = "extra == 'spotlight'", specifier = ">=4.53.3" }, @@ -8228,7 +8228,7 @@ wheels = [ [[package]] name = "toolguard" -version = "0.2.6" +version = "0.2.7" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "datamodel-code-generator" }, @@ -8245,9 +8245,9 @@ dependencies = [ { name = "pytest-json-report" }, { name = "smolagents" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/61/ec/a32b1f223ea0f1a9e3d354f627e184e1772249ab9713438584c26f8b4c7d/toolguard-0.2.6.tar.gz", hash = "sha256:962edc3d20c464e0fd27b4646b5bd6d25b07ebc7a068a2d08010576ce1483ee6", size = 58781, upload-time = "2026-01-29T09:29:05.167Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6e/63/1eed08e9b37eb4b9eac3ab72627625c3dc1f440e9476357b1825e6fc9f87/toolguard-0.2.7.tar.gz", hash = "sha256:ab36801c713bb0856fc9f1dba603a432d74dad246183ecb2f77ce2c9d35111fb", size = 58801, upload-time = "2026-01-29T12:56:52.85Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/77/aa/21f4732222429d2ee593baab0907135bcc776fa334ae38a63603d256eecc/toolguard-0.2.6-py3-none-any.whl", hash = "sha256:927cc22a4652885e1af032013108d20948ba48e2a8a142aaaec857883ffb8d21", size = 90388, upload-time = "2026-01-29T09:29:03.525Z" }, + { url = "https://files.pythonhosted.org/packages/2c/b1/bac4b88c633ed1b92592be3cda8ec69e8788b04c7782939bc61f22e0f21a/toolguard-0.2.7-py3-none-any.whl", hash = "sha256:2dae78eb700ecb8949c3a68a5a4fb0405285a74a9f8fee0749682e8e9103a0d0", size = 90392, upload-time = "2026-01-29T12:56:51.285Z" }, ] [[package]] From b57a5eaf48bf55c23b45cf710c902923f4a1ad6f Mon Sep 17 00:00:00 2001 From: DAVID BOAZ Date: Thu, 29 Jan 2026 18:32:48 +0200 Subject: [PATCH 38/42] add documentation Signed-off-by: DAVID BOAZ --- altk/pre_tool/toolguard/tool_converter.py | 40 +++++++++++++++++++ .../toolguard/toolguard_code_component.py | 23 +---------- .../toolguard/toolguard_spec_component.py | 32 ++++++++++++++- 3 files changed, 73 insertions(+), 22 deletions(-) create mode 100644 altk/pre_tool/toolguard/tool_converter.py diff --git a/altk/pre_tool/toolguard/tool_converter.py b/altk/pre_tool/toolguard/tool_converter.py new file mode 100644 index 0000000..b08da71 --- /dev/null +++ b/altk/pre_tool/toolguard/tool_converter.py @@ -0,0 +1,40 @@ +"""Utility module for converting various tool formats to ToolGuard TOOLS type.""" + +from typing import Callable, List, cast +from langchain_core.tools import BaseTool +from toolguard.buildtime import TOOLS +from toolguard.buildtime.buildtime import OpenAPI +from toolguard.extra.langchain_to_oas import langchain_tools_to_openapi + + +def to_tools(tools: List[Callable] | List[BaseTool] | str) -> TOOLS: + """Convert various tool formats to the TOOLS type expected by ToolGuard. + + Args: + tools: Either a string path to an OpenAPI specification file, + a list of callable functions, or a list of BaseTool instances. + + Returns: + TOOLS: The converted tools in the format expected by ToolGuard. + This can be either a list of callables or an OpenAPI specification dict. + + Raises: + ValueError: If the tools input is invalid or cannot be converted. + This includes invalid OpenAPI spec files, mixed tool types in lists, + or unsupported input types. + """ + if isinstance(tools, str): + try: + return OpenAPI.load_from(tools).model_dump() + except Exception as e: + raise ValueError(f"Invalid OpenAPI spec file: {e}") from e + + elif isinstance(tools, list): + if all(isinstance(tool, Callable) for tool in tools): + return cast(List[Callable], tools) + elif all(isinstance(tool, BaseTool) for tool in tools): + return langchain_tools_to_openapi(cast(List[BaseTool], tools)) + else: + raise ValueError("Invalid tools list") + + raise ValueError("Invalid tools input") diff --git a/altk/pre_tool/toolguard/toolguard_code_component.py b/altk/pre_tool/toolguard/toolguard_code_component.py index 3f18e35..76c4c43 100644 --- a/altk/pre_tool/toolguard/toolguard_code_component.py +++ b/altk/pre_tool/toolguard/toolguard_code_component.py @@ -10,13 +10,11 @@ ToolGuardSpec, generate_guards_code, ) -from toolguard.buildtime.buildtime import OpenAPI -from toolguard.buildtime.data_types import TOOLS -from toolguard.extra.langchain_to_oas import langchain_tools_to_openapi from toolguard.runtime import IToolInvoker, PolicyViolationException, load_toolguards from altk.core.toolkit import AgentPhase, ComponentBase, ComponentConfig, ComponentInput from altk.pre_tool.toolguard.llm_client import TG_LLMClient +from altk.pre_tool.toolguard.tool_converter import to_tools logger = logging.getLogger(__name__) @@ -139,30 +137,13 @@ async def _abuild( config = cast(ToolGuardCodeComponentConfig, self.config) llm = TG_LLMClient(config.llm_client) return await generate_guards_code( - tools=self._to_tools(data.tools), + tools=to_tools(data.tools), tool_specs=data.toolguard_specs, work_dir=data.out_dir, llm=llm, app_name=data.app_name, ) - def _to_tools(self, tools) -> TOOLS: - if isinstance(tools, str): - try: - return OpenAPI.load_from(tools).model_dump() - except Exception as e: - raise ValueError(f"Invalid OpenAPI spec file: {e}") from e - - elif isinstance(tools, list): - if all(isinstance(tool, Callable) for tool in tools): - return cast(List[Callable], tools) - elif all(isinstance(tool, BaseTool) for tool in tools): - return langchain_tools_to_openapi(tools) - else: - raise ValueError("Invalid tools list") - - raise ValueError("Invalid tools input") - def _run(self, data: ToolGuardCodeRunInput) -> ToolGuardCodeRunOutput: raise NotImplementedError("Please use the _arun() function in an async context") diff --git a/altk/pre_tool/toolguard/toolguard_spec_component.py b/altk/pre_tool/toolguard/toolguard_spec_component.py index 86dd214..028fad2 100644 --- a/altk/pre_tool/toolguard/toolguard_spec_component.py +++ b/altk/pre_tool/toolguard/toolguard_spec_component.py @@ -6,6 +6,7 @@ from pydantic import Field from altk.pre_tool.toolguard.llm_client import TG_LLMClient +from altk.pre_tool.toolguard.tool_converter import to_tools from altk.core.toolkit import AgentPhase, ComponentBase, ComponentConfig, ComponentInput from toolguard.buildtime import ToolGuardSpec, generate_guard_specs @@ -14,10 +15,28 @@ class ToolGuardSpecComponentConfig(ComponentConfig): + """Configuration for ToolGuardSpecComponent. + + This component generates ToolGuard specifications from policy documents and tool definitions. + It requires an LLM client configuration for analyzing policies and generating guard specifications. + + Inherits all configuration from ComponentConfig, including llm_client settings. + """ + pass class ToolGuardSpecBuildInput(ComponentInput): + """Input data for building ToolGuard specifications. + + Attributes: + policy_text: Text of the policy document file that defines the constraints + and rules for tool usage. + tools: List of callable functions, BaseTool instances, or a string path + to OpenAPI tool specification that will be analyzed against the policy. + out_dir: Output directory path where generated guard specifications will be saved. + """ + policy_text: str = Field(description="Text of the policy document file") tools: List[Callable] | List[BaseTool] | str out_dir: str | Path @@ -27,6 +46,17 @@ class ToolGuardSpecBuildInput(ComponentInput): class ToolGuardSpecComponent(ComponentBase): + """Component for generating ToolGuard specifications from policy documents. + + This component analyzes policy documents and tool definitions to generate + ToolGuard specifications that define constraints and validation rules. + It operates in the buildtime phase to create specifications that can later + be used to generate executable guard code. + + The component uses an LLM to interpret policy text and map it to specific + tool parameters and constraints, producing structured ToolGuardSpec objects. + """ + def __init__(self, config: ToolGuardSpecComponentConfig): super().__init__(config=config) @@ -45,7 +75,7 @@ async def _abuild(self, data: ToolGuardSpecBuildInput) -> ToolGuardSpecs: llm = TG_LLMClient(config.llm_client) return await generate_guard_specs( policy_text=data.policy_text, - tools=data.tools, + tools=to_tools(data.tools), work_dir=data.out_dir, llm=llm, short=True, From 3571771a7d4b5b1e5348f1370ceb111f1d7fa77e Mon Sep 17 00:00:00 2001 From: DAVID BOAZ Date: Sun, 8 Feb 2026 14:46:06 +0200 Subject: [PATCH 39/42] toolguard comply with py 3.10 --- altk/pre_tool/toolguard/llm_client.py | 6 ++++-- pyproject.toml | 2 +- uv.lock | 8 ++++---- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/altk/pre_tool/toolguard/llm_client.py b/altk/pre_tool/toolguard/llm_client.py index ba11e8e..875ebe1 100644 --- a/altk/pre_tool/toolguard/llm_client.py +++ b/altk/pre_tool/toolguard/llm_client.py @@ -3,6 +3,8 @@ from altk.core.llm.types import GenerationArgs from altk.core.llm import ValidatingLLMClient, LLMClient +MAX_TOKENS = 16000 # min of claude4, oss, gpt 4o, ... + class TG_LLMClient(LanguageModelBase): def __init__(self, llm_client: Union[LLMClient, ValidatingLLMClient]): @@ -14,9 +16,9 @@ async def generate(self, messages: list[dict]) -> str: return await llm_client.generate_async( prompt=messages, schema=str, - generation_args=GenerationArgs(max_tokens=10000), + generation_args=GenerationArgs(max_tokens=MAX_TOKENS), ) return await self.llm_client.generate_async( - prompt=messages, generation_args=GenerationArgs(max_tokens=10000) + prompt=messages, generation_args=GenerationArgs(max_tokens=MAX_TOKENS) ) # type: ignore diff --git a/pyproject.toml b/pyproject.toml index f8f3562..6734141 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -129,7 +129,7 @@ spotlight = [ ] toolguard = [ - "toolguard>=0.2.7", + "toolguard>=0.2.8", ] refraction = [ diff --git a/uv.lock b/uv.lock index c273822..23e5e78 100644 --- a/uv.lock +++ b/uv.lock @@ -192,7 +192,7 @@ requires-dist = [ { name = "sentence-transformers", marker = "extra == 'routing'", specifier = ">=5.0.0" }, { name = "smolagents", specifier = ">=1.21.3" }, { name = "tomli", specifier = "==2.2.1" }, - { name = "toolguard", marker = "extra == 'toolguard'", specifier = ">=0.2.7" }, + { name = "toolguard", marker = "extra == 'toolguard'", specifier = ">=0.2.8" }, { name = "torch", marker = "platform_machine == 'x86_64' and sys_platform == 'darwin' and extra == 'spotlight'", specifier = "==2.2.2" }, { name = "torch", marker = "(platform_machine != 'x86_64' and extra == 'spotlight') or (sys_platform != 'darwin' and extra == 'spotlight')", specifier = ">=2.5.1" }, { name = "transformers", marker = "extra == 'spotlight'", specifier = ">=4.53.3" }, @@ -8228,7 +8228,7 @@ wheels = [ [[package]] name = "toolguard" -version = "0.2.7" +version = "0.2.8" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "datamodel-code-generator" }, @@ -8245,9 +8245,9 @@ dependencies = [ { name = "pytest-json-report" }, { name = "smolagents" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/6e/63/1eed08e9b37eb4b9eac3ab72627625c3dc1f440e9476357b1825e6fc9f87/toolguard-0.2.7.tar.gz", hash = "sha256:ab36801c713bb0856fc9f1dba603a432d74dad246183ecb2f77ce2c9d35111fb", size = 58801, upload-time = "2026-01-29T12:56:52.85Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8f/2f/8aec8abf58b51605f293f4963e0aad521e2f5843e20638fda61fdb576737/toolguard-0.2.8.tar.gz", hash = "sha256:92645431cfe2fb3eee0e5fda13f8df72af4803e4de977921cc8942d8e4a776e5", size = 61771, upload-time = "2026-02-08T10:56:32.943Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/b1/bac4b88c633ed1b92592be3cda8ec69e8788b04c7782939bc61f22e0f21a/toolguard-0.2.7-py3-none-any.whl", hash = "sha256:2dae78eb700ecb8949c3a68a5a4fb0405285a74a9f8fee0749682e8e9103a0d0", size = 90392, upload-time = "2026-01-29T12:56:51.285Z" }, + { url = "https://files.pythonhosted.org/packages/51/37/9ac16f178583827ae2c05589566c067e87f995c957097c1f33573751e341/toolguard-0.2.8-py3-none-any.whl", hash = "sha256:1f62a3b9cf456ffe4cd14a2f7f2480dd6c5580a7fb059b3f564e63ebfb7eea2c", size = 94468, upload-time = "2026-02-08T10:56:31.507Z" }, ] [[package]] From faefb99fbd097e0290d2d7cd32bd30c401642874 Mon Sep 17 00:00:00 2001 From: DAVID BOAZ Date: Sun, 8 Feb 2026 15:00:23 +0200 Subject: [PATCH 40/42] DCO Remediation Commit for DAVID BOAZ I, DAVID BOAZ , hereby add my Signed-off-by to this commit: 3571771a7d4b5b1e5348f1370ceb111f1d7fa77e Signed-off-by: DAVID BOAZ --- altk/pre_tool/toolguard/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/altk/pre_tool/toolguard/README.md b/altk/pre_tool/toolguard/README.md index e24f558..97c6cda 100644 --- a/altk/pre_tool/toolguard/README.md +++ b/altk/pre_tool/toolguard/README.md @@ -24,7 +24,7 @@ uv pip install "agent-lifecycle-toolkit[toolguard]" ``` ## ToolGuardSpecComponent -This component gets a set of tools and a policy document and generates multiple ToolGuard specifications, known as `ToolGuardSpec`s. Each specification is attached to a tool, and it declares a precondition that must apply before invoking the tool. The specification has a `name`, `description`, list of `references` to the original policy document, a set of declarative `compliance_examples`, describing test cases that the toolGuard should allow the tool invocation, and `violation_examples`, where the toolGuard should raise an exception. +This component gets a set of tools and a policy document and generates multiple ToolGuard specifications, known as `ToolGuardSpec`s. Each specification is attached to a tool, and it declares a precondition that must apply before invoking the tool. The specification has a `name`, `description`, list of `references` to the original policy document, a set of declarative `compliance_examples`, describing test cases that the toolGuard should allow the tool invocation, and `violation_examples`, where the toolGuard should raise a PolicyViolationException. This component supports only a `build` phase. The generated specifications are returned as output, and are also saved to a specified file system directory. The specifications are aimed to be used as input into our next component - the `ToolGuardCodeComponent` described below. From 5ff660886ff9f1b7961745ecf69401d549accb56 Mon Sep 17 00:00:00 2001 From: DAVID BOAZ Date: Mon, 9 Feb 2026 14:53:31 +0200 Subject: [PATCH 41/42] update llm Signed-off-by: DAVID BOAZ --- altk/pre_tool/toolguard/llm_client.py | 2 +- pyproject.toml | 2 +- uv.lock | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/altk/pre_tool/toolguard/llm_client.py b/altk/pre_tool/toolguard/llm_client.py index 875ebe1..16e19fc 100644 --- a/altk/pre_tool/toolguard/llm_client.py +++ b/altk/pre_tool/toolguard/llm_client.py @@ -1,5 +1,5 @@ from typing import Union, cast -from toolguard.buildtime.llm.tg_litellm import LanguageModelBase +from toolguard.buildtime.llm import LanguageModelBase from altk.core.llm.types import GenerationArgs from altk.core.llm import ValidatingLLMClient, LLMClient diff --git a/pyproject.toml b/pyproject.toml index 6734141..0f1c710 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -129,7 +129,7 @@ spotlight = [ ] toolguard = [ - "toolguard>=0.2.8", + "toolguard>=0.2.9", ] refraction = [ diff --git a/uv.lock b/uv.lock index 4762aea..1f3d027 100644 --- a/uv.lock +++ b/uv.lock @@ -181,7 +181,7 @@ requires-dist = [ { name = "sentence-transformers", marker = "extra == 'routing'", specifier = ">=5.0.0" }, { name = "smolagents", specifier = ">=1.21.3" }, { name = "tomli", specifier = "==2.2.1" }, - { name = "toolguard", marker = "extra == 'toolguard'", specifier = ">=0.2.8" }, + { name = "toolguard", marker = "extra == 'toolguard'", specifier = ">=0.2.9" }, { name = "torch", marker = "platform_machine == 'x86_64' and sys_platform == 'darwin' and extra == 'spotlight'", specifier = "==2.2.2" }, { name = "torch", marker = "(platform_machine != 'x86_64' and extra == 'spotlight') or (sys_platform != 'darwin' and extra == 'spotlight')", specifier = ">=2.5.1" }, { name = "transformers", marker = "extra == 'spotlight'", specifier = ">=4.53.3" }, @@ -8290,7 +8290,7 @@ wheels = [ [[package]] name = "toolguard" -version = "0.2.8" +version = "0.2.9" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "datamodel-code-generator" }, @@ -8307,9 +8307,9 @@ dependencies = [ { name = "pytest-json-report" }, { name = "smolagents" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8f/2f/8aec8abf58b51605f293f4963e0aad521e2f5843e20638fda61fdb576737/toolguard-0.2.8.tar.gz", hash = "sha256:92645431cfe2fb3eee0e5fda13f8df72af4803e4de977921cc8942d8e4a776e5", size = 61771, upload-time = "2026-02-08T10:56:32.943Z" } +sdist = { url = "https://files.pythonhosted.org/packages/07/9a/b2e98bed9c006bccf98dd4aa0aad4c645b0f9ac796c265b7220217972061/toolguard-0.2.9.tar.gz", hash = "sha256:e1881d1b2e3daeb1e3dc99ee27703a2511be534ea8750ceb2185db7113570fe7", size = 61855, upload-time = "2026-02-09T12:45:59.15Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/51/37/9ac16f178583827ae2c05589566c067e87f995c957097c1f33573751e341/toolguard-0.2.8-py3-none-any.whl", hash = "sha256:1f62a3b9cf456ffe4cd14a2f7f2480dd6c5580a7fb059b3f564e63ebfb7eea2c", size = 94468, upload-time = "2026-02-08T10:56:31.507Z" }, + { url = "https://files.pythonhosted.org/packages/c3/1e/d07cc1744fcab69ba724157bfa5201f540e5b7b931ac5bdf233691259a43/toolguard-0.2.9-py3-none-any.whl", hash = "sha256:51968c067ba5cc02b1adbd2f21b1ecef8ce7b99dff679d1d17059caf56d82c2e", size = 95051, upload-time = "2026-02-09T12:45:58.01Z" }, ] [[package]] From f5ecf53dad1d09d129b16d32a4a35a59c2462b7d Mon Sep 17 00:00:00 2001 From: DAVID BOAZ Date: Thu, 12 Feb 2026 14:43:43 +0200 Subject: [PATCH 42/42] toolguard long Signed-off-by: DAVID BOAZ --- altk/pre_tool/toolguard/toolguard_spec_component.py | 1 - tests/pre_tool/toolguard/inputs/tool_functions.py | 4 +--- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/altk/pre_tool/toolguard/toolguard_spec_component.py b/altk/pre_tool/toolguard/toolguard_spec_component.py index 028fad2..68f886b 100644 --- a/altk/pre_tool/toolguard/toolguard_spec_component.py +++ b/altk/pre_tool/toolguard/toolguard_spec_component.py @@ -78,5 +78,4 @@ async def _abuild(self, data: ToolGuardSpecBuildInput) -> ToolGuardSpecs: tools=to_tools(data.tools), work_dir=data.out_dir, llm=llm, - short=True, ) diff --git a/tests/pre_tool/toolguard/inputs/tool_functions.py b/tests/pre_tool/toolguard/inputs/tool_functions.py index eb4714b..f1addf8 100644 --- a/tests/pre_tool/toolguard/inputs/tool_functions.py +++ b/tests/pre_tool/toolguard/inputs/tool_functions.py @@ -3,13 +3,11 @@ def divide_tool(g: float, h: float) -> float: Args: g (float): The dividend. - h (float): The divisor (must not be zero). + h (float): The divisor. Returns: float: The result of g divided by h. - Raises: - ZeroDivisionError: If h is zero. """ return g / h